• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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