mirror of
https://github.com/espressif/openthread.git
synced 2026-10-01 13:27:42 +00:00
[mlr] introduce state machine and use timer in Mlr::Manager (#13132)
This commit introduces a structured state machine to `Mlr::Manager` to coordinate Multicast Listener Registration (MLR) activities more efficiently. The previous implementation relied on independent delay variables and the global `TimeTicker`, which could lead to redundant or premature registrations, especially when a Primary Backbone Router (PBBR) was newly discovered or updated. The new state machine (`kStateStopped`, `kStateIdle`, `kStateToRegisterAll`, `kStateRegistering`, `kStateRegistered`, `kStateNewAddrToRegister`) provides explicit transitions for the entire MLR lifecycle. This ensures that registrations are properly aggregated and that periodic renewals are correctly rescheduled after successful out-of-band registrations. Additionally, the manager now uses a dedicated `TimerMilli` instead of `TimeTicker`, reducing system-wide overhead and providing more precise timing control.
This commit is contained in:
@@ -111,13 +111,6 @@ void TimeTicker::HandleTimer(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
|
|
||||||
if (mReceivers & Mask(kMlrManager))
|
|
||||||
{
|
|
||||||
Get<Mlr::Manager>().HandleTimeTick();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (mReceivers & Mask(kIp6Mpl))
|
if (mReceivers & Mask(kIp6Mpl))
|
||||||
{
|
{
|
||||||
Get<Ip6::Mpl>().HandleTimeTick();
|
Get<Ip6::Mpl>().HandleTimeTick();
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ public:
|
|||||||
kChildSupervisor, ///< `ChildSupervisor`
|
kChildSupervisor, ///< `ChildSupervisor`
|
||||||
kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages)
|
kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages)
|
||||||
kDuaManager, ///< `DuaManager`
|
kDuaManager, ///< `DuaManager`
|
||||||
kMlrManager, ///< `MlrManager`
|
|
||||||
kNetworkDataNotifier, ///< `NetworkData::Notifier`
|
kNetworkDataNotifier, ///< `NetworkData::Notifier`
|
||||||
kIp6Mpl, ///< `Ip6::Mpl`
|
kIp6Mpl, ///< `Ip6::Mpl`
|
||||||
kBbrLocal, ///< `BackboneRouter::Local`
|
kBbrLocal, ///< `BackboneRouter::Local`
|
||||||
|
|||||||
+254
-143
@@ -44,15 +44,82 @@ RegisterLogModule("MlrManager");
|
|||||||
|
|
||||||
Manager::Manager(Instance &aInstance)
|
Manager::Manager(Instance &aInstance)
|
||||||
: InstanceLocator(aInstance)
|
: InstanceLocator(aInstance)
|
||||||
, mReregistrationDelay(0)
|
|
||||||
, mSendDelay(0)
|
|
||||||
, mPending(false)
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||||
, mRegisterPending(false)
|
, mRegisterPending(false)
|
||||||
#endif
|
#endif
|
||||||
|
, mState(kStateStopped)
|
||||||
|
, mTimer(aInstance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Manager::EnterState(State aState)
|
||||||
|
{
|
||||||
|
State oldState;
|
||||||
|
|
||||||
|
VerifyOrExit(mState != aState);
|
||||||
|
|
||||||
|
oldState = mState;
|
||||||
|
mState = aState;
|
||||||
|
|
||||||
|
if (oldState == kStateRegistering)
|
||||||
|
{
|
||||||
|
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleResponse, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState == kStateToRegisterAll)
|
||||||
|
{
|
||||||
|
// Clear "MlrRegistered" state on all addresses
|
||||||
|
|
||||||
|
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
|
for (Ip6::Netif::MulticastAddress &addr : Get<ThreadNetif>().GetMulticastAddresses())
|
||||||
|
{
|
||||||
|
if (addr.IsMlrCandidate())
|
||||||
|
{
|
||||||
|
addr.SetMlrRegistered(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
|
||||||
|
{
|
||||||
|
child.ClearMlrRegisteredStateOnAllIp6Addresses();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::UpdateState(void)
|
||||||
|
{
|
||||||
|
// `UpdateState()` evaluates whether the MLR manager should be
|
||||||
|
// running or not. It handles starting or stopping the manager.
|
||||||
|
|
||||||
|
bool canRun = false;
|
||||||
|
|
||||||
|
if (Get<Mle::Mle>().IsAttached() && Get<BackboneRouter::Leader>().HasPrimary())
|
||||||
|
{
|
||||||
|
canRun = Get<Mle::Mle>().IsFullThreadDevice() || Get<Mle::Mle>().GetParent().IsThreadVersion1p1();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsRunning())
|
||||||
|
{
|
||||||
|
VerifyOrExit(canRun);
|
||||||
|
EnterState(kStateToRegisterAll);
|
||||||
|
ScheduleTimerForReregistrationDelay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VerifyOrExit(!canRun);
|
||||||
|
EnterState(kStateStopped);
|
||||||
|
mTimer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::HandleNotifierEvents(Events aEvents)
|
void Manager::HandleNotifierEvents(Events aEvents)
|
||||||
{
|
{
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
@@ -62,43 +129,90 @@ void Manager::HandleNotifierEvents(Events aEvents)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::Mle>().IsChild())
|
if (aEvents.Contains(kEventThreadRoleChanged))
|
||||||
{
|
{
|
||||||
ScheduleNextRegistration(kReregister);
|
UpdateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::PrimaryEvent aEvent)
|
void Manager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::PrimaryEvent aEvent)
|
||||||
{
|
{
|
||||||
RegistrationRequest request = kRenew;
|
UpdateState();
|
||||||
|
|
||||||
switch (aEvent)
|
switch (aEvent)
|
||||||
{
|
{
|
||||||
case BackboneRouter::kPrimaryAdded:
|
case BackboneRouter::kPrimaryAdded:
|
||||||
case BackboneRouter::kPrimaryUpdatedReregister:
|
case BackboneRouter::kPrimaryRemoved:
|
||||||
request = kReregister;
|
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
|
case BackboneRouter::kPrimaryUpdatedReregister:
|
||||||
|
VerifyOrExit(IsRunning());
|
||||||
|
EnterState(kStateToRegisterAll);
|
||||||
|
ScheduleTimerForReregistrationDelay();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackboneRouter::kPrimaryConfigParameterChanged:
|
||||||
|
|
||||||
|
// When PBBR config parameters (like MLR Timeout) change, we
|
||||||
|
// need to recalculate and update the renewal time for all
|
||||||
|
// currently registered addresses to ensure they don't expire
|
||||||
|
// prematurely.
|
||||||
|
|
||||||
|
switch (GetState())
|
||||||
|
{
|
||||||
|
case kStateStopped:
|
||||||
|
case kStateIdle:
|
||||||
|
case kStateToRegisterAll:
|
||||||
|
case kStateRegistering:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStateRegistered:
|
||||||
|
mTimer.Stop();
|
||||||
|
OT_FALL_THROUGH;
|
||||||
|
|
||||||
|
case kStateNewAddrToRegister:
|
||||||
|
DetermineRenewTime();
|
||||||
|
mTimer.FireAtIfEarlier(mRenewTime);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScheduleNextRegistration(request);
|
exit:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
void Manager::HandleEventIp6MulticastSubscribed(void)
|
void Manager::HandleEventIp6MulticastSubscribed(void)
|
||||||
{
|
{
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
bool hasUnregistered = false;
|
||||||
|
|
||||||
for (Ip6::Netif::MulticastAddress &addr : Get<ThreadNetif>().GetMulticastAddresses())
|
for (Ip6::Netif::MulticastAddress &addr : Get<ThreadNetif>().GetMulticastAddresses())
|
||||||
{
|
{
|
||||||
if (addr.IsMlrCandidate() && !addr.IsMlrRegistered() && IsAddressRegisteredByAnyChild(addr.GetAddress()))
|
if (!addr.IsMlrCandidate())
|
||||||
{
|
{
|
||||||
addr.SetMlrRegistered(true);
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!addr.IsMlrRegistered())
|
||||||
|
{
|
||||||
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
if (IsAddressRegisteredByAnyChild(addr.GetAddress()))
|
||||||
|
{
|
||||||
|
addr.SetMlrRegistered(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ScheduleSend(0);
|
hasUnregistered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUnregistered)
|
||||||
|
{
|
||||||
|
ScheduleNewAddrRegistration(0, kMaxNewNetifAddrRegistraionDelay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::IsAddressRegisteredByNetif(const Ip6::Address &aAddress) const
|
bool Manager::IsAddressRegisteredByNetif(const Ip6::Address &aAddress) const
|
||||||
@@ -121,6 +235,11 @@ bool Manager::IsAddressRegisteredByNetif(const Ip6::Address &aAddress) const
|
|||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
|
||||||
|
bool Manager::IsAddressRegisteredByAnyChild(const Ip6::Address &aAddress) const
|
||||||
|
{
|
||||||
|
return IsAddressRegisteredByAnyChildExcept(aAddress, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool Manager::IsAddressRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
|
bool Manager::IsAddressRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
|
||||||
{
|
{
|
||||||
bool isRegistered = false;
|
bool isRegistered = false;
|
||||||
@@ -169,7 +288,7 @@ void Manager::UpdateProxiedSubscriptions(Child &aChild, const ChildAddressArray
|
|||||||
|
|
||||||
if (hasUnregistered)
|
if (hasUnregistered)
|
||||||
{
|
{
|
||||||
ScheduleSend(Random::NonCrypto::GenerateInClosedRange<uint16_t>(1, BackboneRouter::kParentAggregateDelay));
|
ScheduleNewAddrRegistration(kMinNewChildAddrRegistrationDelay, kMaxNewChildAddrRegistrationDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
@@ -178,52 +297,39 @@ exit:
|
|||||||
|
|
||||||
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
|
||||||
void Manager::ScheduleSend(uint16_t aDelay)
|
void Manager::ScheduleNewAddrRegistration(uint32_t aMinDelay, uint32_t aMaxDelay)
|
||||||
{
|
{
|
||||||
OT_ASSERT(!mPending || mSendDelay == 0);
|
uint32_t delay;
|
||||||
|
|
||||||
VerifyOrExit(!mPending);
|
switch (GetState())
|
||||||
|
{
|
||||||
|
case kStateIdle:
|
||||||
|
EnterState(kStateToRegisterAll);
|
||||||
|
break;
|
||||||
|
|
||||||
if (aDelay == 0)
|
case kStateRegistered:
|
||||||
{
|
case kStateNewAddrToRegister:
|
||||||
mSendDelay = 0;
|
EnterState(kStateNewAddrToRegister);
|
||||||
Send();
|
break;
|
||||||
}
|
|
||||||
else if (mSendDelay == 0 || mSendDelay > aDelay)
|
case kStateStopped:
|
||||||
{
|
case kStateToRegisterAll:
|
||||||
mSendDelay = aDelay;
|
case kStateRegistering:
|
||||||
|
ExitNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateTimeTickerRegistration();
|
delay = Random::NonCrypto::GenerateInClosedRange(aMinDelay, aMaxDelay);
|
||||||
|
|
||||||
|
// The timer may already be running for a periodic renewal or for a
|
||||||
|
// previously scheduled new address registration. We ensure the
|
||||||
|
// timer fires at the earliest requested time.
|
||||||
|
|
||||||
|
mTimer.FireAtIfEarlier(TimerMilli::GetNow() + delay);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::UpdateTimeTickerRegistration(void)
|
|
||||||
{
|
|
||||||
if (mSendDelay == 0 && mReregistrationDelay == 0)
|
|
||||||
{
|
|
||||||
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMlrManager);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMlrManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Manager::ShouldRegister(void) const
|
|
||||||
{
|
|
||||||
bool shouldRegister = false;
|
|
||||||
|
|
||||||
VerifyOrExit(Get<Mle::Mle>().IsFullThreadDevice() || Get<Mle::Mle>().GetParent().IsThreadVersion1p1());
|
|
||||||
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary());
|
|
||||||
|
|
||||||
shouldRegister = true;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
return shouldRegister;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Manager::DetermineAddressesToRegister(AddressArray &aAddresses) const
|
void Manager::DetermineAddressesToRegister(AddressArray &aAddresses) const
|
||||||
{
|
{
|
||||||
aAddresses.Clear();
|
aAddresses.Clear();
|
||||||
@@ -255,22 +361,63 @@ exit:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Send(void)
|
void Manager::SendNextRequest(void)
|
||||||
{
|
{
|
||||||
Error error;
|
|
||||||
AddressArray addresses;
|
AddressArray addresses;
|
||||||
|
|
||||||
VerifyOrExit(!mPending, error = kErrorBusy);
|
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleResponse, this));
|
||||||
|
|
||||||
VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
|
|
||||||
VerifyOrExit(ShouldRegister(), error = kErrorInvalidState);
|
|
||||||
|
|
||||||
DetermineAddressesToRegister(addresses);
|
DetermineAddressesToRegister(addresses);
|
||||||
VerifyOrExit(!addresses.IsEmpty(), error = kErrorNotFound);
|
|
||||||
|
|
||||||
SuccessOrExit(error = SendMessage(addresses.GetArrayBuffer(), addresses.GetLength(), nullptr, HandleResponse));
|
if (addresses.IsEmpty())
|
||||||
|
{
|
||||||
|
// There are no (more) addresses to register. If we are still
|
||||||
|
// in `kStateToRegisterAll`, it indicates there is no multicast
|
||||||
|
// address to register at all, so we transition to `kStateIdle`.
|
||||||
|
// Otherwise, we at least sent one registration, and all addresses
|
||||||
|
// are now registered, so we transition to `kStateRegistered` and
|
||||||
|
// schedule the next periodic renewal.
|
||||||
|
|
||||||
mPending = true;
|
switch (GetState())
|
||||||
|
{
|
||||||
|
case kStateToRegisterAll:
|
||||||
|
EnterState(kStateIdle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStateRegistering:
|
||||||
|
case kStateNewAddrToRegister:
|
||||||
|
EnterState(kStateRegistered);
|
||||||
|
mTimer.FireAt(mRenewTime);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStateStopped:
|
||||||
|
case kStateIdle:
|
||||||
|
case kStateRegistered:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SendMessage(addresses.GetArrayBuffer(), addresses.GetLength(), nullptr, HandleResponse) != kErrorNone)
|
||||||
|
{
|
||||||
|
mTimer.Start(kSendFailureRetryDelay);
|
||||||
|
ExitNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transitioning from `kStateToRegisterAll` to `kStateRegistering`
|
||||||
|
// indicates the start of a full registration cycle. We record the
|
||||||
|
// start time and determine the renewal time. The start time is
|
||||||
|
// tracked so we can recalculate the renewal time if PBBR config
|
||||||
|
// parameters change later.
|
||||||
|
|
||||||
|
if (GetState() == kStateToRegisterAll)
|
||||||
|
{
|
||||||
|
mStartTime = TimerMilli::GetNow();
|
||||||
|
DetermineRenewTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterState(kStateRegistering);
|
||||||
|
|
||||||
// Generally Thread 1.2 Router would send MLR.req on behalf for MA (scope >=4) subscribed by its MTD child.
|
// Generally Thread 1.2 Router would send MLR.req on behalf for MA (scope >=4) subscribed by its MTD child.
|
||||||
// When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send MLR.req to PBBR itself.
|
// When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send MLR.req to PBBR itself.
|
||||||
@@ -281,10 +428,7 @@ void Manager::Send(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (error == kErrorNoBufs)
|
return;
|
||||||
{
|
|
||||||
ScheduleSend(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||||
@@ -391,6 +535,15 @@ exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Manager::HandleResponse(Coap::Msg *aMsg, Error aResult)
|
void Manager::HandleResponse(Coap::Msg *aMsg, Error aResult)
|
||||||
|
{
|
||||||
|
VerifyOrExit(GetState() == kStateRegistering);
|
||||||
|
ProcessResponse(aMsg, aResult);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::ProcessResponse(Coap::Msg *aMsg, Error aResult)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
@@ -398,8 +551,6 @@ void Manager::HandleResponse(Coap::Msg *aMsg, Error aResult)
|
|||||||
AddressArray registeredAddresses;
|
AddressArray registeredAddresses;
|
||||||
OwnedPtr<Coap::Message> requestMsg;
|
OwnedPtr<Coap::Message> requestMsg;
|
||||||
|
|
||||||
mPending = false;
|
|
||||||
|
|
||||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// Parse the response message
|
// Parse the response message
|
||||||
|
|
||||||
@@ -460,18 +611,14 @@ void Manager::HandleResponse(Coap::Msg *aMsg, Error aResult)
|
|||||||
exit:
|
exit:
|
||||||
if ((error == kErrorNone) && (status == kStatusSuccess))
|
if ((error == kErrorNone) && (status == kStatusSuccess))
|
||||||
{
|
{
|
||||||
// Send an MLR request for any remaining unregistered addresses.
|
SendNextRequest();
|
||||||
ScheduleSend(0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If a registration attempt fails, retry it after a random
|
// If a registration attempt fails, retry it after a random
|
||||||
// delay (same as re-registration delay).
|
// delay (same as re-registration delay).
|
||||||
|
|
||||||
if (Get<BackboneRouter::Leader>().HasPrimary())
|
ScheduleTimerForReregistrationDelay();
|
||||||
{
|
|
||||||
ScheduleSend(Get<BackboneRouter::Leader>().GetConfig().SelectRandomReregistrationDelay());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,94 +674,58 @@ exit:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::HandleTimeTick(void)
|
void Manager::ScheduleTimerForReregistrationDelay(void)
|
||||||
{
|
{
|
||||||
if (mSendDelay > 0 && --mSendDelay == 0)
|
mTimer.Start(Time::SecToMsec(Get<BackboneRouter::Leader>().GetConfig().SelectRandomReregistrationDelay()));
|
||||||
{
|
|
||||||
Send();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
|
|
||||||
{
|
|
||||||
Reregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateTimeTickerRegistration();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Reregister(void)
|
void Manager::HandleTimer(void)
|
||||||
{
|
{
|
||||||
LogInfo("Reregister");
|
switch (GetState())
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
|
||||||
for (Ip6::Netif::MulticastAddress &addr : Get<ThreadNetif>().GetMulticastAddresses())
|
|
||||||
{
|
{
|
||||||
if (addr.IsMlrCandidate())
|
case kStateNewAddrToRegister:
|
||||||
|
if (mRenewTime > TimerMilli::GetNow())
|
||||||
{
|
{
|
||||||
addr.SetMlrRegistered(false);
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
|
||||||
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
|
|
||||||
{
|
|
||||||
child.ClearMlrRegisteredStateOnAllIp6Addresses();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ScheduleSend(0);
|
OT_FALL_THROUGH;
|
||||||
|
|
||||||
ScheduleNextRegistration(kRenew);
|
case kStateRegistered:
|
||||||
|
EnterState(kStateToRegisterAll);
|
||||||
|
|
||||||
|
OT_FALL_THROUGH;
|
||||||
|
|
||||||
|
case kStateToRegisterAll:
|
||||||
|
case kStateRegistering:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kStateStopped:
|
||||||
|
case kStateIdle:
|
||||||
|
ExitNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
SendNextRequest();
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Manager::DetermineRenewDelay(void)
|
void Manager::DetermineRenewTime(void)
|
||||||
{
|
{
|
||||||
// As per Thread spec, the renew delay is randomly chosen
|
// As per Thread spec, the renew delay is randomly chosen
|
||||||
// between (0.5 * MLR-Timeout) and (MLR-Timeout - 9 seconds).
|
// between (0.5 * MLR-Timeout) and (MLR-Timeout - 9 seconds).
|
||||||
// The `kRenewGuardTime`(9 sec) allows time for transmission,
|
// The `RenewGuardTime`(9 sec) allows time for transmission,
|
||||||
// potential retransmissions, and acknowledgment before the
|
// potential retransmissions, and acknowledgment before the
|
||||||
// actual timeout.
|
// actual timeout.
|
||||||
|
|
||||||
uint32_t timeout = Get<BackboneRouter::Leader>().GetConfig().GetMlrTimeout();
|
uint32_t timeout;
|
||||||
|
|
||||||
timeout = Clamp<uint32_t>(timeout, BackboneRouter::kMinMlrTimeout, kLongRenewTimeout);
|
timeout = Get<BackboneRouter::Leader>().GetConfig().GetMlrTimeout();
|
||||||
|
timeout = Time::SecToMsec(Clamp<uint32_t>(timeout, BackboneRouter::kMinMlrTimeout, kLongRenewTimeout));
|
||||||
|
|
||||||
return Random::NonCrypto::GenerateInClosedRange<uint32_t>((timeout / 2) + 1, timeout - kRenewGuardTime);
|
mRenewTime =
|
||||||
}
|
mStartTime + Random::NonCrypto::GenerateInClosedRange((timeout / 2) + 1, timeout - kRenewGuardTimeInMsec);
|
||||||
|
|
||||||
void Manager::ScheduleNextRegistration(RegistrationRequest aRequest)
|
|
||||||
{
|
|
||||||
uint32_t delay;
|
|
||||||
|
|
||||||
if (!ShouldRegister())
|
|
||||||
{
|
|
||||||
mReregistrationDelay = 0;
|
|
||||||
ExitNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (aRequest)
|
|
||||||
{
|
|
||||||
case kReregister:
|
|
||||||
delay = Get<BackboneRouter::Leader>().GetConfig().SelectRandomReregistrationDelay();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kRenew:
|
|
||||||
delay = DetermineRenewDelay();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ExitNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mReregistrationDelay == 0 || mReregistrationDelay > delay)
|
|
||||||
{
|
|
||||||
mReregistrationDelay = delay;
|
|
||||||
|
|
||||||
LogDebg("ScheduleNextRegistration() delay:%lu", ToUlong(delay));
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
UpdateTimeTickerRegistration();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Mlr
|
} // namespace Mlr
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
#include "common/locator.hpp"
|
#include "common/locator.hpp"
|
||||||
#include "common/non_copyable.hpp"
|
#include "common/non_copyable.hpp"
|
||||||
#include "common/notifier.hpp"
|
#include "common/notifier.hpp"
|
||||||
#include "common/time_ticker.hpp"
|
|
||||||
#include "common/timer.hpp"
|
#include "common/timer.hpp"
|
||||||
#include "net/netif.hpp"
|
#include "net/netif.hpp"
|
||||||
#include "thread/child.hpp"
|
#include "thread/child.hpp"
|
||||||
@@ -79,7 +78,6 @@ namespace Mlr {
|
|||||||
class Manager : public InstanceLocator, private NonCopyable
|
class Manager : public InstanceLocator, private NonCopyable
|
||||||
{
|
{
|
||||||
friend class ot::Notifier;
|
friend class ot::Notifier;
|
||||||
friend class ot::TimeTicker;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef otIp6RegisterMulticastListenersCallback RegisterCallback;
|
typedef otIp6RegisterMulticastListenersCallback RegisterCallback;
|
||||||
@@ -142,19 +140,40 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t kLongRenewTimeout = 4 * Time::kOneHourInSec; // `MLR_TIMEOUT_LONG` (in sec)
|
// Delays (in msec) applied before registration attempts when new
|
||||||
static constexpr uint32_t kRenewGuardTime = 9; // (in sec).
|
// `Netif` or child multicast addresses are added. The longer
|
||||||
|
// delay for child addresses allows the parent to aggregate
|
||||||
|
// multiple address updates into a single MLR request.
|
||||||
|
static constexpr uint32_t kMaxNewNetifAddrRegistraionDelay = 100;
|
||||||
|
static constexpr uint32_t kMinNewChildAddrRegistrationDelay = 750;
|
||||||
|
static constexpr uint32_t kMaxNewChildAddrRegistrationDelay = 5000;
|
||||||
|
|
||||||
enum RegistrationRequest : uint8_t
|
static constexpr uint32_t kLongRenewTimeout = 4 * Time::kOneHourInSec; // `MLR_TIMEOUT_LONG` (in sec)
|
||||||
|
static constexpr uint32_t kRenewGuardTimeInMsec = 9 * Time::kOneSecondInMsec; // (in msec)
|
||||||
|
static constexpr uint32_t kSendFailureRetryDelay = 1000; // (in msec)
|
||||||
|
|
||||||
|
enum State : uint8_t
|
||||||
{
|
{
|
||||||
kReregister,
|
kStateStopped, // Manager is stopped (e.g., no PBBR).
|
||||||
kRenew,
|
kStateIdle, // Started but has no multicast addresses to register.
|
||||||
|
kStateToRegisterAll, // Waiting to register (or re-register) all multicast addresses.
|
||||||
|
kStateRegistering, // MLR.req is sent, waiting for MLR.rsp, or waiting to retry.
|
||||||
|
kStateRegistered, // All addresses are registered, waiting for periodic renewal.
|
||||||
|
kStateNewAddrToRegister, // All were registered, but new addresses are pending registration.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
State GetState(void) const { return mState; }
|
||||||
|
bool IsRunning(void) const { return mState != kStateStopped; }
|
||||||
|
void EnterState(State aState);
|
||||||
|
void UpdateState(void);
|
||||||
void HandleNotifierEvents(Events aEvents);
|
void HandleNotifierEvents(Events aEvents);
|
||||||
bool ShouldRegister(void) const;
|
|
||||||
void DetermineAddressesToRegister(AddressArray &aAddresses) const;
|
void DetermineAddressesToRegister(AddressArray &aAddresses) const;
|
||||||
void Send(void);
|
void SendNextRequest(void);
|
||||||
|
void DetermineRenewTime(void);
|
||||||
|
void ScheduleTimerForReregistrationDelay(void);
|
||||||
|
void ScheduleNewAddrRegistration(uint32_t aMinDelay, uint32_t aMaxDelay);
|
||||||
|
void ProcessResponse(Coap::Msg *aMsg, Error aResult);
|
||||||
|
void HandleTimer(void);
|
||||||
Error SendMessage(const Ip6::Address *aAddresses,
|
Error SendMessage(const Ip6::Address *aAddresses,
|
||||||
uint8_t aAddressNum,
|
uint8_t aAddressNum,
|
||||||
const uint32_t *aTimeout,
|
const uint32_t *aTimeout,
|
||||||
@@ -162,8 +181,6 @@ private:
|
|||||||
|
|
||||||
DeclareTmfResponseHandlerIn(Manager, HandleResponse);
|
DeclareTmfResponseHandlerIn(Manager, HandleResponse);
|
||||||
|
|
||||||
uint32_t DetermineRenewDelay(void);
|
|
||||||
|
|
||||||
static Error ParseResponse(Error aResult, Coap::Msg *aMsg, uint8_t &aStatus, AddressArray &aFailedAddresses);
|
static Error ParseResponse(Error aResult, Coap::Msg *aMsg, uint8_t &aStatus, AddressArray &aFailedAddresses);
|
||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||||
@@ -176,31 +193,20 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
bool IsAddressRegisteredByAnyChild(const Ip6::Address &aAddress) const
|
bool IsAddressRegisteredByAnyChild(const Ip6::Address &aAddress) const;
|
||||||
{
|
|
||||||
return IsAddressRegisteredByAnyChildExcept(aAddress, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAddressRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const;
|
bool IsAddressRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ScheduleSend(uint16_t aDelay);
|
using DelayTimer = TimerMilliIn<Manager, &Manager::HandleTimer>;
|
||||||
void UpdateTimeTickerRegistration(void);
|
|
||||||
void ScheduleNextRegistration(RegistrationRequest aRequest);
|
|
||||||
void Reregister(void);
|
|
||||||
void HandleTimeTick(void);
|
|
||||||
|
|
||||||
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||||
Callback<RegisterCallback> mRegisterCallback;
|
Callback<RegisterCallback> mRegisterCallback;
|
||||||
|
bool mRegisterPending;
|
||||||
#endif
|
#endif
|
||||||
|
State mState;
|
||||||
uint32_t mReregistrationDelay;
|
TimeMilli mStartTime;
|
||||||
uint16_t mSendDelay;
|
TimeMilli mRenewTime;
|
||||||
|
DelayTimer mTimer;
|
||||||
bool mPending : 1;
|
|
||||||
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
|
||||||
bool mRegisterPending : 1;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Mlr
|
} // namespace Mlr
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ ot_nexus_test(mac_scan "core;nexus")
|
|||||||
ot_nexus_test(mle_router_role_allowed "core;nexus")
|
ot_nexus_test(mle_router_role_allowed "core;nexus")
|
||||||
ot_nexus_test(mle_blocking_downgrade "core;nexus")
|
ot_nexus_test(mle_blocking_downgrade "core;nexus")
|
||||||
ot_nexus_test(mle_msg_key_seq_jump "core;nexus")
|
ot_nexus_test(mle_msg_key_seq_jump "core;nexus")
|
||||||
ot_nexus_test(mlr_redundant "core;nexus")
|
ot_nexus_test(mlr_manager "core;nexus")
|
||||||
ot_nexus_test(nat64_translator "core;nexus")
|
ot_nexus_test(nat64_translator "core;nexus")
|
||||||
ot_nexus_test(netdata_publisher "core;nexus")
|
ot_nexus_test(netdata_publisher "core;nexus")
|
||||||
ot_nexus_test(on_mesh_prefix "core;nexus")
|
ot_nexus_test(on_mesh_prefix "core;nexus")
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ namespace ot {
|
|||||||
namespace Nexus {
|
namespace Nexus {
|
||||||
|
|
||||||
static constexpr uint32_t kFormNetworkTime = 10 * 1000;
|
static constexpr uint32_t kFormNetworkTime = 10 * 1000;
|
||||||
static constexpr uint32_t kAttachToRouterTime = 200 * 1000;
|
static constexpr uint32_t kAttachAsRouterTime = 200 * 1000;
|
||||||
|
static constexpr uint32_t kAttachAsSedTime = 10 * 1000;
|
||||||
static constexpr uint32_t kMlrRegistrationTime = 10 * 1000;
|
static constexpr uint32_t kMlrRegistrationTime = 10 * 1000;
|
||||||
|
|
||||||
struct Context
|
struct Context
|
||||||
@@ -45,7 +46,7 @@ struct Context
|
|||||||
Context(void) { mMlrReqCount = 0; }
|
Context(void) { mMlrReqCount = 0; }
|
||||||
|
|
||||||
uint32_t mMlrReqCount;
|
uint32_t mMlrReqCount;
|
||||||
Ip6::Address mSharedAddress;
|
Ip6::Address mTargetAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Error BrCoapInterceptor(void *aContext, const Coap::Msg &aMsg)
|
static Error BrCoapInterceptor(void *aContext, const Coap::Msg &aMsg)
|
||||||
@@ -70,7 +71,7 @@ static Error BrCoapInterceptor(void *aContext, const Coap::Msg &aMsg)
|
|||||||
Log(" - %s", address.ToString().AsCString());
|
Log(" - %s", address.ToString().AsCString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addresses.Contains(context->mSharedAddress))
|
if (addresses.Contains(context->mTargetAddress))
|
||||||
{
|
{
|
||||||
context->mMlrReqCount++;
|
context->mMlrReqCount++;
|
||||||
}
|
}
|
||||||
@@ -115,20 +116,25 @@ void TestMlrRedundant(void)
|
|||||||
VerifyOrQuit(br.Get<Mle::Mle>().IsLeader());
|
VerifyOrQuit(br.Get<Mle::Mle>().IsLeader());
|
||||||
|
|
||||||
router.Join(br, Node::kAsFtd);
|
router.Join(br, Node::kAsFtd);
|
||||||
nexus.AdvanceTime(kAttachToRouterTime);
|
nexus.AdvanceTime(kAttachAsRouterTime);
|
||||||
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
|
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
|
||||||
|
|
||||||
sed1.Join(router, Node::kAsSed);
|
sed1.Join(router, Node::kAsSed);
|
||||||
sed2.Join(router, Node::kAsSed);
|
sed2.Join(router, Node::kAsSed);
|
||||||
sed3.Join(router, Node::kAsSed);
|
sed3.Join(router, Node::kAsSed);
|
||||||
nexus.AdvanceTime(kAttachToRouterTime);
|
|
||||||
|
SuccessOrQuit(sed1.Get<DataPollSender>().SetExternalPollPeriod(5 * Time::kOneSecondInMsec));
|
||||||
|
SuccessOrQuit(sed2.Get<DataPollSender>().SetExternalPollPeriod(5 * Time::kOneSecondInMsec));
|
||||||
|
SuccessOrQuit(sed3.Get<DataPollSender>().SetExternalPollPeriod(5 * Time::kOneSecondInMsec));
|
||||||
|
|
||||||
|
nexus.AdvanceTime(kAttachAsSedTime);
|
||||||
|
|
||||||
VerifyOrQuit(sed1.Get<Mle::Mle>().IsChild());
|
VerifyOrQuit(sed1.Get<Mle::Mle>().IsChild());
|
||||||
VerifyOrQuit(sed2.Get<Mle::Mle>().IsChild());
|
VerifyOrQuit(sed2.Get<Mle::Mle>().IsChild());
|
||||||
VerifyOrQuit(sed3.Get<Mle::Mle>().IsChild());
|
VerifyOrQuit(sed3.Get<Mle::Mle>().IsChild());
|
||||||
|
|
||||||
SuccessOrQuit(sharedAddress.FromString("ff04::1"));
|
SuccessOrQuit(sharedAddress.FromString("ff04::1"));
|
||||||
context.mSharedAddress = sharedAddress;
|
context.mTargetAddress = sharedAddress;
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Log("Set an interceptor on leader to count incoming MLR.req messages");
|
Log("Set an interceptor on leader to count incoming MLR.req messages");
|
||||||
@@ -164,12 +170,87 @@ void TestMlrRedundant(void)
|
|||||||
VerifyOrQuit(context.mMlrReqCount == 1);
|
VerifyOrQuit(context.mMlrReqCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestMlrState(void)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This test verifies the state machine transitions and timing behavior of the MLR manager.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Core nexus;
|
||||||
|
Node &br = nexus.CreateNode();
|
||||||
|
Node &router = nexus.CreateNode();
|
||||||
|
Ip6::Address firstAddress;
|
||||||
|
Ip6::Address newAddress;
|
||||||
|
BackboneRouter::Config config;
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
nexus.AdvanceTime(0);
|
||||||
|
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelNote));
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Log("Form topology");
|
||||||
|
|
||||||
|
AllowLinkBetween(br, router);
|
||||||
|
br.Form();
|
||||||
|
nexus.AdvanceTime(kFormNetworkTime);
|
||||||
|
router.Join(br, Node::kAsFtd);
|
||||||
|
nexus.AdvanceTime(kAttachAsRouterTime);
|
||||||
|
|
||||||
|
SuccessOrQuit(firstAddress.FromString("ff04::1"));
|
||||||
|
context.mTargetAddress = firstAddress;
|
||||||
|
br.Get<Tmf::Agent>().SetInterceptor(BrCoapInterceptor, &context);
|
||||||
|
|
||||||
|
Log("Enable BBR and verify initial registration");
|
||||||
|
br.Get<BackboneRouter::Local>().SetEnabled(true);
|
||||||
|
SuccessOrQuit(router.Get<Ip6::Netif>().SubscribeExternalMulticast(firstAddress));
|
||||||
|
|
||||||
|
nexus.AdvanceTime(kMlrRegistrationTime);
|
||||||
|
VerifyOrQuit(context.mMlrReqCount == 1);
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Log("Add new address and verify quick registration");
|
||||||
|
|
||||||
|
SuccessOrQuit(newAddress.FromString("ff04::100"));
|
||||||
|
context.mTargetAddress = newAddress;
|
||||||
|
context.mMlrReqCount = 0;
|
||||||
|
|
||||||
|
SuccessOrQuit(router.Get<Ip6::Netif>().SubscribeExternalMulticast(newAddress));
|
||||||
|
|
||||||
|
// Verify it is registered quickly (within short aggregation window)
|
||||||
|
nexus.AdvanceTime(1000);
|
||||||
|
VerifyOrQuit(context.mMlrReqCount == 1);
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Log("Update MLR Timeout and verify renewal time update");
|
||||||
|
|
||||||
|
config = br.Get<BackboneRouter::Leader>().GetConfig();
|
||||||
|
|
||||||
|
Log("Initial MLR timeout: %lu", ToUlong(br.Get<BackboneRouter::Leader>().GetConfig().GetMlrTimeout()));
|
||||||
|
|
||||||
|
// We set a short MLR timeout (300 seconds is the minimum).
|
||||||
|
config.mReregistrationDelay = 10;
|
||||||
|
config.mMlrTimeout = 300;
|
||||||
|
SuccessOrQuit(br.Get<BackboneRouter::Local>().SetConfig(config));
|
||||||
|
|
||||||
|
context.mTargetAddress = firstAddress;
|
||||||
|
context.mMlrReqCount = 0;
|
||||||
|
|
||||||
|
Log("Wait for renewal registration");
|
||||||
|
|
||||||
|
// Renewal happens between (0.5 * 300) = 150s and (300 - 9) = 291s.
|
||||||
|
nexus.AdvanceTime(300 * Time::kOneSecondInMsec);
|
||||||
|
|
||||||
|
VerifyOrQuit(context.mMlrReqCount >= 1);
|
||||||
|
Log("Renewal registration successful");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Nexus
|
} // namespace Nexus
|
||||||
} // namespace ot
|
} // namespace ot
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
ot::Nexus::TestMlrRedundant();
|
ot::Nexus::TestMlrRedundant();
|
||||||
|
ot::Nexus::TestMlrState();
|
||||||
printf("All tests passed\n");
|
printf("All tests passed\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user