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