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