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