From a07c50c00e4f2cf37b6a4563b43822889adcf970 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 8 May 2026 13:00:00 -0700 Subject: [PATCH] [mlr] extract registration delay scheduling into a helper method (#13074) This commit refactors the logic for scheduling Multicast Listener Registration (MLR) delays by replacing `UpdateReregistrationDelay(bool)` with a new, more expressive method: `ScheduleNextRegistration()`. The new method takes a `RegistrationRequest` enum (`kReregister` or `kRenew`), clearly distinguishing between the two different scheduling scenarios: - `kReregister`: Triggered after re-attaching or when a Primary BBR is added/updated. This schedules a rapid registration attempt using a random delay between 1 and the configured BBR reregistration delay. - `kRenew`: Triggered periodically. This schedules a standard registration renewal using a delay randomized between half the MLR timeout and the timeout minus a 9-second guard time (`kRenewGuardTime`), as mandated by Thread Spec. This change also introduces constants for `kLongRenewTimeout` and `kRenewGuardTime` to replace magic numbers, improving overall code readability and maintainability. --- src/core/thread/mlr_manager.cpp | 111 +++++++++++++++++++------------- src/core/thread/mlr_manager.hpp | 15 ++++- 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index a40c20b6f..f445bb8ae 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -64,8 +64,7 @@ void Manager::HandleNotifierEvents(Events aEvents) if (aEvents.Contains(kEventThreadRoleChanged) && Get().IsChild()) { - // Reregistration after re-attach - UpdateReregistrationDelay(true); + ScheduleNextRegistration(kReregister); } } @@ -74,10 +73,19 @@ void Manager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aS { OT_UNUSED_VARIABLE(aConfig); - bool needRereg = - aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg; + RegistrationRequest request = kRenew; - UpdateReregistrationDelay(needRereg); + switch (aState) + { + case BackboneRouter::Leader::kStateAdded: + case BackboneRouter::Leader::kStateToTriggerRereg: + request = kReregister; + break; + default: + break; + } + + ScheduleNextRegistration(request); } #if OPENTHREAD_CONFIG_MLR_ENABLE @@ -433,17 +441,13 @@ void Manager::HandleResponse(Coap::Msg *aMsg, Error aResult) else { BackboneRouter::Config config; - uint16_t reregDelay; - // The Device has just attempted a Multicast Listener Registration which failed, and it retries the same - // registration with a random time delay chosen in the interval [0, Reregistration Delay]. - // This is required by Thread 1.2 Specification 5.24.2.3 + // If a registration attempt fails, retry it after a random + // delay (same as re-registration delay). + if (Get().GetConfig(config) == kErrorNone) { - reregDelay = config.mReregistrationDelay > 1 - ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay) - : 1; - ScheduleSend(reregDelay); + ScheduleSend(DetermineReregistrationDelay(config)); } } } @@ -603,50 +607,69 @@ void Manager::Reregister(void) ScheduleSend(0); - // Schedule for the next renewing. - UpdateReregistrationDelay(false); + ScheduleNextRegistration(kRenew); } -void Manager::UpdateReregistrationDelay(bool aRereg) +uint16_t Manager::DetermineReregistrationDelay(const BackboneRouter::Config &aConfig) { - bool needSend = ShouldRegister(); + uint16_t delay = 1; - if (!needSend) + VerifyOrExit(aConfig.mReregistrationDelay > 1); + delay = Random::NonCrypto::GetUint16InRange(1, aConfig.mReregistrationDelay); + +exit: + return delay; +} + +uint32_t Manager::DetermineRenewDelay(const BackboneRouter::Config &aConfig) +{ + // As per Thread spec, the renew delay is randomly chosen + // between (0.5 * MLR-Timeout) and (MLR-Timeout - 9 seconds). + // The `kRenewGuardTime`(9 sec) allows time for transmission, + // potential retransmissions, and acknowledgment before the + // actual timeout. + + uint32_t timeout = Clamp(aConfig.mMlrTimeout, BackboneRouter::kMinMlrTimeout, kLongRenewTimeout); + + return Random::NonCrypto::GetUint32InRange((timeout / 2) + 1, timeout - kRenewGuardTime); +} + +void Manager::ScheduleNextRegistration(RegistrationRequest aRequest) +{ + uint32_t delay; + BackboneRouter::Config config; + + if (!ShouldRegister()) { mReregistrationDelay = 0; + ExitNow(); } - else + + IgnoreError(Get().GetConfig(config)); + + switch (aRequest) { - BackboneRouter::Config config; - uint32_t reregDelay; - uint32_t effectiveTimeout; + case kReregister: + delay = DetermineReregistrationDelay(config); + break; - IgnoreError(Get().GetConfig(config)); + case kRenew: + delay = DetermineRenewDelay(config); + break; - if (aRereg) - { - reregDelay = config.mReregistrationDelay > 1 - ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay) - : 1; - } - else - { - // Calculate renewing period according to Thread Spec. 5.24.2.3.2 - // The random time t SHOULD be chosen such that (0.5* MLR-Timeout) < t < (MLR-Timeout – 9 seconds). - effectiveTimeout = Max(config.mMlrTimeout, BackboneRouter::kMinMlrTimeout); - reregDelay = Random::NonCrypto::GetUint32InRange((effectiveTimeout >> 1u) + 1, effectiveTimeout - 9); - } - - if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay) - { - mReregistrationDelay = reregDelay; - } + default: + ExitNow(); } - UpdateTimeTickerRegistration(); + if (mReregistrationDelay == 0 || mReregistrationDelay > delay) + { + mReregistrationDelay = delay; - LogDebg("Manager::UpdateReregistrationDelay: rereg=%d, needSend=%d, ReregDelay=%lu", aRereg, needSend, - ToUlong(mReregistrationDelay)); + LogDebg("ScheduleNextRegistration() delay:%lu", ToUlong(delay)); + } + +exit: + UpdateTimeTickerRegistration(); } void Manager::LogMulticastAddresses(void) diff --git a/src/core/thread/mlr_manager.hpp b/src/core/thread/mlr_manager.hpp index 95af09bc5..968c47a38 100644 --- a/src/core/thread/mlr_manager.hpp +++ b/src/core/thread/mlr_manager.hpp @@ -143,6 +143,15 @@ public: #endif private: + static constexpr uint32_t kLongRenewTimeout = 4 * Time::kOneHourInSec; // `MLR_TIMEOUT_LONG` (in sec) + static constexpr uint32_t kRenewGuardTime = 9; // (in sec). + + enum RegistrationRequest : uint8_t + { + kReregister, + kRenew, + }; + class AddressArray : public Array { public: @@ -159,6 +168,9 @@ private: DeclareTmfResponseHandlerIn(Manager, HandleResponse); + static uint16_t DetermineReregistrationDelay(const BackboneRouter::Config &aConfig); + static uint32_t DetermineRenewDelay(const BackboneRouter::Config &aConfig); + static Error ParseResponse(Error aResult, Coap::Msg *aMsg, uint8_t &aStatus, AddressArray &aFailedAddresses); static bool DidRegisterSuccessfully(const Ip6::Address &aAddress, bool aSuccess, @@ -184,10 +196,9 @@ private: void SetMulticastAddressState(State aFromState, State aToState); void Finish(bool aSuccess, const AddressArray &aFailedAddresses); - void ScheduleSend(uint16_t aDelay); void UpdateTimeTickerRegistration(void); - void UpdateReregistrationDelay(bool aRereg); + void ScheduleNextRegistration(RegistrationRequest aRequest); void Reregister(void); void HandleTimeTick(void);