1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
54
55
70
71
72
81
82
83
84
85
86
96
97
98
99
100
101
102
103
104
105
106
107
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
154
155
156
157
158
160
161
162
163
164
165
166
167
168
169
170
171
175
176
177
178
179
180
181
182
183
184
185
190
194
195
196
197
198
199
200
211
212
213
214
215
216
217
218
219
220
221
222
223
224
240
241
242
243
244
245
249
250
254
255
256
257
261
262
266
267
272
273
274
275
276
277
278
279
280
281
282
283
284
285
289
290
291
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
399
400
401
402
403
410
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
436
437
438
439
440
441
442
443
444
445
446
447
448
449
454
455
456
457
458
459
460
461
462
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
498
499
500
501
502
503
504
505
506
507
537
538
539
540
541
542
543
544
545
556
557
575
576
577
578
579
580
581
582
586
587
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
619
620
621
622
623
624
625
626
627
633
640
641
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
664
665
666
680
681
682
683
684
691
692
693
694
695
696
697
698
699
700
715
716
717
718
719
720
721
722
723
724
725
726
727
733
734
747
748
749
750
751
752
753
754
/* ... */
/* ... */
#include "mlr_manager.hpp"
#if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
#include "instance/instance.hpp"
namespace ot {
RegisterLogModule("MlrManager");
MlrManager::MlrManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mReregistrationDelay(0)
, mSendDelay(0)
, mMlrPending(false)
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
, mRegisterPending(false)
#endif
{
}{...}
void MlrManager::HandleNotifierEvents(Events aEvents)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
if (aEvents.Contains(kEventIp6MulticastSubscribed))
{
UpdateLocalSubscriptions();
}{...}
#endif/* ... */
if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::MleRouter>().IsChild())
{
UpdateReregistrationDelay(true);
}{...}
}{ ... }
void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
const BackboneRouter::Config &aConfig)
{
OT_UNUSED_VARIABLE(aConfig);
bool needRereg =
aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg;
UpdateReregistrationDelay(needRereg);
}{ ... }
#if OPENTHREAD_CONFIG_MLR_ENABLE
void MlrManager::UpdateLocalSubscriptions(void)
{
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == kMlrStateToRegister && IsAddressMlrRegisteredByAnyChild(addr.GetAddress()))
{
addr.SetMlrState(kMlrStateRegistered);
}{...}
}{...}
#endif/* ... */
CheckInvariants();
ScheduleSend(0);
}{ ... }
bool MlrManager::IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const
{
bool ret = false;
OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress() == aAddress && addr.GetMlrState() == kMlrStateRegistered)
{
ExitNow(ret = true);
}{...}
}{...}
exit:
return ret;
}{...}
/* ... */#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
bool MlrManager::IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
{
bool ret = false;
OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
if (&child != aExceptChild && child.HasMlrRegisteredAddress(aAddress))
{
ExitNow(ret = true);
}{...}
}{...}
exit:
return ret;
}{...}
void MlrManager::UpdateProxiedSubscriptions(Child &aChild, const MlrAddressArray &aOldMlrRegisteredAddresses)
{
VerifyOrExit(aChild.IsStateValid());
for (Child::Ip6AddrEntry &addrEntry : aChild.GetIp6Addresses())
{
bool isMlrRegistered;
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
isMlrRegistered = aOldMlrRegisteredAddresses.Contains(addrEntry);
#if OPENTHREAD_CONFIG_MLR_ENABLE
isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(addrEntry);/* ... */
#endif
isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(addrEntry, &aChild);
addrEntry.SetMlrState(isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister, aChild);
}{...}
exit:
LogMulticastAddresses();
CheckInvariants();
if (aChild.HasAnyMlrToRegisterAddress())
{
ScheduleSend(Random::NonCrypto::GetUint16InRange(1, BackboneRouter::kParentAggregateDelay));
}{...}
}{ ... }
/* ... */#endif
void MlrManager::ScheduleSend(uint16_t aDelay)
{
OT_ASSERT(!mMlrPending || mSendDelay == 0);
VerifyOrExit(!mMlrPending);
if (aDelay == 0)
{
mSendDelay = 0;
SendMlr();
}{...}
else if (mSendDelay == 0 || mSendDelay > aDelay)
{
mSendDelay = aDelay;
}{...}
UpdateTimeTickerRegistration();
exit:
return;
}{ ... }
void MlrManager::UpdateTimeTickerRegistration(void)
{
if (mSendDelay == 0 && mReregistrationDelay == 0)
{
Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMlrManager);
}{...}
else
{
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMlrManager);
}{...}
}{ ... }
void MlrManager::SendMlr(void)
{
Error error;
Mle::MleRouter &mle = Get<Mle::MleRouter>();
AddressArray addresses;
VerifyOrExit(!mMlrPending, error = kErrorBusy);
VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addresses.IsFull())
{
break;
}{...}
if (addr.GetMlrState() == kMlrStateToRegister)
{
addresses.AddUnique(addr.GetAddress());
addr.SetMlrState(kMlrStateRegistering);
}{...}
}{...}
#endif/* ... */
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
if (addresses.IsFull())
{
break;
}{...}
if (!child.HasAnyMlrToRegisterAddress())
{
continue;
}{...}
for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
{
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
if (addresses.IsFull())
{
break;
}{...}
if (addrEntry.GetMlrState(child) == kMlrStateToRegister)
{
addresses.AddUnique(addrEntry);
addrEntry.SetMlrState(kMlrStateRegistering, child);
}{...}
}{...}
}{...}
#endif/* ... */
VerifyOrExit(!addresses.IsEmpty(), error = kErrorNotFound);
SuccessOrExit(
error = SendMlrMessage(addresses.GetArrayBuffer(), addresses.GetLength(), nullptr, HandleMlrResponse, this));
mMlrPending = true;
if (!Get<Mle::Mle>().IsRxOnWhenIdle())
{
Get<DataPollSender>().SendFastPolls();
}{...}
exit:
if (error != kErrorNone)
{
SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
if (error == kErrorNoBufs)
{
ScheduleSend(1);
}{...}
}{...}
LogMulticastAddresses();
CheckInvariants();
}{ ... }
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
Error MlrManager::RegisterMulticastListeners(const Ip6::Address *aAddresses,
uint8_t aAddressNum,
const uint32_t *aTimeout,
MlrCallback aCallback,
void *aContext)
{
Error error;
VerifyOrExit(aAddresses != nullptr, error = kErrorInvalidArgs);
VerifyOrExit(aAddressNum > 0 && aAddressNum <= Ip6AddressesTlv::kMaxAddresses, error = kErrorInvalidArgs);
VerifyOrExit(aContext == nullptr || aCallback != nullptr, error = kErrorInvalidArgs);
#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
#else
if (!Get<MeshCoP::Commissioner>().IsActive())
{
LogWarn("MLR.req without active commissioner session for test.");
}{...}
/* ... */#endif
VerifyOrExit(!mRegisterPending, error = kErrorBusy);
SuccessOrExit(error = SendMlrMessage(aAddresses, aAddressNum, aTimeout, HandleRegisterResponse, this));
mRegisterPending = true;
mRegisterCallback.Set(aCallback, aContext);
exit:
return error;
}{...}
void MlrManager::HandleRegisterResponse(void *aContext,
otMessage *aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
static_cast<MlrManager *>(aContext)->HandleRegisterResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
aResult);
}{...}
void MlrManager::HandleRegisterResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status;
Error error;
AddressArray failedAddresses;
mRegisterPending = false;
error = ParseMlrResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses);
mRegisterCallback.InvokeAndClearIfSet(error, status, failedAddresses.GetArrayBuffer(), failedAddresses.GetLength());
}{...}
/* ... */
#endif
Error MlrManager::SendMlrMessage(const Ip6::Address *aAddresses,
uint8_t aAddressNum,
const uint32_t *aTimeout,
Coap::ResponseHandler aResponseHandler,
void *aResponseContext)
{
OT_UNUSED_VARIABLE(aTimeout);
Error error = kErrorNone;
Mle::MleRouter &mle = Get<Mle::MleRouter>();
Coap::Message *message = nullptr;
Tmf::MessageInfo messageInfo(GetInstance());
Ip6AddressesTlv addressesTlv;
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
message = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriMlr);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
addressesTlv.Init();
addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
SuccessOrExit(error = message->Append(addressesTlv));
SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum));
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
if (Get<MeshCoP::Commissioner>().IsActive())
{
SuccessOrExit(
error = Tlv::Append<ThreadCommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
}{...}
if (aTimeout != nullptr)
{
SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, *aTimeout));
}{...}
#else/* ... */
OT_ASSERT(aTimeout == nullptr);
#endif
if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
{
uint8_t pbbrServiceId;
SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr());
}{...}
else
{
messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
Get<BackboneRouter::Leader>().GetServer16());
}{...}
messageInfo.SetSockAddrToRloc();
error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
LogInfo("Sent MLR.req: addressNum=%d", aAddressNum);
exit:
LogInfo("SendMlrMessage(): %s", ErrorToString(error));
FreeMessageOnError(message, error);
return error;
}{ ... }
void MlrManager::HandleMlrResponse(void *aContext,
otMessage *aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
static_cast<MlrManager *>(aContext)->HandleMlrResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
aResult);
}{ ... }
void MlrManager::HandleMlrResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status;
Error error;
AddressArray failedAddresses;
error = ParseMlrResponse(aResult, aMessage, status, failedAddresses);
FinishMlr(error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess, failedAddresses);
if (error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess)
{
ScheduleSend(0);
}{...}
else
{
BackboneRouter::Config config;
uint16_t reregDelay;
if (Get<BackboneRouter::Leader>().GetConfig(config) == kErrorNone)
{
reregDelay = config.mReregistrationDelay > 1
? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
: 1;
ScheduleSend(reregDelay);
}{...}
}{...}
}{ ... }
Error MlrManager::ParseMlrResponse(Error aResult,
Coap::Message *aMessage,
uint8_t &aStatus,
AddressArray &aFailedAddresses)
{
Error error;
OffsetRange offsetRange;
aStatus = ThreadStatusTlv::kMlrGeneralFailure;
VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse);
VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse);
SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(*aMessage, aStatus));
if (ThreadTlv::FindTlvValueOffsetRange(*aMessage, Ip6AddressesTlv::kIp6Addresses, offsetRange) == kErrorNone)
{
VerifyOrExit(offsetRange.GetLength() % sizeof(Ip6::Address) == 0, error = kErrorParse);
VerifyOrExit(offsetRange.GetLength() / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses,
error = kErrorParse);
while (!offsetRange.IsEmpty())
{
IgnoreError(aMessage->Read(offsetRange, *aFailedAddresses.PushBack()));
offsetRange.AdvanceOffset(sizeof(Ip6::Address));
}{...}
}{...}
VerifyOrExit(aFailedAddresses.IsEmpty() || aStatus != ThreadStatusTlv::kMlrSuccess, error = kErrorParse);
exit:
LogMlrResponse(aResult, error, aStatus, aFailedAddresses);
return aResult != kErrorNone ? aResult : error;
}{ ... }
void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == aFromState)
{
addr.SetMlrState(aToState);
}{...}
}{...}
#endif/* ... */
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
{
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
if (addrEntry.GetMlrState(child) == aFromState)
{
addrEntry.SetMlrState(aToState, child);
}{...}
}{...}
}{...}
#endif/* ... */
}{ ... }
void MlrManager::FinishMlr(bool aSuccess, const AddressArray &aFailedAddresses)
{
OT_ASSERT(mMlrPending);
mMlrPending = false;
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == kMlrStateRegistering)
{
bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addr.GetAddress());
addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister);
}{...}
}{...}
#endif/* ... */
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
{
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
if (addrEntry.GetMlrState(child) == kMlrStateRegistering)
{
bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addrEntry);
addrEntry.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister, child);
}{...}
}{...}
}{...}
#endif/* ... */
LogMulticastAddresses();
CheckInvariants();
}{ ... }
void MlrManager::HandleTimeTick(void)
{
if (mSendDelay > 0 && --mSendDelay == 0)
{
SendMlr();
}{...}
if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
{
Reregister();
}{...}
UpdateTimeTickerRegistration();
}{ ... }
void MlrManager::Reregister(void)
{
LogInfo("MLR Reregister!");
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
CheckInvariants();
ScheduleSend(0);
UpdateReregistrationDelay(false);
}{ ... }
void MlrManager::UpdateReregistrationDelay(bool aRereg)
{
Mle::MleRouter &mle = Get<Mle::MleRouter>();
bool needSendMlr = (mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1()) &&
Get<BackboneRouter::Leader>().HasPrimary();
if (!needSendMlr)
{
mReregistrationDelay = 0;
}{...}
else
{
BackboneRouter::Config config;
uint32_t reregDelay;
uint32_t effectiveMlrTimeout;
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
if (aRereg)
{
reregDelay = config.mReregistrationDelay > 1
? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
: 1;
}{...}
else
{
effectiveMlrTimeout = Max(config.mMlrTimeout, BackboneRouter::kMinMlrTimeout);
reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9);
}{...}
if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay)
{
mReregistrationDelay = reregDelay;
}{...}
}{...}
UpdateTimeTickerRegistration();
LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
ToUlong(mReregistrationDelay));
}{ ... }
void MlrManager::LogMulticastAddresses(void)
{
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
LogDebg("-------- Multicast Addresses --------");
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
LogDebg("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
}{...}
#endif/* ... */
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
{
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
LogDebg("%-32s%c %04x", addrEntry.ToString().AsCString(), "-rR"[addrEntry.GetMlrState(child)],
child.GetRloc16());
}{...}
}{...}
#endif/* ... */
/* ... */
#endif
}{ ... }
void MlrManager::AddressArray::AddUnique(const Ip6::Address &aAddress)
{
if (!Contains(aAddress))
{
IgnoreError(PushBack(aAddress));
}{...}
}{ ... }
void MlrManager::LogMlrResponse(Error aResult, Error aError, uint8_t aStatus, const AddressArray &aFailedAddresses)
{
OT_UNUSED_VARIABLE(aResult);
OT_UNUSED_VARIABLE(aError);
OT_UNUSED_VARIABLE(aStatus);
OT_UNUSED_VARIABLE(aFailedAddresses);
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::kMlrSuccess)
{
LogInfo("Receive MLR.rsp OK");
}{...}
else
{
LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
ErrorToString(aError), aStatus, aFailedAddresses.GetLength());
for (const Ip6::Address &address : aFailedAddresses)
{
LogWarn("MA failed: %s", address.ToString().AsCString());
}{...}
}{...}
#endif/* ... */
}{ ... }
void MlrManager::CheckInvariants(void) const
{
#if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
uint16_t registeringNum = 0;
OT_UNUSED_VARIABLE(registeringNum);
OT_ASSERT(!mMlrPending || mSendDelay == 0);
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::Netif::ExternalMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
registeringNum += (addr.GetMlrState() == kMlrStateRegistering);
}{...}
#endif/* ... */
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses())
{
if (!addrEntry.IsMulticastLargerThanRealmLocal())
{
continue;
}{...}
registeringNum += (addrEntry.GetMlrState(child) == kMlrStateRegistering);
}{...}
}{...}
#endif/* ... */
OT_ASSERT(registeringNum == 0 || mMlrPending);/* ... */
#endif
}{...}
}{...}
/* ... */
#endif