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/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/settings.hpp"
43 #include "instance/instance.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, 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         LogWarnOnError(error, "generate DUA");
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.GetRouterRoleTransitionTimeout() + 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         Child   *child = nullptr;
485 
486         OT_ASSERT(mChildIndexDuaRegistering == Mle::kMaxChildren);
487 
488         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
489         {
490             uint16_t childIndex = Get<ChildTable>().GetChildIndex(iter);
491 
492             if (mChildDuaMask.Get(childIndex) && !mChildDuaRegisteredMask.Get(childIndex))
493             {
494                 mChildIndexDuaRegistering = childIndex;
495                 break;
496             }
497         }
498 
499         child = Get<ChildTable>().GetChildAtIndex(mChildIndexDuaRegistering);
500         SuccessOrAssert(child->GetDomainUnicastAddress(dua));
501 
502         SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, dua));
503         SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, child->GetMeshLocalIid()));
504 
505         lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child->GetLastHeard());
506         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, lastTransactionTime));
507 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
508     }
509 
510     if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
511     {
512         uint8_t pbbrServiceId;
513 
514         SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
515         SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
516     }
517     else
518     {
519         messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
520                                                       Get<BackboneRouter::Leader>().GetServer16());
521     }
522 
523     messageInfo.SetSockAddrToRloc();
524 
525     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &DuaManager::HandleDuaResponse, this));
526 
527     mIsDuaPending   = true;
528     mRegisteringDua = dua;
529     mDelay.mValue   = 0;
530 
531     // Generally Thread 1.2 Router would send DUA.req on behalf for DUA registered by its MTD child.
532     // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send DUA.req to PBBR itself.
533     // In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely.
534     if (!Get<Mle::Mle>().IsRxOnWhenIdle())
535     {
536         Get<DataPollSender>().SendFastPolls();
537     }
538 
539     LogInfo("Sent %s for DUA %s", UriToString<kUriDuaRegistrationRequest>(), dua.ToString().AsCString());
540 
541 exit:
542     if (error == kErrorNoBufs)
543     {
544         UpdateCheckDelay(kNoBufDelay);
545     }
546 
547     LogWarnOnError(error, "perform next registration");
548     FreeMessageOnError(message, error);
549 }
550 
HandleDuaResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)551 void DuaManager::HandleDuaResponse(void                *aContext,
552                                    otMessage           *aMessage,
553                                    const otMessageInfo *aMessageInfo,
554                                    Error                aResult)
555 {
556     static_cast<DuaManager *>(aContext)->HandleDuaResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
557                                                            aResult);
558 }
559 
HandleDuaResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)560 void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
561 {
562     OT_UNUSED_VARIABLE(aMessageInfo);
563     Error error;
564 
565     mIsDuaPending = false;
566 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
567     mChildIndexDuaRegistering = Mle::kMaxChildren;
568 #endif
569 
570     if (aResult == kErrorResponseTimeout)
571     {
572         UpdateCheckDelay(KResponseTimeoutDelay);
573         ExitNow(error = aResult);
574     }
575 
576     VerifyOrExit(aResult == kErrorNone, error = kErrorParse);
577     OT_ASSERT(aMessage != nullptr);
578 
579     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged || aMessage->GetCode() >= Coap::kCodeBadRequest,
580                  error = kErrorParse);
581 
582     error = ProcessDuaResponse(*aMessage);
583 
584 exit:
585     if (error != kErrorResponseTimeout)
586     {
587         mRegistrationTask.Post();
588     }
589 
590     LogInfo("Received %s response: %s", UriToString<kUriDuaRegistrationRequest>(), ErrorToString(error));
591 }
592 
593 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)594 void DuaManager::HandleTmf<kUriDuaRegistrationNotify>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
595 {
596     OT_UNUSED_VARIABLE(aMessageInfo);
597 
598     Error error;
599 
600     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse);
601 
602     if (aMessage.IsConfirmable() && Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
603     {
604         LogInfo("Sent %s ack", UriToString<kUriDuaRegistrationNotify>());
605     }
606 
607     error = ProcessDuaResponse(aMessage);
608 
609 exit:
610     OT_UNUSED_VARIABLE(error);
611     LogInfo("Received %s: %s", UriToString<kUriDuaRegistrationNotify>(), ErrorToString(error));
612 }
613 
ProcessDuaResponse(Coap::Message & aMessage)614 Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage)
615 {
616     Error        error = kErrorNone;
617     Ip6::Address target;
618     uint8_t      status;
619 
620     if (aMessage.GetCode() >= Coap::kCodeBadRequest)
621     {
622         status = ThreadStatusTlv::kDuaGeneralFailure;
623         target = mRegisteringDua;
624     }
625     else
626     {
627         SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(aMessage, status));
628         SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
629     }
630 
631     VerifyOrExit(Get<BackboneRouter::Leader>().IsDomainUnicast(target), error = kErrorDrop);
632 
633 #if OPENTHREAD_CONFIG_DUA_ENABLE
634     if (Get<ThreadNetif>().HasUnicastAddress(target))
635     {
636         switch (static_cast<ThreadStatusTlv::DuaStatus>(status))
637         {
638         case ThreadStatusTlv::kDuaSuccess:
639             mLastRegistrationTime = TimerMilli::GetNow();
640             mDuaState             = kRegistered;
641             break;
642         case ThreadStatusTlv::kDuaReRegister:
643             if (Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
644             {
645                 RemoveDomainUnicastAddress();
646                 AddDomainUnicastAddress();
647             }
648             break;
649         case ThreadStatusTlv::kDuaInvalid:
650             // Domain Prefix might be invalid.
651             RemoveDomainUnicastAddress();
652             break;
653         case ThreadStatusTlv::kDuaDuplicate:
654             NotifyDuplicateDomainUnicastAddress();
655             break;
656         case ThreadStatusTlv::kDuaNoResources:
657         case ThreadStatusTlv::kDuaNotPrimary:
658         case ThreadStatusTlv::kDuaGeneralFailure:
659             UpdateReregistrationDelay();
660             break;
661         }
662     }
663     else
664 #endif
665 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
666     {
667         Child   *child = nullptr;
668         uint16_t childIndex;
669 
670         for (Child &iter : Get<ChildTable>().Iterate(Child::kInStateValid))
671         {
672             if (iter.HasIp6Address(target))
673             {
674                 child = &iter;
675                 break;
676             }
677         }
678 
679         VerifyOrExit(child != nullptr, error = kErrorNotFound);
680 
681         childIndex = Get<ChildTable>().GetChildIndex(*child);
682 
683         switch (status)
684         {
685         case ThreadStatusTlv::kDuaSuccess:
686             // Mark as Registered
687             if (mChildDuaMask.Get(childIndex))
688             {
689                 mChildDuaRegisteredMask.Set(childIndex, true);
690             }
691             break;
692         case ThreadStatusTlv::kDuaReRegister:
693             // Parent stops registering for the Child's DUA until next Child Update Request
694             mChildDuaMask.Set(childIndex, false);
695             mChildDuaRegisteredMask.Set(childIndex, false);
696             break;
697         case ThreadStatusTlv::kDuaInvalid:
698         case ThreadStatusTlv::kDuaDuplicate:
699             IgnoreError(child->RemoveIp6Address(target));
700             mChildDuaMask.Set(childIndex, false);
701             mChildDuaRegisteredMask.Set(childIndex, false);
702             break;
703         case ThreadStatusTlv::kDuaNoResources:
704         case ThreadStatusTlv::kDuaNotPrimary:
705         case ThreadStatusTlv::kDuaGeneralFailure:
706             UpdateReregistrationDelay();
707             break;
708         }
709 
710         if (status != ThreadStatusTlv::kDuaSuccess)
711         {
712             SendAddressNotification(target, static_cast<ThreadStatusTlv::DuaStatus>(status), *child);
713         }
714     }
715 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
716 
717 exit:
718     UpdateTimeTickerRegistration();
719     return error;
720 }
721 
722 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
SendAddressNotification(Ip6::Address & aAddress,ThreadStatusTlv::DuaStatus aStatus,const Child & aChild)723 void DuaManager::SendAddressNotification(Ip6::Address              &aAddress,
724                                          ThreadStatusTlv::DuaStatus aStatus,
725                                          const Child               &aChild)
726 {
727     Coap::Message   *message = nullptr;
728     Tmf::MessageInfo messageInfo(GetInstance());
729     Error            error;
730 
731     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriDuaRegistrationNotify);
732     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
733 
734     SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
735     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aAddress));
736 
737     messageInfo.SetSockAddrToRlocPeerAddrTo(aChild.GetRloc16());
738 
739     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
740 
741     LogInfo("Sent %s for child %04x DUA %s", UriToString<kUriDuaRegistrationNotify>(), aChild.GetRloc16(),
742             aAddress.ToString().AsCString());
743 
744 exit:
745 
746     if (error != kErrorNone)
747     {
748         FreeMessage(message);
749 
750         // TODO: (DUA) (P4) may enhance to  guarantee the delivery of DUA.ntf
751         LogWarn("Sent %s for child %04x DUA %s Error %s", UriToString<kUriDuaRegistrationNotify>(), aChild.GetRloc16(),
752                 aAddress.ToString().AsCString(), ErrorToString(error));
753     }
754 }
755 
HandleChildDuaAddressEvent(const Child & aChild,ChildDuaAddressEvent aEvent)756 void DuaManager::HandleChildDuaAddressEvent(const Child &aChild, ChildDuaAddressEvent aEvent)
757 {
758     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
759 
760     if ((aEvent == kAddressRemoved || aEvent == kAddressChanged) && mChildDuaMask.Get(childIndex))
761     {
762         // Abort on going proxy DUA.req for this child
763         if (mChildIndexDuaRegistering == childIndex)
764         {
765             IgnoreError(Get<Tmf::Agent>().AbortTransaction(&DuaManager::HandleDuaResponse, this));
766         }
767 
768         mChildDuaMask.Set(childIndex, false);
769         mChildDuaRegisteredMask.Set(childIndex, false);
770     }
771 
772     if (aEvent == kAddressAdded || aEvent == kAddressChanged ||
773         (aEvent == kAddressUnchanged && !mChildDuaMask.Get(childIndex)))
774     {
775         if (mChildDuaMask == mChildDuaRegisteredMask)
776         {
777             UpdateCheckDelay(Random::NonCrypto::GetUint8InRange(1, BackboneRouter::kParentAggregateDelay));
778         }
779 
780         mChildDuaMask.Set(childIndex, true);
781         mChildDuaRegisteredMask.Set(childIndex, false);
782     }
783 }
784 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
785 
786 } // namespace ot
787 
788 #endif // OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE)
789