diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp index 096fdbf7a..ca61092b2 100644 --- a/src/core/backbone_router/bbr_leader.cpp +++ b/src/core/backbone_router/bbr_leader.cpp @@ -227,7 +227,7 @@ void Leader::UpdateBackboneRouterPrimary(void) Get().HandleBackboneRouterPrimaryUpdate(state, mConfig); #endif -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE Get().HandleBackboneRouterPrimaryUpdate(state, mConfig); #endif } diff --git a/src/core/common/bit_vector.hpp b/src/core/common/bit_vector.hpp index 1646391c5..edddb3bd3 100644 --- a/src/core/common/bit_vector.hpp +++ b/src/core/common/bit_vector.hpp @@ -86,6 +86,7 @@ public: void Set(uint16_t aIndex) { OT_ASSERT(aIndex < N); + mMask[aIndex / 8] |= 0x80 >> (aIndex % 8); } @@ -98,6 +99,7 @@ public: void Clear(uint16_t aIndex) { OT_ASSERT(aIndex < N); + mMask[aIndex / 8] &= ~(0x80 >> (aIndex % 8)); } diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 0f8c524ec..c0c3b67bc 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -750,7 +750,7 @@ template <> inline DuaManager &Instance::Get(void) } #endif -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE template <> inline MlrManager &Instance::Get(void) { return mThreadNetif.mMlrManager; diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h index e1f54c915..118564d59 100644 --- a/src/core/config/tmf.h +++ b/src/core/config/tmf.h @@ -172,4 +172,14 @@ #define OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE 0 #endif +/** + * + * This setting configures the Multicast Listener Registration parent proxing in Thread 1.2. + * + * This is compulsory for 1.2 FTD. + * + */ +#define OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE \ + ((OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_FTD) + #endif // CONFIG_TMF_H_ diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index b58dfc294..79887df2f 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2040,11 +2040,34 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse uint16_t offset = 0; uint16_t end = 0; +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + Ip6::Address oldMlrRegisteredAddresses[OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1]; + uint16_t oldMlrRegisteredAddressNum = 0; +#endif + VerifyOrExit(aMessage.Read(aOffset, sizeof(tlv), &tlv) == sizeof(tlv), error = OT_ERROR_PARSE); VerifyOrExit(tlv.GetLength() <= (aMessage.GetLength() - aOffset - sizeof(tlv)), error = OT_ERROR_PARSE); offset = aOffset + sizeof(tlv); end = offset + tlv.GetLength(); + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + // Retrieve registered multicast addresses of the Child + if (aChild.HasAnyMlrRegisteredAddress()) + { + OT_ASSERT(aChild.IsStateValid()); + + for (const Ip6::Address &childAddress : + aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (aChild.GetAddressMlrState(childAddress) == kMlrStateRegistered) + { + oldMlrRegisteredAddresses[oldMlrRegisteredAddressNum++] = childAddress; + } + } + } +#endif + aChild.ClearIp6Addresses(); while (offset < end) @@ -2134,6 +2157,10 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse Get().Remove(address); } +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + Get().UpdateProxiedSubscriptions(aChild, oldMlrRegisteredAddresses, oldMlrRegisteredAddressNum); +#endif + if (registeredCount == 0) { otLogInfoMle("Child 0x%04x has no registered IPv6 address", aChild.GetRloc16()); @@ -4626,6 +4653,11 @@ void MleRouter::SetChildStateToValid(Child &aChild) aChild.SetState(Neighbor::kStateValid); IgnoreError(StoreChild(aChild)); + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + Get().UpdateProxiedSubscriptions(aChild, nullptr, 0); +#endif + Signal(OT_NEIGHBOR_TABLE_EVENT_CHILD_ADDED, aChild); exit: diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index 6920a8ff8..866a1940a 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -249,7 +249,7 @@ enum kServiceMaxId = 0x0f, ///< Maximal Service ID. }; -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 /** * Backbone Router / MLR constants @@ -261,12 +261,15 @@ enum kMlrTimeoutDefault = 3600, //< In seconds. kMlrTimeoutMin = 300, //< In seconds. kBackboneRouterRegistrationJitter = 5, //< In seconds. + kParentAggregateDelay = 5, //< In seconds. }; static_assert(kMlrTimeoutDefault >= kMlrTimeoutMin, "kMlrTimeoutDefault must be larger than or equal to kMlrTimeoutMin"); -#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_MLR_ENABLE +static_assert(Mle::kParentAggregateDelay > 1, "kParentAggregateDelay should be larger than 1 second"); + +#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 /** * This type represents a MLE device mode. diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index 0f5b8111a..aff6e087c 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -33,7 +33,7 @@ #include "mlr_manager.hpp" -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE #include "common/code_utils.hpp" #include "common/instance.hpp" @@ -58,10 +58,12 @@ MlrManager::MlrManager(Instance &aInstance) void MlrManager::HandleNotifierEvents(Events aEvents) { +#if OPENTHREAD_CONFIG_MLR_ENABLE if (aEvents.Contains(kEventIp6MulticastSubscribed)) { - ScheduleSend(0); + UpdateLocalSubscriptions(); } +#endif if (aEvents.Contains(kEventThreadRoleChanged) && Get().IsChild()) { @@ -81,6 +83,130 @@ void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State UpdateReregistrationDelay(needRereg); } +#if OPENTHREAD_CONFIG_MLR_ENABLE +void MlrManager::UpdateLocalSubscriptions(void) +{ +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + // Check multicast addresses are newly listened against Children + for (Ip6::ExternalNetifMulticastAddress &addr : + Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (addr.GetMlrState() == kMlrStateToRegister && IsAddressMlrRegisteredByAnyChild(addr.GetAddress())) + { + addr.SetMlrState(kMlrStateRegistered); + } + } +#endif + + ScheduleSend(0); +} + +bool MlrManager::IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const +{ + bool ret = false; + + OT_ASSERT(aAddress.IsMulticastLargerThanRealmLocal()); + + for (const Ip6::ExternalNetifMulticastAddress &addr : Get().IterateExternalMulticastAddresses()) + { + if (addr.GetAddress() == aAddress && addr.GetMlrState() == kMlrStateRegistered) + { + ExitNow(ret = true); + } + } + +exit: + return ret; +} + +void MlrManager::SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState) +{ + for (Ip6::ExternalNetifMulticastAddress &addr : + Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (addr.GetMlrState() == aFromState) + { + addr.SetMlrState(aToState); + } + } +} +#endif // OPENTHREAD_CONFIG_MLR_ENABLE + +#if 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().Iterate(Child::kInStateValid)) + { + if (&child != aExceptChild && child.HasMlrRegisteredAddress(aAddress)) + { + ExitNow(ret = true); + } + } + +exit: + return ret; +} + +void MlrManager::UpdateProxiedSubscriptions(Child & aChild, + const Ip6::Address *aOldMlrRegisteredAddresses, + uint16_t aOldMlrRegisteredAddressNum) +{ + VerifyOrExit(aChild.IsStateValid(), OT_NOOP); + + // Search the new multicast addresses and set its flag accordingly + for (const Ip6::Address &address : aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + bool isMlrRegistered = false; + + // Check if it's a new multicast address against old addresses + for (size_t i = 0; i < aOldMlrRegisteredAddressNum; i++) + { + if (aOldMlrRegisteredAddresses[i] == address) + { + isMlrRegistered = true; + break; + } + } + +#if OPENTHREAD_CONFIG_MLR_ENABLE + // Check if it's a new multicast address against parent Netif + isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(address); +#endif + // Check if it's a new multicast address against other Children + isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(address, &aChild); + + aChild.SetAddressMlrState(address, isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister); + } + +exit: + LogMulticastAddresses(); + + if (aChild.HasAnyMlrToRegisterAddress()) + { + ScheduleSend(Random::NonCrypto::GetUint16InRange(1, Mle::kParentAggregateDelay)); + } +} + +void MlrManager::SetChildMulticastAddressMlrState(MlrState aFromState, MlrState aToState) +{ + for (Child &child : Get().Iterate(Child::kInStateValid)) + { + for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (child.GetAddressMlrState(address) == aFromState) + { + child.SetAddressMlrState(address, aToState); + } + } + } +} +#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + void MlrManager::ScheduleSend(uint16_t aDelay) { OT_ASSERT(!mMlrPending || mSendDelay == 0); @@ -121,19 +247,63 @@ void MlrManager::SendMulticastListenerRegistration(void) Coap::Message * message = nullptr; Ip6::MessageInfo messageInfo; IPv6AddressesTlv addressesTlv; - uint8_t num; + Ip6::Address addresses[kIPv6AddressesNumMax]; + uint8_t addressesNum = 0; VerifyOrExit(!mMlrPending, error = OT_ERROR_BUSY); VerifyOrExit(mle.IsAttached(), error = OT_ERROR_INVALID_STATE); VerifyOrExit(mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1(), error = OT_ERROR_INVALID_STATE); VerifyOrExit(Get().HasPrimary(), error = OT_ERROR_INVALID_STATE); - num = static_cast(CountNetifMulticastAddressesToRegister()); - VerifyOrExit(num > 0, error = OT_ERROR_NOT_FOUND); - if (num > kIPv6AddressesNumMax) +#if OPENTHREAD_CONFIG_MLR_ENABLE + // Append Netif multicast addresses + for (Ip6::ExternalNetifMulticastAddress &addr : + Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) { - num = kIPv6AddressesNumMax; + if (addressesNum >= kIPv6AddressesNumMax) + { + break; + } + + if (addr.GetMlrState() == kMlrStateToRegister) + { + AppendToUniqueAddressList(addresses, addressesNum, addr.GetAddress()); + addr.SetMlrState(kMlrStateRegistering); + } } +#endif + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + // Append Child multicast addresses + for (Child &child : Get().Iterate(Child::kInStateValid)) + { + if (addressesNum >= kIPv6AddressesNumMax) + { + break; + } + + if (!child.HasAnyMlrToRegisterAddress()) + { + continue; + } + + for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (addressesNum >= kIPv6AddressesNumMax) + { + break; + } + + if (child.GetAddressMlrState(address) == kMlrStateToRegister) + { + AppendToUniqueAddressList(addresses, addressesNum, address); + child.SetAddressMlrState(address, kMlrStateRegistering); + } + } + } +#endif + + VerifyOrExit(addressesNum > 0, error = OT_ERROR_NOT_FOUND); VerifyOrExit((message = Get().NewMessage()) != nullptr, error = OT_ERROR_NO_BUFS); @@ -143,23 +313,9 @@ void MlrManager::SendMulticastListenerRegistration(void) SuccessOrExit(message->SetPayloadMarker()); addressesTlv.Init(); - addressesTlv.SetLength(sizeof(Ip6::Address) * num); + addressesTlv.SetLength(sizeof(Ip6::Address) * addressesNum); SuccessOrExit(error = message->Append(&addressesTlv, sizeof(addressesTlv))); - - for (Ip6::ExternalNetifMulticastAddress &addr : - Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) - { - if (addr.GetMlrState() == kMlrStateToRegister) - { - SuccessOrExit(error = message->Append(&addr.GetAddress(), sizeof(Ip6::Address))); - addr.SetMlrState(kMlrStateRegistering); - - if (--num == 0) - { - break; - } - } - } + SuccessOrExit(error = message->Append(&addresses, sizeof(Ip6::Address) * addressesNum)); if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1()) { @@ -196,7 +352,7 @@ exit: message->Free(); } - SetNetifMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister); + SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister); if (error == OT_ERROR_NO_BUFS) { @@ -225,10 +381,15 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, status)); exit: + SetMulticastAddressMlrState(kMlrStateRegistering, status == ThreadStatusTlv::MlrStatus::kMlrSuccess + ? kMlrStateRegistered + : kMlrStateToRegister); + otLogInfoBbr("Receive MLR.rsp, result=%s, status=%d, error=%s", otThreadErrorToString(aResult), status, + otThreadErrorToString(error)); + LogMulticastAddresses(); if (status == ThreadStatusTlv::MlrStatus::kMlrSuccess) { - SetNetifMulticastAddressMlrState(kMlrStateRegistering, kMlrStateRegistered); // keep sending until all multicast addresses are registered. ScheduleSend(0); } @@ -237,8 +398,6 @@ exit: otBackboneRouterConfig config; uint16_t reregDelay; - SetNetifMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister); - // The Device has just attempted a Multicast Listener Registration which failed, and it retries the same // registration with a random time delay chosen in the interval [0, Reregistration Delay]. // This is required by Thread 1.2 Specification 5.24.2.3 @@ -251,9 +410,6 @@ exit: ScheduleSend(reregDelay); } } - - otLogInfoMlr("Receive MLR.rsp, result=%s, status=%d, error=%s", otThreadErrorToString(aResult), status, - otThreadErrorToString(error)); } void MlrManager::HandleTimer(void) @@ -273,7 +429,8 @@ void MlrManager::HandleTimer(void) void MlrManager::Reregister(void) { - SetNetifMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister); + SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister); + ScheduleSend(0); // Schedule for the next renewing. @@ -328,45 +485,50 @@ void MlrManager::UpdateReregistrationDelay(bool aRereg) void MlrManager::LogMulticastAddresses(void) { -#if OPENTHREAD_CONFIG_LOG_BBR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG +#if OPENTHREAD_CONFIG_LOG_MLR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG otLogDebgMlr("-------- Multicast Addresses --------"); - for (const Ip6::ExternalNetifMulticastAddress &addr : - Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) +#if OPENTHREAD_CONFIG_MLR_ENABLE + for (const Ip6::ExternalNetifMulticastAddress &addr : Get().IterateExternalMulticastAddresses()) { - MlrState state = addr.GetMlrState(); - - otLogDebgMlr("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[state]); + otLogDebgMlr("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]); } #endif -} -uint16_t MlrManager::CountNetifMulticastAddressesToRegister(void) const -{ - uint16_t count = 0; - - for (const Ip6::ExternalNetifMulticastAddress &addr : - Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + for (Child &child : Get().Iterate(Child::kInStateValid)) { - if (addr.GetMlrState() == kMlrStateToRegister) + for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) { - count++; + otLogDebgMlr("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)], + child.GetRloc16()); } } +#endif - return count; +#endif // OPENTHREAD_CONFIG_LOG_MLR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG } -void MlrManager::SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState) +void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax], + uint8_t & aAddressNum, + const Ip6::Address &aAddress) { - for (Ip6::ExternalNetifMulticastAddress &addr : - Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + for (uint8_t i = 0; i < aAddressNum; i++) { - if (addr.GetMlrState() == aFromState) + if (aAddresses[i] == aAddress) { - addr.SetMlrState(aToState); + ExitNow(); } } +#endif + + aAddresses[aAddressNum++] = aAddress; + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +exit: +#endif + return; } } // namespace ot diff --git a/src/core/thread/mlr_manager.hpp b/src/core/thread/mlr_manager.hpp index 10681a799..9b1c6d922 100644 --- a/src/core/thread/mlr_manager.hpp +++ b/src/core/thread/mlr_manager.hpp @@ -36,7 +36,7 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE #include "backbone_router/bbr_leader.hpp" #include "coap/coap_message.hpp" @@ -44,6 +44,8 @@ #include "common/notifier.hpp" #include "common/timer.hpp" #include "net/netif.hpp" +#include "thread/thread_tlvs.hpp" +#include "thread/topology.hpp" namespace ot { @@ -86,6 +88,21 @@ public: void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, const BackboneRouter::BackboneRouterConfig &aConfig); +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + /** + * This method updates the Multicast Subscription Table according to the Child information. + * + * @param[in] aChild A reference to the child information. + * @param[in] aOldMlrRegisteredAddresses A pointer to an array of the Child's previously registered Ip6 + * addresses. + * @param[in] aOldMlrRegisteredAddressNum The number of previously registered Ip6 addresses. + * + */ + void UpdateProxiedSubscriptions(Child & aChild, + const Ip6::Address *aOldMlrRegisteredAddresses, + uint16_t aOldMlrRegisteredAddressNum); +#endif + private: enum { @@ -113,8 +130,34 @@ private: const Ip6::MessageInfo *aMessageInfo, otError aResult); - uint16_t CountNetifMulticastAddressesToRegister(void) const; - void SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState); +#if OPENTHREAD_CONFIG_MLR_ENABLE + void UpdateLocalSubscriptions(void); + void SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState); + bool IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const; +#endif + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + void SetChildMulticastAddressMlrState(MlrState aFromState, MlrState aToState); + bool IsAddressMlrRegisteredByAnyChild(const Ip6::Address &aAddress) const + { + return IsAddressMlrRegisteredByAnyChildExcept(aAddress, nullptr); + } + bool IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const; +#endif + + void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState) + { +#if OPENTHREAD_CONFIG_MLR_ENABLE + SetNetifMulticastAddressMlrState(aFromState, aToState); +#endif +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + SetChildMulticastAddressMlrState(aFromState, aToState); +#endif + } + + void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax], + uint8_t & aAddressNum, + const Ip6::Address &aAddress); void ScheduleSend(uint16_t aDelay); void ResetTimer(void); @@ -134,5 +177,5 @@ private: } // namespace ot -#endif // OPENTHREAD_CONFIG_MLR_ENABLE +#endif // OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE #endif // MLR_MANAGER_HPP_ diff --git a/src/core/thread/mlr_types.hpp b/src/core/thread/mlr_types.hpp index be6e11691..366a34ea2 100644 --- a/src/core/thread/mlr_types.hpp +++ b/src/core/thread/mlr_types.hpp @@ -36,7 +36,7 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE namespace ot { @@ -51,14 +51,14 @@ namespace ot { * Multicast Listener Registration state for multicast addresses. * */ -typedef enum MlrState +enum MlrState { kMlrStateToRegister, ///< The multicast address is to be registered. kMlrStateRegistering, ///< The multicast address is being registered. kMlrStateRegistered, ///< The multicast address is registered. -} MlrState; +}; } // namespace ot -#endif // OPENTHREAD_CONFIG_MLR_ENABLE +#endif // OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE #endif // MLR_TYPES_HPP_ diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 16fd3a190..f02864e73 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -114,7 +114,7 @@ ThreadNetif::ThreadNetif(Instance &aInstance) #if OPENTHREAD_CONFIG_DUA_ENABLE , mDuaManager(aInstance) #endif -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE , mMlrManager(aInstance) #endif , mChildSupervisor(aInstance) diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 5e35a6b81..39426fb67 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -59,7 +59,7 @@ #include "thread/dua_manager.hpp" #endif -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE #include "thread/mlr_manager.hpp" #endif @@ -264,7 +264,7 @@ private: #if OPENTHREAD_CONFIG_DUA_ENABLE DuaManager mDuaManager; #endif -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE MlrManager mMlrManager; #endif Utils::ChildSupervisor mChildSupervisor; diff --git a/src/core/thread/thread_tlvs.hpp b/src/core/thread/thread_tlvs.hpp index 565107f4c..709e382b9 100644 --- a/src/core/thread/thread_tlvs.hpp +++ b/src/core/thread/thread_tlvs.hpp @@ -244,7 +244,7 @@ private: uint8_t mTlvs[kMaxSize]; } OT_TOOL_PACKED_END; -#if OPENTHREAD_CONFIG_MLR_ENABLE +#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** * This class implements IPv6 Addresses TLV generation and parsing. @@ -287,7 +287,7 @@ public: } } OT_TOOL_PACKED_END; -#endif // OPENTHREAD_CONFIG_MLR_ENABLE +#endif // OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE } // namespace ot diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index 4daeb6c09..70c714480 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -156,6 +156,10 @@ void Child::ClearIp6Addresses(void) { mMeshLocalIid.Clear(); memset(mIp6Address, 0, sizeof(mIp6Address)); +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + mMlrToRegisterMask.ClearAll(); + mMlrRegisteredMask.ClearAll(); +#endif } otError Child::GetMeshLocalIp6Address(Ip6::Address &aAddress) const @@ -274,6 +278,66 @@ void Child::GenerateChallenge(void) IgnoreError(Random::Crypto::FillBuffer(mAttachChallenge, sizeof(mAttachChallenge))); } +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +bool Child::HasMlrRegisteredAddress(const Ip6::Address &aAddress) const +{ + bool has = false; + + VerifyOrExit(mMlrRegisteredMask.HasAny(), OT_NOOP); + + for (const Ip6::Address &address : IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + { + if (GetAddressMlrState(address) == kMlrStateRegistered && address == aAddress) + { + ExitNow(has = true); + } + } + +exit: + return has; +} + +MlrState Child::GetAddressMlrState(const Ip6::Address &aAddress) const +{ + uint16_t addressIndex; + + OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < OT_ARRAY_END(mIp6Address)); + + addressIndex = static_cast(&aAddress - mIp6Address); + + return mMlrToRegisterMask.Get(addressIndex) + ? kMlrStateToRegister + : (mMlrRegisteredMask.Get(addressIndex) ? kMlrStateRegistered : kMlrStateRegistering); +} + +void Child::SetAddressMlrState(const Ip6::Address &aAddress, MlrState aState) +{ + uint16_t addressIndex; + + OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < OT_ARRAY_END(mIp6Address)); + + addressIndex = static_cast(&aAddress - mIp6Address); + + if (aState == kMlrStateToRegister) + { + mMlrToRegisterMask.Set(addressIndex); + } + else + { + mMlrToRegisterMask.Clear(addressIndex); + } + + if (aState == kMlrStateRegistered) + { + mMlrRegisteredMask.Set(addressIndex); + } + else + { + mMlrRegisteredMask.Clear(addressIndex); + } +} +#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + void Router::Clear(void) { Instance &instance = GetInstance(); diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index d1c95fabf..ee5b9cfbb 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -862,6 +862,60 @@ public: #endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + /** + * This method returns MLR state of an Ip6 multicast address. + * + * @note The @p aAdddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. + * + * @param[in] aAddress The Ip6 multicast address. + * + * @returns MLR state of the Ip6 multicast address. + * + */ + MlrState GetAddressMlrState(const Ip6::Address &aAddress) const; + + /** + * This method sets MLR state of an Ip6 multicast address. + * + * @note The @p aAdddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. + * + * @param[in] aAddress The Ip6 multicast address. + * @param[in] aState The target MLR state. + * + */ + void SetAddressMlrState(const Ip6::Address &aAddress, MlrState aState); + + /** + * This method returns if the Child has Ip6 address @p aAddress of MLR state `kMlrStateRegistered`. + * + * @param[in] aAddress The Ip6 address. + * + * @retval true If the Child has Ip6 address @p aAddress of MLR state `kMlrStateRegistered`. + * @retval false If the Child does not have Ip6 address @p aAddress of MLR state `kMlrStateRegistered`. + * + */ + bool HasMlrRegisteredAddress(const Ip6::Address &aAddress) const; + + /** + * This method returns if the Child has any Ip6 address of MLR state `kMlrStateRegistered`. + * + * @retval true If the Child has any Ip6 address of MLR state `kMlrStateRegistered`. + * @retval false If the Child does not have any Ip6 address of MLR state `kMlrStateRegistered`. + * + */ + bool HasAnyMlrRegisteredAddress(void) const { return mMlrRegisteredMask.HasAny(); } + + /** + * This method returns if the Child has any Ip6 address of MLR state `kMlrStateToRegister`. + * + * @retval true If the Child has any Ip6 address of MLR state `kMlrStateToRegister`. + * @retval false If the Child does not have any Ip6 address of MLR state `kMlrStateToRegister`. + * + */ + bool HasAnyMlrToRegisterAddress(void) const { return mMlrToRegisterMask.HasAny(); } +#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + private: #if OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD < 2 #error OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD should be at least set to 2. @@ -872,6 +926,8 @@ private: kNumIp6Addresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1, }; + typedef BitVector ChildIp6AddressMask; + class AddressIteratorBuilder { public: @@ -889,11 +945,16 @@ private: Ip6::Address::TypeFilter mFilter; }; - uint8_t mNetworkDataVersion; ///< Current Network Data version Ip6::InterfaceIdentifier mMeshLocalIid; ///< IPv6 address IID for mesh-local address Ip6::Address mIp6Address[kNumIp6Addresses]; ///< Registered IPv6 addresses + uint32_t mTimeout; ///< Child timeout - uint32_t mTimeout; ///< Child timeout +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + ChildIp6AddressMask mMlrToRegisterMask; + ChildIp6AddressMask mMlrRegisteredMask; +#endif + + uint8_t mNetworkDataVersion; ///< Current Network Data version union { diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py index 83c2d13c7..7dac3cc52 100755 --- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py +++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py @@ -29,12 +29,13 @@ import ipaddress import unittest +from typing import Union, List import config import network_layer import thread_cert -_, BBR_1, BBR_2, ROUTER_1_2, ROUTER_1_1, SED_1, MED_1, FED_1 = range(8) +_, BBR_1, BBR_2, ROUTER_1_2, ROUTER_1_1, SED_1, MED_1, MED_2, FED_1 = range(9) WAIT_ATTACH = 5 WAIT_REDUNDANCE = 3 @@ -44,6 +45,7 @@ SED_POLL_PERIOD = 1000 # 1s REREG_DELAY = 10 MLR_TIMEOUT = 300 +PARENT_AGGREGATE_DELAY = 5 MA1 = 'ff04::1234:777a:1' MA1g = 'ff0e::1234:777a:1' @@ -60,9 +62,9 @@ MA6 = 'ff02::10' | / \ | | / \ | ROUTER_1_2 ROUTER_1_1 - | | \ | - | | \ | - SED_1 MED_1 FED_1 + | | \____ | + | | \ | + SED_1 MED_1/2 FED_1 """ @@ -82,7 +84,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): }, ROUTER_1_2: { 'version': '1.2', - 'whitelist': [BBR_1, BBR_2, SED_1, MED_1, FED_1], + 'whitelist': [BBR_1, BBR_2, SED_1, MED_1, MED_2, FED_1], 'router_selection_jitter': 1, }, ROUTER_1_1: { @@ -96,6 +98,12 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): 'whitelist': [ROUTER_1_2], 'timeout': config.DEFAULT_CHILD_TIMEOUT, }, + MED_2: { + 'mode': 'rsn', + 'version': '1.2', + 'whitelist': [ROUTER_1_2], + 'timeout': config.DEFAULT_CHILD_TIMEOUT, + }, SED_1: { 'mode': 'sn', 'version': '1.2', @@ -112,7 +120,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): } """All nodes are created with default configurations""" - def test(self): + def _bootstrap(self): # starting context id context_id = 1 @@ -179,12 +187,20 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): self.simulator.go(WAIT_ATTACH) self.assertEqual(self.nodes[MED_1].get_state(), 'child') + # Bring up MED_2 + self.nodes[MED_2].start() + self.simulator.go(WAIT_ATTACH) + self.assertEqual(self.nodes[MED_2].get_state(), 'child') + # Bring up SED_1 self.nodes[SED_1].set_pollperiod(SED_POLL_PERIOD) self.nodes[SED_1].start() self.simulator.go(WAIT_ATTACH) self.assertEqual(self.nodes[SED_1].get_state(), 'child') + def test(self): + self._bootstrap() + # Verify MLR.req for each device when parent is 1.2 self.__check_mlr_ok(ROUTER_1_2, is_ftd=True) self.__check_mlr_ok(FED_1, is_ftd=True) @@ -202,6 +218,18 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): # Switch to parent 1.2 self.__switch_to_1_2_parent() + def testParentMergeMedMlrReq(self): + self._bootstrap() + + # Make sure Parent registers multiple MAs of MED Children in one MLR.req + self.__check_parent_merge_med_mlr_req([MED_1, MED_2], ROUTER_1_2) + + def testNotSendMlrReqIfSubscribed(self): + self._bootstrap() + + # Make sure Parent does not send MLR.req of Child if it's already subscribed by Netif or other Children + self.__check_not_send_mlr_req_if_subscribed([MED_1, MED_2], ROUTER_1_2) + def __check_mlr_ok(self, id, is_ftd, is_parent_1p1=False): """Check if MLR works for the node""" # Add MA1 and send MLR.req @@ -210,36 +238,94 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): (id, 'FTD' if is_ftd else 'MTD', '1.1' if is_parent_1p1 else '1.2')) expect_mlr_req = is_ftd or is_parent_1p1 + if id == ROUTER_1_2: + parent_id = None + else: + parent_id = ROUTER_1_1 if is_parent_1p1 else ROUTER_1_2 + for addr in [MA1, MA1g, MA2, MA3, MA4]: - self.__check_ipmaddr_add(id, addr, expect_mlr_req=expect_mlr_req) + self.__check_ipmaddr_add( + id, + parent_id, + addr, + expect_mlr_req=expect_mlr_req, + expect_mlr_req_proxied=(not expect_mlr_req)) for addr in [MA5, MA6]: - self.__check_ipmaddr_add(id, addr, expect_mlr_req=False) + self.__check_ipmaddr_add(id, + parent_id, + addr, + expect_mlr_req=False, + expect_mlr_req_proxied=False) print('=' * 120) - def __check_ipmaddr_add(self, id, addr, expect_mlr_req=True): + def __check_ipmaddr_add(self, + id, + parent_id, + addr, + expect_mlr_req=True, + expect_mlr_req_proxied=False): """Check MLR works for the added multicast address""" + print("Node %d: ipmaddr %s" % (id, addr)) self.flush_all() self.nodes[id].add_ipmaddr(addr) self.assertTrue(self.nodes[id].has_ipmaddr(addr)) - self.simulator.go(3) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) - reg_mas = self.__get_registered_MAs(id) - if expect_mlr_req: - assert ipaddress.IPv6Address(addr) in reg_mas, (addr, reg_mas) + self.__check_send_mlr_req(id, + addr, + should_send=expect_mlr_req, + expect_mlr_rsp=expect_mlr_req) + # Parent should either forward or proxy the MLR.req + if parent_id: + self.__check_send_mlr_req(parent_id, + addr, + should_send=expect_mlr_req or + expect_mlr_req_proxied, + expect_mlr_rsp=expect_mlr_req_proxied) - messages = self.simulator.get_messages_sent_by(BBR_1) - messages.next_coap_message('2.04') - - self.__check_rereg(id, addr) - self.__check_renewing(id, addr) - else: - assert ipaddress.IPv6Address(addr) not in reg_mas, (addr, reg_mas) + self.__check_rereg(id, + parent_id, + addr, + expect_mlr_req=expect_mlr_req, + expect_mlr_req_proxied=expect_mlr_req_proxied) + self.__check_renewing(id, + parent_id, + addr, + expect_mlr_req=expect_mlr_req, + expect_mlr_req_proxied=expect_mlr_req_proxied) self.nodes[id].del_ipmaddr(addr) self.simulator.go(1) - def __get_registered_MAs(self, id): + def __check_send_mlr_req(self, + id, + addrs: Union[List[str], str], + should_send=True, + expect_mlr_rsp=False, + expect_mlr_req_num=None, + expect_unique_reg=False): + if isinstance(addrs, str): + addrs = [addrs] + + reg_mas = self.__get_registered_MAs( + id, expect_mlr_req_num=expect_mlr_req_num) + if should_send: + for addr in addrs: + self.assertIn(ipaddress.IPv6Address(addr), reg_mas) + if expect_unique_reg: + self.assertEqual(1, + reg_mas.count(ipaddress.IPv6Address(addr))) + + # BBR should send MLR.rsp ACK + if expect_mlr_rsp: + messages = self.simulator.get_messages_sent_by(BBR_1) + messages.next_coap_message('2.04') + else: + for addr in addrs: + self.assertNotIn(ipaddress.IPv6Address(addr), reg_mas) + + def __get_registered_MAs(self, id, expect_mlr_req_num=None): """Get MAs registered via MLR.req by the node""" messages = self.simulator.get_messages_sent_by(id) reg_mas = [] @@ -250,25 +336,65 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): if not msg: break addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses) - reg_mas.extend(addrs) + reg_mas.append(addrs) print('Node %d registered MAs: %s' % (id, reg_mas)) + + if expect_mlr_req_num is not None: + self.assertEqual(len(reg_mas), expect_mlr_req_num) + + # expand from [[...], [...], ...] to [...] + reg_mas = [ma for mas in reg_mas for ma in mas] + return reg_mas - def __check_renewing(self, id, addr): + def __check_renewing(self, + id, + parent_id, + addr, + expect_mlr_req=True, + expect_mlr_req_proxied=False): """Check if MLR works that a node can renew it's registered MAs""" self.flush_all() self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE) - reg_mas = self.__get_registered_MAs(id) - assert ipaddress.IPv6Address(addr) in reg_mas, (addr, reg_mas) + self.__check_send_mlr_req(id, + addr, + should_send=expect_mlr_req, + expect_mlr_rsp=expect_mlr_req) + # Parent should either forward or proxy the MLR.req + if parent_id: + self.__check_send_mlr_req(parent_id, + addr, + should_send=expect_mlr_req or + expect_mlr_req_proxied, + expect_mlr_rsp=expect_mlr_req_proxied) - def __check_rereg(self, id, addr): + def __check_rereg(self, + id, + parent_id, + addr, + expect_mlr_req=True, + expect_mlr_req_proxied=False): """Check if MLR works that a node can do MLR reregistration when necessary""" - self.__check_rereg_seqno(id, addr) - self.__check_rereg_pbbr_change(id, addr) + self.__check_rereg_seqno(id, + parent_id, + addr, + expect_mlr_req=expect_mlr_req, + expect_mlr_req_proxied=expect_mlr_req_proxied) + self.__check_rereg_pbbr_change( + id, + parent_id, + addr, + expect_mlr_req=expect_mlr_req, + expect_mlr_req_proxied=expect_mlr_req_proxied) - def __check_rereg_seqno(self, id, addr): + def __check_rereg_seqno(self, + id, + parent_id, + addr, + expect_mlr_req=True, + expect_mlr_req_proxied=False): """Check if MLR works that a node can do MLR reregistration when PBBR seqno changes""" # Change seq on PBBR and expect MLR.req within REREG_DELAY self.flush_all() @@ -276,10 +402,24 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): self.nodes[BBR_1].set_backbone_router(seqno=self.pbbr_seq) self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE) - reg_mas = self.__get_registered_MAs(id) - assert ipaddress.IPv6Address(addr) in reg_mas, (addr, reg_mas) + self.__check_send_mlr_req(id, + addr, + should_send=expect_mlr_req, + expect_mlr_rsp=expect_mlr_req) + # Parent should either forward or proxy the MLR.req + if parent_id: + self.__check_send_mlr_req(parent_id, + addr, + should_send=expect_mlr_req or + expect_mlr_req_proxied, + expect_mlr_rsp=expect_mlr_req_proxied) - def __check_rereg_pbbr_change(self, id, addr): + def __check_rereg_pbbr_change(self, + id, + parent_id, + addr, + expect_mlr_req=True, + expect_mlr_req_proxied=False): """Check if MLR works that a node can do MLR reregistration when PBBR changes""" # Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY self.flush_all() @@ -288,8 +428,18 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Primary') self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE) - reg_mas = self.__get_registered_MAs(id) - assert ipaddress.IPv6Address(addr) in reg_mas, (addr, reg_mas) + + self.__check_send_mlr_req(id, + addr, + should_send=expect_mlr_req, + expect_mlr_rsp=expect_mlr_req) + # Parent should either forward or proxy the MLR.req + if parent_id: + self.__check_send_mlr_req(parent_id, + addr, + should_send=expect_mlr_req or + expect_mlr_req_proxied, + expect_mlr_rsp=expect_mlr_req_proxied) # Restore BBR_1 to be Primary and BBR_2 to be Secondary self.nodes[BBR_2].disable_backbone_router() @@ -395,6 +545,83 @@ class TestMulticastListenerRegistration(thread_cert.TestCase): self.simulator.go(WAIT_REDUNDANCE) + def __check_parent_merge_med_mlr_req(self, meds, parent_id): + """Check that the 1.2 parent merge multiple multicast addresses for MED children.""" + self.flush_all() + for med in meds: + self.nodes[med].add_ipmaddr(MA1) + + self.nodes[meds[0]].add_ipmaddr(MA2) + self.nodes[meds[1]].add_ipmaddr(MA3) + + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + + self.__check_send_mlr_req(parent_id, [MA1, MA2, MA3], + should_send=True, + expect_mlr_rsp=True, + expect_mlr_req_num=1, + expect_unique_reg=True) + + # restore + self.nodes[meds[0]].del_ipmaddr(MA2) + self.nodes[meds[1]].del_ipmaddr(MA3) + for med in meds: + self.nodes[med].del_ipmaddr(MA1) + + self.simulator.go(WAIT_REDUNDANCE) + + def __check_not_send_mlr_req_if_subscribed(self, meds, parent_id): + """Check that the 1.2 parent does not send MLR.req if the MA is already subscribed.""" + # Parent should register MA1 on Netif + self.flush_all() + self.nodes[parent_id].add_ipmaddr(MA1) + self.simulator.go(WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, + MA1, + should_send=True, + expect_mlr_rsp=True) + + # Parent should not register MA1 of Child 1 because it's already registerd + self.flush_all() + self.nodes[meds[0]].add_ipmaddr(MA1) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, MA1, should_send=False) + + # Parent should register MA2 of Child 1 because it's new + self.flush_all() + self.nodes[meds[0]].add_ipmaddr(MA2) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, MA2, should_send=True) + + # Parent should not register MA2 of Child 2 because it's already registered for Child 1 + self.flush_all() + self.nodes[meds[1]].add_ipmaddr(MA2) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, MA2, should_send=False) + + # Parent should register MA3 of Child 2 because it's new + self.flush_all() + self.nodes[meds[1]].add_ipmaddr(MA3) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, MA3, should_send=True) + + # Parent should not register MA2 and MA3 because they are already registered for Child2 itself + self.flush_all() + self.nodes[meds[1]].del_ipmaddr(MA2) + self.nodes[meds[1]].del_ipmaddr(MA3) + self.nodes[meds[1]].add_ipmaddr(MA2) + self.nodes[meds[1]].add_ipmaddr(MA3) + self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE) + self.__check_send_mlr_req(parent_id, [MA2, MA3], should_send=False) + + # Restore + self.nodes[parent_id].del_ipmaddr(MA1) + self.nodes[meds[0]].del_ipmaddr(MA1) + self.nodes[meds[0]].del_ipmaddr(MA2) + self.nodes[meds[1]].del_ipmaddr(MA2) + self.nodes[meds[1]].del_ipmaddr(MA3) + self.simulator.go(WAIT_REDUNDANCE) + if __name__ == '__main__': unittest.main()