From 361f7311de1723388cd12cb32484a27bba43f070 Mon Sep 17 00:00:00 2001 From: Tom Rebbert <109624508+trebbert-lutron@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:47:51 -0600 Subject: [PATCH] [mle] add 2 additional retries on child ID request messages to improve attachment robustness (#12025) In testing I have observed some failures to attach due to a `Child ID Request` or `Child ID Response` getting lost in the air causing the attaching device to start it's own new partition (assuming FTD). When attaching, after selecting a parent candidate from the responses to a multicast `Parent Request` message, devices only have one shot to send and receive a response to a `Child ID Request`. There is a higher rate of failure in this sequence during high traffic periods such as formation and reset, which will cause more devices than desired to fail the attachment process (and become their own leader if an FTD). This commit aims to help address this by adding 2 additional retries to `Child ID Request` messages, which gives devices a much better chance of attaching the first time. --- src/core/thread/mle.cpp | 14 +++++++++++--- src/core/thread/mle.hpp | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index fdf1947c6..0b90a4dac 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -4431,6 +4431,7 @@ Mle::Attacher::Attacher(Instance &aInstance) , mAddressRegistrationMode(kAppendAllAddresses) , mParentRequestCounter(0) , mAnnounceChannel(0) + , mChildIdRequestsRemaining(kMaxChildIdRequests) , mAttachCounter(0) , mAnnounceDelay(kAnnounceTimeout) , mTimer(aInstance) @@ -4667,15 +4668,15 @@ bool Mle::Attacher::HasAcceptableParentCandidate(void) const bool hasAcceptableParent = false; ParentRequestType parentReqType; - VerifyOrExit(mParentCandidate.IsStateParentResponse()); - switch (mState) { case kStateAnnounce: + VerifyOrExit(mParentCandidate.IsStateParentResponse()); VerifyOrExit(!HasMoreChannelsToAnnounce()); break; case kStateParentRequest: + VerifyOrExit(mParentCandidate.IsStateParentResponse()); SuccessOrAssert(DetermineParentRequestType(parentReqType)); if (parentReqType == kToRouters) @@ -4689,6 +4690,11 @@ bool Mle::Attacher::HasAcceptableParentCandidate(void) const break; + case kStateChildIdRequest: + VerifyOrExit(mParentCandidate.IsStateValid()); + VerifyOrExit(mChildIdRequestsRemaining > 0); + break; + default: ExitNow(); } @@ -4732,7 +4738,8 @@ void Mle::Attacher::HandleTimer(void) if (HasAcceptableParentCandidate() && (SendChildIdRequest() == kErrorNone)) { SetState(kStateChildIdRequest); - delay = kChildIdResponseTimeout; + mChildIdRequestsRemaining--; + delay = Random::NonCrypto::AddJitter(kChildIdResponseTimeout, kChildIdResponseJitter); ExitNow(); } @@ -4750,6 +4757,7 @@ void Mle::Attacher::HandleTimer(void) mParentCandidate.SetState(Neighbor::kStateInvalid); mReceivedResponseFromParent = false; mParentRequestCounter = 0; + mChildIdRequestsRemaining = kMaxChildIdRequests; Get().SetRxOnWhenIdle(true); OT_FALL_THROUGH; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index d46372156..c957e02fb 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1264,6 +1264,7 @@ private: static constexpr uint32_t kParentRequestReedTimeout = 1250; // Wait timer after tx of Parent Req to REEDs static constexpr uint32_t kParentRequestDuplicateTimeout = 700; // Min time to detect duplicate Parent Req rx static constexpr uint32_t kChildIdResponseTimeout = 1250; // Wait time to receive Child ID Response + static constexpr uint32_t kChildIdResponseJitter = kChildIdResponseTimeout * 1 / 10; // Jitter 10% static constexpr uint32_t kAttachStartJitter = 50; // Max jitter time added to start of attach static constexpr uint32_t kAnnounceProcessTimeout = 250; // Delay after Announce rx before processing static constexpr uint32_t kAnnounceTimeout = 1400; // Total timeout for sending Announce messages @@ -1941,6 +1942,8 @@ private: kReattachModePending, // Reattach using stored Pending Dataset }; + static constexpr uint8_t kMaxChildIdRequests = 3; + void SetState(State aState); uint32_t GetStartDelay(void) const; bool HasAcceptableParentCandidate(void) const; @@ -1975,6 +1978,7 @@ private: AddressRegistrationMode mAddressRegistrationMode; uint8_t mParentRequestCounter; uint8_t mAnnounceChannel; + uint8_t mChildIdRequestsRemaining; uint16_t mAttachCounter; uint16_t mAnnounceDelay; TxChallenge mParentRequestChallenge;