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