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