mirror of
https://github.com/espressif/openthread.git
synced 2026-09-06 09:10:04 +00:00
[mlr] Parent send MLR.req on behalf of Children (#5236)
This commit implements parents sending MLR.req on behave of Children. - Parent sends MLR.req for Children after delaying for a random time < PARENT_AGGREGATE_DELAY (5s). - Update test script to check for proxied MLR.req - Parent proxy MLR.req for Children after a delay t in [0, 5]
This commit is contained in:
@@ -227,7 +227,7 @@ void Leader::UpdateBackboneRouterPrimary(void)
|
||||
Get<BackboneRouter::Local>().HandleBackboneRouterPrimaryUpdate(state, mConfig);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||
#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||
Get<MlrManager>().HandleBackboneRouterPrimaryUpdate(state, mConfig);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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<AddressResolver>().Remove(address);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||
Get<MlrManager>().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<MlrManager>().UpdateProxiedSubscriptions(aChild, nullptr, 0);
|
||||
#endif
|
||||
|
||||
Signal(OT_NEIGHBOR_TABLE_EVENT_CHILD_ADDED, aChild);
|
||||
|
||||
exit:
|
||||
|
||||
@@ -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.
|
||||
|
||||
+214
-52
@@ -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<Mle::MleRouter>().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<ThreadNetif>().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<ThreadNetif>().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<ThreadNetif>().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<ChildTable>().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<ChildTable>().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<BackboneRouter::Leader>().HasPrimary(), error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
num = static_cast<uint8_t>(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<ThreadNetif>().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<ChildTable>().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<Coap::Coap>().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<ThreadNetif>().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<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
|
||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||
for (const Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().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<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||
for (Child &child : Get<ChildTable>().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<ThreadNetif>().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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<uint16_t>(&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<uint16_t>(&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();
|
||||
|
||||
@@ -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<kNumIp6Addresses> 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
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user