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