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 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
55 , mRegisterMulticastListenersCallback(nullptr)
56 , mRegisterMulticastListenersContext(nullptr)
57 #endif
58 , mReregistrationDelay(0)
59 , mSendDelay(0)
60 , mMlrPending(false)
61 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
62 , mRegisterMulticastListenersPending(false)
63 #endif
64 {
65 }
66
HandleNotifierEvents(Events aEvents)67 void MlrManager::HandleNotifierEvents(Events aEvents)
68 {
69 #if OPENTHREAD_CONFIG_MLR_ENABLE
70 if (aEvents.Contains(kEventIp6MulticastSubscribed))
71 {
72 UpdateLocalSubscriptions();
73 }
74 #endif
75
76 if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::MleRouter>().IsChild())
77 {
78 // Reregistration after re-attach
79 UpdateReregistrationDelay(true);
80 }
81 }
82
HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,const BackboneRouter::BackboneRouterConfig & aConfig)83 void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
84 const BackboneRouter::BackboneRouterConfig &aConfig)
85 {
86 OT_UNUSED_VARIABLE(aConfig);
87
88 bool needRereg =
89 aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg;
90
91 UpdateReregistrationDelay(needRereg);
92 }
93
94 #if OPENTHREAD_CONFIG_MLR_ENABLE
UpdateLocalSubscriptions(void)95 void MlrManager::UpdateLocalSubscriptions(void)
96 {
97 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
98 // Check multicast addresses are newly listened against Children
99 for (Ip6::Netif::ExternalMulticastAddress &addr :
100 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
101 {
102 if (addr.GetMlrState() == kMlrStateToRegister && IsAddressMlrRegisteredByAnyChild(addr.GetAddress()))
103 {
104 addr.SetMlrState(kMlrStateRegistered);
105 }
106 }
107 #endif
108
109 CheckInvariants();
110 ScheduleSend(0);
111 }
112
IsAddressMlrRegisteredByNetif(const Ip6::Address & aAddress) const113 bool MlrManager::IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const
114 {
115 bool ret = false;
116
117 OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
118
119 for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
120 {
121 if (addr.GetAddress() == aAddress && addr.GetMlrState() == kMlrStateRegistered)
122 {
123 ExitNow(ret = true);
124 }
125 }
126
127 exit:
128 return ret;
129 }
130
131 #endif // OPENTHREAD_CONFIG_MLR_ENABLE
132
133 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
134
IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address & aAddress,const Child * aExceptChild) const135 bool MlrManager::IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const
136 {
137 bool ret = false;
138
139 OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal());
140
141 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
142 {
143 if (&child != aExceptChild && child.HasMlrRegisteredAddress(aAddress))
144 {
145 ExitNow(ret = true);
146 }
147 }
148
149 exit:
150 return ret;
151 }
152
UpdateProxiedSubscriptions(Child & aChild,const Ip6::Address * aOldMlrRegisteredAddresses,uint16_t aOldMlrRegisteredAddressNum)153 void MlrManager::UpdateProxiedSubscriptions(Child & aChild,
154 const Ip6::Address *aOldMlrRegisteredAddresses,
155 uint16_t aOldMlrRegisteredAddressNum)
156 {
157 VerifyOrExit(aChild.IsStateValid());
158
159 // Search the new multicast addresses and set its flag accordingly
160 for (const Ip6::Address &address : aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
161 {
162 bool isMlrRegistered = false;
163
164 // Check if it's a new multicast address against old addresses
165 for (size_t i = 0; i < aOldMlrRegisteredAddressNum; i++)
166 {
167 if (aOldMlrRegisteredAddresses[i] == address)
168 {
169 isMlrRegistered = true;
170 break;
171 }
172 }
173
174 #if OPENTHREAD_CONFIG_MLR_ENABLE
175 // Check if it's a new multicast address against parent Netif
176 isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(address);
177 #endif
178 // Check if it's a new multicast address against other Children
179 isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(address, &aChild);
180
181 aChild.SetAddressMlrState(address, isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister);
182 }
183
184 exit:
185 LogMulticastAddresses();
186 CheckInvariants();
187
188 if (aChild.HasAnyMlrToRegisterAddress())
189 {
190 ScheduleSend(Random::NonCrypto::GetUint16InRange(1, Mle::kParentAggregateDelay));
191 }
192 }
193
194 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
195
ScheduleSend(uint16_t aDelay)196 void MlrManager::ScheduleSend(uint16_t aDelay)
197 {
198 OT_ASSERT(!mMlrPending || mSendDelay == 0);
199
200 VerifyOrExit(!mMlrPending);
201
202 if (aDelay == 0)
203 {
204 mSendDelay = 0;
205 SendMulticastListenerRegistration();
206 }
207 else if (mSendDelay == 0 || mSendDelay > aDelay)
208 {
209 mSendDelay = aDelay;
210 }
211
212 UpdateTimeTickerRegistration();
213 exit:
214 return;
215 }
216
UpdateTimeTickerRegistration(void)217 void MlrManager::UpdateTimeTickerRegistration(void)
218 {
219 if (mSendDelay == 0 && mReregistrationDelay == 0)
220 {
221 Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMlrManager);
222 }
223 else
224 {
225 Get<TimeTicker>().RegisterReceiver(TimeTicker::kMlrManager);
226 }
227 }
228
SendMulticastListenerRegistration(void)229 void MlrManager::SendMulticastListenerRegistration(void)
230 {
231 Error error;
232 Mle::MleRouter &mle = Get<Mle::MleRouter>();
233 Ip6::Address addresses[Ip6AddressesTlv::kMaxAddresses];
234 uint8_t addressesNum = 0;
235
236 VerifyOrExit(!mMlrPending, error = kErrorBusy);
237 VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
238 VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = kErrorInvalidState);
239 VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
240
241 #if OPENTHREAD_CONFIG_MLR_ENABLE
242 // Append Netif multicast addresses
243 for (Ip6::Netif::ExternalMulticastAddress &addr :
244 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
245 {
246 if (addressesNum >= Ip6AddressesTlv::kMaxAddresses)
247 {
248 break;
249 }
250
251 if (addr.GetMlrState() == kMlrStateToRegister)
252 {
253 AppendToUniqueAddressList(addresses, addressesNum, addr.GetAddress());
254 addr.SetMlrState(kMlrStateRegistering);
255 }
256 }
257 #endif
258
259 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
260 // Append Child multicast addresses
261 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
262 {
263 if (addressesNum >= Ip6AddressesTlv::kMaxAddresses)
264 {
265 break;
266 }
267
268 if (!child.HasAnyMlrToRegisterAddress())
269 {
270 continue;
271 }
272
273 for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
274 {
275 if (addressesNum >= Ip6AddressesTlv::kMaxAddresses)
276 {
277 break;
278 }
279
280 if (child.GetAddressMlrState(address) == kMlrStateToRegister)
281 {
282 AppendToUniqueAddressList(addresses, addressesNum, address);
283 child.SetAddressMlrState(address, kMlrStateRegistering);
284 }
285 }
286 }
287 #endif
288
289 VerifyOrExit(addressesNum > 0, error = kErrorNotFound);
290 SuccessOrExit(
291 error = SendMulticastListenerRegistrationMessage(
292 addresses, addressesNum, nullptr, &MlrManager::HandleMulticastListenerRegistrationResponse, this));
293
294 mMlrPending = true;
295
296 // Generally Thread 1.2 Router would send MLR.req on bebelf for MA (scope >=4) subscribed by its MTD child.
297 // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send MLR.req to PBBR itself.
298 // In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely.
299 if (!Get<Mle::Mle>().IsRxOnWhenIdle())
300 {
301 Get<DataPollSender>().SendFastPolls();
302 }
303
304 exit:
305 if (error != kErrorNone)
306 {
307 SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
308
309 if (error == kErrorNoBufs)
310 {
311 ScheduleSend(1);
312 }
313 }
314
315 LogMulticastAddresses();
316 CheckInvariants();
317 }
318
319 #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)320 Error MlrManager::RegisterMulticastListeners(const otIp6Address * aAddresses,
321 uint8_t aAddressNum,
322 const uint32_t * aTimeout,
323 otIp6RegisterMulticastListenersCallback aCallback,
324 void * aContext)
325 {
326 Error error;
327
328 VerifyOrExit(aAddresses != nullptr, error = kErrorInvalidArgs);
329 VerifyOrExit(aAddressNum > 0 && aAddressNum <= Ip6AddressesTlv::kMaxAddresses, error = kErrorInvalidArgs);
330 VerifyOrExit(aContext == nullptr || aCallback != nullptr, error = kErrorInvalidArgs);
331 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
332 VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
333 #else
334 if (!Get<MeshCoP::Commissioner>().IsActive())
335 {
336 LogWarn("MLR.req without active commissioner session for test.");
337 }
338 #endif
339
340 // Only allow one outstanding registration if callback is specified.
341 VerifyOrExit(!mRegisterMulticastListenersPending, error = kErrorBusy);
342
343 SuccessOrExit(error = SendMulticastListenerRegistrationMessage(
344 aAddresses, aAddressNum, aTimeout, &MlrManager::HandleRegisterMulticastListenersResponse, this));
345
346 mRegisterMulticastListenersPending = true;
347 mRegisterMulticastListenersCallback = aCallback;
348 mRegisterMulticastListenersContext = aContext;
349
350 exit:
351 return error;
352 }
353
HandleRegisterMulticastListenersResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)354 void MlrManager::HandleRegisterMulticastListenersResponse(void * aContext,
355 otMessage * aMessage,
356 const otMessageInfo *aMessageInfo,
357 Error aResult)
358 {
359 static_cast<MlrManager *>(aContext)->HandleRegisterMulticastListenersResponse(AsCoapMessagePtr(aMessage),
360 AsCoreTypePtr(aMessageInfo), aResult);
361 }
362
HandleRegisterMulticastListenersResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)363 void MlrManager::HandleRegisterMulticastListenersResponse(otMessage * aMessage,
364 const otMessageInfo *aMessageInfo,
365 Error aResult)
366 {
367 OT_UNUSED_VARIABLE(aMessageInfo);
368
369 uint8_t status;
370 Error error;
371 Ip6::Address failedAddresses[Ip6AddressesTlv::kMaxAddresses];
372 uint8_t failedAddressNum = 0;
373 otIp6RegisterMulticastListenersCallback callback = mRegisterMulticastListenersCallback;
374 void * context = mRegisterMulticastListenersContext;
375
376 mRegisterMulticastListenersPending = false;
377 mRegisterMulticastListenersCallback = nullptr;
378 mRegisterMulticastListenersContext = nullptr;
379
380 error = ParseMulticastListenerRegistrationResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses,
381 failedAddressNum);
382
383 if (callback != nullptr)
384 {
385 callback(context, error, status, failedAddresses, failedAddressNum);
386 }
387 }
388
389 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
390
SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses,uint8_t aAddressNum,const uint32_t * aTimeout,Coap::ResponseHandler aResponseHandler,void * aResponseContext)391 Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses,
392 uint8_t aAddressNum,
393 const uint32_t * aTimeout,
394 Coap::ResponseHandler aResponseHandler,
395 void * aResponseContext)
396 {
397 OT_UNUSED_VARIABLE(aTimeout);
398
399 Error error = kErrorNone;
400 Mle::MleRouter & mle = Get<Mle::MleRouter>();
401 Coap::Message * message = nullptr;
402 Tmf::MessageInfo messageInfo(GetInstance());
403 Ip6AddressesTlv addressesTlv;
404
405 VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
406
407 message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kMlr);
408 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
409
410 addressesTlv.Init();
411 addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
412 SuccessOrExit(error = message->Append(addressesTlv));
413 SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum));
414
415 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
416 if (Get<MeshCoP::Commissioner>().IsActive())
417 {
418 SuccessOrExit(
419 error = Tlv::Append<ThreadCommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
420 }
421
422 if (aTimeout != nullptr)
423 {
424 SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, *aTimeout));
425 }
426 #else
427 OT_ASSERT(aTimeout == nullptr);
428 #endif
429
430 if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
431 {
432 uint8_t pbbrServiceId;
433
434 SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
435 SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
436 }
437 else
438 {
439 messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
440 Get<BackboneRouter::Leader>().GetServer16());
441 }
442
443 messageInfo.SetSockAddrToRloc();
444
445 error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
446
447 LogInfo("Sent MLR.req: addressNum=%d", aAddressNum);
448
449 exit:
450 LogInfo("SendMulticastListenerRegistrationMessage(): %s", ErrorToString(error));
451 FreeMessageOnError(message, error);
452 return error;
453 }
454
HandleMulticastListenerRegistrationResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)455 void MlrManager::HandleMulticastListenerRegistrationResponse(void * aContext,
456 otMessage * aMessage,
457 const otMessageInfo *aMessageInfo,
458 Error aResult)
459 {
460 static_cast<MlrManager *>(aContext)->HandleMulticastListenerRegistrationResponse(
461 AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aResult);
462 }
463
HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,const Ip6::MessageInfo * aMessageInfo,Error aResult)464 void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
465 const Ip6::MessageInfo *aMessageInfo,
466 Error aResult)
467 {
468 OT_UNUSED_VARIABLE(aMessageInfo);
469
470 uint8_t status;
471 Error error;
472 Ip6::Address failedAddresses[Ip6AddressesTlv::kMaxAddresses];
473 uint8_t failedAddressNum = 0;
474
475 error = ParseMulticastListenerRegistrationResponse(aResult, aMessage, status, failedAddresses, failedAddressNum);
476
477 FinishMulticastListenerRegistration(error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess,
478 failedAddresses, failedAddressNum);
479
480 if (error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess)
481 {
482 // keep sending until all multicast addresses are registered.
483 ScheduleSend(0);
484 }
485 else
486 {
487 otBackboneRouterConfig config;
488 uint16_t reregDelay;
489
490 // The Device has just attempted a Multicast Listener Registration which failed, and it retries the same
491 // registration with a random time delay chosen in the interval [0, Reregistration Delay].
492 // This is required by Thread 1.2 Specification 5.24.2.3
493 if (Get<BackboneRouter::Leader>().GetConfig(config) == kErrorNone)
494 {
495 reregDelay = config.mReregistrationDelay > 1
496 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
497 : 1;
498 ScheduleSend(reregDelay);
499 }
500 }
501 }
502
ParseMulticastListenerRegistrationResponse(Error aResult,Coap::Message * aMessage,uint8_t & aStatus,Ip6::Address * aFailedAddresses,uint8_t & aFailedAddressNum)503 Error MlrManager::ParseMulticastListenerRegistrationResponse(Error aResult,
504 Coap::Message *aMessage,
505 uint8_t & aStatus,
506 Ip6::Address * aFailedAddresses,
507 uint8_t & aFailedAddressNum)
508 {
509 Error error;
510 uint16_t addressesOffset, addressesLength;
511
512 aStatus = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
513
514 VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse);
515 VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse);
516
517 SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(*aMessage, aStatus));
518
519 if (ThreadTlv::FindTlvValueOffset(*aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) ==
520 kErrorNone)
521 {
522 VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = kErrorParse);
523 VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, error = kErrorParse);
524
525 for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
526 {
527 IgnoreError(aMessage->Read(addressesOffset + offset, aFailedAddresses[aFailedAddressNum]));
528 aFailedAddressNum++;
529 }
530 }
531
532 VerifyOrExit(aFailedAddressNum == 0 || aStatus != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = kErrorParse);
533
534 exit:
535 LogMlrResponse(aResult, error, aStatus, aFailedAddresses, aFailedAddressNum);
536 return aResult != kErrorNone ? aResult : error;
537 }
538
SetMulticastAddressMlrState(MlrState aFromState,MlrState aToState)539 void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
540 {
541 #if OPENTHREAD_CONFIG_MLR_ENABLE
542 for (Ip6::Netif::ExternalMulticastAddress &addr :
543 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
544 {
545 if (addr.GetMlrState() == aFromState)
546 {
547 addr.SetMlrState(aToState);
548 }
549 }
550 #endif
551 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
552 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
553 {
554 for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
555 {
556 if (child.GetAddressMlrState(address) == aFromState)
557 {
558 child.SetAddressMlrState(address, aToState);
559 }
560 }
561 }
562 #endif
563 }
564
FinishMulticastListenerRegistration(bool aSuccess,const Ip6::Address * aFailedAddresses,uint8_t aFailedAddressNum)565 void MlrManager::FinishMulticastListenerRegistration(bool aSuccess,
566 const Ip6::Address *aFailedAddresses,
567 uint8_t aFailedAddressNum)
568 {
569 OT_ASSERT(mMlrPending);
570
571 mMlrPending = false;
572
573 #if OPENTHREAD_CONFIG_MLR_ENABLE
574 for (Ip6::Netif::ExternalMulticastAddress &addr :
575 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
576 {
577 if (addr.GetMlrState() == kMlrStateRegistering)
578 {
579 bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, addr.GetAddress());
580
581 addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister);
582 }
583 }
584 #endif
585 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
586 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
587 {
588 for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
589 {
590 if (child.GetAddressMlrState(address) == kMlrStateRegistering)
591 {
592 bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, address);
593
594 child.SetAddressMlrState(address, success ? kMlrStateRegistered : kMlrStateToRegister);
595 }
596 }
597 }
598 #endif
599
600 LogMulticastAddresses();
601 CheckInvariants();
602 }
603
HandleTimeTick(void)604 void MlrManager::HandleTimeTick(void)
605 {
606 if (mSendDelay > 0 && --mSendDelay == 0)
607 {
608 SendMulticastListenerRegistration();
609 }
610
611 if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
612 {
613 Reregister();
614 }
615
616 UpdateTimeTickerRegistration();
617 }
618
Reregister(void)619 void MlrManager::Reregister(void)
620 {
621 LogInfo("MLR Reregister!");
622
623 SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
624 CheckInvariants();
625
626 ScheduleSend(0);
627
628 // Schedule for the next renewing.
629 UpdateReregistrationDelay(false);
630 }
631
UpdateReregistrationDelay(bool aRereg)632 void MlrManager::UpdateReregistrationDelay(bool aRereg)
633 {
634 Mle::MleRouter &mle = Get<Mle::MleRouter>();
635
636 bool needSendMlr = (mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1()) &&
637 Get<BackboneRouter::Leader>().HasPrimary();
638
639 if (!needSendMlr)
640 {
641 mReregistrationDelay = 0;
642 }
643 else
644 {
645 BackboneRouter::BackboneRouterConfig config;
646 uint32_t reregDelay;
647 uint32_t effectiveMlrTimeout;
648
649 IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
650
651 if (aRereg)
652 {
653 reregDelay = config.mReregistrationDelay > 1
654 ? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
655 : 1;
656 }
657 else
658 {
659 // Calculate renewing period according to Thread Spec. 5.24.2.3.2
660 // The random time t SHOULD be chosen such that (0.5* MLR-Timeout) < t < (MLR-Timeout – 9 seconds).
661 effectiveMlrTimeout = config.mMlrTimeout > Mle::kMlrTimeoutMin ? config.mMlrTimeout
662 : static_cast<uint32_t>(Mle::kMlrTimeoutMin);
663 reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9);
664 }
665
666 if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay)
667 {
668 mReregistrationDelay = reregDelay;
669 }
670 }
671
672 UpdateTimeTickerRegistration();
673
674 LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
675 mReregistrationDelay);
676 }
677
LogMulticastAddresses(void)678 void MlrManager::LogMulticastAddresses(void)
679 {
680 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
681 LogDebg("-------- Multicast Addresses --------");
682
683 #if OPENTHREAD_CONFIG_MLR_ENABLE
684 for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
685 {
686 LogDebg("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
687 }
688 #endif
689
690 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
691 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
692 {
693 for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
694 {
695 LogDebg("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)],
696 child.GetRloc16());
697 }
698 }
699 #endif
700
701 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
702 }
703
AppendToUniqueAddressList(Ip6::Address (& aAddresses)[Ip6AddressesTlv::kMaxAddresses],uint8_t & aAddressNum,const Ip6::Address & aAddress)704 void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[Ip6AddressesTlv::kMaxAddresses],
705 uint8_t & aAddressNum,
706 const Ip6::Address &aAddress)
707 {
708 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
709 for (uint8_t i = 0; i < aAddressNum; i++)
710 {
711 if (aAddresses[i] == aAddress)
712 {
713 ExitNow();
714 }
715 }
716 #endif
717
718 aAddresses[aAddressNum++] = aAddress;
719
720 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
721 exit:
722 #endif
723 return;
724 }
725
AddressListContains(const Ip6::Address * aAddressList,uint8_t aAddressListSize,const Ip6::Address & aAddress)726 bool MlrManager::AddressListContains(const Ip6::Address *aAddressList,
727 uint8_t aAddressListSize,
728 const Ip6::Address &aAddress)
729 {
730 bool contains = false;
731
732 // An empty address list is treated as if it contains all failed addresses.
733 VerifyOrExit(aAddressListSize > 0, contains = true);
734
735 for (uint8_t i = 0; i < aAddressListSize; i++)
736 {
737 if (aAddressList[i] == aAddress)
738 {
739 ExitNow(contains = true);
740 }
741 }
742
743 exit:
744 return contains;
745 }
746
LogMlrResponse(Error aResult,Error aError,uint8_t aStatus,const Ip6::Address * aFailedAddresses,uint8_t aFailedAddressNum)747 void MlrManager::LogMlrResponse(Error aResult,
748 Error aError,
749 uint8_t aStatus,
750 const Ip6::Address *aFailedAddresses,
751 uint8_t aFailedAddressNum)
752 {
753 OT_UNUSED_VARIABLE(aResult);
754 OT_UNUSED_VARIABLE(aError);
755 OT_UNUSED_VARIABLE(aStatus);
756 OT_UNUSED_VARIABLE(aFailedAddresses);
757 OT_UNUSED_VARIABLE(aFailedAddressNum);
758
759 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
760 if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::MlrStatus::kMlrSuccess)
761 {
762 LogInfo("Receive MLR.rsp OK");
763 }
764 else
765 {
766 LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
767 ErrorToString(aError), aStatus, aFailedAddressNum);
768
769 for (uint8_t i = 0; i < aFailedAddressNum; i++)
770 {
771 LogWarn("MA failed: %s", aFailedAddresses[i].ToString().AsCString());
772 }
773 }
774 #endif
775 }
776
CheckInvariants(void) const777 void MlrManager::CheckInvariants(void) const
778 {
779 #if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
780 uint16_t registeringNum = 0;
781
782 OT_ASSERT(!mMlrPending || mSendDelay == 0);
783
784 #if OPENTHREAD_CONFIG_MLR_ENABLE
785 for (Ip6::Netif::ExternalMulticastAddress &addr :
786 Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
787 {
788 registeringNum += (addr.GetMlrState() == kMlrStateRegistering);
789 }
790 #endif
791 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
792 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
793 {
794 for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
795 {
796 registeringNum += (child.GetAddressMlrState(address) == kMlrStateRegistering);
797 }
798 }
799 #endif
800
801 OT_ASSERT(registeringNum == 0 || mMlrPending);
802 #endif // OPENTHREAD_EXAMPLES_SIMULATION
803 }
804
805 } // namespace ot
806
807 #endif // OPENTHREAD_CONFIG_MLR_ENABLE
808