[mle] block router downgrade if triggered by child ID request (#12725)

This commit updates the `Mle` router role downgrade logic. When a
REED transitions to a router in response to a Child ID Request, it
indicates that the attaching child has no other viable parent options.
To ensure this child remains connected to the mesh, this commit
prevents the newly promoted router from downgrading back to a REED.

A new flag `mBlockDowngrade` is added to `Mle`, and a matching
property `mBlockParentDowngrade` is added to `Child` to track if it is
blocking its parent's downgrade. The downgrade restriction is lifted
under specific conditions: when the device detaches, when a new router
is added to the network (providing a potential alternative parent for
the child), or when all children blocking the downgrade are removed.

A new nexus test `test_mle_blocking_downgrade` is added to validate
the new behavior.
This commit is contained in:
Abtin Keshavarzian
2026-03-25 12:47:36 -05:00
committed by GitHub
parent a5593e7980
commit 9b00c024a8
7 changed files with 305 additions and 9 deletions
+23 -1
View File
@@ -478,8 +478,15 @@ uint32_t Mle::DetermineAdvertiseIntervalMax(void) const
return interval;
}
void Mle::UpdateAdvertiseInterval(void)
void Mle::HandleRouterTableEvent(RouterTable::Events aEvents)
{
// Callback from `RouterTable` when there is a change.
if (aEvents & RouterTable::kEventRouterAdded)
{
mBlockDowngrade = false;
}
if (IsRouterOrLeader() && mAdvertiseTrickleTimer.IsRunning())
{
mAdvertiseTrickleTimer.SetIntervalMax(DetermineAdvertiseIntervalMax());
@@ -3420,6 +3427,20 @@ void Mle::HandleAddressSolicitResponse(Coap::Msg *aMsg, Error aResult)
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateChildIdRequest))
{
IgnoreError(SendChildIdResponse(child));
// The transition to the router role was triggered by a Child
// ID Request. This indicates that the child has no other
// parent option. We set the flags to prevent the parent
// router from downgrading back to a REED to ensure this
// child remains connected.
//
// The `mBlockDowngrade` is cleared in various situations:
// - From `SetStateDetached()` (e.g. partition change).
// - If a new router is added (new possible parent).
// - If all children blocking downgrade are disconnected.
child.SetBlockParentDowngrade(true);
mBlockDowngrade = true;
}
exit:
@@ -3724,6 +3745,7 @@ bool Mle::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const
VerifyOrExit(IsRouter());
VerifyOrExit(mRouterTable.IsAllocated(aNeighborId));
VerifyOrExit(!mBlockDowngrade);
VerifyOrExit(!mRouterRoleTransition.IsPending());