/* * 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 managing DUA. */ #include "dua_manager.hpp" #if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_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 "common/settings.hpp" #include "net/ip6_address.hpp" #include "thread/mle_types.hpp" #include "thread/thread_netif.hpp" #include "thread/thread_tlvs.hpp" #include "thread/uri_paths.hpp" #include "utils/slaac_address.hpp" namespace ot { RegisterLogModule("DuaManager"); DuaManager::DuaManager(Instance &aInstance) : InstanceLocator(aInstance) , mRegistrationTask(aInstance, DuaManager::HandleRegistrationTask) , mDuaNotification(UriPath::kDuaRegistrationNotify, &DuaManager::HandleDuaNotification, this) , mIsDuaPending(false) #if OPENTHREAD_CONFIG_DUA_ENABLE , mDuaState(kNotExist) , mDadCounter(0) , mLastRegistrationTime(0) #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE , mChildIndexDuaRegistering(Mle::kMaxChildren) #endif { mDelay.mValue = 0; #if OPENTHREAD_CONFIG_DUA_ENABLE mDomainUnicastAddress.InitAsThreadOriginGlobalScope(); mFixedDuaInterfaceIdentifier.Clear(); #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE mChildDuaMask.Clear(); mChildDuaRegisteredMask.Clear(); #endif Get().AddResource(mDuaNotification); } void DuaManager::HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixState aState) { if ((aState == BackboneRouter::Leader::kDomainPrefixRemoved) || (aState == BackboneRouter::Leader::kDomainPrefixRefreshed)) { if (mIsDuaPending) { IgnoreError(Get().AbortTransaction(&DuaManager::HandleDuaResponse, this)); } #if OPENTHREAD_CONFIG_DUA_ENABLE RemoveDomainUnicastAddress(); #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE if (mChildDuaMask.HasAny()) { mChildDuaMask.Clear(); mChildDuaRegisteredMask.Clear(); } #endif } #if OPENTHREAD_CONFIG_DUA_ENABLE switch (aState) { case BackboneRouter::Leader::kDomainPrefixUnchanged: // In case removed for some reason e.g. the kDuaInvalid response from PBBR forcely VerifyOrExit(!Get().HasUnicastAddress(GetDomainUnicastAddress())); OT_FALL_THROUGH; case BackboneRouter::Leader::kDomainPrefixRefreshed: case BackboneRouter::Leader::kDomainPrefixAdded: { const Ip6::Prefix *prefix = Get().GetDomainPrefix(); OT_ASSERT(prefix != nullptr); mDomainUnicastAddress.mPrefixLength = prefix->GetLength(); mDomainUnicastAddress.GetAddress().Clear(); mDomainUnicastAddress.GetAddress().SetPrefix(*prefix); } break; default: ExitNow(); } // Apply cached DUA Interface Identifier manually specified. if (IsFixedDuaInterfaceIdentifierSet()) { mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier); } else { SuccessOrExit(GenerateDomainUnicastAddressIid()); } AddDomainUnicastAddress(); exit: return; #endif } #if OPENTHREAD_CONFIG_DUA_ENABLE Error DuaManager::GenerateDomainUnicastAddressIid(void) { Error error; uint8_t dadCounter = mDadCounter; if ((error = Get().GenerateIid(mDomainUnicastAddress, nullptr, 0, &dadCounter)) == kErrorNone) { if (dadCounter != mDadCounter) { mDadCounter = dadCounter; IgnoreError(Store()); } LogInfo("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString()); } else { LogWarn("Generate DUA: %s", ErrorToString(error)); } return error; } Error DuaManager::SetFixedDuaInterfaceIdentifier(const Ip6::InterfaceIdentifier &aIid) { Error error = kErrorNone; VerifyOrExit(!aIid.IsReserved(), error = kErrorInvalidArgs); VerifyOrExit(mFixedDuaInterfaceIdentifier.IsUnspecified() || mFixedDuaInterfaceIdentifier != aIid); mFixedDuaInterfaceIdentifier = aIid; LogInfo("Set DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString()); if (Get().HasUnicastAddress(GetDomainUnicastAddress())) { RemoveDomainUnicastAddress(); mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier); AddDomainUnicastAddress(); } exit: return error; } void DuaManager::ClearFixedDuaInterfaceIdentifier(void) { // Nothing to clear. VerifyOrExit(IsFixedDuaInterfaceIdentifierSet()); if (GetDomainUnicastAddress().GetIid() == mFixedDuaInterfaceIdentifier && Get().HasUnicastAddress(GetDomainUnicastAddress())) { RemoveDomainUnicastAddress(); if (GenerateDomainUnicastAddressIid() == kErrorNone) { AddDomainUnicastAddress(); } } LogInfo("Cleared DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString()); mFixedDuaInterfaceIdentifier.Clear(); exit: return; } void DuaManager::Restore(void) { Settings::DadInfo dadInfo; SuccessOrExit(Get().Read(dadInfo)); mDadCounter = dadInfo.GetDadCounter(); exit: return; } Error DuaManager::Store(void) { Settings::DadInfo dadInfo; dadInfo.SetDadCounter(mDadCounter); return Get().Save(dadInfo); } void DuaManager::AddDomainUnicastAddress(void) { mDuaState = kToRegister; mLastRegistrationTime = TimerMilli::GetNow(); Get().AddUnicastAddress(mDomainUnicastAddress); } void DuaManager::RemoveDomainUnicastAddress(void) { if (mDuaState == kRegistering && mIsDuaPending) { IgnoreError(Get().AbortTransaction(&DuaManager::HandleDuaResponse, this)); } mDuaState = kNotExist; mDomainUnicastAddress.mPreferred = false; Get().RemoveUnicastAddress(mDomainUnicastAddress); } void DuaManager::UpdateRegistrationDelay(uint8_t aDelay) { if (mDelay.mFields.mRegistrationDelay == 0 || mDelay.mFields.mRegistrationDelay > aDelay) { mDelay.mFields.mRegistrationDelay = aDelay; LogDebg("update regdelay %d", mDelay.mFields.mRegistrationDelay); UpdateTimeTickerRegistration(); } } void DuaManager::NotifyDuplicateDomainUnicastAddress(void) { RemoveDomainUnicastAddress(); mDadCounter++; if (GenerateDomainUnicastAddressIid() == kErrorNone) { AddDomainUnicastAddress(); } } #endif // OPENTHREAD_CONFIG_DUA_ENABLE void DuaManager::UpdateReregistrationDelay(void) { uint16_t delay = 0; otBackboneRouterConfig config; VerifyOrExit(Get().GetConfig(config) == kErrorNone); delay = config.mReregistrationDelay > 1 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay) : 1; if (mDelay.mFields.mReregistrationDelay == 0 || mDelay.mFields.mReregistrationDelay > delay) { mDelay.mFields.mReregistrationDelay = delay; UpdateTimeTickerRegistration(); LogDebg("update reregdelay %d", mDelay.mFields.mReregistrationDelay); } exit: return; } void DuaManager::UpdateCheckDelay(uint8_t aDelay) { if (mDelay.mFields.mCheckDelay == 0 || mDelay.mFields.mCheckDelay > aDelay) { mDelay.mFields.mCheckDelay = aDelay; LogDebg("update checkdelay %d", mDelay.mFields.mCheckDelay); UpdateTimeTickerRegistration(); } } void DuaManager::HandleNotifierEvents(Events aEvents) { Mle::MleRouter &mle = Get(); VerifyOrExit(mle.IsAttached(), mDelay.mValue = 0); if (aEvents.Contains(kEventThreadRoleChanged)) { if (mle.HasRestored()) { UpdateReregistrationDelay(); } #if OPENTHREAD_CONFIG_DUA_ENABLE && OPENTHREAD_FTD else if (mle.IsRouter()) { // Wait for link establishment with neighboring routers. UpdateRegistrationDelay(kNewRouterRegistrationDelay); } else if (mle.IsExpectedToBecomeRouterSoon()) { // Will check again in case the device decides to stay REED when jitter timeout expires. UpdateRegistrationDelay(mle.GetRouterSelectionJitterTimeout() + kNewRouterRegistrationDelay + 1); } #endif } #if OPENTHREAD_CONFIG_DUA_ENABLE if (aEvents.ContainsAny(kEventIp6AddressAdded)) { UpdateRegistrationDelay(kNewDuaRegistrationDelay); } #endif exit: return; } void DuaManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, const BackboneRouter::BackboneRouterConfig &aConfig) { OT_UNUSED_VARIABLE(aConfig); if (aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg) { UpdateReregistrationDelay(); } } void DuaManager::HandleTimeTick(void) { bool attempt = false; #if OPENTHREAD_CONFIG_DUA_ENABLE LogDebg("regdelay %d, reregdelay %d, checkdelay %d", mDelay.mFields.mRegistrationDelay, mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay); if ((mDuaState != kNotExist) && (TimerMilli::GetNow() > (mLastRegistrationTime + TimeMilli::SecToMsec(Mle::kDuaDadPeriod)))) { mDomainUnicastAddress.mPreferred = true; } if ((mDelay.mFields.mRegistrationDelay > 0) && (--mDelay.mFields.mRegistrationDelay == 0)) { attempt = true; } #else LogDebg("reregdelay %d, checkdelay %d", mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay); #endif if ((mDelay.mFields.mCheckDelay > 0) && (--mDelay.mFields.mCheckDelay == 0)) { attempt = true; } if ((mDelay.mFields.mReregistrationDelay > 0) && (--mDelay.mFields.mReregistrationDelay == 0)) { #if OPENTHREAD_CONFIG_DUA_ENABLE if (mDuaState != kNotExist) { mDuaState = kToRegister; } #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE mChildDuaRegisteredMask.Clear(); #endif attempt = true; } if (attempt) { mRegistrationTask.Post(); } UpdateTimeTickerRegistration(); } void DuaManager::HandleRegistrationTask(Tasklet &aTasklet) { aTasklet.Get().PerformNextRegistration(); } void DuaManager::UpdateTimeTickerRegistration(void) { if (mDelay.mValue == 0) { Get().UnregisterReceiver(TimeTicker::kDuaManager); } else { Get().RegisterReceiver(TimeTicker::kDuaManager); } } void DuaManager::PerformNextRegistration(void) { Error error = kErrorNone; Mle::MleRouter & mle = Get(); Coap::Message * message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Ip6::Address dua; VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState); VerifyOrExit(Get().HasPrimary(), error = kErrorInvalidState); // Only allow one outgoing DUA.req VerifyOrExit(!mIsDuaPending, error = kErrorBusy); // Only send DUA.req when necessary #if OPENTHREAD_CONFIG_DUA_ENABLE #if OPENTHREAD_FTD VerifyOrExit(mle.IsRouterOrLeader() || !mle.IsExpectedToBecomeRouterSoon(), error = kErrorInvalidState); #endif VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState); #endif // OPENTHREAD_CONFIG_DUA_ENABLE { bool needReg = false; #if OPENTHREAD_CONFIG_DUA_ENABLE needReg = (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0); #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE needReg = needReg || (mChildDuaMask.HasAny() && mChildDuaMask != mChildDuaRegisteredMask); #endif VerifyOrExit(needReg, error = kErrorNotFound); } // Prepare DUA.req message = Get().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationRequest); VerifyOrExit(message != nullptr, error = kErrorNoBufs); #if OPENTHREAD_CONFIG_DUA_ENABLE if (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0) { dua = GetDomainUnicastAddress(); SuccessOrExit(error = Tlv::Append(*message, dua)); SuccessOrExit(error = Tlv::Append(*message, mle.GetMeshLocal64().GetIid())); mDuaState = kRegistering; mLastRegistrationTime = TimerMilli::GetNow(); } else #endif // OPENTHREAD_CONFIG_DUA_ENABLE { #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE uint32_t lastTransactionTime; const Ip6::Address *duaPtr = nullptr; Child * child = nullptr; OT_ASSERT(mChildIndexDuaRegistering == Mle::kMaxChildren); for (Child &iter : Get().Iterate(Child::kInStateValid)) { uint16_t childIndex = Get().GetChildIndex(iter); if (mChildDuaMask.Get(childIndex) && !mChildDuaRegisteredMask.Get(childIndex)) { mChildIndexDuaRegistering = childIndex; break; } } child = Get().GetChildAtIndex(mChildIndexDuaRegistering); duaPtr = child->GetDomainUnicastAddress(); OT_ASSERT(duaPtr != nullptr); dua = *duaPtr; SuccessOrExit(error = Tlv::Append(*message, dua)); SuccessOrExit(error = Tlv::Append(*message, child->GetMeshLocalIid())); lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child->GetLastHeard()); SuccessOrExit(error = Tlv::Append(*message, lastTransactionTime)); #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE } if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1()) { uint8_t pbbrServiceId; SuccessOrExit(error = Get().GetServiceId(pbbrServiceId)); SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr())); } else { messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(), Get().GetServer16()); } messageInfo.SetSockAddrToRloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, &DuaManager::HandleDuaResponse, this)); mIsDuaPending = true; mRegisteringDua = dua; mDelay.mValue = 0; // Generally Thread 1.2 Router would send DUA.req on behalf for DUA registered by its MTD child. // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send DUA.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().IsRxOnWhenIdle()) { Get().SendFastPolls(); } LogInfo("Sent DUA.req for DUA %s", dua.ToString().AsCString()); exit: if (error == kErrorNoBufs) { UpdateCheckDelay(Mle::kNoBufDelay); } LogInfo("PerformNextRegistration: %s", ErrorToString(error)); FreeMessageOnError(message, error); } void DuaManager::HandleDuaResponse(void * aContext, otMessage * aMessage, const otMessageInfo *aMessageInfo, Error aResult) { static_cast(aContext)->HandleDuaResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aResult); } void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { OT_UNUSED_VARIABLE(aMessageInfo); Error error; mIsDuaPending = false; #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE mChildIndexDuaRegistering = Mle::kMaxChildren; #endif if (aResult == kErrorResponseTimeout) { UpdateCheckDelay(Mle::KResponseTimeoutDelay); ExitNow(error = aResult); } VerifyOrExit(aResult == kErrorNone, error = kErrorParse); OT_ASSERT(aMessage != nullptr); VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged || aMessage->GetCode() >= Coap::kCodeBadRequest, error = kErrorParse); error = ProcessDuaResponse(*aMessage); exit: if (error != kErrorResponseTimeout) { mRegistrationTask.Post(); } LogInfo("Received DUA.rsp: %s", ErrorToString(error)); } void DuaManager::HandleDuaNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { static_cast(aContext)->HandleDuaNotification(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); } void DuaManager::HandleDuaNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); Error error; VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse); if (aMessage.IsConfirmable() && Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { LogInfo("Sent DUA.ntf acknowledgment"); } error = ProcessDuaResponse(aMessage); exit: OT_UNUSED_VARIABLE(error); LogInfo("Received DUA.ntf: %d", ErrorToString(error)); } Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) { Error error = kErrorNone; Ip6::Address target; uint8_t status; if (aMessage.GetCode() >= Coap::kCodeBadRequest) { status = ThreadStatusTlv::kDuaGeneralFailure; target = mRegisteringDua; } else { SuccessOrExit(error = Tlv::Find(aMessage, status)); SuccessOrExit(error = Tlv::Find(aMessage, target)); } VerifyOrExit(Get().IsDomainUnicast(target), error = kErrorDrop); #if OPENTHREAD_CONFIG_DUA_ENABLE if (Get().HasUnicastAddress(target)) { switch (static_cast(status)) { case ThreadStatusTlv::kDuaSuccess: mLastRegistrationTime = TimerMilli::GetNow(); mDuaState = kRegistered; break; case ThreadStatusTlv::kDuaReRegister: if (Get().HasUnicastAddress(GetDomainUnicastAddress())) { RemoveDomainUnicastAddress(); AddDomainUnicastAddress(); } break; case ThreadStatusTlv::kDuaInvalid: // Domain Prefix might be invalid. RemoveDomainUnicastAddress(); break; case ThreadStatusTlv::kDuaDuplicate: NotifyDuplicateDomainUnicastAddress(); break; case ThreadStatusTlv::kDuaNoResources: case ThreadStatusTlv::kDuaNotPrimary: case ThreadStatusTlv::kDuaGeneralFailure: UpdateReregistrationDelay(); break; } } else #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE { Child * child = nullptr; uint16_t childIndex; for (Child &iter : Get().Iterate(Child::kInStateValid)) { if (iter.HasIp6Address(target)) { child = &iter; break; } } VerifyOrExit(child != nullptr, error = kErrorNotFound); childIndex = Get().GetChildIndex(*child); switch (status) { case ThreadStatusTlv::kDuaSuccess: // Mark as Registered if (mChildDuaMask.Get(childIndex)) { mChildDuaRegisteredMask.Set(childIndex, true); } break; case ThreadStatusTlv::kDuaReRegister: // Parent stops registering for the Child's DUA until next Child Update Request mChildDuaMask.Set(childIndex, false); mChildDuaRegisteredMask.Set(childIndex, false); break; case ThreadStatusTlv::kDuaInvalid: case ThreadStatusTlv::kDuaDuplicate: IgnoreError(child->RemoveIp6Address(target)); mChildDuaMask.Set(childIndex, false); mChildDuaRegisteredMask.Set(childIndex, false); break; case ThreadStatusTlv::kDuaNoResources: case ThreadStatusTlv::kDuaNotPrimary: case ThreadStatusTlv::kDuaGeneralFailure: UpdateReregistrationDelay(); break; } if (status != ThreadStatusTlv::kDuaSuccess) { SendAddressNotification(target, static_cast(status), *child); } } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE exit: UpdateTimeTickerRegistration(); return error; } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE void DuaManager::SendAddressNotification(Ip6::Address & aAddress, ThreadStatusTlv::DuaStatus aStatus, const Child & aChild) { Coap::Message * message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Error error; message = Get().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationNotify); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aStatus)); SuccessOrExit(error = Tlv::Append(*message, aAddress)); messageInfo.SetSockAddrToRlocPeerAddrTo(aChild.GetRloc16()); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); LogInfo("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString()); exit: if (error != kErrorNone) { FreeMessage(message); // TODO: (DUA) (P4) may enhance to guarantee the delivery of DUA.ntf LogWarn("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(), aAddress.ToString().AsCString(), ErrorToString(error)); } } void DuaManager::UpdateChildDomainUnicastAddress(const Child &aChild, Mle::ChildDuaState aState) { uint16_t childIndex = Get().GetChildIndex(aChild); if ((aState == Mle::ChildDuaState::kRemoved || aState == Mle::ChildDuaState::kChanged) && mChildDuaMask.Get(childIndex)) { // Abort on going proxy DUA.req for this child if (mChildIndexDuaRegistering == childIndex) { IgnoreError(Get().AbortTransaction(&DuaManager::HandleDuaResponse, this)); } mChildDuaMask.Set(childIndex, false); mChildDuaRegisteredMask.Set(childIndex, false); } if (aState == Mle::ChildDuaState::kAdded || aState == Mle::ChildDuaState::kChanged || (aState == Mle::ChildDuaState::kUnchanged && !mChildDuaMask.Get(childIndex))) { if (mChildDuaMask == mChildDuaRegisteredMask) { UpdateCheckDelay(Random::NonCrypto::GetUint8InRange(1, Mle::kParentAggregateDelay)); } mChildDuaMask.Set(childIndex, true); mChildDuaRegisteredMask.Set(childIndex, false); } return; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE } // namespace ot #endif // OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)