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