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