From c4a85578f5d779abc252fd27108d82bc2ecc011b Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 4 Jun 2026 13:33:07 -0700 Subject: [PATCH] [mle] validate peer extended address in Link Accept (#13207) This commit ensures that the peer's extended address matches the stored extended address when receiving a Link Accept for an already valid link, preventing unintended frame counter resets and neighbor table updates. To achieve this: - We validate that the peer's extended address (extracted from the IPv6 peer address IID) matches the router's stored extended address when processing Link Accepts for a neighbor that is already in the kStateValid state. If there is a mismatch, the packet is rejected with kErrorSecurity. - We gate InitNeighbor() and the resetting of MLE frame counters so they only execute if the neighbor is not already kStateValid. For valid neighbors, we only update link statistics (RSS, last heard, link quality, key sequence) and clear the Link Accept timeout without modifying the frame counters or average RSS history. --- src/core/thread/mle_ftd.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/core/thread/mle_ftd.cpp b/src/core/thread/mle_ftd.cpp index dde058c59..1f8a0ed63 100644 --- a/src/core/thread/mle_ftd.cpp +++ b/src/core/thread/mle_ftd.cpp @@ -903,6 +903,7 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType) LeaderData leaderData; uint8_t linkMargin; bool shouldUpdateRoutes = false; + Mac::ExtAddress extAddress; SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); @@ -928,6 +929,8 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType) break; case Neighbor::kStateValid: + extAddress.SetFromIid(aRxInfo.mMessageInfo.GetPeerAddr().GetIid()); + VerifyOrExit(router->GetExtAddress() == extAddress, error = kErrorSecurity); break; default: @@ -1042,20 +1045,32 @@ void Mle::HandleLinkAcceptVariant(RxInfo &aRxInfo, MessageType aMessageType) OT_ASSERT(false); } - InitNeighbor(*router, aRxInfo); - router->SetRloc16(sourceAddress); - router->GetLinkFrameCounters().SetAll(linkFrameCounter); - router->SetLinkAckFrameCounter(linkFrameCounter); - router->SetMleFrameCounter(mleFrameCounter); - router->SetVersion(version); - router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | - DeviceMode::kModeFullNetworkData)); + if (neighborState != Neighbor::kStateValid) + { + InitNeighbor(*router, aRxInfo); + router->SetRloc16(sourceAddress); + router->GetLinkFrameCounters().SetAll(linkFrameCounter); + router->SetLinkAckFrameCounter(linkFrameCounter); + router->SetMleFrameCounter(mleFrameCounter); + router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | + DeviceMode::kModeFullNetworkData)); + router->SetKeySequence(aRxInfo.mKeySequence); + router->SetState(Neighbor::kStateValid); + } + else + { + router->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss()); + router->SetLastHeard(TimerMilli::GetNow()); + } + router->SetLinkQualityOut(LinkQualityForLinkMargin(linkMargin)); - router->SetState(Neighbor::kStateValid); - router->SetKeySequence(aRxInfo.mKeySequence); + router->SetVersion(version); router->ClearLinkAcceptTimeout(); - mNeighborTable.Signal(NeighborTable::kRouterAdded, *router); + if (neighborState != Neighbor::kStateValid) + { + mNeighborTable.Signal(NeighborTable::kRouterAdded, *router); + } mDelayedSender.RemoveScheduledLinkRequest(*router);