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