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