[mle] introduce RouteTlv::Data to represent parsed Route TLV (#13098)

This commit introduces a new model for handling `RouteTlv` by
adding the `RouteTlv::Data` and `RouteTlv::Data::Entry` classes.
Previously, `RouteTlv` directly represented the packed on-wire
format, which made it difficult to work with, especially when
supporting different configurations such as
`OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE`.

The new `RouteTlv::Data` class decouples the on-wire serialization
from the in-memory representation, providing a cleaner API for
parsing the TLV from a `Message` and accessing its entries and
their properties (Router ID, Route Cost, and Link Qualities).

This change improves code clarity and maintainability by providing
a structured way to handle route information.
This commit is contained in:
Abtin Keshavarzian
2026-05-14 11:17:37 -07:00
committed by GitHub
parent c6fa686dd7
commit 181405efc9
12 changed files with 381 additions and 315 deletions
+8 -5
View File
@@ -4252,16 +4252,19 @@ exit:
#endif
#if OPENTHREAD_FTD
Error Mle::RxMessage::ReadRouteTlv(RouteTlv &aRouteTlv) const
{
Error error;
SuccessOrExit(error = Tlv::FindTlv(*this, aRouteTlv));
VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse);
Error Mle::RxMessage::ReadRouteTlv(RouteTlv::Data &aRouteTlvData) const
{
Error error;
OffsetRange offsetRange;
SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(*this, RouteTlv::kType, offsetRange));
error = aRouteTlvData.ParseFrom(*this, offsetRange);
exit:
return error;
}
#endif
//---------------------------------------------------------------------------------------------------------------------
+4 -4
View File
@@ -1642,7 +1642,7 @@ private:
Error ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const;
#endif
#if OPENTHREAD_FTD
Error ReadRouteTlv(RouteTlv &aRouteTlv) const;
Error ReadRouteTlv(RouteTlv::Data &aRouteTlvData) const;
#endif
private:
@@ -2243,7 +2243,7 @@ private:
bool IsRouterCountBelowUpgradeThreshold(void) const;
void HandleTimeTick(void);
void DecideWhetherToUpgrade(void);
void DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv);
void DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv::Data &aRouteTlvData);
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
void SetCcmEnabled(bool aEnabled);
void SetThreadVersionCheckEnabled(bool aEnabled);
@@ -2251,7 +2251,7 @@ private:
private:
bool DetermineIfRouterRoleAllowed(void) const;
bool NeighborHasComparableConnectivity(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const;
bool NeighborHasComparableConnectivity(uint8_t aNeighborId, const RouteTlv::Data &aRouteTlvData) const;
bool mRouterEligible : 1;
bool mRouterRoleAllowed : 1;
@@ -2466,7 +2466,7 @@ private:
void HandleNetworkDataUpdateRouter(void);
void HandleDiscoveryRequest(RxInfo &aRxInfo);
void EstablishRouterLinkOnFtdChild(Router &aRouter, RxInfo &aRxInfo, uint8_t aLinkMargin);
Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo);
Error ProcessRouteTlv(const RouteTlv::Data &aRouteTlvData, RxInfo &aRxInfo);
Error ReadAndProcessRouteTlvOnFtdChild(RxInfo &aRxInfo, uint8_t aParentId);
void StopAdvertiseTrickleTimer(void);
uint32_t DetermineAdvertiseIntervalMax(void) const;
+56 -51
View File
@@ -899,7 +899,7 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType)
uint32_t mleFrameCounter;
uint8_t routerId;
uint16_t address16;
RouteTlv routeTlv;
RouteTlv::Data routeTlvData;
LeaderData leaderData;
uint8_t linkMargin;
bool shouldUpdateRoutes = false;
@@ -969,8 +969,8 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType)
SetLeaderData(leaderData);
mRouterTable.Clear();
SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv));
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlvData));
SuccessOrExit(error = ProcessRouteTlv(routeTlvData, aRxInfo));
router = mRouterTable.FindRouterById(routerId);
VerifyOrExit(router != nullptr);
@@ -1010,14 +1010,14 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType)
IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
}
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlvData))
{
case kErrorNone:
VerifyOrExit(routeTlv.IsRouterIdSet(routerId), error = kErrorParse);
VerifyOrExit(routeTlvData.IsAllocated(routerId), error = kErrorParse);
if (mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
if (mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlvData))
{
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
SuccessOrExit(error = ProcessRouteTlv(routeTlvData, aRxInfo));
router = mRouterTable.FindRouterById(routerId);
OT_ASSERT(router != nullptr);
}
@@ -1061,7 +1061,7 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType)
if (shouldUpdateRoutes)
{
mRouterTable.UpdateRoutes(routeTlv, routerId);
mRouterTable.UpdateRoutes(routeTlvData, routerId);
}
aRxInfo.mClass = RxInfo::kAuthoritativeMessage;
@@ -1093,9 +1093,9 @@ exit:
LogProcessError(aMessageType, error);
}
Error Mle::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo)
Error Mle::ProcessRouteTlv(const RouteTlv::Data &aRouteTlvData, RxInfo &aRxInfo)
{
// This method processes `aRouteTlv` read from an MLE message.
// This method processes `aRouteTlvData` read from an MLE message.
//
// During processing of Route TLV, the entries in the router table
// may shuffle. This method ensures that the `aRxInfo.mNeighbor`
@@ -1112,7 +1112,7 @@ Error Mle::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo)
neighborRloc16 = aRxInfo.mNeighbor->GetRloc16();
}
mRouterTable.UpdateRouterIdMask(aRouteTlv.GetRouterIdMask());
mRouterTable.UpdateRouterIdMask(aRouteTlvData);
if (IsAttached() && !mRouterTable.IsAllocated(RouterIdFromRloc16(GetRloc16())))
{
@@ -1141,16 +1141,16 @@ Error Mle::ReadAndProcessRouteTlvOnFtdChild(RxInfo &aRxInfo, uint8_t aParentId)
// It MUST be used only when device is acting as a child and
// for a message received from device's current parent.
Error error = kErrorNone;
RouteTlv routeTlv;
Error error = kErrorNone;
RouteTlv::Data routeTlvData;
VerifyOrExit(IsFullThreadDevice());
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlvData))
{
case kErrorNone:
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
mRouterTable.UpdateRouterOnFtdChild(routeTlv, aParentId);
SuccessOrExit(error = ProcessRouteTlv(routeTlvData, aRxInfo));
mRouterTable.UpdateRouterOnFtdChild(routeTlvData, aParentId);
mRequestRouteTlv = false;
break;
case kErrorNotFound:
@@ -1204,19 +1204,20 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
// - `aSourceAddress` is the read value from `SourceAddressTlv`.
// - `aLeaderData` is the read value from `LeaderDataTlv`.
Error error = kErrorNone;
uint8_t linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
RouteTlv routeTlv;
Router *router;
uint8_t routerId;
uint32_t delay;
Error error = kErrorNone;
uint8_t linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
RouteTlv::Data routeTlvData;
bool hasRouteTlv = false;
Router *router;
uint8_t routerId;
uint32_t delay;
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlvData))
{
case kErrorNone:
hasRouteTlv = true;
break;
case kErrorNotFound:
routeTlv.SetLength(0); // Mark that a Route TLV was not included.
break;
default:
ExitNow(error = kErrorParse);
@@ -1232,11 +1233,11 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
VerifyOrExit(linkMargin >= kPartitionMergeMinMargin, error = kErrorLinkMarginLow);
if (routeTlv.IsValid() && (mPreviousPartitionIdTimeout > 0) &&
(aLeaderData.GetPartitionId() == mPreviousPartitionId))
if (hasRouteTlv && (mPreviousPartitionIdTimeout > 0) && (aLeaderData.GetPartitionId() == mPreviousPartitionId))
{
VerifyOrExit(SerialNumber::IsGreater(routeTlv.GetRouterIdSequence(), mPreviousPartitionRouterIdSequence),
error = kErrorDrop);
VerifyOrExit(
SerialNumber::IsGreater(routeTlvData.GetRouterIdSequence(), mPreviousPartitionRouterIdSequence),
error = kErrorDrop);
}
if (IsChild() && (aRxInfo.mNeighbor == &mParent))
@@ -1244,7 +1245,8 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
ExitNow();
}
if (ComparePartitions(routeTlv.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0
if (hasRouteTlv &&
ComparePartitions(routeTlvData.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0
#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
// Allow a better partition if it also enables time sync.
&& aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
@@ -1274,7 +1276,7 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
ExitNow();
}
VerifyOrExit(IsRouterRloc16(aSourceAddress) && routeTlv.IsValid());
VerifyOrExit(IsRouterRloc16(aSourceAddress) && hasRouteTlv);
routerId = RouterIdFromRloc16(aSourceAddress);
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
@@ -1284,9 +1286,9 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Process `RouteTlv`
if (aRxInfo.IsNeighborStateValid() && mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv))
if (aRxInfo.IsNeighborStateValid() && mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlvData))
{
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
SuccessOrExit(error = ProcessRouteTlv(routeTlvData, aRxInfo));
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1305,7 +1307,7 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
ExitNow(error = kErrorDetached);
}
mRouterTable.UpdateRouterOnFtdChild(routeTlv, routerId);
mRouterTable.UpdateRouterOnFtdChild(routeTlvData, routerId);
mRoleTransitioner.DecideWhetherToUpgrade();
}
@@ -1330,7 +1332,7 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Inform `RoleTransitioner` to decide whether we need to downgrade
mRoleTransitioner.DecideWhetherToDowngrade(routerId, routeTlv);
mRoleTransitioner.DecideWhetherToDowngrade(routerId, routeTlvData);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Update routers as a router or leader.
@@ -1359,7 +1361,7 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
// unicast/multicast link request in progress
if (!router->IsStateValid() && !router->IsStateLinkRequest() && (linkMargin >= kLinkRequestMinMargin) &&
routeTlv.IsRouterIdSet(mRouterId))
routeTlvData.IsAllocated(mRouterId))
{
InitNeighbor(*router, aRxInfo);
router->SetState(Neighbor::kStateLinkRequest);
@@ -1371,7 +1373,7 @@ Error Mle::HandleAdvertisementOnFtd(RxInfo &aRxInfo, uint16_t aSourceAddress, co
router->SetLastHeard(TimerMilli::GetNow());
mRouterTable.UpdateRoutes(routeTlv, routerId);
mRouterTable.UpdateRoutes(routeTlvData, routerId);
exit:
if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress)
@@ -3724,11 +3726,11 @@ void Mle::FillConnectivityTlvValue(ConnectivityTlvValue &aTlvValue) const
aTlvValue.InitFrom(connectivity);
}
void Mle::RoleTransitioner::DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv)
void Mle::RoleTransitioner::DecideWhetherToDowngrade(uint8_t aNeighborId, const RouteTlv::Data &aRouteTlvData)
{
// Determine whether all conditions are satisfied for the router
// to downgrade after receiving info for a neighboring router
// with Router ID `aNeighborId` along with its `aRouteTlv`.
// with Router ID `aNeighborId` along with its `aRouteTlvData`.
uint8_t activeRouterCount = Get<RouterTable>().GetActiveRouterCount();
uint8_t count;
@@ -3773,7 +3775,7 @@ void Mle::RoleTransitioner::DecideWhetherToDowngrade(uint8_t aNeighborId, const
// Check that the neighbor has as good or better-quality links to
// same routers.
VerifyOrExit(NeighborHasComparableConnectivity(aNeighborId, aRouteTlv));
VerifyOrExit(NeighborHasComparableConnectivity(aNeighborId, aRouteTlvData));
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
// Check if we are eligible to be router due to being a BR.
@@ -3787,21 +3789,22 @@ exit:
return;
}
bool Mle::RoleTransitioner::NeighborHasComparableConnectivity(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const
bool Mle::RoleTransitioner::NeighborHasComparableConnectivity(uint8_t aNeighborId,
const RouteTlv::Data &aRouteTlvData) const
{
// Check whether the neighboring router with Router ID `aNeighborId`
// (along with its `aRouteTlv`) has as good or better-quality links
// to all our neighboring routers which have a two-way link quality
// of two or better.
// (along with its `aRouteTlvData`) has as good or better-quality
// links to all our neighboring routers which have a two-way link
// quality of two or better.
bool isComparable = true;
for (uint8_t routerId = 0, index = 0; routerId <= kMaxRouterId;
index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
for (uint8_t routerId = 0; routerId <= kMaxRouterId; routerId++)
{
const Router *router;
LinkQuality localLinkQuality;
LinkQuality peerLinkQuality;
const Router *router;
LinkQuality localLinkQuality;
LinkQuality peerLinkQuality;
const RouteTlv::Data::Entry *entry;
if ((routerId == Get<Mle>().mRouterId) || (routerId == aNeighborId))
{
@@ -3823,15 +3826,17 @@ bool Mle::RoleTransitioner::NeighborHasComparableConnectivity(uint8_t aNeighborI
}
// `router` is our neighbor with two-way link quality of
// at least two. Check that `aRouteTlv` has as good or
// at least two. Check that `aRouteTlvData` has as good or
// better-quality link to it as well.
if (!aRouteTlv.IsRouterIdSet(routerId))
entry = aRouteTlvData.GetEntries().FindMatching(routerId);
if (entry == nullptr)
{
ExitNow(isComparable = false);
}
peerLinkQuality = Min(aRouteTlv.GetLinkQualityIn(index), aRouteTlv.GetLinkQualityOut(index));
peerLinkQuality = Min(entry->GetLinkQualityIn(), entry->GetLinkQualityOut());
if (peerLinkQuality < localLinkQuality)
{
+70 -13
View File
@@ -44,25 +44,82 @@ namespace Mle {
//---------------------------------------------------------------------------------------------------------------------
// RouteTlv
void RouteTlv::Init(void)
Error RouteTlv::Data::ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange)
{
SetType(kRoute);
SetLength(sizeof(*this) - sizeof(Tlv));
mRouterIdMask.Clear();
ClearAllBytes(mRouteData);
}
Error error;
RouterIdMask routerIdMask;
OffsetRange offsetRange = aOffsetRange;
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
bool isEven = true;
#endif
bool RouteTlv::IsValid(void) const
{
bool isValid = false;
SuccessOrExit(error = aMessage.Read(offsetRange, routerIdMask));
offsetRange.AdvanceOffset(sizeof(routerIdMask));
VerifyOrExit(GetLength() >= sizeof(mRouterIdMask));
mIdSequence = routerIdMask.GetSequence();
VerifyOrExit(mRouterIdMask.IsValid());
isValid = (GetRouteDataEntryCount() >= mRouterIdMask.DetermineAllocatedCount());
mEntries.Clear();
for (uint8_t routerId = 0; routerId <= kMaxRouterId; routerId++)
{
Entry *entry;
if (!routerIdMask.IsAllocated(routerId))
{
continue;
}
entry = mEntries.PushBack();
// If `mEntries` is full, it indicates that there are more than
// `kMaxRouters` allocated IDs in the mask, which makes it invalid.
VerifyOrExit(entry != nullptr, error = kErrorParse);
entry->mRouterId = routerId;
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
SuccessOrExit(error = aMessage.Read<uint8_t>(offsetRange, entry->mRouteData));
offsetRange.AdvanceOffset(sizeof(uint8_t));
#else
{
EntryType value;
SuccessOrExit(error = aMessage.Read<EntryType>(offsetRange, value));
value = BigEndian::HostSwap16(value);
if (isEven)
{
entry->mRouteData = ReadBits<uint16_t, kEvenEntryMask>(value);
offsetRange.AdvanceOffset(sizeof(uint8_t));
}
else
{
entry->mRouteData = ReadBits<uint16_t, kOddEntryMask>(value);
offsetRange.AdvanceOffset(sizeof(uint16_t));
}
isEven = !isEven;
}
#endif
}
exit:
return isValid;
return error;
}
bool RouteTlv::Data::IsAllocated(uint8_t aRouterId) const { return mEntries.FindMatching(aRouterId); }
void RouteTlv::Data::DetermineRouterIdMask(RouterIdMask &aRouterIdMask) const
{
aRouterIdMask.Clear();
aRouterIdMask.SetSequence(mIdSequence);
for (const Entry &entry : mEntries)
{
aRouterIdMask.Add(entry.mRouterId);
}
}
Error RouteTlv::AppendRouteDataEntry(Message &aMessage,
+128 -137
View File
@@ -230,125 +230,142 @@ typedef UintTlvInfo<Tlv::kCslTimeout, uint32_t> CslTimeoutTlv;
typedef UintTlvInfo<Tlv::kXtalAccuracy, uint16_t> XtalAccuracyTlv;
/**
* Implements Route TLV generation and parsing.
* Defines Route TLV constants and types.
*/
OT_TOOL_PACKED_BEGIN
class RouteTlv : public Tlv, public TlvInfo<Tlv::kRoute>
class RouteTlv : public TlvInfo<Tlv::kRoute>
{
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
typedef uint8_t EntryType;
#else
typedef uint16_t EntryType;
#endif
public:
/**
* Initializes the TLV.
* Represents the parsed Route TLV data.
*/
void Init(void);
/**
* 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;
/**
* Returns the Router ID Sequence value.
*
* @returns The Router ID Sequence value.
*/
uint8_t GetRouterIdSequence(void) const { return mRouterIdMask.GetSequence(); }
/**
* Gets the Router ID Mask.
*
* @returns The Router ID Mask.
*/
const RouterIdMask &GetRouterIdMask(void) const { return mRouterIdMask; }
/**
* Gets the Router ID Mask.
*
* @returns The Router ID Mask.
*/
RouterIdMask &GetRouterIdMask(void) { return mRouterIdMask; }
/**
* Indicates whether or not a Router ID bit is set.
*
* @param[in] aRouterId The Router ID.
*
* @retval TRUE If the Router ID bit is set.
* @retval FALSE If the Router ID bit is not set.
*/
bool IsRouterIdSet(uint8_t aRouterId) const { return mRouterIdMask.IsAllocated(aRouterId); }
/**
* Indicates whether the `RouteTlv` is a singleton, i.e., only one router is allocated.
*
* @retval TRUE It is a singleton.
* @retval FALSE It is not a singleton.
*/
bool IsSingleton(void) const { return IsValid() && (mRouterIdMask.DetermineAllocatedCount() <= 1); }
/**
* Returns the number of Route Data entries in the Route TLV.
*
* @returns The Route Data Entry Count.
*/
uint8_t GetRouteDataEntryCount(void) const
class Data
{
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
return GetLength() - sizeof(mRouterIdMask);
#else
return (GetLength() - sizeof(mRouterIdMask)) * 2 / 3;
#endif
}
public:
/**
* Represents a single route entry.
*/
class Entry
{
friend class Data;
/**
* Returns the Route Cost value for a given Router index.
*
* @param[in] aRouterIndex The Router index.
*
* @returns The Route Cost value for a given Router index.
*/
uint8_t GetRouteCost(uint8_t aRouterIndex) const
{
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
return ReadBits<uint8_t, kRouteCostMask>(mRouteData[aRouterIndex]);
#else
return static_cast<uint8_t>(ReadBits<uint16_t, kRouteCostMask>(ReadEntry(aRouterIndex)));
#endif
}
public:
/**
* Gets the Router ID.
*
* @returns The Router ID.
*/
uint8_t GetRouterId(void) const { return mRouterId; }
/**
* Returns the Link Quality In value for a given Router index.
*
* @param[in] aRouterIndex The Router index.
*
* @returns The Link Quality In value for a given Router index.
*/
LinkQuality GetLinkQualityIn(uint8_t aRouterIndex) const
{
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
return static_cast<LinkQuality>(ReadBits<uint8_t, kLinkQualityInMask>(mRouteData[aRouterIndex]));
#else
return static_cast<LinkQuality>(ReadBits<uint16_t, kLinkQualityInMask>(ReadEntry(aRouterIndex)));
#endif
}
/**
* Gets the Route Cost value.
*
* @returns The Route Cost value.
*/
uint8_t GetRouteCost(void) const
{
return static_cast<uint8_t>(ReadBits<EntryType, kRouteCostMask>(mRouteData));
}
/**
* Returns the Link Quality Out value for a given Router index.
*
* @param[in] aRouterIndex The Router index.
*
* @returns The Link Quality Out value for a given Router index.
*/
LinkQuality GetLinkQualityOut(uint8_t aRouterIndex) const
{
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
return static_cast<LinkQuality>(ReadBits<uint8_t, kLinkQualityOutMask>(mRouteData[aRouterIndex]));
#else
return static_cast<LinkQuality>(ReadBits<uint16_t, kLinkQualityOutMask>(ReadEntry(aRouterIndex)));
#endif
}
/**
* Gets the Link Quality Out value.
*
* @returns The Link Quality Out value.
*/
LinkQuality GetLinkQualityOut(void) const
{
return static_cast<LinkQuality>(ReadBits<EntryType, kLinkQualityOutMask>(mRouteData));
}
/**
* Gets the Link Quality In value.
*
* @returns The Link Quality In value.
*/
LinkQuality GetLinkQualityIn(void) const
{
return static_cast<LinkQuality>(ReadBits<EntryType, kLinkQualityInMask>(mRouteData));
}
/**
* Indicates whether the entry matches a given Router ID.
*
* @param[in] aRouterId The Router ID to match against.
*
* @retval TRUE The entry matches @p aRouterId.
* @retval FALSE The entry does not match @p aRouterId.
*/
bool Matches(uint8_t aRouterId) const { return (mRouterId == aRouterId); }
private:
uint8_t mRouterId;
EntryType mRouteData;
};
/**
* Represents an array of route entries.
*/
typedef Array<Entry, kMaxRouters> EntryArray;
/**
* Parses the Route TLV data from a given message and offset range.
*
* @param[in] aMessage The message to parse from.
* @param[in] aOffsetRange The offset range within the message to read from.
*
* @retval kErrorNone Successfully parsed the Route TLV data.
* @retval kErrorParse Failed to parse the Route TLV data.
*/
Error ParseFrom(const Message &aMessage, const OffsetRange &aOffsetRange);
/**
* Gets the Router ID Sequence.
*
* @returns The Router ID Sequence.
*/
uint8_t GetRouterIdSequence(void) const { return mIdSequence; }
/**
* Gets the array of route entries.
*
* @returns The array of route entries.
*/
const EntryArray &GetEntries(void) const { return mEntries; }
/**
* Indicates whether a given Router ID is allocated in the route data.
*
* @param[in] aRouterId The Router ID to check.
*
* @retval TRUE The Router ID is allocated.
* @retval FALSE The Router ID is not allocated.
*/
bool IsAllocated(uint8_t aRouterId) const;
/**
* Indicates whether the Route TLV data is a singleton (at most one router is allocated).
*
* @retval TRUE It is a singleton.
* @retval FALSE It is not a singleton.
*/
bool IsSingleton(void) const { return mEntries.GetLength() <= 1; }
/**
* Determines the Router ID Mask from the route data.
*
* @param[out] aRouterIdMask A reference to a `RouterIdMask` to populate.
*/
void DetermineRouterIdMask(RouterIdMask &aRouterIdMask) const;
private:
uint8_t mIdSequence;
EntryArray mEntries;
};
#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
/**
@@ -394,21 +411,16 @@ private:
// | LQOut | LQIn | Route Cost |
// +---+---+---+---+---+---+---+---+
typedef uint8_t EntryType;
static constexpr uint8_t kLinkQualityOutMask = 0x03 << 6;
static constexpr uint8_t kLinkQualityInMask = 0x03 << 4;
static constexpr uint8_t kRouteCostMask = 0x0f << 0;
static constexpr uint16_t kMaxRouteDataSize = kMaxRouterId + 1;
#else
// Under `LOG_ROUTES` feature, Route Data is 12 bits per route
// (1.5 bytes). The first 4 bits are link qualities (out/in),
// remaining 8 bits are for the route cost. The even and odd
// entries are staggered.
typedef uint16_t EntryType;
static constexpr uint16_t kEvenEntryMask = 0xfff << 4;
static constexpr uint16_t kOddEntryMask = 0xfff << 0;
@@ -416,29 +428,8 @@ private:
static constexpr uint16_t kLinkQualityInMask = 0x03 << 8;
static constexpr uint16_t kRouteCostMask = 0xff << 0;
static constexpr uint16_t kMaxRouteDataSize = kMaxRouterId + 1 + kMaxRouterId / 2 + 1;
uint16_t ReadEntry(uint8_t aRouterIndex) const
{
uint16_t data;
uint16_t offset = (aRouterIndex + aRouterIndex / 2);
if (aRouterIndex & 0x1)
{
data = ReadBits<uint16_t, kOddEntryMask>(BigEndian::ReadUint16(&mRouteData[offset]));
}
else
{
data = ReadBits<uint16_t, kEvenEntryMask>(BigEndian::ReadUint16(&mRouteData[offset]));
}
return data;
}
#endif // OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
RouterIdMask mRouterIdMask;
uint8_t mRouteData[kMaxRouteDataSize];
} OT_TOOL_PACKED_END;
};
/**
* Represents Leader Data TLV value.
+14 -19
View File
@@ -1010,24 +1010,22 @@ Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_
return SendCommand(kUriDiagnosticReset, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
}
static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
void Client::GetRouteInfo(const RouteTlv::Data &aRouteTlvData, RouteInfo &aNetDiagRouteInfo)
{
uint8_t routeCount = 0;
for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i)
aNetDiagRouteInfo.mIdSequence = aRouteTlvData.GetRouterIdSequence();
for (const RouteTlv::Data::Entry &entry : aRouteTlvData.GetEntries())
{
if (!aRouteTlv.IsRouterIdSet(i))
{
continue;
}
aNetworkDiagRoute.mRouteData[routeCount].mRouterId = i;
aNetworkDiagRoute.mRouteData[routeCount].mRouteCost = aRouteTlv.GetRouteCost(routeCount);
aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn = aRouteTlv.GetLinkQualityIn(routeCount);
aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount);
++routeCount;
aNetDiagRouteInfo.mRouteData[routeCount].mRouterId = entry.GetRouterId();
aNetDiagRouteInfo.mRouteData[routeCount].mRouteCost = entry.GetRouteCost();
aNetDiagRouteInfo.mRouteData[routeCount].mLinkQualityIn = entry.GetLinkQualityIn();
aNetDiagRouteInfo.mRouteData[routeCount].mLinkQualityOut = entry.GetLinkQualityOut();
routeCount++;
}
aNetworkDiagRoute.mRouteCount = routeCount;
aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence();
aNetDiagRouteInfo.mRouteCount = routeCount;
}
static Error ParseEnhancedRoute(const Message &aMessage, uint16_t aOffset, otNetworkDiagEnhRoute &aNetworkDiagEnhRoute)
@@ -1167,13 +1165,10 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
case Tlv::kRoute:
{
RouteTlv routeTlv;
uint16_t bytesToRead = Min<uint16_t>(tlvInfo.GetSize(), sizeof(routeTlv));
RouteTlv::Data routeTlvData;
VerifyOrExit(!tlvInfo.IsExtended(), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead));
VerifyOrExit(routeTlv.IsValid(), error = kErrorParse);
ParseRoute(routeTlv, aDiagTlv.mData.mRoute);
SuccessOrExit(error = routeTlvData.ParseFrom(aMessage, tlvInfo.GetValueOffsetRange()));
GetRouteInfo(routeTlvData, aDiagTlv.mData.mRoute);
break;
}
+2
View File
@@ -263,6 +263,7 @@ private:
typedef otNetworkDiagData DiagData;
typedef otNetworkDiagChildTable ChildTable;
typedef otNetworkDiagIp6AddrList Ip6AddrList;
typedef otNetworkDiagRoute RouteInfo;
Error SendCommand(Uri aUri,
Message::Priority aPriority,
@@ -285,6 +286,7 @@ private:
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);
static void GetRouteInfo(const RouteTlv::Data &aRouteTlvData, RouteInfo &aNetDiagRouteInfo);
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
static const char *UriToString(Uri aUri);
@@ -101,6 +101,21 @@ void ChildTableTlvEntry::Parse(ParseInfo &aParseInfo) const
Mle::DeviceMode(mMode).Get(aParseInfo.mMode);
}
//---------------------------------------------------------------------------------------------------------------------
// RouteTlv
Error RouteTlv::FindIn(const Message &aMessage, RouteTlv::Data &aData)
{
Error error;
OffsetRange offsetRange;
SuccessOrExit(error = Tlv::FindTlvValueOffsetRange(aMessage, kType, offsetRange));
error = aData.ParseFrom(aMessage, offsetRange);
exit:
return error;
}
//---------------------------------------------------------------------------------------------------------------------
// EnhancedRouteTlvEntry
+10 -8
View File
@@ -296,21 +296,23 @@ typedef TlvInfo<Tlv::kConnectivity> ConnectivityTlv;
/**
* Implements Route TLV generation and parsing.
*/
OT_TOOL_PACKED_BEGIN
class RouteTlv : public Mle::RouteTlv
{
public:
static constexpr uint8_t kType = ot::NetworkDiagnostic::Tlv::kRoute; ///< The TLV Type value.
/**
* Initializes the TLV.
* Finds and parses a Route TLV in a given message.
*
* @param[in] aMessage The message to search within.
* @param[out] aData A reference to a `Data` to populate upon success.
*
* @retval kErrorNone Successfully found and parsed the Route TLV.
* @retval kErrorNotFound Could not find a Route TLV in @p aMessage.
* @retval kErrorParse Found the Route TLV but failed to parse it.
*/
void Init(void)
{
Mle::RouteTlv::Init();
ot::Tlv::SetType(kType);
}
} OT_TOOL_PACKED_END;
static Error FindIn(const Message &aMessage, Data &aData);
};
/**
* Represents a Leader Data TLV value.
+49 -56
View File
@@ -61,10 +61,10 @@ void RouterTable::Clear(void)
SignalTableChanged(events);
}
bool RouterTable::IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const
bool RouterTable::IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv::Data &aRouteTlvData) const
{
return (GetActiveRouterCount() == 0) ||
SerialNumber::IsGreater(aRouteTlv.GetRouterIdSequence(), GetRouterIdSequence());
SerialNumber::IsGreater(aRouteTlvData.GetRouterIdSequence(), GetRouterIdSequence());
}
void RouterTable::ClearNeighbors(void)
@@ -525,6 +525,14 @@ uint16_t RouterTable::GetNextHop(uint16_t aDestRloc16) const
return nextHopRloc16;
}
void RouterTable::UpdateRouterIdMask(const Mle::RouteTlv::Data &aRouteTlvData)
{
Mle::RouterIdMask routerIdMask;
aRouteTlvData.DetermineRouterIdMask(routerIdMask);
UpdateRouterIdMask(routerIdMask);
}
void RouterTable::UpdateRouterIdMask(const Mle::RouterIdMask &aRouterIdMask)
{
bool shouldAdd = false;
@@ -575,11 +583,12 @@ exit:
return;
}
void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighborId)
void RouterTable::UpdateRoutes(const Mle::RouteTlv::Data &aRouteTlvData, uint8_t aNeighborId)
{
Router *neighbor;
Mle::RouterIdMask finitePathCostIds;
uint8_t linkCostToNeighbor;
Router *neighbor;
Mle::RouterIdMask finitePathCostIds;
uint8_t linkCostToNeighbor;
const Mle::RouteTlv::Data::Entry *matchingEntry;
neighbor = FindRouterById(aNeighborId);
VerifyOrExit(neighbor != nullptr);
@@ -600,66 +609,51 @@ void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighbor
}
// Find the entry corresponding to our Router ID in the received
// `aRouteTlv` to get the `LinkQualityIn` from the perspective of
// neighbor. We use this to update our `LinkQualityOut` to the
// `aRouteTlvData` to get the `LinkQualityIn` from the perspective
// of neighbor. We use this to update our `LinkQualityOut` to the
// neighbor.
for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
matchingEntry = aRouteTlvData.GetEntries().FindMatching(Mle::RouterIdFromRloc16(Get<Mle::Mle>().GetRloc16()));
if (matchingEntry != nullptr)
{
if (!Get<Mle::Mle>().MatchesRouterId(routerId))
LinkQuality linkQuality = matchingEntry->GetLinkQualityIn();
if (neighbor->GetLinkQualityOut() != linkQuality)
{
continue;
neighbor->SetLinkQualityOut(linkQuality);
SignalTableChanged(kEventLinkQualityOutChanged);
}
if (aRouteTlv.IsRouterIdSet(routerId))
// If the `aRouteTlvData` indicates that the neighboring
// router claims to have no link to us (by setting its
// `GetLinkQualityOut()` towards us as `kLinkQuality0`),
// and we have previously established a link with it, and
// our two-way link quality to this router is at least
// `kLinkQuality2`, we schedule a unicast Advertisement to
// be sent to this neighbor. This helps expedite recovery
// from any temporary router link quality mismatch.
// Otherwise, the neighboring router will continue to
// advertise that it has no link to us until our next
// trickle timer-triggered Advertisement transmission
// (which can be up to 32 seconds later).
if (neighbor->IsStateValid() && (matchingEntry->GetLinkQualityOut() == kLinkQuality0) &&
(neighbor->GetTwoWayLinkQuality() >= kLinkQuality2))
{
LinkQuality linkQuality = aRouteTlv.GetLinkQualityIn(index);
if (neighbor->GetLinkQualityOut() != linkQuality)
{
neighbor->SetLinkQualityOut(linkQuality);
SignalTableChanged(kEventLinkQualityOutChanged);
}
// If the `aRouteTlv` indicates that the neighboring
// router claims to have no link to us (by setting its
// `GetLinkQualityOut()` towards us as `kLinkQuality0`),
// and we have previously established a link with it, and
// our two-way link quality to this router is at least
// `kLinkQuality2`, we schedule a unicast Advertisement to
// be sent to this neighbor. This helps expedite recovery
// from any temporary router link quality mismatch.
// Otherwise, the neighboring router will continue to
// advertise that it has no link to us until our next
// trickle timer-triggered Advertisement transmission
// (which can be up to 32 seconds later).
if (neighbor->IsStateValid() && (aRouteTlv.GetLinkQualityOut(index) == kLinkQuality0) &&
(neighbor->GetTwoWayLinkQuality() >= kLinkQuality2))
{
Get<Mle::Mle>().ScheduleUnicastAdvertisementTo(*neighbor);
}
Get<Mle::Mle>().ScheduleUnicastAdvertisementTo(*neighbor);
}
break;
}
linkCostToNeighbor = GetLinkCost(*neighbor);
for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
for (const Mle::RouteTlv::Data::Entry &entry : aRouteTlvData.GetEntries())
{
Router *router;
Router *nextHop;
uint8_t cost;
if (!aRouteTlv.IsRouterIdSet(routerId))
{
continue;
}
router = FindRouterById(routerId);
router = FindRouterById(entry.GetRouterId());
if (router == nullptr || Get<Mle::Mle>().HasRloc16(router->GetRloc16()) || router == neighbor)
{
@@ -668,7 +662,7 @@ void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighbor
nextHop = FindNextHopTowards(*router);
cost = aRouteTlv.GetRouteCost(index);
cost = entry.GetRouteCost();
cost = (cost == 0) ? Mle::kMaxRouteCost : cost;
if ((nextHop == nullptr) || (nextHop == neighbor))
@@ -718,28 +712,27 @@ exit:
return;
}
void RouterTable::UpdateRouterOnFtdChild(const Mle::RouteTlv &aRouteTlv, uint8_t aParentId)
void RouterTable::UpdateRouterOnFtdChild(const Mle::RouteTlv::Data &aRouteTlvData, uint8_t aParentId)
{
for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId;
index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++)
for (const Mle::RouteTlv::Data::Entry &entry : aRouteTlvData.GetEntries())
{
Router *router;
uint8_t cost;
uint8_t nextHopId;
if (!aRouteTlv.IsRouterIdSet(routerId) || (routerId == aParentId))
if (entry.GetRouterId() == aParentId)
{
continue;
}
router = FindRouterById(routerId);
router = FindRouterById(entry.GetRouterId());
if (router == nullptr)
{
continue;
}
cost = aRouteTlv.GetRouteCost(index);
cost = entry.GetRouteCost();
nextHopId = (cost == 0) ? Mle::kInvalidRouterId : aParentId;
if (router->SetNextHopAndCost(nextHopId, cost))
+19 -12
View File
@@ -312,12 +312,12 @@ public:
* Determines whether the Router ID Sequence in a received Route TLV is more recent than the current
* Router ID Sequence being used by `RouterTable`.
*
* @param[in] aRouteTlv The Route TLV to compare.
* @param[in] aRouteTlvData The Route TLV Data to compare.
*
* @retval TRUE The Router ID Sequence in @p aRouteTlv is more recent.
* @retval FALSE The Router ID Sequence in @p aRouteTlv is not more recent.
* @retval TRUE The Router ID Sequence in @p aRouteTlvData is more recent.
* @retval FALSE The Router ID Sequence in @p aRouteTlvData is not more recent.
*/
bool IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const;
bool IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv::Data &aRouteTlvData) const;
/**
* Gets the number of router neighbors with `GetLinkQualityIn()` better than or equal to a given threshold.
@@ -346,22 +346,29 @@ public:
void UpdateRouterIdMask(const Mle::RouterIdMask &aRouterIdMask);
/**
* Updates the routes based on a received `RouteTlv` from a neighboring router.
* Updates the allocated Router ID mask from a received `RouteTlv::Data`.
*
* @param[in] aRouteTlv The received `RouteTlv`
* @param[in] aNeighborId The router ID of neighboring router from which @p aRouteTlv is received.
* @param[in] aRouteTlvData The received `RouteTlv::Data`.
*/
void UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighborId);
void UpdateRouterIdMask(const Mle::RouteTlv::Data &aRouteTlvData);
/**
* Updates the routes on an FTD child based on a received `RouteTlv` from the parent.
* Updates the routes based on a received `RouteTlv::Data` from a neighboring router.
*
* MUST be called when device is an FTD child and @p aRouteTlv is received from its current parent.
* @param[in] aRouteTlvData The received `RouteTlv::Data`
* @param[in] aNeighborId The router ID of neighboring router from which @p aRouteTlvData is received.
*/
void UpdateRoutes(const Mle::RouteTlv::Data &aRouteTlvData, uint8_t aNeighborId);
/**
* Updates the routes on an FTD child based on a received `RouteTlv::Data` from the parent.
*
* @param[in] aRouteTlv The received `RouteTlv` from parent.
* MUST be called when device is an FTD child and @p aRouteTlvData is received from its current parent.
*
* @param[in] aRouteTlvData The received `RouteTlvData` from parent.
* @param[in] aParentId The Router ID of parent.
*/
void UpdateRouterOnFtdChild(const Mle::RouteTlv &aRouteTlv, uint8_t aParentId);
void UpdateRouterOnFtdChild(const Mle::RouteTlv::Data &aRouteTlvData, uint8_t aParentId);
/**
* Gets the allocated Router ID Mask.
+6 -10
View File
@@ -467,15 +467,15 @@ void MeshDiag::HandleTimer(void) { Finalize(kErrorResponseTimeout); }
Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage)
{
Error error = kErrorNone;
Mle::Mle &mle = aMessage.Get<Mle::Mle>();
RouteTlv routeTlv;
Error error = kErrorNone;
Mle::Mle &mle = aMessage.Get<Mle::Mle>();
RouteTlv::Data routeTlvData;
Clear();
SuccessOrExit(error = Tlv::Find<Address16Tlv>(aMessage, mRloc16));
SuccessOrExit(error = Tlv::Find<ExtMacAddressTlv>(aMessage, AsCoreType(&mExtAddress)));
SuccessOrExit(error = Tlv::FindTlv(aMessage, routeTlv));
SuccessOrExit(error = RouteTlv::FindIn(aMessage, routeTlvData));
switch (error = Tlv::Find<VersionTlv>(aMessage, mVersion))
{
@@ -495,13 +495,9 @@ Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage)
mIsLeader = (mRouterId == mle.GetLeaderId());
mIsBorderRouter = aMessage.Get<NetworkData::Leader>().ContainsBorderRouterWithRloc(mRloc16);
for (uint8_t id = 0, index = 0; id <= Mle::kMaxRouterId; id++)
for (const RouteTlv::Data::Entry &entry : routeTlvData.GetEntries())
{
if (routeTlv.IsRouterIdSet(id))
{
mLinkQualities[id] = routeTlv.GetLinkQualityIn(index);
index++;
}
mLinkQualities[entry.GetRouterId()] = entry.GetLinkQualityIn();
}
exit: