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 MLR management.
32 */
33
34 #include "mlr_manager.hpp"
35
36 #if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
37
38 #include "instance/instance.hpp"
39
40 namespace ot {
41
42 RegisterLogModule("MlrManager");
43
MlrManager(Instance & aInstance)44 MlrManager::MlrManager(Instance &aInstance)
45 : InstanceLocator(aInstance)
46 , mReregistrationDelay(0)
47 , mSendDelay(0)
48 , mMlrPending(false)
49 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
50 , mRegisterPending(false)
51 #endif
52 {
53 }
54
HandleNotifierEvents(Events aEvents)55 void MlrManager::HandleNotifierEvents(Events aEvents)
56 {
57 #if OPENTHREAD_CONFIG_MLR_ENABLE
58 if (aEvents.Contains(kEventIp6MulticastSubscribed))
59 {
60 UpdateLocalSubscriptions();
61 }
62 #endif
63
64 if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::MleRouter>().IsChild())
65 {
66 // Reregistration after re-attach
67 UpdateReregistrationDelay(true);
68 }
69 }
70
HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,const BackboneRouter::Config & aConfig)71 void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
72 const BackboneRouter::Config &aConfig)
73 {
74 OT_UNUSED_VARIABLE(aConfig);
75
76 bool needRereg =
77 aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg;
78
79 UpdateReregistrationDelay(needRereg);
80 }
81
82 #if OPENTHREAD_CONFIG_MLR_ENABLE
UpdateLocalSubscriptions(void)83 void MlrManager::UpdateLocalSubscriptions(void)
84 {
85 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
86 // Check multicast addresses are newly listened against Children
87 for (Ip6::Netif::ExternalMulticastAddress &addr :
88 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
89 {
90 if (addr.GetMlrState() == kMlrStateToRegister && IsAddressMlrRegisteredByAnyChild(addr.GetAddress()))
91 {
92 addr.SetMlrState(kMlrStateRegistered);
93 }
94 }
95 #endif
96
97 CheckInvariants();
98 ScheduleSend(0);
99 }
100
IsAddressMlrRegisteredByNetif(const Ip6::Address & aAddress) const101 bool MlrManager::IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const
102 {
103 bool ret = false;
104
105 OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
106
107 for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
108 {
109 if (addr.GetAddress() == aAddress && addr.GetMlrState() == kMlrStateRegistered)
110 {
111 ExitNow(ret = true);
112 }
113 }
114
115 exit:
116 return ret;
117 }
118
119 #endif // OPENTHREAD_CONFIG_MLR_ENABLE
120
121 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
122
IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address & aAddress,const Child * aExceptChild) const123 bool MlrManager::IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
124 {
125 bool ret = false;
126
127 OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
128
129 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
130 {
131 if (&child != aExceptChild && child.HasMlrRegisteredAddress(aAddress))
132 {
133 ExitNow(ret = true);
134 }
135 }
136
137 exit:
138 return ret;
139 }
140
UpdateProxiedSubscriptions(Child & aChild,const MlrAddressArray & aOldMlrRegisteredAddresses)141 void MlrManager::UpdateProxiedSubscriptions(Child &aChild, const MlrAddressArray &aOldMlrRegisteredAddresses)
142 {
143 VerifyOrExit(aChild.IsStateValid());
144
145 // Search the new multicast addresses and set its flag accordingly
146 for (Child::Ip6AddrEntry &addrEntry : aChild.GetIp6Addresses())
147 {
148 bool isMlrRegistered;
149
150 if (!addrEntry.IsMulticastLargerThanRealmLocal())
151 {
152 continue;
153 }
154
155 isMlrRegistered = aOldMlrRegisteredAddresses.Contains(addrEntry);
156
157 #if OPENTHREAD_CONFIG_MLR_ENABLE
158 // Check if it's a new multicast address against parent Netif
159 isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(addrEntry);
160 #endif
161 // Check if it's a new multicast address against other Children
162 isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(addrEntry, &aChild);
163
164 addrEntry.SetMlrState(isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister, aChild);
165 }
166
167 exit:
168 LogMulticastAddresses();
169 CheckInvariants();
170
171 if (aChild.HasAnyMlrToRegisterAddress())
172 {
173 ScheduleSend(Random::NonCrypto::GetUint16InRange(1, BackboneRouter::kParentAggregateDelay));
174 }
175 }
176
177 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
178
ScheduleSend(uint16_t aDelay)179 void MlrManager::ScheduleSend(uint16_t aDelay)
180 {
181 OT_ASSERT(!mMlrPending || mSendDelay == 0);
182
183 VerifyOrExit(!mMlrPending);
184
185 if (aDelay == 0)
186 {
187 mSendDelay = 0;
188 SendMlr();
189 }
190 else if (mSendDelay == 0 || mSendDelay > aDelay)
191 {
192 mSendDelay = aDelay;
193 }
194
195 UpdateTimeTickerRegistration();
196 exit:
197 return;
198 }
199
UpdateTimeTickerRegistration(void)200 void MlrManager::UpdateTimeTickerRegistration(void)
201 {
202 if (mSendDelay == 0 && mReregistrationDelay == 0)
203 {
204 Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMlrManager);
205 }
206 else
207 {
208 Get<TimeTicker>().RegisterReceiver(TimeTicker::kMlrManager);
209 }
210 }
211
SendMlr(void)212 void MlrManager::SendMlr(void)
213 {
214 Error error;
215 Mle::MleRouter &mle = Get<Mle::MleRouter>();
216 AddressArray addresses;
217
218 VerifyOrExit(!mMlrPending, error = kErrorBusy);
219 VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
220 VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
221 VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
222
223 #if OPENTHREAD_CONFIG_MLR_ENABLE
224 // Append Netif multicast addresses
225 for (Ip6::Netif::ExternalMulticastAddress &addr :
226 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
227 {
228 if (addresses.IsFull())
229 {
230 break;
231 }
232
233 if (addr.GetMlrState() == kMlrStateToRegister)
234 {
235 addresses.AddUnique(addr.GetAddress());
236 addr.SetMlrState(kMlrStateRegistering);
237 }
238 }
239 #endif
240
241 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
242 // Append Child multicast addresses
243 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
244 {
245 if (addresses.IsFull())
246 {
247 break;
248 }
249
250 if (!child.HasAnyMlrToRegisterAddress())
251 {
252 continue;
253 }
254
255 for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
256 {
257 if (!addrEntry.IsMulticastLargerThanRealmLocal())
258 {
259 continue;
260 }
261
262 if (addresses.IsFull())
263 {
264 break;
265 }
266
267 if (addrEntry.GetMlrState(child) == kMlrStateToRegister)
268 {
269 addresses.AddUnique(addrEntry);
270 addrEntry.SetMlrState(kMlrStateRegistering, child);
271 }
272 }
273 }
274 #endif
275
276 VerifyOrExit(!addresses.IsEmpty(), error = kErrorNotFound);
277 SuccessOrExit(
278 error = SendMlrMessage(addresses.GetArrayBuffer(), addresses.GetLength(), nullptr, HandleMlrResponse, this));
279
280 mMlrPending = true;
281
282 // Generally Thread 1.2 Router would send MLR.req on behalf for MA (scope >=4) subscribed by its MTD child.
283 // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send MLR.req to PBBR itself.
284 // In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely.
285 if (!Get<Mle::Mle>().IsRxOnWhenIdle())
286 {
287 Get<DataPollSender>().SendFastPolls();
288 }
289
290 exit:
291 if (error != kErrorNone)
292 {
293 SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
294
295 if (error == kErrorNoBufs)
296 {
297 ScheduleSend(1);
298 }
299 }
300
301 LogMulticastAddresses();
302 CheckInvariants();
303 }
304
305 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
RegisterMulticastListeners(const Ip6::Address * aAddresses,uint8_t aAddressNum,const uint32_t * aTimeout,MlrCallback aCallback,void * aContext)306 Error MlrManager::RegisterMulticastListeners(const Ip6::Address *aAddresses,
307 uint8_t aAddressNum,
308 const uint32_t *aTimeout,
309 MlrCallback aCallback,
310 void *aContext)
311 {
312 Error error;
313
314 VerifyOrExit(aAddresses != nullptr, error = kErrorInvalidArgs);
315 VerifyOrExit(aAddressNum > 0 && aAddressNum <= Ip6AddressesTlv::kMaxAddresses, error = kErrorInvalidArgs);
316 VerifyOrExit(aContext == nullptr || aCallback != nullptr, error = kErrorInvalidArgs);
317 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
318 VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
319 #else
320 if (!Get<MeshCoP::Commissioner>().IsActive())
321 {
322 LogWarn("MLR.req without active commissioner session for test.");
323 }
324 #endif
325
326 // Only allow one outstanding registration if callback is specified.
327 VerifyOrExit(!mRegisterPending, error = kErrorBusy);
328
329 SuccessOrExit(error = SendMlrMessage(aAddresses, aAddressNum, aTimeout, HandleRegisterResponse, this));
330
331 mRegisterPending = true;
332 mRegisterCallback.Set(aCallback, aContext);
333
334 exit:
335 return error;
336 }
337
HandleRegisterResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)338 void MlrManager::HandleRegisterResponse(void *aContext,
339 otMessage *aMessage,
340 const otMessageInfo *aMessageInfo,
341 otError aResult)
342 {
343 static_cast<MlrManager *>(aContext)->HandleRegisterResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
344 aResult);
345 }
346
HandleRegisterResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)347 void MlrManager::HandleRegisterResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult)
348 {
349 OT_UNUSED_VARIABLE(aMessageInfo);
350
351 uint8_t status;
352 Error error;
353 AddressArray failedAddresses;
354
355 mRegisterPending = false;
356
357 error = ParseMlrResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses);
358
359 mRegisterCallback.InvokeAndClearIfSet(error, status, failedAddresses.GetArrayBuffer(), failedAddresses.GetLength());
360 }
361
362 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
363
SendMlrMessage(const Ip6::Address * aAddresses,uint8_t aAddressNum,const uint32_t * aTimeout,Coap::ResponseHandler aResponseHandler,void * aResponseContext)364 Error MlrManager::SendMlrMessage(const Ip6::Address *aAddresses,
365 uint8_t aAddressNum,
366 const uint32_t *aTimeout,
367 Coap::ResponseHandler aResponseHandler,
368 void *aResponseContext)
369 {
370 OT_UNUSED_VARIABLE(aTimeout);
371
372 Error error = kErrorNone;
373 Mle::MleRouter &mle = Get<Mle::MleRouter>();
374 Coap::Message *message = nullptr;
375 Tmf::MessageInfo messageInfo(GetInstance());
376 Ip6AddressesTlv addressesTlv;
377
378 VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
379
380 message = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriMlr);
381 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
382
383 addressesTlv.Init();
384 addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
385 SuccessOrExit(error = message->Append(addressesTlv));
386 SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum));
387
388 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
389 if (Get<MeshCoP::Commissioner>().IsActive())
390 {
391 SuccessOrExit(
392 error = Tlv::Append<ThreadCommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
393 }
394
395 if (aTimeout != nullptr)
396 {
397 SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, *aTimeout));
398 }
399 #else
400 OT_ASSERT(aTimeout == nullptr);
401 #endif
402
403 if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
404 {
405 uint8_t pbbrServiceId;
406
407 SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
408 mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr());
409 }
410 else
411 {
412 messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
413 Get<BackboneRouter::Leader>().GetServer16());
414 }
415
416 messageInfo.SetSockAddrToRloc();
417
418 error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
419
420 LogInfo("Sent MLR.req: addressNum=%d", aAddressNum);
421
422 exit:
423 LogInfo("SendMlrMessage(): %s", ErrorToString(error));
424 FreeMessageOnError(message, error);
425 return error;
426 }
427
HandleMlrResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aResult)428 void MlrManager::HandleMlrResponse(void *aContext,
429 otMessage *aMessage,
430 const otMessageInfo *aMessageInfo,
431 otError aResult)
432 {
433 static_cast<MlrManager *>(aContext)->HandleMlrResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
434 aResult);
435 }
436
HandleMlrResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)437 void MlrManager::HandleMlrResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
438 {
439 OT_UNUSED_VARIABLE(aMessageInfo);
440
441 uint8_t status;
442 Error error;
443 AddressArray failedAddresses;
444
445 error = ParseMlrResponse(aResult, aMessage, status, failedAddresses);
446
447 FinishMlr(error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess, failedAddresses);
448
449 if (error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess)
450 {
451 // keep sending until all multicast addresses are registered.
452 ScheduleSend(0);
453 }
454 else
455 {
456 BackboneRouter::Config config;
457 uint16_t reregDelay;
458
459 // The Device has just attempted a Multicast Listener Registration which failed, and it retries the same
460 // registration with a random time delay chosen in the interval [0, Reregistration Delay].
461 // This is required by Thread 1.2 Specification 5.24.2.3
462 if (Get<BackboneRouter::Leader>().GetConfig(config) == kErrorNone)
463 {
464 reregDelay = config.mReregistrationDelay > 1
465 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
466 : 1;
467 ScheduleSend(reregDelay);
468 }
469 }
470 }
471
ParseMlrResponse(Error aResult,Coap::Message * aMessage,uint8_t & aStatus,AddressArray & aFailedAddresses)472 Error MlrManager::ParseMlrResponse(Error aResult,
473 Coap::Message *aMessage,
474 uint8_t &aStatus,
475 AddressArray &aFailedAddresses)
476 {
477 Error error;
478 OffsetRange offsetRange;
479
480 aStatus = ThreadStatusTlv::kMlrGeneralFailure;
481
482 VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse);
483 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse);
484
485 SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(*aMessage, aStatus));
486
487 if (ThreadTlv::FindTlvValueOffsetRange(*aMessage, Ip6AddressesTlv::kIp6Addresses, offsetRange) == kErrorNone)
488 {
489 VerifyOrExit(offsetRange.GetLength() % sizeof(Ip6::Address) == 0, error = kErrorParse);
490 VerifyOrExit(offsetRange.GetLength() / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses,
491 error = kErrorParse);
492
493 while (!offsetRange.IsEmpty())
494 {
495 IgnoreError(aMessage->Read(offsetRange, *aFailedAddresses.PushBack()));
496 offsetRange.AdvanceOffset(sizeof(Ip6::Address));
497 }
498 }
499
500 VerifyOrExit(aFailedAddresses.IsEmpty() || aStatus != ThreadStatusTlv::kMlrSuccess, error = kErrorParse);
501
502 exit:
503 LogMlrResponse(aResult, error, aStatus, aFailedAddresses);
504 return aResult != kErrorNone ? aResult : error;
505 }
506
SetMulticastAddressMlrState(MlrState aFromState,MlrState aToState)507 void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
508 {
509 #if OPENTHREAD_CONFIG_MLR_ENABLE
510 for (Ip6::Netif::ExternalMulticastAddress &addr :
511 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
512 {
513 if (addr.GetMlrState() == aFromState)
514 {
515 addr.SetMlrState(aToState);
516 }
517 }
518 #endif
519 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
520 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
521 {
522 for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
523 {
524 if (!addrEntry.IsMulticastLargerThanRealmLocal())
525 {
526 continue;
527 }
528
529 if (addrEntry.GetMlrState(child) == aFromState)
530 {
531 addrEntry.SetMlrState(aToState, child);
532 }
533 }
534 }
535 #endif
536 }
537
FinishMlr(bool aSuccess,const AddressArray & aFailedAddresses)538 void MlrManager::FinishMlr(bool aSuccess, const AddressArray &aFailedAddresses)
539 {
540 OT_ASSERT(mMlrPending);
541
542 mMlrPending = false;
543
544 #if OPENTHREAD_CONFIG_MLR_ENABLE
545 for (Ip6::Netif::ExternalMulticastAddress &addr :
546 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
547 {
548 if (addr.GetMlrState() == kMlrStateRegistering)
549 {
550 bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addr.GetAddress());
551
552 addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister);
553 }
554 }
555 #endif
556 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
557 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
558 {
559 for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
560 {
561 if (!addrEntry.IsMulticastLargerThanRealmLocal())
562 {
563 continue;
564 }
565
566 if (addrEntry.GetMlrState(child) == kMlrStateRegistering)
567 {
568 bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addrEntry);
569
570 addrEntry.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister, child);
571 }
572 }
573 }
574 #endif
575
576 LogMulticastAddresses();
577 CheckInvariants();
578 }
579
HandleTimeTick(void)580 void MlrManager::HandleTimeTick(void)
581 {
582 if (mSendDelay > 0 && --mSendDelay == 0)
583 {
584 SendMlr();
585 }
586
587 if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
588 {
589 Reregister();
590 }
591
592 UpdateTimeTickerRegistration();
593 }
594
Reregister(void)595 void MlrManager::Reregister(void)
596 {
597 LogInfo("MLR Reregister!");
598
599 SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
600 CheckInvariants();
601
602 ScheduleSend(0);
603
604 // Schedule for the next renewing.
605 UpdateReregistrationDelay(false);
606 }
607
UpdateReregistrationDelay(bool aRereg)608 void MlrManager::UpdateReregistrationDelay(bool aRereg)
609 {
610 Mle::MleRouter &mle = Get<Mle::MleRouter>();
611
612 bool needSendMlr = (mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1()) &&
613 Get<BackboneRouter::Leader>().HasPrimary();
614
615 if (!needSendMlr)
616 {
617 mReregistrationDelay = 0;
618 }
619 else
620 {
621 BackboneRouter::Config config;
622 uint32_t reregDelay;
623 uint32_t effectiveMlrTimeout;
624
625 IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
626
627 if (aRereg)
628 {
629 reregDelay = config.mReregistrationDelay > 1
630 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
631 : 1;
632 }
633 else
634 {
635 // Calculate renewing period according to Thread Spec. 5.24.2.3.2
636 // The random time t SHOULD be chosen such that (0.5* MLR-Timeout) < t < (MLR-Timeout – 9 seconds).
637 effectiveMlrTimeout = Max(config.mMlrTimeout, BackboneRouter::kMinMlrTimeout);
638 reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9);
639 }
640
641 if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay)
642 {
643 mReregistrationDelay = reregDelay;
644 }
645 }
646
647 UpdateTimeTickerRegistration();
648
649 LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
650 ToUlong(mReregistrationDelay));
651 }
652
LogMulticastAddresses(void)653 void MlrManager::LogMulticastAddresses(void)
654 {
655 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
656 LogDebg("-------- Multicast Addresses --------");
657
658 #if OPENTHREAD_CONFIG_MLR_ENABLE
659 for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
660 {
661 LogDebg("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
662 }
663 #endif
664
665 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
666 for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
667 {
668 for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
669 {
670 if (!addrEntry.IsMulticastLargerThanRealmLocal())
671 {
672 continue;
673 }
674
675 LogDebg("%-32s%c %04x", addrEntry.ToString().AsCString(), "-rR"[addrEntry.GetMlrState(child)],
676 child.GetRloc16());
677 }
678 }
679 #endif
680
681 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
682 }
683
AddUnique(const Ip6::Address & aAddress)684 void MlrManager::AddressArray::AddUnique(const Ip6::Address &aAddress)
685 {
686 if (!Contains(aAddress))
687 {
688 IgnoreError(PushBack(aAddress));
689 }
690 }
691
LogMlrResponse(Error aResult,Error aError,uint8_t aStatus,const AddressArray & aFailedAddresses)692 void MlrManager::LogMlrResponse(Error aResult, Error aError, uint8_t aStatus, const AddressArray &aFailedAddresses)
693 {
694 OT_UNUSED_VARIABLE(aResult);
695 OT_UNUSED_VARIABLE(aError);
696 OT_UNUSED_VARIABLE(aStatus);
697 OT_UNUSED_VARIABLE(aFailedAddresses);
698
699 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
700 if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::kMlrSuccess)
701 {
702 LogInfo("Receive MLR.rsp OK");
703 }
704 else
705 {
706 LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
707 ErrorToString(aError), aStatus, aFailedAddresses.GetLength());
708
709 for (const Ip6::Address &address : aFailedAddresses)
710 {
711 LogWarn("MA failed: %s", address.ToString().AsCString());
712 }
713 }
714 #endif
715 }
716
CheckInvariants(void) const717 void MlrManager::CheckInvariants(void) const
718 {
719 #if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
720 uint16_t registeringNum = 0;
721
722 OT_UNUSED_VARIABLE(registeringNum);
723
724 OT_ASSERT(!mMlrPending || mSendDelay == 0);
725
726 #if OPENTHREAD_CONFIG_MLR_ENABLE
727 for (Ip6::Netif::ExternalMulticastAddress &addr :
728 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
729 {
730 registeringNum += (addr.GetMlrState() == kMlrStateRegistering);
731 }
732 #endif
733 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
734 for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
735 {
736 for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
737 {
738 if (!addrEntry.IsMulticastLargerThanRealmLocal())
739 {
740 continue;
741 }
742
743 registeringNum += (addrEntry.GetMlrState(child) == kMlrStateRegistering);
744 }
745 }
746 #endif
747
748 OT_ASSERT(registeringNum == 0 || mMlrPending);
749 #endif // OPENTHREAD_EXAMPLES_SIMULATION
750 }
751
752 } // namespace ot
753
754 #endif // OPENTHREAD_CONFIG_MLR_ENABLE
755