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