diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index d2b53d54f..4970f374b 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -1342,6 +1342,71 @@ #define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -65 #endif +/** + * @def OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + * + * Define as 1 to enable attach backoff feature + * + * When this feature is enabled, an exponentially increasing backoff wait time is added between attach attempts. + * If device is sleepy, the radio will be put to sleep during the wait time. This ensures that a battery-powered sleepy + * end-device does not drain its battery by continuously searching for a parent to attach to (when there is no + * router/parent for it to attach). + * + * The backoff time starts from a minimum interval specified by `OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL`, + * and every attach attempt the wait time is doubled up to `OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL` which + * specifies the maximum wait time. + * + * Once the wait time reaches the maximum, a random jitter interval is added to it. The maximum value for jitter is + * specified by `OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL`. The random jitter is selected uniformly within + * range `[-JITTER, +JITTER]`. It is only added when the backoff wait interval is at maximum value. + * + */ +#ifndef OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF +#define OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL + * + * Specifies the minimum backoff wait interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL 251 +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL + * + * Specifies the maximum backoff wait interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL 1200000 // 1200 seconds = 20 minutes +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL + * + * Specifies the maximum jitter interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL 2000 +#endif + /** * @def OPENTHREAD_CONFIG_SEND_UNICAST_ANNOUNCE_RESPONSE * diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 773af43b3..68bef6f3a 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -520,6 +520,12 @@ otError Mle::BecomeDetached(void) VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); + // In case role is already detached and attach state is `kAttachStateStart` + // (i.e., waiting to start an attach attempt), there is no need to make any + // changes. + + VerifyOrExit(mRole != OT_DEVICE_ROLE_DETACHED || mAttachState != kAttachStateStart); + // not in reattach stage after reset if (mReattachState == kReattachStop) { @@ -563,10 +569,6 @@ otError Mle::BecomeChild(AttachMode aMode) SetAttachState(kAttachStateStart); mParentRequestMode = aMode; - mAttachCounter++; - otLogInfoMle(GetInstance(), "Attempt to attach - attempt %d, %s %s", mAttachCounter, AttachModeToString(aMode), - ReattachStateToString(mReattachState)); - if (aMode != kAttachBetter) { if (IsFullThreadDevice()) @@ -575,12 +577,71 @@ otError Mle::BecomeChild(AttachMode aMode) } } - mAttachTimer.Start(1 + ((mAttachCounter == 1) ? Random::GetUint32InRange(0, kParentRequestRouterTimeout) : 0)); + mAttachTimer.Start(GetAttachStartDelay()); + + if (mRole == OT_DEVICE_ROLE_DETACHED) + { + mAttachCounter++; + + if (mAttachCounter == 0) + { + mAttachCounter--; + } + + if (!IsRxOnWhenIdle()) + { + netif.GetMac().SetRxOnWhenIdle(false); + } + } exit: return error; } +uint32_t Mle::GetAttachStartDelay(void) const +{ + uint32_t delay = 1; + uint32_t jitter; + + VerifyOrExit(mRole == OT_DEVICE_ROLE_DETACHED); + + if (mAttachCounter == 0) + { + delay = 1 + Random::GetUint32InRange(0, kParentRequestRouterTimeout); + ExitNow(); + } +#if OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + else + { + uint16_t counter = mAttachCounter - 1; + const uint32_t ratio = kAttachBackoffMaxInterval / kAttachBackoffMinInterval; + + if ((counter <= sizeof(ratio) * CHAR_BIT) && ((1U << counter) <= ratio)) + { + delay = kAttachBackoffMinInterval; + delay <<= counter; + } + else + { + delay = Random::AddJitter(kAttachBackoffMaxInterval, kAttachBackoffJitter); + } + } +#endif // OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + + jitter = Random::GetUint32InRange(0, kAttachStartJitter); + + if (jitter + delay > delay) // check for overflow + { + delay += jitter; + } + + otLogInfoMle(GetInstance(), "Attach attempt %d unsuccessful, will try again in %u.%03u seconds", mAttachCounter, + delay / 1000, delay % 1000); + +exit: + return delay; +} + bool Mle::IsAttached(void) const { return (mRole == OT_DEVICE_ROLE_CHILD || mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); @@ -1484,6 +1545,17 @@ void Mle::HandleAttachTimer(void) break; case kAttachStateStart: + if (mAttachCounter > 0) + { + otLogInfoMle(GetInstance(), "Attempt to attach - attempt %d, %s %s", mAttachCounter, + AttachModeToString(mParentRequestMode), ReattachStateToString(mReattachState)); + } + else + { + otLogInfoMle(GetInstance(), "Attempt to attach - %s %s", AttachModeToString(mParentRequestMode), + ReattachStateToString(mReattachState)); + } + SetAttachState(kAttachStateParentRequestRouter); mParentCandidate.SetState(Neighbor::kStateInvalid); mReceivedResponseFromParent = false; @@ -1600,7 +1672,7 @@ uint32_t Mle::Reattach(void) netif.GetPendingDataset().ApplyConfiguration(); mReattachState = kReattachPending; SetAttachState(kAttachStateStart); - delay = 1 + Random::GetUint32InRange(0, kParentRequestJitter); + delay = 1 + Random::GetUint32InRange(0, kAttachStartJitter); } else { diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 49b6aacf8..cf1da67a8 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1520,6 +1520,18 @@ private: { kMleMessagePriority = Message::kPriorityHigh, kMleHopLimit = 255, + + // Parameters related to "periodic parent search" feature (CONFIG_ENABLE_PERIODIC_PARENT_SEARCH). + // All timer intervals are converted to milliseconds. + kParentSearchCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u), + kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u), + kParentSearchJitterInterval = (15 * 1000u), + kParentSearchRssThreadhold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD, + + // Parameters for "attach backoff" feature (CONFIG_ENABLE_ATTACH_BACKOFF) - Intervals are in milliseconds. + kAttachBackoffMinInterval = OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL, + kAttachBackoffMaxInterval = OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL, + kAttachBackoffJitter = OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL, }; enum ParentRequestType @@ -1528,17 +1540,6 @@ private: kParentRequestTypeRoutersAndReeds, ///< Parent Request to all routers and REEDs. }; -#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH - enum - { - // All timer intervals are converted to milliseconds - kParentSearchCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u), - kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u), - kParentSearchJitterInterval = (15 * 1000u), - kParentSearchRssThreadhold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD, - }; -#endif - void GenerateNonce(const Mac::ExtAddress &aMacAddr, uint32_t aFrameCounter, uint8_t aSecurityLevel, @@ -1568,6 +1569,7 @@ private: otError HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessAnnounce(void); + uint32_t GetAttachStartDelay(void) const; otError SendParentRequest(ParentRequestType aType); otError SendChildIdRequest(void); otError SendOrphanAnnounce(void); diff --git a/src/core/thread/mle_constants.hpp b/src/core/thread/mle_constants.hpp index 9bcd5bcfb..f38a0344b 100644 --- a/src/core/thread/mle_constants.hpp +++ b/src/core/thread/mle_constants.hpp @@ -61,7 +61,7 @@ enum kUdpPort = 19788, ///< MLE UDP Port kParentRequestRouterTimeout = 750, ///< Router Parent Request timeout kParentRequestReedTimeout = 1250, ///< Router and REEDs Parent Request timeout - kParentRequestJitter = 50, ///< Maximum jitter time added to Parent Request timeout + kAttachStartJitter = 50, ///< Maximum jitter time added to start of attach. kAnnounceProcessTimeout = 250, ///< Timeout after receiving Announcement before channel/pan-id change kAnnounceTimeout = 1400, ///< Total timeout used for sending Announcement messages kMinAnnounceDelay = 80, ///< Minimum delay between Announcement messages