[net-diag] simplify processing of TLVs, allow extended TLV (#8368)

This commit updates `NetworkDiagnostic` class's generation and
processing of TLVs. When appending list based TLVs (IPv6 Address List
TLV or Child Table TLV) depending on the number of entries we use
regular or extended TLV. When processing the message and reading the
TLVs, this commit updates the code to handle extended TLVs as well.

This commit changes the behavior of `GetNextDiagTlv()`. The
`otNetworkDiagTlv` data structure defines limited-size arrays for
IPv6 address list and child table. When processing a received message
if the number of entries is larger than the array size, the first set
entries (as many as fits in the array) are read and the rest are
ignored. This changes the existing behavior which returned a
`kErrorPrase` error in such a case.
This commit is contained in:
Abtin Keshavarzian
2022-11-07 09:41:46 +01:00
committed by GitHub
parent 727002e9fe
commit 4e52ffb54e
3 changed files with 211 additions and 324 deletions
+157 -120
View File
@@ -188,11 +188,8 @@ exit:
Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
{
Error error = kErrorNone;
Ip6AddressListTlv tlv;
uint8_t count = 0;
tlv.Init();
Error error = kErrorNone;
uint16_t count = 0;
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
@@ -200,8 +197,22 @@ Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
count++;
}
tlv.SetLength(count * sizeof(Ip6::Address));
SuccessOrExit(error = aMessage.Append(tlv));
if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength)
{
Tlv tlv;
tlv.SetType(NetworkDiagnosticTlv::kIp6AddressList);
tlv.SetLength(static_cast<uint8_t>(count * Ip6::Address::kSize));
SuccessOrExit(error = aMessage.Append(tlv));
}
else
{
ExtendedTlv extTlv;
extTlv.SetType(NetworkDiagnosticTlv::kIp6AddressList);
extTlv.SetLength(count * Ip6::Address::kSize);
SuccessOrExit(error = aMessage.Append(extTlv));
}
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
@@ -209,50 +220,48 @@ Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
}
exit:
return error;
}
#if OPENTHREAD_FTD
Error NetworkDiagnostic::AppendChildTable(Message &aMessage)
{
Error error = kErrorNone;
uint16_t count = 0;
uint8_t timeout = 0;
ChildTableTlv tlv;
ChildTableEntry entry;
Error error = kErrorNone;
uint16_t count = 0;
tlv.Init();
count = Min(Get<ChildTable>().GetNumChildren(Child::kInStateValid), kMaxChildEntries);
count = Get<ChildTable>().GetNumChildren(Child::kInStateValid);
// The length of the Child Table TLV may exceed the outgoing link's MTU (1280B).
// As a workaround we limit the number of entries in the Child Table TLV,
// also to avoid using extended TLV format. The issue is processed by the
// Thread Group (SPEC-894).
if (count > (Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry)))
if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength)
{
count = Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry);
Tlv tlv;
tlv.SetType(NetworkDiagnosticTlv::kChildTable);
tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
SuccessOrExit(error = aMessage.Append(tlv));
}
else
{
ExtendedTlv extTlv;
tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
SuccessOrExit(error = aMessage.Append(tlv));
extTlv.SetType(NetworkDiagnosticTlv::kChildTable);
extTlv.SetLength(count * sizeof(ChildTableEntry));
SuccessOrExit(error = aMessage.Append(extTlv));
}
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
VerifyOrExit(count--);
uint8_t timeout = 0;
ChildTableEntry entry;
timeout = 0;
VerifyOrExit(count--);
while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
{
timeout++;
}
entry.SetReserved(0);
entry.Clear();
entry.SetTimeout(timeout + 4);
entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16()));
entry.SetMode(child.GetDeviceMode());
@@ -260,7 +269,6 @@ Error NetworkDiagnostic::AppendChildTable(Message &aMessage)
}
exit:
return error;
}
#endif // OPENTHREAD_FTD
@@ -297,8 +305,6 @@ Error NetworkDiagnostic::FillRequestedTlvs(const Message & aRequest,
{
SuccessOrExit(error = aRequest.Read(offset, type));
LogInfo("Type %d", type);
switch (type)
{
case NetworkDiagnosticTlv::kExtMacAddress:
@@ -362,10 +368,8 @@ Error NetworkDiagnostic::FillRequestedTlvs(const Message & aRequest,
}
case NetworkDiagnosticTlv::kIp6AddressList:
{
SuccessOrExit(error = AppendIp6AddressList(aResponse));
break;
}
case NetworkDiagnosticTlv::kMacCounters:
{
@@ -619,36 +623,47 @@ static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNet
aMacCounters.mIfOutDiscards = aMacCountersTlv.GetIfOutDiscards();
}
static inline void ParseChildEntry(const ChildTableEntry &aChildTableTlvEntry, otNetworkDiagChildEntry &aChildEntry)
Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo)
{
aChildEntry.mTimeout = aChildTableTlvEntry.GetTimeout();
aChildEntry.mChildId = aChildTableTlvEntry.GetChildId();
aChildTableTlvEntry.GetMode().Get(aChildEntry.mMode);
}
Error error = kErrorNotFound;
uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator;
Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage,
Iterator & aIterator,
otNetworkDiagTlv & aNetworkDiagTlv)
{
Error error = kErrorNone;
uint16_t offset = aMessage.GetOffset() + aIterator;
NetworkDiagnosticTlv tlv;
while (true)
while (offset < aMessage.GetLength())
{
uint16_t tlvTotalLength;
bool skipTlv = false;
uint16_t valueOffset;
uint16_t tlvLength;
union
{
Tlv tlv;
ExtendedTlv extTlv;
};
VerifyOrExit(aMessage.Read(offset, tlv) == kErrorNone, error = kErrorNotFound);
SuccessOrExit(error = aMessage.Read(offset, tlv));
if (tlv.IsExtended())
{
SuccessOrExit(error = aMessage.Read(offset, extTlv));
valueOffset = offset + sizeof(ExtendedTlv);
tlvLength = extTlv.GetLength();
}
else
{
valueOffset = offset + sizeof(Tlv);
tlvLength = tlv.GetLength();
}
VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse);
switch (tlv.GetType())
{
case NetworkDiagnosticTlv::kExtMacAddress:
SuccessOrExit(
error = Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aNetworkDiagTlv.mData.mExtAddress)));
SuccessOrExit(error =
Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress)));
break;
case NetworkDiagnosticTlv::kAddress16:
SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aNetworkDiagTlv.mData.mAddr16));
SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aTlvInfo.mData.mAddr16));
break;
case NetworkDiagnosticTlv::kMode:
@@ -656,137 +671,159 @@ Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage,
uint8_t mode;
SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
Mle::DeviceMode(mode).Get(aNetworkDiagTlv.mData.mMode);
Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode);
break;
}
case NetworkDiagnosticTlv::kTimeout:
SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mTimeout));
SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aTlvInfo.mData.mTimeout));
break;
case NetworkDiagnosticTlv::kConnectivity:
{
ConnectivityTlv connectivity;
ConnectivityTlv connectivityTlv;
SuccessOrExit(error = aMessage.Read(offset, connectivity));
VerifyOrExit(connectivity.IsValid(), error = kErrorParse);
connectivity.GetConnectivity(aNetworkDiagTlv.mData.mConnectivity);
VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, connectivityTlv));
VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity);
break;
}
case NetworkDiagnosticTlv::kRoute:
{
RouteTlv route;
RouteTlv routeTlv;
uint16_t bytesToRead = static_cast<uint16_t>(Min(tlv.GetSize(), static_cast<uint32_t>(sizeof(routeTlv))));
tlvTotalLength = sizeof(tlv) + tlv.GetLength();
VerifyOrExit(tlvTotalLength <= sizeof(route), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &route, tlvTotalLength));
VerifyOrExit(route.IsValid(), error = kErrorParse);
ParseRoute(route, aNetworkDiagTlv.mData.mRoute);
VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
ParseRoute(routeTlv, aTlvInfo.mData.mRoute);
break;
}
case NetworkDiagnosticTlv::kLeaderData:
{
LeaderDataTlv leaderData;
LeaderDataTlv leaderDataTlv;
SuccessOrExit(error = aMessage.Read(offset, leaderData));
VerifyOrExit(leaderData.IsValid(), error = kErrorParse);
leaderData.Get(AsCoreType(&aNetworkDiagTlv.mData.mLeaderData));
VerifyOrExit(!tlv.IsExtended(), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv));
VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse);
leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData));
break;
}
case NetworkDiagnosticTlv::kNetworkData:
{
NetworkDataTlv networkData;
static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize,
"NetworkData array in `otNetworkDiagTlv` is too small");
tlvTotalLength = sizeof(tlv) + tlv.GetLength();
VerifyOrExit(tlvTotalLength <= sizeof(networkData), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &networkData, tlvTotalLength));
VerifyOrExit(networkData.IsValid(), error = kErrorParse);
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mNetworkData.m8) >= networkData.GetLength(), error = kErrorParse);
memcpy(aNetworkDiagTlv.mData.mNetworkData.m8, networkData.GetNetworkData(), networkData.GetLength());
aNetworkDiagTlv.mData.mNetworkData.mCount = networkData.GetLength();
VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(tlvLength);
aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength);
break;
}
case NetworkDiagnosticTlv::kIp6AddressList:
{
Ip6AddressListTlv &ip6AddrList = As<Ip6AddressListTlv>(tlv);
uint16_t addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList);
Ip6::Address *addrEntry = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]);
uint8_t & addrCount = aTlvInfo.mData.mIp6AddrList.mCount;
VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse);
// `TlvInfo` has a fixed array for IPv6 addresses. If there
// are more addresses in the message, we read and return as
// many as can fit in array and ignore the rest.
addrCount = 0;
while ((tlvLength > 0) && (addrCount < addrListLength))
{
SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry));
addrCount++;
addrEntry++;
valueOffset += Ip6::Address::kSize;
tlvLength -= Ip6::Address::kSize;
}
VerifyOrExit(ip6AddrList.IsValid(), error = kErrorParse);
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mIp6AddrList.mList) >= ip6AddrList.GetLength(),
error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset + sizeof(ip6AddrList), aNetworkDiagTlv.mData.mIp6AddrList.mList,
ip6AddrList.GetLength()));
aNetworkDiagTlv.mData.mIp6AddrList.mCount = ip6AddrList.GetLength() / OT_IP6_ADDRESS_SIZE;
break;
}
case NetworkDiagnosticTlv::kMacCounters:
{
MacCountersTlv macCounters;
MacCountersTlv macCountersTlv;
SuccessOrExit(error = aMessage.Read(offset, macCounters));
VerifyOrExit(macCounters.IsValid(), error = kErrorParse);
ParseMacCounters(macCounters, aNetworkDiagTlv.mData.mMacCounters);
SuccessOrExit(error = aMessage.Read(offset, macCountersTlv));
VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse);
ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters);
break;
}
case NetworkDiagnosticTlv::kBatteryLevel:
SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aNetworkDiagTlv.mData.mBatteryLevel));
SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
break;
case NetworkDiagnosticTlv::kSupplyVoltage:
SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aNetworkDiagTlv.mData.mSupplyVoltage));
SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aTlvInfo.mData.mSupplyVoltage));
break;
case NetworkDiagnosticTlv::kChildTable:
{
ChildTableTlv &childTable = As<ChildTableTlv>(tlv);
uint16_t childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable);
ChildInfo *childInfo = &aTlvInfo.mData.mChildTable.mTable[0];
uint8_t & childCount = aTlvInfo.mData.mChildTable.mCount;
VerifyOrExit(childTable.IsValid(), error = kErrorParse);
VerifyOrExit(childTable.GetNumEntries() <= GetArrayLength(aNetworkDiagTlv.mData.mChildTable.mTable),
error = kErrorParse);
VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i)
// `TlvInfo` 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;
while ((tlvLength > 0) && (childCount < childInfoLength))
{
ChildTableEntry childEntry;
VerifyOrExit(childTable.ReadEntry(childEntry, aMessage, offset, i) == kErrorNone, error = kErrorParse);
ParseChildEntry(childEntry, aNetworkDiagTlv.mData.mChildTable.mTable[i]);
ChildTableEntry entry;
SuccessOrExit(error = aMessage.Read(valueOffset, entry));
childInfo->mTimeout = entry.GetTimeout();
childInfo->mChildId = entry.GetChildId();
entry.GetMode().Get(childInfo->mMode);
childCount++;
childInfo++;
tlvLength -= sizeof(ChildTableEntry);
valueOffset += sizeof(ChildTableEntry);
}
aNetworkDiagTlv.mData.mChildTable.mCount = childTable.GetNumEntries();
break;
}
case NetworkDiagnosticTlv::kChannelPages:
{
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mChannelPages.m8) >= tlv.GetLength(), error = kErrorParse);
SuccessOrExit(
error = aMessage.Read(offset + sizeof(tlv), aNetworkDiagTlv.mData.mChannelPages.m8, tlv.GetLength()));
aNetworkDiagTlv.mData.mChannelPages.mCount = tlv.GetLength();
aTlvInfo.mData.mChannelPages.mCount =
static_cast<uint8_t>(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount);
break;
}
case NetworkDiagnosticTlv::kMaxChildTimeout:
SuccessOrExit(error =
Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mMaxChildTimeout));
SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
break;
default:
// Ignore unrecognized Network Diagnostic TLV silently and
// continue to top of the `while(true)` loop.
offset += tlv.GetSize();
continue;
// Skip unrecognized TLVs.
skipTlv = true;
break;
}
// Exit if a TLV is recognized and parsed successfully.
aNetworkDiagTlv.mType = tlv.GetType();
aIterator = static_cast<uint16_t>(offset - aMessage.GetOffset() + tlv.GetSize());
ExitNow();
offset += tlv.GetSize();
if (!skipTlv)
{
// Exit if a TLV is recognized and parsed successfully.
aTlvInfo.mType = tlv.GetType();
aIterator = offset;
ExitNow();
}
}
exit:
+16 -2
View File
@@ -76,6 +76,18 @@ public:
static constexpr Iterator kIteratorInit = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; ///< Initializer for Iterator.
/**
* This type represents parsed information from a Network Diagnostic TLV.
*
*/
typedef otNetworkDiagTlv TlvInfo;
/**
* This structure represents parsed information from Network Diagnostic Child Table entry.
*
*/
typedef otNetworkDiagChildEntry ChildInfo;
/**
* This constructor initializes the object.
*
@@ -116,16 +128,18 @@ public:
* @param[in] aMessage A message.
* @param[in,out] aIterator The Network Diagnostic iterator. To get the first TLV set it to
* `kIteratorInit`.
* @param[out] aNetworkDiagTlv A reference to a Network Diagnostic TLV to output the next TLV.
* @param[out] aTlvInfo A reference to a `TlvInfo` to output the next TLV data.
*
* @retval kErrorNone Successfully found the next Network Diagnostic TLV.
* @retval kErrorNotFound No subsequent Network Diagnostic TLV exists in the message.
* @retval kErrorParse Parsing the next Network Diagnostic failed.
*
*/
static Error GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, otNetworkDiagTlv &aNetworkDiagTlv);
static Error GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo);
private:
static constexpr uint16_t kMaxChildEntries = 398;
enum CommandType : uint8_t
{
kDiagnosticGet,
+38 -202
View File
@@ -38,6 +38,7 @@
#include <openthread/thread.h>
#include "common/clearable.hpp"
#include "common/encoding.hpp"
#include "common/message.hpp"
#include "common/tlvs.hpp"
@@ -126,6 +127,18 @@ typedef UintTlvInfo<NetworkDiagnosticTlv::kMode, uint8_t> ModeTlv;
*/
typedef UintTlvInfo<NetworkDiagnosticTlv::kTimeout, uint32_t> TimeoutTlv;
/**
* This class defines Network Data TLV constants and types.
*
*/
typedef TlvInfo<NetworkDiagnosticTlv::kNetworkData> NetworkDataTlv;
/**
* This class defines IPv6 Address List TLV constants and types.
*
*/
typedef TlvInfo<NetworkDiagnosticTlv::kIp6AddressList> Ip6AddressListTlv;
/**
* This class defines Battery Level TLV constants and types.
*
@@ -138,6 +151,12 @@ typedef UintTlvInfo<NetworkDiagnosticTlv::kBatteryLevel, uint8_t> BatteryLevelTl
*/
typedef UintTlvInfo<NetworkDiagnosticTlv::kSupplyVoltage, uint16_t> SupplyVoltageTlv;
/**
* This class defines Child Table TLV constants and types.
*
*/
typedef TlvInfo<NetworkDiagnosticTlv::kChildTable> ChildTableTlv;
/**
* This class defines Max Child Timeout TLV constants and types.
*
@@ -229,95 +248,6 @@ public:
}
} OT_TOOL_PACKED_END;
/**
* This class implements Network Data TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class NetworkDataTlv : public NetworkDiagnosticTlv, public TlvInfo<NetworkDiagnosticTlv::kNetworkData>
{
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 Ip6AddressListTlv : public NetworkDiagnosticTlv, public TlvInfo<NetworkDiagnosticTlv::kIp6AddressList>
{
public:
/**
* This method initializes the TLV.
*
*/
void Init(void)
{
SetType(kIp6AddressList);
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 !IsExtended() && (GetLength() % sizeof(Ip6::Address) == 0); }
/**
* This method returns a pointer to the IPv6 address entry.
*
* @param[in] aIndex The index into the IPv6 address list.
*
* @returns A reference to the IPv6 address.
*
*/
const Ip6::Address &GetIp6Address(uint8_t aIndex) const
{
return *reinterpret_cast<const Ip6::Address *>(GetValue() + (aIndex * sizeof(Ip6::Address)));
}
} OT_TOOL_PACKED_END;
/**
* This class implements Mac Counters TLV generation and parsing.
*
@@ -514,26 +444,16 @@ private:
*
*/
OT_TOOL_PACKED_BEGIN
class ChildTableEntry
class ChildTableEntry : public Clearable<ChildTableEntry>
{
public:
/**
* Default constructor.
*
*/
ChildTableEntry(void)
: mTimeoutRsvChildId(0)
, mMode(0)
{
}
/**
* This method returns the Timeout value.
*
* @returns The Timeout value.
*
*/
uint8_t GetTimeout(void) const { return (HostSwap16(mTimeoutRsvChildId) & kTimeoutMask) >> kTimeoutOffset; }
uint8_t GetTimeout(void) const { return (GetTimeoutChildId() & kTimeoutMask) >> kTimeoutOffset; }
/**
* This method sets the Timeout value.
@@ -543,8 +463,7 @@ public:
*/
void SetTimeout(uint8_t aTimeout)
{
mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kTimeoutMask) |
((aTimeout << kTimeoutOffset) & kTimeoutMask));
SetTimeoutChildId((GetTimeoutChildId() & ~kTimeoutMask) | ((aTimeout << kTimeoutOffset) & kTimeoutMask));
}
/**
@@ -553,7 +472,7 @@ public:
* @returns The Child ID value.
*
*/
uint16_t GetChildId(void) const { return HostSwap16(mTimeoutRsvChildId) & kChildIdMask; }
uint16_t GetChildId(void) const { return (GetTimeoutChildId() & kChildIdMask) >> kChildIdOffset; }
/**
* This method sets the Child ID value.
@@ -563,7 +482,7 @@ public:
*/
void SetChildId(uint16_t aChildId)
{
mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kChildIdMask) | (aChildId & kChildIdMask));
SetTimeoutChildId((GetTimeoutChildId() & ~kChildIdMask) | ((aChildId << kChildIdOffset) & kChildIdMask));
}
/**
@@ -582,108 +501,25 @@ public:
*/
void SetMode(Mle::DeviceMode aMode) { mMode = aMode.Get(); }
/**
* This method returns the Reserved value.
*
* @returns The Reserved value.
*
*/
uint8_t GetReserved(void) const { return (HostSwap16(mTimeoutRsvChildId) & kReservedMask) >> kReservedOffset; }
/**
* This method sets the Reserved value.
*
* @param[in] aReserved The Reserved value.
*
*/
void SetReserved(uint8_t aReserved)
{
mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kReservedMask) |
((aReserved << kReservedOffset) & kReservedMask));
}
private:
static constexpr uint8_t kTimeoutOffset = 11;
static constexpr uint8_t kReservedOffset = 9;
static constexpr uint16_t kTimeoutMask = 0xf800;
static constexpr uint16_t kReservedMask = 0x0600;
static constexpr uint16_t kChildIdMask = 0x1ff;
// 1 0
// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Timeout |RSV| Child ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
uint16_t mTimeoutRsvChildId;
static constexpr uint8_t kTimeoutOffset = 11;
static constexpr uint8_t kChildIdOffset = 0;
static constexpr uint16_t kTimeoutMask = 0x1f << kTimeoutOffset;
static constexpr uint16_t kChildIdMask = 0x1ff << kChildIdOffset;
uint16_t GetTimeoutChildId(void) const { return HostSwap16(mTimeoutChildId); }
void SetTimeoutChildId(uint16_t aTimeoutChildIf) { mTimeoutChildId = HostSwap16(aTimeoutChildIf); }
uint16_t mTimeoutChildId;
uint8_t mMode;
} OT_TOOL_PACKED_END;
/**
* This class implements Child Table TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class ChildTableTlv : public NetworkDiagnosticTlv, public TlvInfo<NetworkDiagnosticTlv::kChildTable>
{
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(ChildTableEntry)) == 0; }
/**
* This method returns the number of Child Table entries.
*
* @returns The number of Child Table entries.
*
*/
uint8_t GetNumEntries(void) const { return GetLength() / sizeof(ChildTableEntry); }
/**
* This method returns the Child Table entry at @p aIndex.
*
* @param[in] aIndex The index into the Child Table list.
*
* @returns A reference to the Child Table entry.
*
*/
ChildTableEntry &GetEntry(uint16_t aIndex)
{
return *reinterpret_cast<ChildTableEntry *>(GetValue() + (aIndex * sizeof(ChildTableEntry)));
}
/**
* This method reads the Child Table entry at @p aIndex.
*
* @param[out] aEntry A reference to a ChildTableEntry.
* @param[in] aMessage A reference to the message.
* @param[in] aOffset The offset of the ChildTableTLV in aMessage.
* @param[in] aIndex The index into the Child Table list.
*
* @retval kErrorNotFound No such entry is found.
* @retval kErrorNone Successfully read the entry.
*
*/
Error ReadEntry(ChildTableEntry &aEntry, const Message &aMessage, uint16_t aOffset, uint8_t aIndex) const
{
return ((aIndex < GetNumEntries()) &&
(aMessage.Read(aOffset + sizeof(ChildTableTlv) + (aIndex * sizeof(ChildTableEntry)), aEntry) ==
kErrorNone))
? kErrorNone
: kErrorInvalidArgs;
}
} OT_TOOL_PACKED_END;
/**
* This class implements Channel Pages TLV generation and parsing.
*