blob: 108f07a73579913583f289267c36243b7550a340 [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 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<Tmf::Agent>().AddResource(mDuaNotification);
}
void DuaManager::HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixState aState)
{
if ((aState == BackboneRouter::Leader::kDomainPrefixRemoved) ||
(aState == BackboneRouter::Leader::kDomainPrefixRefreshed))
{
if (mIsDuaPending)
{
IgnoreError(Get<Tmf::Agent>().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<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()));
OT_FALL_THROUGH;
case BackboneRouter::Leader::kDomainPrefixRefreshed:
case BackboneRouter::Leader::kDomainPrefixAdded:
{
const Ip6::Prefix *prefix = Get<BackboneRouter::Leader>().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<Utils::Slaac>().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<ThreadNetif>().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<ThreadNetif>().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<Settings>().Read(dadInfo));
mDadCounter = dadInfo.GetDadCounter();
exit:
return;
}
Error DuaManager::Store(void)
{
Settings::DadInfo dadInfo;
dadInfo.SetDadCounter(mDadCounter);
return Get<Settings>().Save(dadInfo);
}
void DuaManager::AddDomainUnicastAddress(void)
{
mDuaState = kToRegister;
mLastRegistrationTime = TimerMilli::GetNow();
Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
}
void DuaManager::RemoveDomainUnicastAddress(void)
{
if (mDuaState == kRegistering && mIsDuaPending)
{
IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
}
mDuaState = kNotExist;
mDomainUnicastAddress.mPreferred = false;
Get<ThreadNetif>().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<BackboneRouter::Leader>().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<Mle::MleRouter>();
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<DuaManager>().PerformNextRegistration();
}
void DuaManager::UpdateTimeTickerRegistration(void)
{
if (mDelay.mValue == 0)
{
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kDuaManager);
}
else
{
Get<TimeTicker>().RegisterReceiver(TimeTicker::kDuaManager);
}
}
void DuaManager::PerformNextRegistration(void)
{
Error error = kErrorNone;
Mle::MleRouter & mle = Get<Mle::MleRouter>();
Coap::Message * message = nullptr;
Tmf::MessageInfo messageInfo(GetInstance());
Ip6::Address dua;
VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
VerifyOrExit(Get<BackboneRouter::Leader>().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<Tmf::Agent>().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<ThreadTargetTlv>(*message, dua));
SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*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<ChildTable>().Iterate(Child::kInStateValid))
{
uint16_t childIndex = Get<ChildTable>().GetChildIndex(iter);
if (mChildDuaMask.Get(childIndex) && !mChildDuaRegisteredMask.Get(childIndex))
{
mChildIndexDuaRegistering = childIndex;
break;
}
}
child = Get<ChildTable>().GetChildAtIndex(mChildIndexDuaRegistering);
duaPtr = child->GetDomainUnicastAddress();
OT_ASSERT(duaPtr != nullptr);
dua = *duaPtr;
SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, child->GetMeshLocalIid()));
lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child->GetLastHeard());
SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, lastTransactionTime));
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
}
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();
SuccessOrExit(error = Get<Tmf::Agent>().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<Mle::Mle>().IsRxOnWhenIdle())
{
Get<DataPollSender>().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<DuaManager *>(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<DuaManager *>(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<Tmf::Agent>().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<ThreadStatusTlv>(aMessage, status));
SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
}
VerifyOrExit(Get<BackboneRouter::Leader>().IsDomainUnicast(target), error = kErrorDrop);
#if OPENTHREAD_CONFIG_DUA_ENABLE
if (Get<ThreadNetif>().HasUnicastAddress(target))
{
switch (static_cast<ThreadStatusTlv::DuaStatus>(status))
{
case ThreadStatusTlv::kDuaSuccess:
mLastRegistrationTime = TimerMilli::GetNow();
mDuaState = kRegistered;
break;
case ThreadStatusTlv::kDuaReRegister:
if (Get<ThreadNetif>().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<ChildTable>().Iterate(Child::kInStateValid))
{
if (iter.HasIp6Address(target))
{
child = &iter;
break;
}
}
VerifyOrExit(child != nullptr, error = kErrorNotFound);
childIndex = Get<ChildTable>().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<ThreadStatusTlv::DuaStatus>(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<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationNotify);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aAddress));
messageInfo.SetSockAddrToRlocPeerAddrTo(aChild.GetRloc16());
SuccessOrExit(error = Get<Tmf::Agent>().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<ChildTable>().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<Tmf::Agent>().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)