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/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "common/settings.hpp"
43 #include "net/ip6_address.hpp"
44 #include "thread/mle_types.hpp"
45 #include "thread/thread_netif.hpp"
46 #include "thread/thread_tlvs.hpp"
47 #include "thread/uri_paths.hpp"
48 #include "utils/slaac_address.hpp"
49 
50 namespace ot {
51 
DuaManager(Instance & aInstance)52 DuaManager::DuaManager(Instance &aInstance)
53     : InstanceLocator(aInstance)
54     , mRegistrationTask(aInstance, DuaManager::HandleRegistrationTask)
55     , mDuaNotification(UriPath::kDuaRegistrationNotify, &DuaManager::HandleDuaNotification, this)
56     , mIsDuaPending(false)
57 #if OPENTHREAD_CONFIG_DUA_ENABLE
58     , mDuaState(kNotExist)
59     , mDadCounter(0)
60     , mLastRegistrationTime(0)
61 #endif
62 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
63     , mChildIndexDuaRegistering(Mle::kMaxChildren)
64 #endif
65 {
66     mDelay.mValue = 0;
67 
68 #if OPENTHREAD_CONFIG_DUA_ENABLE
69     mDomainUnicastAddress.InitAsThreadOriginGlobalScope();
70     mFixedDuaInterfaceIdentifier.Clear();
71 #endif
72 
73 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
74     mChildDuaMask.Clear();
75     mChildDuaRegisteredMask.Clear();
76 #endif
77 
78     Get<Tmf::Agent>().AddResource(mDuaNotification);
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 forcely
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         otLogInfoDua("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString());
159     }
160     else
161     {
162         otLogWarnDua("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     otLogInfoDua("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     otLogInfoDua("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         otLogDebgDua("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     otBackboneRouterConfig 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         otLogDebgDua("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         otLogDebgDua("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     VerifyOrExit(mle.IsAttached(), mDelay.mValue = 0);
309 
310     if (aEvents.Contains(kEventThreadRoleChanged))
311     {
312         if (mle.HasRestored())
313         {
314             UpdateReregistrationDelay();
315         }
316 #if OPENTHREAD_CONFIG_DUA_ENABLE && OPENTHREAD_FTD
317         else if (mle.IsRouter())
318         {
319             // Wait for link establishment with neighboring routers.
320             UpdateRegistrationDelay(kNewRouterRegistrationDelay);
321         }
322         else if (mle.IsExpectedToBecomeRouterSoon())
323         {
324             // Will check again in case the device decides to stay REED when jitter timeout expires.
325             UpdateRegistrationDelay(mle.GetRouterSelectionJitterTimeout() + kNewRouterRegistrationDelay + 1);
326         }
327 #endif
328     }
329 
330 #if OPENTHREAD_CONFIG_DUA_ENABLE
331     if (aEvents.ContainsAny(kEventIp6AddressAdded))
332     {
333         UpdateRegistrationDelay(kNewDuaRegistrationDelay);
334     }
335 #endif
336 
337 exit:
338     return;
339 }
340 
HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,const BackboneRouter::BackboneRouterConfig & aConfig)341 void DuaManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State               aState,
342                                                    const BackboneRouter::BackboneRouterConfig &aConfig)
343 {
344     OT_UNUSED_VARIABLE(aConfig);
345 
346     if (aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg)
347     {
348         UpdateReregistrationDelay();
349     }
350 }
351 
HandleTimeTick(void)352 void DuaManager::HandleTimeTick(void)
353 {
354     bool attempt = false;
355 
356 #if OPENTHREAD_CONFIG_DUA_ENABLE
357     otLogDebgDua("regdelay %d, reregdelay %d, checkdelay %d", mDelay.mFields.mRegistrationDelay,
358                  mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
359 
360     if ((mDuaState != kNotExist) &&
361         (TimerMilli::GetNow() > (mLastRegistrationTime + TimeMilli::SecToMsec(Mle::kDuaDadPeriod))))
362     {
363         mDomainUnicastAddress.mPreferred = true;
364     }
365 
366     if ((mDelay.mFields.mRegistrationDelay > 0) && (--mDelay.mFields.mRegistrationDelay == 0))
367     {
368         attempt = true;
369     }
370 #else
371     otLogDebgDua("reregdelay %d, checkdelay %d", mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
372 #endif
373 
374     if ((mDelay.mFields.mCheckDelay > 0) && (--mDelay.mFields.mCheckDelay == 0))
375     {
376         attempt = true;
377     }
378 
379     if ((mDelay.mFields.mReregistrationDelay > 0) && (--mDelay.mFields.mReregistrationDelay == 0))
380     {
381 #if OPENTHREAD_CONFIG_DUA_ENABLE
382         if (mDuaState != kNotExist)
383         {
384             mDuaState = kToRegister;
385         }
386 #endif
387 
388 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
389         mChildDuaRegisteredMask.Clear();
390 #endif
391         attempt = true;
392     }
393 
394     if (attempt)
395     {
396         mRegistrationTask.Post();
397     }
398 
399     UpdateTimeTickerRegistration();
400 }
401 
HandleRegistrationTask(Tasklet & aTasklet)402 void DuaManager::HandleRegistrationTask(Tasklet &aTasklet)
403 {
404     aTasklet.Get<DuaManager>().PerformNextRegistration();
405 }
406 
UpdateTimeTickerRegistration(void)407 void DuaManager::UpdateTimeTickerRegistration(void)
408 {
409     if (mDelay.mValue == 0)
410     {
411         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kDuaManager);
412     }
413     else
414     {
415         Get<TimeTicker>().RegisterReceiver(TimeTicker::kDuaManager);
416     }
417 }
418 
PerformNextRegistration(void)419 void DuaManager::PerformNextRegistration(void)
420 {
421     Error            error   = kErrorNone;
422     Mle::MleRouter & mle     = Get<Mle::MleRouter>();
423     Coap::Message *  message = nullptr;
424     Ip6::MessageInfo messageInfo;
425     Ip6::Address     dua;
426 
427     VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
428     VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
429 
430     // Only allow one outgoing DUA.req
431     VerifyOrExit(!mIsDuaPending, error = kErrorBusy);
432 
433     // Only send DUA.req when necessary
434 #if OPENTHREAD_CONFIG_DUA_ENABLE
435 #if OPENTHREAD_FTD
436     VerifyOrExit(mle.IsRouterOrLeader() || !mle.IsExpectedToBecomeRouterSoon(), error = kErrorInvalidState);
437 #endif
438     VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
439 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
440 
441     {
442         bool needReg = false;
443 
444 #if OPENTHREAD_CONFIG_DUA_ENABLE
445         needReg = (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0);
446 #endif
447 
448 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
449         needReg = needReg || (mChildDuaMask.HasAny() && mChildDuaMask != mChildDuaRegisteredMask);
450 #endif
451         VerifyOrExit(needReg, error = kErrorNotFound);
452     }
453 
454     // Prepare DUA.req
455     VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
456 
457     SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDuaRegistrationRequest));
458     SuccessOrExit(error = message->SetPayloadMarker());
459 
460 #if OPENTHREAD_CONFIG_DUA_ENABLE
461     if (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0)
462     {
463         dua = GetDomainUnicastAddress();
464         SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
465         SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, mle.GetMeshLocal64().GetIid()));
466         mDuaState             = kRegistering;
467         mLastRegistrationTime = TimerMilli::GetNow();
468     }
469     else
470 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
471     {
472 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
473         uint32_t            lastTransactionTime;
474         const Ip6::Address *duaPtr = nullptr;
475         Child *             child  = nullptr;
476 
477         OT_ASSERT(mChildIndexDuaRegistering == Mle::kMaxChildren);
478 
479         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
480         {
481             uint16_t childIndex = Get<ChildTable>().GetChildIndex(iter);
482 
483             if (mChildDuaMask.Get(childIndex) && !mChildDuaRegisteredMask.Get(childIndex))
484             {
485                 mChildIndexDuaRegistering = childIndex;
486                 break;
487             }
488         }
489 
490         child  = Get<ChildTable>().GetChildAtIndex(mChildIndexDuaRegistering);
491         duaPtr = child->GetDomainUnicastAddress();
492 
493         OT_ASSERT(duaPtr != nullptr);
494 
495         dua = *duaPtr;
496         SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
497         SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, child->GetMeshLocalIid()));
498 
499         lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child->GetLastHeard());
500         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, lastTransactionTime));
501 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
502     }
503 
504     if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
505     {
506         uint8_t pbbrServiceId;
507 
508         SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
509         SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
510     }
511     else
512     {
513         messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
514                                                       Get<BackboneRouter::Leader>().GetServer16());
515     }
516 
517     messageInfo.SetPeerPort(Tmf::kUdpPort);
518     messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
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     otLogInfoDua("Sent DUA.req for DUA %s", dua.ToString().AsCString());
535 
536 exit:
537     if (error == kErrorNoBufs)
538     {
539         UpdateCheckDelay(Mle::kNoBufDelay);
540     }
541 
542     otLogInfoDua("PerformNextRegistration: %s", ErrorToString(error));
543     FreeMessageOnError(message, error);
544 }
545 
HandleDuaResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)546 void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
547 {
548     OT_UNUSED_VARIABLE(aMessageInfo);
549     Error error;
550 
551     mIsDuaPending = false;
552 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
553     mChildIndexDuaRegistering = Mle::kMaxChildren;
554 #endif
555 
556     if (aResult == kErrorResponseTimeout)
557     {
558         UpdateCheckDelay(Mle::KResponseTimeoutDelay);
559         ExitNow(error = aResult);
560     }
561 
562     VerifyOrExit(aResult == kErrorNone, error = kErrorParse);
563     OT_ASSERT(aMessage != nullptr);
564 
565     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged || aMessage->GetCode() >= Coap::kCodeBadRequest,
566                  error = kErrorParse);
567 
568     error = ProcessDuaResponse(*aMessage);
569 
570 exit:
571     if (error != kErrorResponseTimeout)
572     {
573         mRegistrationTask.Post();
574     }
575 
576     otLogInfoDua("Received DUA.rsp: %s", ErrorToString(error));
577 }
578 
HandleDuaNotification(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)579 void DuaManager::HandleDuaNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
580 {
581     OT_UNUSED_VARIABLE(aMessageInfo);
582 
583     Error error;
584 
585     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse);
586 
587     if (aMessage.IsConfirmable() && Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
588     {
589         otLogInfoDua("Sent DUA.ntf acknowledgment");
590     }
591 
592     error = ProcessDuaResponse(aMessage);
593 
594 exit:
595     OT_UNUSED_VARIABLE(error);
596     otLogInfoDua("Received DUA.ntf: %d", ErrorToString(error));
597 }
598 
ProcessDuaResponse(Coap::Message & aMessage)599 Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage)
600 {
601     Error        error = kErrorNone;
602     Ip6::Address target;
603     uint8_t      status;
604 
605     if (aMessage.GetCode() >= Coap::kCodeBadRequest)
606     {
607         status = ThreadStatusTlv::kDuaGeneralFailure;
608         target = mRegisteringDua;
609     }
610     else
611     {
612         SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(aMessage, status));
613         SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
614     }
615 
616     VerifyOrExit(Get<BackboneRouter::Leader>().IsDomainUnicast(target), error = kErrorDrop);
617 
618 #if OPENTHREAD_CONFIG_DUA_ENABLE
619     if (Get<ThreadNetif>().HasUnicastAddress(target))
620     {
621         switch (static_cast<ThreadStatusTlv::DuaStatus>(status))
622         {
623         case ThreadStatusTlv::kDuaSuccess:
624             mLastRegistrationTime = TimerMilli::GetNow();
625             mDuaState             = kRegistered;
626             break;
627         case ThreadStatusTlv::kDuaReRegister:
628             if (Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
629             {
630                 RemoveDomainUnicastAddress();
631                 AddDomainUnicastAddress();
632             }
633             break;
634         case ThreadStatusTlv::kDuaInvalid:
635             // Domain Prefix might be invalid.
636             RemoveDomainUnicastAddress();
637             break;
638         case ThreadStatusTlv::kDuaDuplicate:
639             NotifyDuplicateDomainUnicastAddress();
640             break;
641         case ThreadStatusTlv::kDuaNoResources:
642         case ThreadStatusTlv::kDuaNotPrimary:
643         case ThreadStatusTlv::kDuaGeneralFailure:
644             UpdateReregistrationDelay();
645             break;
646         }
647     }
648     else
649 #endif
650 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
651     {
652         Child *  child = nullptr;
653         uint16_t childIndex;
654 
655         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
656         {
657             if (iter.HasIp6Address(target))
658             {
659                 child = &iter;
660                 break;
661             }
662         }
663 
664         VerifyOrExit(child != nullptr, error = kErrorNotFound);
665 
666         childIndex = Get<ChildTable>().GetChildIndex(*child);
667 
668         switch (status)
669         {
670         case ThreadStatusTlv::kDuaSuccess:
671             // Mark as Registered
672             if (mChildDuaMask.Get(childIndex))
673             {
674                 mChildDuaRegisteredMask.Set(childIndex, true);
675             }
676             break;
677         case ThreadStatusTlv::kDuaReRegister:
678             // Parent stops registering for the Child's DUA until next Child Update Request
679             mChildDuaMask.Set(childIndex, false);
680             mChildDuaRegisteredMask.Set(childIndex, false);
681             break;
682         case ThreadStatusTlv::kDuaInvalid:
683         case ThreadStatusTlv::kDuaDuplicate:
684             IgnoreError(child->RemoveIp6Address(target));
685             mChildDuaMask.Set(childIndex, false);
686             mChildDuaRegisteredMask.Set(childIndex, false);
687             break;
688         case ThreadStatusTlv::kDuaNoResources:
689         case ThreadStatusTlv::kDuaNotPrimary:
690         case ThreadStatusTlv::kDuaGeneralFailure:
691             UpdateReregistrationDelay();
692             break;
693         }
694 
695         if (status != ThreadStatusTlv::kDuaSuccess)
696         {
697             SendAddressNotification(target, static_cast<ThreadStatusTlv::DuaStatus>(status), *child);
698         }
699     }
700 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
701 
702 exit:
703     UpdateTimeTickerRegistration();
704     return error;
705 }
706 
707 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
SendAddressNotification(Ip6::Address & aAddress,ThreadStatusTlv::DuaStatus aStatus,const Child & aChild)708 void DuaManager::SendAddressNotification(Ip6::Address &             aAddress,
709                                          ThreadStatusTlv::DuaStatus aStatus,
710                                          const Child &              aChild)
711 {
712     Coap::Message *  message = nullptr;
713     Ip6::MessageInfo messageInfo;
714     Error            error;
715 
716     VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
717 
718     SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDuaRegistrationNotify));
719     SuccessOrExit(error = message->SetPayloadMarker());
720 
721     SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
722     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aAddress));
723 
724     messageInfo.GetPeerAddr().SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aChild.GetRloc16());
725     messageInfo.SetPeerPort(Tmf::kUdpPort);
726     messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
727 
728     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
729 
730     otLogInfoDua("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString());
731 
732 exit:
733 
734     if (error != kErrorNone)
735     {
736         FreeMessage(message);
737 
738         // TODO: (DUA) (P4) may enhance to  guarantee the delivery of DUA.ntf
739         otLogWarnDua("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(),
740                      aAddress.ToString().AsCString(), ErrorToString(error));
741     }
742 }
743 
UpdateChildDomainUnicastAddress(const Child & aChild,Mle::ChildDuaState aState)744 void DuaManager::UpdateChildDomainUnicastAddress(const Child &aChild, Mle::ChildDuaState aState)
745 {
746     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
747 
748     if ((aState == Mle::ChildDuaState::kRemoved || aState == Mle::ChildDuaState::kChanged) &&
749         mChildDuaMask.Get(childIndex))
750     {
751         // Abort on going proxy DUA.req for this child
752         if (mChildIndexDuaRegistering == childIndex)
753         {
754             IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
755         }
756 
757         mChildDuaMask.Set(childIndex, false);
758         mChildDuaRegisteredMask.Set(childIndex, false);
759     }
760 
761     if (aState == Mle::ChildDuaState::kAdded || aState == Mle::ChildDuaState::kChanged ||
762         (aState == Mle::ChildDuaState::kUnchanged && !mChildDuaMask.Get(childIndex)))
763     {
764         if (mChildDuaMask == mChildDuaRegisteredMask)
765         {
766             UpdateCheckDelay(Random::NonCrypto::GetUint8InRange(1, Mle::kParentAggregateDelay));
767         }
768 
769         mChildDuaMask.Set(childIndex, true);
770         mChildDuaRegisteredMask.Set(childIndex, false);
771     }
772 
773     return;
774 }
775 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
776 
777 } // namespace ot
778 
779 #endif // OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
780