blob: 29aeaa4d8d30b47d6a29ad1e3e1b2c2749d3b00f [file] [log] [blame]
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements MLR management.
*/
#include "mlr_manager.hpp"
#if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
#include "common/as_core_type.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "net/ip6_address.hpp"
#include "thread/thread_netif.hpp"
#include "thread/uri_paths.hpp"
#include "utils/slaac_address.hpp"
namespace ot {
RegisterLogModule("MlrManager");
MlrManager::MlrManager(Instance &aInstance)
: InstanceLocator(aInstance)
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
, mRegisterMulticastListenersCallback(nullptr)
, mRegisterMulticastListenersContext(nullptr)
#endif
, mReregistrationDelay(0)
, mSendDelay(0)
, mMlrPending(false)
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
, mRegisterMulticastListenersPending(false)
#endif
{
}
void MlrManager::HandleNotifierEvents(Events aEvents)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
if (aEvents.Contains(kEventIp6MulticastSubscribed))
{
UpdateLocalSubscriptions();
}
#endif
if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::MleRouter>().IsChild())
{
// Reregistration after re-attach
UpdateReregistrationDelay(true);
}
}
void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
const BackboneRouter::BackboneRouterConfig &aConfig)
{
OT_UNUSED_VARIABLE(aConfig);
bool needRereg =
aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg;
UpdateReregistrationDelay(needRereg);
}
#if OPENTHREAD_CONFIG_MLR_ENABLE
void MlrManager::UpdateLocalSubscriptions(void)
{
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
// Check multicast addresses are newly listened against Children
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == kMlrStateToRegister && IsAddressMlrRegisteredByAnyChild(addr.GetAddress()))
{
addr.SetMlrState(kMlrStateRegistered);
}
}
#endif
CheckInvariants();
ScheduleSend(0);
}
bool MlrManager::IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const
{
bool ret = false;
OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress() == aAddress && addr.GetMlrState() == kMlrStateRegistered)
{
ExitNow(ret = true);
}
}
exit:
return ret;
}
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
bool MlrManager::IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
{
bool ret = false;
OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
if (&child != aExceptChild && child.HasMlrRegisteredAddress(aAddress))
{
ExitNow(ret = true);
}
}
exit:
return ret;
}
void MlrManager::UpdateProxiedSubscriptions(Child & aChild,
const Ip6::Address *aOldMlrRegisteredAddresses,
uint16_t aOldMlrRegisteredAddressNum)
{
VerifyOrExit(aChild.IsStateValid());
// Search the new multicast addresses and set its flag accordingly
for (const Ip6::Address &address : aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
bool isMlrRegistered = false;
// Check if it's a new multicast address against old addresses
for (size_t i = 0; i < aOldMlrRegisteredAddressNum; i++)
{
if (aOldMlrRegisteredAddresses[i] == address)
{
isMlrRegistered = true;
break;
}
}
#if OPENTHREAD_CONFIG_MLR_ENABLE
// Check if it's a new multicast address against parent Netif
isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(address);
#endif
// Check if it's a new multicast address against other Children
isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(address, &aChild);
aChild.SetAddressMlrState(address, isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister);
}
exit:
LogMulticastAddresses();
CheckInvariants();
if (aChild.HasAnyMlrToRegisterAddress())
{
ScheduleSend(Random::NonCrypto::GetUint16InRange(1, Mle::kParentAggregateDelay));
}
}
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
void MlrManager::ScheduleSend(uint16_t aDelay)
{
OT_ASSERT(!mMlrPending || mSendDelay == 0);
VerifyOrExit(!mMlrPending);
if (aDelay == 0)
{
mSendDelay = 0;
SendMulticastListenerRegistration();
}
else if (mSendDelay == 0 || mSendDelay > aDelay)
{
mSendDelay = aDelay;
}
UpdateTimeTickerRegistration();
exit:
return;
}
void MlrManager::UpdateTimeTickerRegistration(void)
{
if (mSendDelay == 0 && mReregistrationDelay == 0)
{
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMlrManager);
}
else
{
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMlrManager);
}
}
void MlrManager::SendMulticastListenerRegistration(void)
{
Error error;
Mle::MleRouter &mle = Get<Mle::MleRouter>();
Ip6::Address addresses[kIp6AddressesNumMax];
uint8_t addressesNum = 0;
VerifyOrExit(!mMlrPending, error = kErrorBusy);
VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
#if OPENTHREAD_CONFIG_MLR_ENABLE
// Append Netif multicast addresses
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addressesNum >= kIp6AddressesNumMax)
{
break;
}
if (addr.GetMlrState() == kMlrStateToRegister)
{
AppendToUniqueAddressList(addresses, addressesNum, addr.GetAddress());
addr.SetMlrState(kMlrStateRegistering);
}
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
// Append Child multicast addresses
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
if (addressesNum >= kIp6AddressesNumMax)
{
break;
}
if (!child.HasAnyMlrToRegisterAddress())
{
continue;
}
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addressesNum >= kIp6AddressesNumMax)
{
break;
}
if (child.GetAddressMlrState(address) == kMlrStateToRegister)
{
AppendToUniqueAddressList(addresses, addressesNum, address);
child.SetAddressMlrState(address, kMlrStateRegistering);
}
}
}
#endif
VerifyOrExit(addressesNum > 0, error = kErrorNotFound);
SuccessOrExit(
error = SendMulticastListenerRegistrationMessage(
addresses, addressesNum, nullptr, &MlrManager::HandleMulticastListenerRegistrationResponse, this));
mMlrPending = true;
// Generally Thread 1.2 Router would send MLR.req on bebelf 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.
// In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely.
if (!Get<Mle::Mle>().IsRxOnWhenIdle())
{
Get<DataPollSender>().SendFastPolls();
}
exit:
if (error != kErrorNone)
{
SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
if (error == kErrorNoBufs)
{
ScheduleSend(1);
}
}
LogMulticastAddresses();
CheckInvariants();
}
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
Error MlrManager::RegisterMulticastListeners(const otIp6Address * aAddresses,
uint8_t aAddressNum,
const uint32_t * aTimeout,
otIp6RegisterMulticastListenersCallback aCallback,
void * aContext)
{
Error error;
VerifyOrExit(aAddresses != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aAddressNum > 0 && aAddressNum <= kIp6AddressesNumMax, error = kErrorInvalidArgs);
VerifyOrExit(aContext == nullptr || aCallback != nullptr, error = kErrorInvalidArgs);
#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
#else
if (!Get<MeshCoP::Commissioner>().IsActive())
{
LogWarn("MLR.req without active commissioner session for test.");
}
#endif
// Only allow one outstanding registration if callback is specified.
VerifyOrExit(!mRegisterMulticastListenersPending, error = kErrorBusy);
SuccessOrExit(error = SendMulticastListenerRegistrationMessage(
aAddresses, aAddressNum, aTimeout, &MlrManager::HandleRegisterMulticastListenersResponse, this));
mRegisterMulticastListenersPending = true;
mRegisterMulticastListenersCallback = aCallback;
mRegisterMulticastListenersContext = aContext;
exit:
return error;
}
void MlrManager::HandleRegisterMulticastListenersResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
static_cast<MlrManager *>(aContext)->HandleRegisterMulticastListenersResponse(AsCoapMessagePtr(aMessage),
AsCoreTypePtr(aMessageInfo), aResult);
}
void MlrManager::HandleRegisterMulticastListenersResponse(otMessage * aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status;
Error error;
Ip6::Address failedAddresses[kIp6AddressesNumMax];
uint8_t failedAddressNum = 0;
otIp6RegisterMulticastListenersCallback callback = mRegisterMulticastListenersCallback;
void * context = mRegisterMulticastListenersContext;
mRegisterMulticastListenersPending = false;
mRegisterMulticastListenersCallback = nullptr;
mRegisterMulticastListenersContext = nullptr;
error = ParseMulticastListenerRegistrationResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses,
failedAddressNum);
if (callback != nullptr)
{
callback(context, error, status, failedAddresses, failedAddressNum);
}
}
#endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses,
uint8_t aAddressNum,
const uint32_t * aTimeout,
Coap::ResponseHandler aResponseHandler,
void * aResponseContext)
{
OT_UNUSED_VARIABLE(aTimeout);
Error error = kErrorNone;
Mle::MleRouter & mle = Get<Mle::MleRouter>();
Coap::Message * message = nullptr;
Tmf::MessageInfo messageInfo(GetInstance());
Ip6AddressesTlv addressesTlv;
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kMlr);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
addressesTlv.Init();
addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
SuccessOrExit(error = message->Append(addressesTlv));
SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum));
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
if (Get<MeshCoP::Commissioner>().IsActive())
{
SuccessOrExit(
error = Tlv::Append<ThreadCommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
}
if (aTimeout != nullptr)
{
SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, *aTimeout));
}
#else
OT_ASSERT(aTimeout == nullptr);
#endif
if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
{
uint8_t pbbrServiceId;
SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
}
else
{
messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
Get<BackboneRouter::Leader>().GetServer16());
}
messageInfo.SetSockAddrToRloc();
error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
LogInfo("Sent MLR.req: addressNum=%d", aAddressNum);
exit:
LogInfo("SendMulticastListenerRegistrationMessage(): %s", ErrorToString(error));
FreeMessageOnError(message, error);
return error;
}
void MlrManager::HandleMulticastListenerRegistrationResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
static_cast<MlrManager *>(aContext)->HandleMulticastListenerRegistrationResponse(
AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aResult);
}
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
const Ip6::MessageInfo *aMessageInfo,
Error aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status;
Error error;
Ip6::Address failedAddresses[kIp6AddressesNumMax];
uint8_t failedAddressNum = 0;
error = ParseMulticastListenerRegistrationResponse(aResult, aMessage, status, failedAddresses, failedAddressNum);
FinishMulticastListenerRegistration(error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess,
failedAddresses, failedAddressNum);
if (error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess)
{
// keep sending until all multicast addresses are registered.
ScheduleSend(0);
}
else
{
otBackboneRouterConfig 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 (Get<BackboneRouter::Leader>().GetConfig(config) == kErrorNone)
{
reregDelay = config.mReregistrationDelay > 1
? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
: 1;
ScheduleSend(reregDelay);
}
}
}
Error MlrManager::ParseMulticastListenerRegistrationResponse(Error aResult,
Coap::Message *aMessage,
uint8_t & aStatus,
Ip6::Address * aFailedAddresses,
uint8_t & aFailedAddressNum)
{
Error error;
uint16_t addressesOffset, addressesLength;
aStatus = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse);
VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse);
SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(*aMessage, aStatus));
if (ThreadTlv::FindTlvValueOffset(*aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) ==
kErrorNone)
{
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = kErrorParse);
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIp6AddressesNumMax, error = kErrorParse);
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
{
IgnoreError(aMessage->Read(addressesOffset + offset, aFailedAddresses[aFailedAddressNum]));
aFailedAddressNum++;
}
}
VerifyOrExit(aFailedAddressNum == 0 || aStatus != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = kErrorParse);
exit:
LogMlrResponse(aResult, error, aStatus, aFailedAddresses, aFailedAddressNum);
return aResult != kErrorNone ? aResult : error;
}
void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == aFromState)
{
addr.SetMlrState(aToState);
}
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (child.GetAddressMlrState(address) == aFromState)
{
child.SetAddressMlrState(address, aToState);
}
}
}
#endif
}
void MlrManager::FinishMulticastListenerRegistration(bool aSuccess,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum)
{
OT_ASSERT(mMlrPending);
mMlrPending = false;
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == kMlrStateRegistering)
{
bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, addr.GetAddress());
addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister);
}
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (child.GetAddressMlrState(address) == kMlrStateRegistering)
{
bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, address);
child.SetAddressMlrState(address, success ? kMlrStateRegistered : kMlrStateToRegister);
}
}
}
#endif
LogMulticastAddresses();
CheckInvariants();
}
void MlrManager::HandleTimeTick(void)
{
if (mSendDelay > 0 && --mSendDelay == 0)
{
SendMulticastListenerRegistration();
}
if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
{
Reregister();
}
UpdateTimeTickerRegistration();
}
void MlrManager::Reregister(void)
{
LogInfo("MLR Reregister!");
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
CheckInvariants();
ScheduleSend(0);
// Schedule for the next renewing.
UpdateReregistrationDelay(false);
}
void MlrManager::UpdateReregistrationDelay(bool aRereg)
{
Mle::MleRouter &mle = Get<Mle::MleRouter>();
bool needSendMlr = (mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1()) &&
Get<BackboneRouter::Leader>().HasPrimary();
if (!needSendMlr)
{
mReregistrationDelay = 0;
}
else
{
BackboneRouter::BackboneRouterConfig config;
uint32_t reregDelay;
uint32_t effectiveMlrTimeout;
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
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).
effectiveMlrTimeout = config.mMlrTimeout > Mle::kMlrTimeoutMin ? config.mMlrTimeout
: static_cast<uint32_t>(Mle::kMlrTimeoutMin);
reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9);
}
if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay)
{
mReregistrationDelay = reregDelay;
}
}
UpdateTimeTickerRegistration();
LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
mReregistrationDelay);
}
void MlrManager::LogMulticastAddresses(void)
{
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
LogDebg("-------- Multicast Addresses --------");
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
LogDebg("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
LogDebg("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)],
child.GetRloc16());
}
}
#endif
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
}
void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIp6AddressesNumMax],
uint8_t & aAddressNum,
const Ip6::Address &aAddress)
{
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (uint8_t i = 0; i < aAddressNum; i++)
{
if (aAddresses[i] == aAddress)
{
ExitNow();
}
}
#endif
aAddresses[aAddressNum++] = aAddress;
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
exit:
#endif
return;
}
bool MlrManager::AddressListContains(const Ip6::Address *aAddressList,
uint8_t aAddressListSize,
const Ip6::Address &aAddress)
{
bool contains = false;
// An empty address list is treated as if it contains all failed addresses.
VerifyOrExit(aAddressListSize > 0, contains = true);
for (uint8_t i = 0; i < aAddressListSize; i++)
{
if (aAddressList[i] == aAddress)
{
ExitNow(contains = true);
}
}
exit:
return contains;
}
void MlrManager::LogMlrResponse(Error aResult,
Error aError,
uint8_t aStatus,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum)
{
OT_UNUSED_VARIABLE(aResult);
OT_UNUSED_VARIABLE(aError);
OT_UNUSED_VARIABLE(aStatus);
OT_UNUSED_VARIABLE(aFailedAddresses);
OT_UNUSED_VARIABLE(aFailedAddressNum);
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::MlrStatus::kMlrSuccess)
{
LogInfo("Receive MLR.rsp OK");
}
else
{
LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
ErrorToString(aError), aStatus, aFailedAddressNum);
for (uint8_t i = 0; i < aFailedAddressNum; i++)
{
LogWarn("MA failed: %s", aFailedAddresses[i].ToString().AsCString());
}
}
#endif
}
void MlrManager::CheckInvariants(void) const
{
#if OPENTHREAD_EXAMPLES_SIMULATION
uint16_t registeringNum = 0;
OT_ASSERT(!mMlrPending || mSendDelay == 0);
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
registeringNum += (addr.GetMlrState() == kMlrStateRegistering);
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
registeringNum += (child.GetAddressMlrState(address) == kMlrStateRegistering);
}
}
#endif
OT_ASSERT(registeringNum == 0 || mMlrPending);
#endif // OPENTHREAD_EXAMPLES_SIMULATION
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLR_ENABLE