• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements managing DUA.
32  */
33 
34 #include "dua_manager.hpp"
35 
36 #if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
37 
38 #include "common/as_core_type.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/log.hpp"
43 #include "common/settings.hpp"
44 #include "net/ip6_address.hpp"
45 #include "thread/mle_types.hpp"
46 #include "thread/thread_netif.hpp"
47 #include "thread/thread_tlvs.hpp"
48 #include "thread/uri_paths.hpp"
49 #include "utils/slaac_address.hpp"
50 
51 namespace ot {
52 
53 RegisterLogModule("DuaManager");
54 
DuaManager(Instance & aInstance)55 DuaManager::DuaManager(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mRegistrationTask(aInstance, DuaManager::HandleRegistrationTask)
58     , mDuaNotification(UriPath::kDuaRegistrationNotify, &DuaManager::HandleDuaNotification, this)
59     , mIsDuaPending(false)
60 #if OPENTHREAD_CONFIG_DUA_ENABLE
61     , mDuaState(kNotExist)
62     , mDadCounter(0)
63     , mLastRegistrationTime(0)
64 #endif
65 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
66     , mChildIndexDuaRegistering(Mle::kMaxChildren)
67 #endif
68 {
69     mDelay.mValue = 0;
70 
71 #if OPENTHREAD_CONFIG_DUA_ENABLE
72     mDomainUnicastAddress.InitAsThreadOriginGlobalScope();
73     mFixedDuaInterfaceIdentifier.Clear();
74 #endif
75 
76 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
77     mChildDuaMask.Clear();
78     mChildDuaRegisteredMask.Clear();
79 #endif
80 
81     Get<Tmf::Agent>().AddResource(mDuaNotification);
82 }
83 
HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixState aState)84 void DuaManager::HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixState aState)
85 {
86     if ((aState == BackboneRouter::Leader::kDomainPrefixRemoved) ||
87         (aState == BackboneRouter::Leader::kDomainPrefixRefreshed))
88     {
89         if (mIsDuaPending)
90         {
91             IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
92         }
93 
94 #if OPENTHREAD_CONFIG_DUA_ENABLE
95         RemoveDomainUnicastAddress();
96 #endif
97 
98 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
99         if (mChildDuaMask.HasAny())
100         {
101             mChildDuaMask.Clear();
102             mChildDuaRegisteredMask.Clear();
103         }
104 #endif
105     }
106 
107 #if OPENTHREAD_CONFIG_DUA_ENABLE
108     switch (aState)
109     {
110     case BackboneRouter::Leader::kDomainPrefixUnchanged:
111         // In case removed for some reason e.g. the kDuaInvalid response from PBBR forcely
112         VerifyOrExit(!Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()));
113 
114         OT_FALL_THROUGH;
115 
116     case BackboneRouter::Leader::kDomainPrefixRefreshed:
117     case BackboneRouter::Leader::kDomainPrefixAdded:
118     {
119         const Ip6::Prefix *prefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
120         OT_ASSERT(prefix != nullptr);
121         mDomainUnicastAddress.mPrefixLength = prefix->GetLength();
122         mDomainUnicastAddress.GetAddress().Clear();
123         mDomainUnicastAddress.GetAddress().SetPrefix(*prefix);
124     }
125     break;
126     default:
127         ExitNow();
128     }
129 
130     // Apply cached DUA Interface Identifier manually specified.
131     if (IsFixedDuaInterfaceIdentifierSet())
132     {
133         mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier);
134     }
135     else
136     {
137         SuccessOrExit(GenerateDomainUnicastAddressIid());
138     }
139 
140     AddDomainUnicastAddress();
141 
142 exit:
143     return;
144 #endif
145 }
146 
147 #if OPENTHREAD_CONFIG_DUA_ENABLE
GenerateDomainUnicastAddressIid(void)148 Error DuaManager::GenerateDomainUnicastAddressIid(void)
149 {
150     Error   error;
151     uint8_t dadCounter = mDadCounter;
152 
153     if ((error = Get<Utils::Slaac>().GenerateIid(mDomainUnicastAddress, nullptr, 0, &dadCounter)) == kErrorNone)
154     {
155         if (dadCounter != mDadCounter)
156         {
157             mDadCounter = dadCounter;
158             IgnoreError(Store());
159         }
160 
161         LogInfo("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString());
162     }
163     else
164     {
165         LogWarn("Generate DUA: %s", ErrorToString(error));
166     }
167 
168     return error;
169 }
170 
SetFixedDuaInterfaceIdentifier(const Ip6::InterfaceIdentifier & aIid)171 Error DuaManager::SetFixedDuaInterfaceIdentifier(const Ip6::InterfaceIdentifier &aIid)
172 {
173     Error error = kErrorNone;
174 
175     VerifyOrExit(!aIid.IsReserved(), error = kErrorInvalidArgs);
176     VerifyOrExit(mFixedDuaInterfaceIdentifier.IsUnspecified() || mFixedDuaInterfaceIdentifier != aIid);
177 
178     mFixedDuaInterfaceIdentifier = aIid;
179     LogInfo("Set DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
180 
181     if (Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
182     {
183         RemoveDomainUnicastAddress();
184         mDomainUnicastAddress.GetAddress().SetIid(mFixedDuaInterfaceIdentifier);
185         AddDomainUnicastAddress();
186     }
187 
188 exit:
189     return error;
190 }
191 
ClearFixedDuaInterfaceIdentifier(void)192 void DuaManager::ClearFixedDuaInterfaceIdentifier(void)
193 {
194     // Nothing to clear.
195     VerifyOrExit(IsFixedDuaInterfaceIdentifierSet());
196 
197     if (GetDomainUnicastAddress().GetIid() == mFixedDuaInterfaceIdentifier &&
198         Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
199     {
200         RemoveDomainUnicastAddress();
201 
202         if (GenerateDomainUnicastAddressIid() == kErrorNone)
203         {
204             AddDomainUnicastAddress();
205         }
206     }
207 
208     LogInfo("Cleared DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
209     mFixedDuaInterfaceIdentifier.Clear();
210 
211 exit:
212     return;
213 }
214 
Restore(void)215 void DuaManager::Restore(void)
216 {
217     Settings::DadInfo dadInfo;
218 
219     SuccessOrExit(Get<Settings>().Read(dadInfo));
220     mDadCounter = dadInfo.GetDadCounter();
221 
222 exit:
223     return;
224 }
225 
Store(void)226 Error DuaManager::Store(void)
227 {
228     Settings::DadInfo dadInfo;
229 
230     dadInfo.SetDadCounter(mDadCounter);
231     return Get<Settings>().Save(dadInfo);
232 }
233 
AddDomainUnicastAddress(void)234 void DuaManager::AddDomainUnicastAddress(void)
235 {
236     mDuaState             = kToRegister;
237     mLastRegistrationTime = TimerMilli::GetNow();
238     Get<ThreadNetif>().AddUnicastAddress(mDomainUnicastAddress);
239 }
240 
RemoveDomainUnicastAddress(void)241 void DuaManager::RemoveDomainUnicastAddress(void)
242 {
243     if (mDuaState == kRegistering && mIsDuaPending)
244     {
245         IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
246     }
247 
248     mDuaState                        = kNotExist;
249     mDomainUnicastAddress.mPreferred = false;
250     Get<ThreadNetif>().RemoveUnicastAddress(mDomainUnicastAddress);
251 }
252 
UpdateRegistrationDelay(uint8_t aDelay)253 void DuaManager::UpdateRegistrationDelay(uint8_t aDelay)
254 {
255     if (mDelay.mFields.mRegistrationDelay == 0 || mDelay.mFields.mRegistrationDelay > aDelay)
256     {
257         mDelay.mFields.mRegistrationDelay = aDelay;
258 
259         LogDebg("update regdelay %d", mDelay.mFields.mRegistrationDelay);
260         UpdateTimeTickerRegistration();
261     }
262 }
263 
NotifyDuplicateDomainUnicastAddress(void)264 void DuaManager::NotifyDuplicateDomainUnicastAddress(void)
265 {
266     RemoveDomainUnicastAddress();
267     mDadCounter++;
268 
269     if (GenerateDomainUnicastAddressIid() == kErrorNone)
270     {
271         AddDomainUnicastAddress();
272     }
273 }
274 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
275 
UpdateReregistrationDelay(void)276 void DuaManager::UpdateReregistrationDelay(void)
277 {
278     uint16_t               delay = 0;
279     otBackboneRouterConfig config;
280 
281     VerifyOrExit(Get<BackboneRouter::Leader>().GetConfig(config) == kErrorNone);
282 
283     delay = config.mReregistrationDelay > 1 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay) : 1;
284 
285     if (mDelay.mFields.mReregistrationDelay == 0 || mDelay.mFields.mReregistrationDelay > delay)
286     {
287         mDelay.mFields.mReregistrationDelay = delay;
288         UpdateTimeTickerRegistration();
289         LogDebg("update reregdelay %d", mDelay.mFields.mReregistrationDelay);
290     }
291 
292 exit:
293     return;
294 }
295 
UpdateCheckDelay(uint8_t aDelay)296 void DuaManager::UpdateCheckDelay(uint8_t aDelay)
297 {
298     if (mDelay.mFields.mCheckDelay == 0 || mDelay.mFields.mCheckDelay > aDelay)
299     {
300         mDelay.mFields.mCheckDelay = aDelay;
301 
302         LogDebg("update checkdelay %d", mDelay.mFields.mCheckDelay);
303         UpdateTimeTickerRegistration();
304     }
305 }
306 
HandleNotifierEvents(Events aEvents)307 void DuaManager::HandleNotifierEvents(Events aEvents)
308 {
309     Mle::MleRouter &mle = Get<Mle::MleRouter>();
310 
311     VerifyOrExit(mle.IsAttached(), mDelay.mValue = 0);
312 
313     if (aEvents.Contains(kEventThreadRoleChanged))
314     {
315         if (mle.HasRestored())
316         {
317             UpdateReregistrationDelay();
318         }
319 #if OPENTHREAD_CONFIG_DUA_ENABLE && OPENTHREAD_FTD
320         else if (mle.IsRouter())
321         {
322             // Wait for link establishment with neighboring routers.
323             UpdateRegistrationDelay(kNewRouterRegistrationDelay);
324         }
325         else if (mle.IsExpectedToBecomeRouterSoon())
326         {
327             // Will check again in case the device decides to stay REED when jitter timeout expires.
328             UpdateRegistrationDelay(mle.GetRouterSelectionJitterTimeout() + kNewRouterRegistrationDelay + 1);
329         }
330 #endif
331     }
332 
333 #if OPENTHREAD_CONFIG_DUA_ENABLE
334     if (aEvents.ContainsAny(kEventIp6AddressAdded))
335     {
336         UpdateRegistrationDelay(kNewDuaRegistrationDelay);
337     }
338 #endif
339 
340 exit:
341     return;
342 }
343 
HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,const BackboneRouter::BackboneRouterConfig & aConfig)344 void DuaManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State               aState,
345                                                    const BackboneRouter::BackboneRouterConfig &aConfig)
346 {
347     OT_UNUSED_VARIABLE(aConfig);
348 
349     if (aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg)
350     {
351         UpdateReregistrationDelay();
352     }
353 }
354 
HandleTimeTick(void)355 void DuaManager::HandleTimeTick(void)
356 {
357     bool attempt = false;
358 
359 #if OPENTHREAD_CONFIG_DUA_ENABLE
360     LogDebg("regdelay %d, reregdelay %d, checkdelay %d", mDelay.mFields.mRegistrationDelay,
361             mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
362 
363     if ((mDuaState != kNotExist) &&
364         (TimerMilli::GetNow() > (mLastRegistrationTime + TimeMilli::SecToMsec(Mle::kDuaDadPeriod))))
365     {
366         mDomainUnicastAddress.mPreferred = true;
367     }
368 
369     if ((mDelay.mFields.mRegistrationDelay > 0) && (--mDelay.mFields.mRegistrationDelay == 0))
370     {
371         attempt = true;
372     }
373 #else
374     LogDebg("reregdelay %d, checkdelay %d", mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
375 #endif
376 
377     if ((mDelay.mFields.mCheckDelay > 0) && (--mDelay.mFields.mCheckDelay == 0))
378     {
379         attempt = true;
380     }
381 
382     if ((mDelay.mFields.mReregistrationDelay > 0) && (--mDelay.mFields.mReregistrationDelay == 0))
383     {
384 #if OPENTHREAD_CONFIG_DUA_ENABLE
385         if (mDuaState != kNotExist)
386         {
387             mDuaState = kToRegister;
388         }
389 #endif
390 
391 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
392         mChildDuaRegisteredMask.Clear();
393 #endif
394         attempt = true;
395     }
396 
397     if (attempt)
398     {
399         mRegistrationTask.Post();
400     }
401 
402     UpdateTimeTickerRegistration();
403 }
404 
HandleRegistrationTask(Tasklet & aTasklet)405 void DuaManager::HandleRegistrationTask(Tasklet &aTasklet)
406 {
407     aTasklet.Get<DuaManager>().PerformNextRegistration();
408 }
409 
UpdateTimeTickerRegistration(void)410 void DuaManager::UpdateTimeTickerRegistration(void)
411 {
412     if (mDelay.mValue == 0)
413     {
414         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kDuaManager);
415     }
416     else
417     {
418         Get<TimeTicker>().RegisterReceiver(TimeTicker::kDuaManager);
419     }
420 }
421 
PerformNextRegistration(void)422 void DuaManager::PerformNextRegistration(void)
423 {
424     Error            error   = kErrorNone;
425     Mle::MleRouter & mle     = Get<Mle::MleRouter>();
426     Coap::Message *  message = nullptr;
427     Tmf::MessageInfo messageInfo(GetInstance());
428     Ip6::Address     dua;
429 
430     VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
431     VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
432 
433     // Only allow one outgoing DUA.req
434     VerifyOrExit(!mIsDuaPending, error = kErrorBusy);
435 
436     // Only send DUA.req when necessary
437 #if OPENTHREAD_CONFIG_DUA_ENABLE
438 #if OPENTHREAD_FTD
439     VerifyOrExit(mle.IsRouterOrLeader() || !mle.IsExpectedToBecomeRouterSoon(), error = kErrorInvalidState);
440 #endif
441     VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
442 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
443 
444     {
445         bool needReg = false;
446 
447 #if OPENTHREAD_CONFIG_DUA_ENABLE
448         needReg = (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0);
449 #endif
450 
451 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
452         needReg = needReg || (mChildDuaMask.HasAny() && mChildDuaMask != mChildDuaRegisteredMask);
453 #endif
454         VerifyOrExit(needReg, error = kErrorNotFound);
455     }
456 
457     // Prepare DUA.req
458     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationRequest);
459     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
460 
461 #if OPENTHREAD_CONFIG_DUA_ENABLE
462     if (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0)
463     {
464         dua = GetDomainUnicastAddress();
465         SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
466         SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, mle.GetMeshLocal64().GetIid()));
467         mDuaState             = kRegistering;
468         mLastRegistrationTime = TimerMilli::GetNow();
469     }
470     else
471 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
472     {
473 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
474         uint32_t            lastTransactionTime;
475         const Ip6::Address *duaPtr = nullptr;
476         Child *             child  = nullptr;
477 
478         OT_ASSERT(mChildIndexDuaRegistering == Mle::kMaxChildren);
479 
480         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
481         {
482             uint16_t childIndex = Get<ChildTable>().GetChildIndex(iter);
483 
484             if (mChildDuaMask.Get(childIndex) && !mChildDuaRegisteredMask.Get(childIndex))
485             {
486                 mChildIndexDuaRegistering = childIndex;
487                 break;
488             }
489         }
490 
491         child  = Get<ChildTable>().GetChildAtIndex(mChildIndexDuaRegistering);
492         duaPtr = child->GetDomainUnicastAddress();
493 
494         OT_ASSERT(duaPtr != nullptr);
495 
496         dua = *duaPtr;
497         SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
498         SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, child->GetMeshLocalIid()));
499 
500         lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child->GetLastHeard());
501         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, lastTransactionTime));
502 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
503     }
504 
505     if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
506     {
507         uint8_t pbbrServiceId;
508 
509         SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
510         SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
511     }
512     else
513     {
514         messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
515                                                       Get<BackboneRouter::Leader>().GetServer16());
516     }
517 
518     messageInfo.SetSockAddrToRloc();
519 
520     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &DuaManager::HandleDuaResponse, this));
521 
522     mIsDuaPending   = true;
523     mRegisteringDua = dua;
524     mDelay.mValue   = 0;
525 
526     // Generally Thread 1.2 Router would send DUA.req on behalf for DUA registered by its MTD child.
527     // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send DUA.req to PBBR itself.
528     // In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely.
529     if (!Get<Mle::Mle>().IsRxOnWhenIdle())
530     {
531         Get<DataPollSender>().SendFastPolls();
532     }
533 
534     LogInfo("Sent DUA.req for DUA %s", dua.ToString().AsCString());
535 
536 exit:
537     if (error == kErrorNoBufs)
538     {
539         UpdateCheckDelay(Mle::kNoBufDelay);
540     }
541 
542     LogInfo("PerformNextRegistration: %s", ErrorToString(error));
543     FreeMessageOnError(message, error);
544 }
545 
HandleDuaResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)546 void DuaManager::HandleDuaResponse(void *               aContext,
547                                    otMessage *          aMessage,
548                                    const otMessageInfo *aMessageInfo,
549                                    Error                aResult)
550 {
551     static_cast<DuaManager *>(aContext)->HandleDuaResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
552                                                            aResult);
553 }
554 
HandleDuaResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)555 void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
556 {
557     OT_UNUSED_VARIABLE(aMessageInfo);
558     Error error;
559 
560     mIsDuaPending = false;
561 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
562     mChildIndexDuaRegistering = Mle::kMaxChildren;
563 #endif
564 
565     if (aResult == kErrorResponseTimeout)
566     {
567         UpdateCheckDelay(Mle::KResponseTimeoutDelay);
568         ExitNow(error = aResult);
569     }
570 
571     VerifyOrExit(aResult == kErrorNone, error = kErrorParse);
572     OT_ASSERT(aMessage != nullptr);
573 
574     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged || aMessage->GetCode() >= Coap::kCodeBadRequest,
575                  error = kErrorParse);
576 
577     error = ProcessDuaResponse(*aMessage);
578 
579 exit:
580     if (error != kErrorResponseTimeout)
581     {
582         mRegistrationTask.Post();
583     }
584 
585     LogInfo("Received DUA.rsp: %s", ErrorToString(error));
586 }
587 
HandleDuaNotification(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)588 void DuaManager::HandleDuaNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
589 {
590     static_cast<DuaManager *>(aContext)->HandleDuaNotification(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
591 }
HandleDuaNotification(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)592 void DuaManager::HandleDuaNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
593 {
594     OT_UNUSED_VARIABLE(aMessageInfo);
595 
596     Error error;
597 
598     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse);
599 
600     if (aMessage.IsConfirmable() && Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
601     {
602         LogInfo("Sent DUA.ntf acknowledgment");
603     }
604 
605     error = ProcessDuaResponse(aMessage);
606 
607 exit:
608     OT_UNUSED_VARIABLE(error);
609     LogInfo("Received DUA.ntf: %d", ErrorToString(error));
610 }
611 
ProcessDuaResponse(Coap::Message & aMessage)612 Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage)
613 {
614     Error        error = kErrorNone;
615     Ip6::Address target;
616     uint8_t      status;
617 
618     if (aMessage.GetCode() >= Coap::kCodeBadRequest)
619     {
620         status = ThreadStatusTlv::kDuaGeneralFailure;
621         target = mRegisteringDua;
622     }
623     else
624     {
625         SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(aMessage, status));
626         SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
627     }
628 
629     VerifyOrExit(Get<BackboneRouter::Leader>().IsDomainUnicast(target), error = kErrorDrop);
630 
631 #if OPENTHREAD_CONFIG_DUA_ENABLE
632     if (Get<ThreadNetif>().HasUnicastAddress(target))
633     {
634         switch (static_cast<ThreadStatusTlv::DuaStatus>(status))
635         {
636         case ThreadStatusTlv::kDuaSuccess:
637             mLastRegistrationTime = TimerMilli::GetNow();
638             mDuaState             = kRegistered;
639             break;
640         case ThreadStatusTlv::kDuaReRegister:
641             if (Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
642             {
643                 RemoveDomainUnicastAddress();
644                 AddDomainUnicastAddress();
645             }
646             break;
647         case ThreadStatusTlv::kDuaInvalid:
648             // Domain Prefix might be invalid.
649             RemoveDomainUnicastAddress();
650             break;
651         case ThreadStatusTlv::kDuaDuplicate:
652             NotifyDuplicateDomainUnicastAddress();
653             break;
654         case ThreadStatusTlv::kDuaNoResources:
655         case ThreadStatusTlv::kDuaNotPrimary:
656         case ThreadStatusTlv::kDuaGeneralFailure:
657             UpdateReregistrationDelay();
658             break;
659         }
660     }
661     else
662 #endif
663 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
664     {
665         Child *  child = nullptr;
666         uint16_t childIndex;
667 
668         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
669         {
670             if (iter.HasIp6Address(target))
671             {
672                 child = &iter;
673                 break;
674             }
675         }
676 
677         VerifyOrExit(child != nullptr, error = kErrorNotFound);
678 
679         childIndex = Get<ChildTable>().GetChildIndex(*child);
680 
681         switch (status)
682         {
683         case ThreadStatusTlv::kDuaSuccess:
684             // Mark as Registered
685             if (mChildDuaMask.Get(childIndex))
686             {
687                 mChildDuaRegisteredMask.Set(childIndex, true);
688             }
689             break;
690         case ThreadStatusTlv::kDuaReRegister:
691             // Parent stops registering for the Child's DUA until next Child Update Request
692             mChildDuaMask.Set(childIndex, false);
693             mChildDuaRegisteredMask.Set(childIndex, false);
694             break;
695         case ThreadStatusTlv::kDuaInvalid:
696         case ThreadStatusTlv::kDuaDuplicate:
697             IgnoreError(child->RemoveIp6Address(target));
698             mChildDuaMask.Set(childIndex, false);
699             mChildDuaRegisteredMask.Set(childIndex, false);
700             break;
701         case ThreadStatusTlv::kDuaNoResources:
702         case ThreadStatusTlv::kDuaNotPrimary:
703         case ThreadStatusTlv::kDuaGeneralFailure:
704             UpdateReregistrationDelay();
705             break;
706         }
707 
708         if (status != ThreadStatusTlv::kDuaSuccess)
709         {
710             SendAddressNotification(target, static_cast<ThreadStatusTlv::DuaStatus>(status), *child);
711         }
712     }
713 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
714 
715 exit:
716     UpdateTimeTickerRegistration();
717     return error;
718 }
719 
720 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
SendAddressNotification(Ip6::Address & aAddress,ThreadStatusTlv::DuaStatus aStatus,const Child & aChild)721 void DuaManager::SendAddressNotification(Ip6::Address &             aAddress,
722                                          ThreadStatusTlv::DuaStatus aStatus,
723                                          const Child &              aChild)
724 {
725     Coap::Message *  message = nullptr;
726     Tmf::MessageInfo messageInfo(GetInstance());
727     Error            error;
728 
729     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationNotify);
730     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
731 
732     SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
733     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aAddress));
734 
735     messageInfo.SetSockAddrToRlocPeerAddrTo(aChild.GetRloc16());
736 
737     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
738 
739     LogInfo("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString());
740 
741 exit:
742 
743     if (error != kErrorNone)
744     {
745         FreeMessage(message);
746 
747         // TODO: (DUA) (P4) may enhance to  guarantee the delivery of DUA.ntf
748         LogWarn("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(), aAddress.ToString().AsCString(),
749                 ErrorToString(error));
750     }
751 }
752 
UpdateChildDomainUnicastAddress(const Child & aChild,Mle::ChildDuaState aState)753 void DuaManager::UpdateChildDomainUnicastAddress(const Child &aChild, Mle::ChildDuaState aState)
754 {
755     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
756 
757     if ((aState == Mle::ChildDuaState::kRemoved || aState == Mle::ChildDuaState::kChanged) &&
758         mChildDuaMask.Get(childIndex))
759     {
760         // Abort on going proxy DUA.req for this child
761         if (mChildIndexDuaRegistering == childIndex)
762         {
763             IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
764         }
765 
766         mChildDuaMask.Set(childIndex, false);
767         mChildDuaRegisteredMask.Set(childIndex, false);
768     }
769 
770     if (aState == Mle::ChildDuaState::kAdded || aState == Mle::ChildDuaState::kChanged ||
771         (aState == Mle::ChildDuaState::kUnchanged && !mChildDuaMask.Get(childIndex)))
772     {
773         if (mChildDuaMask == mChildDuaRegisteredMask)
774         {
775             UpdateCheckDelay(Random::NonCrypto::GetUint8InRange(1, Mle::kParentAggregateDelay));
776         }
777 
778         mChildDuaMask.Set(childIndex, true);
779         mChildDuaRegisteredMask.Set(childIndex, false);
780     }
781 
782     return;
783 }
784 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
785 
786 } // namespace ot
787 
788 #endif // OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
789