[mlr] send MLR.req for local multicast addresses (#5248)

This commit includes:

- Adding compile configurations necessary for MLR features
- Adding source/header files necessary for MLR features
- Send MLR.req for local MAs on ThreadNetif
  - At most one MLR.req is outstanding
  - Reregistration according to PBBR configuration
  - Renewing periods according to PBBR configuration
- BBRs receive MLR.req with minimal processing and send MLR.rsp
- Add a test to verify that MLR.req is sent for local MAs.
This commit is contained in:
Simon Lin
2020-07-22 08:38:05 -07:00
committed by GitHub
parent 2129b567b4
commit 00f3043a06
29 changed files with 1564 additions and 12 deletions
+2
View File
@@ -176,6 +176,7 @@ LOCAL_SRC_FILES := \
src/core/api/udp_api.cpp \
src/core/backbone_router/bbr_leader.cpp \
src/core/backbone_router/bbr_local.cpp \
src/core/backbone_router/bbr_manager.cpp \
src/core/coap/coap.cpp \
src/core/coap/coap_message.cpp \
src/core/coap/coap_secure.cpp \
@@ -254,6 +255,7 @@ LOCAL_SRC_FILES := \
src/core/thread/mle.cpp \
src/core/thread/mle_router.cpp \
src/core/thread/mle_types.cpp \
src/core/thread/mlr_manager.cpp \
src/core/thread/network_data.cpp \
src/core/thread/network_data_leader.cpp \
src/core/thread/network_data_leader_ftd.cpp \
+2
View File
@@ -68,6 +68,7 @@ static_library("lib-ot-core") {
"src/core/api/udp_api.cpp",
"src/core/backbone_router/bbr_leader.cpp",
"src/core/backbone_router/bbr_local.cpp",
"src/core/backbone_router/bbr_manager.cpp",
"src/core/coap/coap.cpp",
"src/core/coap/coap_message.cpp",
"src/core/coap/coap_secure.cpp",
@@ -149,6 +150,7 @@ static_library("lib-ot-core") {
"src/core/thread/mle.cpp",
"src/core/thread/mle_router.cpp",
"src/core/thread/mle_types.cpp",
"src/core/thread/mlr_manager.cpp",
"src/core/thread/network_data.cpp",
"src/core/thread/network_data_leader.cpp",
"src/core/thread/network_data_leader_ftd.cpp",
+5
View File
@@ -130,6 +130,11 @@ if(OT_DUA)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DUA_ENABLE=1")
endif()
option(OT_MLR "enable Multicast Listener Registration feature for Thread 1.2")
if(OT_MLR)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLR_ENABLE=1")
endif()
option(OT_EXTERNAL_HEAP "enable external heap support")
if(OT_EXTERNAL_HEAP)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE=1")
+5
View File
@@ -61,6 +61,7 @@ endif
LINK_RAW ?= 0
MAC_FILTER ?= 0
MLE_LONG_ROUTES ?= 0
MLR ?= 0
MTD_NETDIAG ?= 0
MULTIPLE_INSTANCE ?= 0
OTNS ?= 0
@@ -205,6 +206,10 @@ ifeq ($(MLE_LONG_ROUTES),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE=1
endif
ifeq ($(MLR),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_MLR_ENABLE=1
endif
ifeq ($(MTD_NETDIAG),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1
endif
+2
View File
@@ -52,6 +52,7 @@ build_simulation()
if [[ ${version} == "1.2" ]]; then
options+=("-DOT_DUA=ON")
options+=("-DOT_MLR=ON")
fi
if [[ ${VIRTUAL_TIME} == 1 ]]; then
@@ -89,6 +90,7 @@ build_posix()
if [[ ${version} == "1.2" ]]; then
options+=("-DOT_DUA=ON")
options+=("-DOT_MLR=ON")
fi
if [[ ${VIRTUAL_TIME} == 1 ]]; then
+2
View File
@@ -104,6 +104,7 @@ set(COMMON_SOURCES
api/udp_api.cpp
backbone_router/bbr_leader.cpp
backbone_router/bbr_local.cpp
backbone_router/bbr_manager.cpp
coap/coap.cpp
coap/coap_message.cpp
coap/coap_secure.cpp
@@ -185,6 +186,7 @@ set(COMMON_SOURCES
thread/mle.cpp
thread/mle_router.cpp
thread/mle_types.cpp
thread/mlr_manager.cpp
thread/network_data.cpp
thread/network_data_leader.cpp
thread/network_data_leader_ftd.cpp
+5
View File
@@ -146,6 +146,7 @@ SOURCES_COMMON = \
api/udp_api.cpp \
backbone_router/bbr_leader.cpp \
backbone_router/bbr_local.cpp \
backbone_router/bbr_manager.cpp \
coap/coap.cpp \
coap/coap_message.cpp \
coap/coap_secure.cpp \
@@ -227,6 +228,7 @@ SOURCES_COMMON = \
thread/mle.cpp \
thread/mle_router.cpp \
thread/mle_types.cpp \
thread/mlr_manager.cpp \
thread/network_data.cpp \
thread/network_data_leader.cpp \
thread/network_data_leader_ftd.cpp \
@@ -317,6 +319,7 @@ HEADERS_COMMON = \
openthread-core-config.h \
backbone_router/bbr_leader.hpp \
backbone_router/bbr_local.hpp \
backbone_router/bbr_manager.hpp \
coap/coap.hpp \
coap/coap_message.hpp \
coap/coap_secure.hpp \
@@ -439,6 +442,8 @@ HEADERS_COMMON = \
thread/mle_router.hpp \
thread/mle_tlvs.hpp \
thread/mle_types.hpp \
thread/mlr_manager.hpp \
thread/mlr_types.hpp \
thread/network_data.hpp \
thread/network_data_leader.hpp \
thread/network_data_leader_ftd.hpp \
-1
View File
@@ -39,7 +39,6 @@
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
using namespace ot;
+4
View File
@@ -226,6 +226,10 @@ void Leader::UpdateBackboneRouterPrimary(void)
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
Get<BackboneRouter::Local>().HandleBackboneRouterPrimaryUpdate(state, mConfig);
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE
Get<MlrManager>().HandleBackboneRouterPrimaryUpdate(state, mConfig);
#endif
}
void Leader::UpdateDomainPrefixConfig(void)
+125
View File
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements Backbone Router management.
*/
#include "bbr_manager.hpp"
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/random.hpp"
#include "thread/mle_types.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_uri_paths.hpp"
namespace ot {
namespace BackboneRouter {
Manager::Manager(Instance &aInstance)
: InstanceLocator(aInstance)
, Notifier::Receiver(aInstance, Manager::HandleNotifierEvents)
, mMulticastListenerRegistration(OT_URI_PATH_MLR, Manager::HandleMulticastListenerRegistration, this)
{
}
void Manager::HandleNotifierEvents(Events aEvents)
{
if (aEvents.Contains(kEventThreadBackboneRouterStateChanged))
{
if (Get<BackboneRouter::Local>().GetState() == OT_BACKBONE_ROUTER_STATE_DISABLED)
{
Get<Coap::Coap>().RemoveResource(mMulticastListenerRegistration);
}
else
{
Get<Coap::Coap>().AddResource(mMulticastListenerRegistration);
}
}
}
void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
bool isPrimary = Get<BackboneRouter::Local>().IsPrimary();
ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess;
VerifyOrExit(aMessage.IsConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_PARSE);
VerifyOrExit(isPrimary, status = ThreadStatusTlv::kMlrBbrNotPrimary);
// TODO: (MLR) send configured MLR response for Reference Device
// TODO: (MLR) handle Commissioner Session TLV
// TODO: (MLR) handle Timeout TLV
// TODO: (MLR) handle addresses, updated listener groups, send BMLR.req
exit:
if (error == OT_ERROR_NONE)
{
SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status);
}
}
void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
const Ip6::MessageInfo & aMessageInfo,
ThreadStatusTlv::MlrStatus aStatus)
{
otError error = OT_ERROR_NONE;
Coap::Message *message = nullptr;
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
SuccessOrExit(message->SetPayloadMarker());
SuccessOrExit(Tlv::AppendUint8Tlv(*message, ThreadTlv::kStatus, aStatus));
// TODO: (MLR) append the failed addresses
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo));
exit:
if (error != OT_ERROR_NONE && message != nullptr)
{
message->Free();
}
otLogInfoBbr("Sent MLR.rsp (status=%d): %s", aStatus, otThreadErrorToString(error));
}
} // namespace BackboneRouter
} // namespace ot
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+99
View File
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for Backbone Router management.
*/
#ifndef BACKBONE_ROUTER_MANAGER_HPP_
#define BACKBONE_ROUTER_MANAGER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <openthread/backbone_router.h>
#include <openthread/backbone_router_ftd.h>
#include "backbone_router/bbr_leader.hpp"
#include "common/locator.hpp"
#include "net/netif.hpp"
#include "thread/network_data.hpp"
namespace ot {
namespace BackboneRouter {
/**
* This class implements the definitions for Backbone Router management.
*
*/
class Manager : public InstanceLocator, public Notifier::Receiver
{
public:
/**
* This constructor initializes the Backbone Router manager.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Manager(Instance &aInstance);
private:
static void HandleMulticastListenerRegistration(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<Manager *>(aContext)->HandleMulticastListenerRegistration(
*static_cast<const Coap::Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
const Ip6::MessageInfo & aMessageInfo,
ThreadStatusTlv::MlrStatus aStatus);
static void HandleNotifierEvents(Notifier::Receiver &aReceiver, Events aEvents)
{
static_cast<Manager &>(aReceiver).HandleNotifierEvents(aEvents);
}
void HandleNotifierEvents(Events aEvents);
Coap::Resource mMulticastListenerRegistration;
};
} // namespace BackboneRouter
/**
* @}
*/
} // namespace ot
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#endif // BACKBONE_ROUTER_MANAGER_HPP_
+12
View File
@@ -736,6 +736,11 @@ template <> inline BackboneRouter::Local &Instance::Get(void)
{
return mThreadNetif.mBackboneRouterLocal;
}
template <> inline BackboneRouter::Manager &Instance::Get(void)
{
return mThreadNetif.mBackboneRouterManager;
}
#endif
#if OPENTHREAD_CONFIG_DUA_ENABLE
@@ -745,6 +750,13 @@ template <> inline DuaManager &Instance::Get(void)
}
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE
template <> inline MlrManager &Instance::Get(void)
{
return mThreadNetif.mMlrManager;
}
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_OTNS_ENABLE
@@ -521,6 +521,10 @@
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_DUA_ENABLE"
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_MLR_ENABLE"
#endif
#ifdef OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL
#error "OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL is removed, use OPENTHREAD_CONFIG_LOG_OUTPUT_APP instead"
#endif
@@ -460,4 +460,14 @@
#define OPENTHREAD_CONFIG_DUA_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MLR_ENABLE
*
* Define as 1 to support Thread 1.2 Multicast Listener Registration feature.
*
*/
#ifndef OPENTHREAD_CONFIG_MLR_ENABLE
#define OPENTHREAD_CONFIG_MLR_ENABLE 0
#endif
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
+6 -3
View File
@@ -285,7 +285,7 @@ exit:
bool Netif::IsMulticastAddressExternal(const NetifMulticastAddress &aAddress) const
{
return mExtMulticastAddressPool.IsPoolEntry(aAddress);
return mExtMulticastAddressPool.IsPoolEntry(static_cast<const ExternalNetifMulticastAddress &>(aAddress));
}
void Netif::SubscribeMulticast(NetifMulticastAddress &aAddress)
@@ -319,7 +319,7 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress)
otError error = OT_ERROR_NONE;
NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kLinkLocalAllRoutersMulticastAddress));
NetifMulticastAddress *entry;
ExternalNetifMulticastAddress *entry;
VerifyOrExit(!IsMulticastSubscribed(aAddress), error = OT_ERROR_ALREADY);
@@ -336,6 +336,9 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress)
VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
entry->mAddress = aAddress;
#if OPENTHREAD_CONFIG_MLR_ENABLE
entry->mMlrState = kMlrStateToRegister;
#endif
mMulticastAddresses.Push(*entry);
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
@@ -356,7 +359,7 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress)
mMulticastAddresses.PopAfter(prev);
mExtMulticastAddressPool.Free(*entry);
mExtMulticastAddressPool.Free(static_cast<ExternalNetifMulticastAddress &>(*entry));
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
+189 -2
View File
@@ -46,6 +46,7 @@
#include "mac/mac_types.hpp"
#include "net/ip6_address.hpp"
#include "net/socket.hpp"
#include "thread/mlr_types.hpp"
namespace ot {
namespace Ip6 {
@@ -156,6 +157,41 @@ private:
bool Matches(const Address &aAddress) const { return GetAddress() == aAddress; }
};
class ExternalNetifMulticastAddress : public NetifMulticastAddress
{
friend class Netif;
friend class LinkedList<ExternalNetifMulticastAddress>;
public:
#if OPENTHREAD_CONFIG_MLR_ENABLE
/**
* This method returns the current Multicast Listener Registration (MLR) state.
*
* @returns The current Multicast Listener Registration state.
*
*/
MlrState GetMlrState(void) const { return mMlrState; }
/**
* This method sets the Multicast Listener Registration (MLR) state.
*
* @param[in] aState The new Multicast Listener Registration state.
*
*/
void SetMlrState(MlrState aState) { mMlrState = aState; }
#endif
private:
ExternalNetifMulticastAddress *GetNext(void)
{
return static_cast<ExternalNetifMulticastAddress *>(const_cast<otNetifMulticastAddress *>(mNext));
}
#if OPENTHREAD_CONFIG_MLR_ENABLE
MlrState mMlrState : 2;
#endif
};
/**
* This class implements an IPv6 network interface.
*
@@ -164,8 +200,125 @@ class Netif : public InstanceLocator, public LinkedListEntry<Netif>, private Non
{
friend class Ip6;
friend class Address;
class ExternalMulticastAddressIteratorBuilder;
public:
/**
* This class represents an iterator for iterating external multicast addresses in a Netif instance.
*
*/
class ExternalMulticastAddressIterator
{
friend class ExternalMulticastAddressIteratorBuilder;
public:
/**
* This constructor initializes an `ExternalMulticastAddressIterator` instance to start from the first external
* multicast address.
*
* @param[in] aNetif A reference to the Netif instance.
*
*/
explicit ExternalMulticastAddressIterator(Netif &aNetif)
: mNetif(aNetif)
{
AdvanceFrom(mNetif.GetMulticastAddresses());
}
/**
* This method overloads `++` operator (pre-increment) to advance the iterator.
*
* The iterator is moved to point to the next entry. If there are no more entries matching the iterator becomes
* empty.
*
*/
void operator++(void) { AdvanceFrom(mCurrent->GetNext()); }
/**
* This method overloads `++` operator (post-increment) to advance the iterator.
*
* The iterator is moved to point to the next entry. If there are no more entries matching the iterator becomes
* empty.
*
*/
void operator++(int) { AdvanceFrom(mCurrent->GetNext()); }
/**
* This method overloads the `*` dereference operator and gets a reference to `ExternalNetifMulticastAddress`
* entry to which the iterator is currently pointing.
*
* This method MUST be used when the iterator is not empty.
*
* @returns A reference to the `ExternalNetifMulticastAddress` entry currently pointed by the iterator.
*
*/
ExternalNetifMulticastAddress &operator*(void) { return *mCurrent; }
/**
* This method overloads the `->` dereference operator and gets a pointer to `ExternalNetifMulticastAddress`
* entry to which the iterator is current pointing.
*
* @returns A pointer to the `ExternalNetifMulticastAddress` entry associated with the iterator, or `nullptr` if
* iterator is empty.
*
*/
ExternalNetifMulticastAddress *operator->(void) { return mCurrent; }
/**
* This method overloads operator `==` to evaluate whether or not two `ExternalMulticastAddressIterator`
* instances point to the same `ExternalNetifMulticastAddress` entry.
*
* @param[in] aOther The other `Iterator` to compare with.
*
* @retval TRUE If the two `ExternalMulticastAddressIterator` objects point to the same
* `ExternalNetifMulticastAddress` entry or both are done.
* @retval FALSE If the two `ExternalMulticastAddressIterator` objects do not point to the same
* `ExternalNetifMulticastAddress` entry.
*
*/
bool operator==(const ExternalMulticastAddressIterator &aOther) { return mCurrent == aOther.mCurrent; }
/**
* This method overloads operator `!=` to evaluate whether or not two `ExternalMulticastAddressIterator`
* instances point to the same `ExternalNetifMulticastAddress` entry.
*
* @param[in] aOther The other `ExternalMulticastAddressIterator` to compare with.
*
* @retval TRUE If the two `ExternalMulticastAddressIterator` objects do not point to the same
* `ExternalNetifMulticastAddress` entry.
* @retval FALSE If the two `ExternalMulticastAddressIterator` objects point to the same
* `ExternalNetifMulticastAddress` entry or both are done.
*
*/
bool operator!=(const ExternalMulticastAddressIterator &aOther) { return mCurrent != aOther.mCurrent; }
private:
enum IteratorType
{
kEndIterator,
};
ExternalMulticastAddressIterator(Netif &aNetif, IteratorType)
: mNetif(aNetif)
, mCurrent(nullptr)
{
}
void AdvanceFrom(const NetifMulticastAddress *aAddr)
{
while (aAddr != nullptr && !mNetif.IsMulticastAddressExternal(*aAddr))
{
aAddr = aAddr->GetNext();
}
mCurrent =
const_cast<ExternalNetifMulticastAddress *>(static_cast<const ExternalNetifMulticastAddress *>(aAddr));
}
Netif & mNetif;
ExternalNetifMulticastAddress *mCurrent;
};
/**
* This constructor initializes the network interface.
*
@@ -400,6 +553,22 @@ public:
*/
void SetMulticastPromiscuous(bool aEnabled) { mMulticastPromiscuous = aEnabled; }
/**
* This method enables range-based `for` loop iteration over all external multicast addresses on the Netif.
*
* This method should be used like follows:
*
* for (Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
* { ... }
*
* @returns An `ExternalMulticastAddressIteratorBuilder` instance.
*
*/
ExternalMulticastAddressIteratorBuilder IterateExternalMulticastAddresses(void)
{
return ExternalMulticastAddressIteratorBuilder(*this);
}
protected:
/**
* This method subscribes the network interface to the realm-local all MPL forwarders, link-local, and realm-local
@@ -423,6 +592,24 @@ private:
kMulticastPrefixLength = 128, ///< Multicast prefix length used to notify internal address changes.
};
class ExternalMulticastAddressIteratorBuilder
{
public:
ExternalMulticastAddressIteratorBuilder(Netif &aNetif)
: mNetif(aNetif)
{
}
ExternalMulticastAddressIterator begin(void) { return ExternalMulticastAddressIterator(mNetif); }
ExternalMulticastAddressIterator end(void)
{
return ExternalMulticastAddressIterator(mNetif, ExternalMulticastAddressIterator::kEndIterator);
}
private:
Netif &mNetif;
};
LinkedList<NetifUnicastAddress> mUnicastAddresses;
LinkedList<NetifMulticastAddress> mMulticastAddresses;
bool mMulticastPromiscuous;
@@ -430,8 +617,8 @@ private:
otIp6AddressCallback mAddressCallback;
void * mAddressCallbackContext;
Pool<NetifUnicastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS> mExtUnicastAddressPool;
Pool<NetifMulticastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS> mExtMulticastAddressPool;
Pool<NetifUnicastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS> mExtUnicastAddressPool;
Pool<ExternalNetifMulticastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS> mExtMulticastAddressPool;
static const otNetifMulticastAddress kRealmLocalAllMplForwardersMulticastAddress;
static const otNetifMulticastAddress kLinkLocalAllNodesMulticastAddress;
+3 -3
View File
@@ -249,10 +249,10 @@ enum
kServiceMaxId = 0x0f, ///< Maximal Service ID.
};
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_MLR_ENABLE
/**
* Backbone Router constants
* Backbone Router / MLR constants
*
*/
enum
@@ -266,7 +266,7 @@ enum
static_assert(kMlrTimeoutDefault >= kMlrTimeoutMin,
"kMlrTimeoutDefault must be larger than or equal to kMlrTimeoutMin");
#endif
#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_MLR_ENABLE
/**
* This type represents a MLE device mode.
+373
View File
@@ -0,0 +1,373 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements MLR management.
*/
#include "mlr_manager.hpp"
#if OPENTHREAD_CONFIG_MLR_ENABLE
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "net/ip6_address.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_uri_paths.hpp"
#include "utils/slaac_address.hpp"
namespace ot {
MlrManager::MlrManager(Instance &aInstance)
: InstanceLocator(aInstance)
, Notifier::Receiver(aInstance, MlrManager::HandleNotifierEvents)
, mTimer(aInstance, MlrManager::HandleTimer, this)
, mReregistrationDelay(0)
, mSendDelay(0)
, mMlrPending(false)
{
}
void MlrManager::HandleNotifierEvents(Events aEvents)
{
if (aEvents.Contains(kEventIp6MulticastSubscribed))
{
ScheduleSend(0);
}
if (aEvents.Contains(kEventThreadRoleChanged) && Get<Mle::MleRouter>().IsChild())
{
// Reregistration after re-attach
UpdateReregistrationDelay(true);
}
}
void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
const BackboneRouter::BackboneRouterConfig &aConfig)
{
OT_UNUSED_VARIABLE(aConfig);
bool needRereg =
aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg;
UpdateReregistrationDelay(needRereg);
}
void MlrManager::ScheduleSend(uint16_t aDelay)
{
OT_ASSERT(!mMlrPending || mSendDelay == 0);
VerifyOrExit(!mMlrPending, OT_NOOP);
if (aDelay == 0)
{
mSendDelay = 0;
SendMulticastListenerRegistration();
}
else if (mSendDelay == 0 || mSendDelay > aDelay)
{
mSendDelay = aDelay;
}
ResetTimer();
exit:
return;
}
void MlrManager::ResetTimer(void)
{
if (mSendDelay == 0 && mReregistrationDelay == 0)
{
mTimer.Stop();
}
else if (!mTimer.IsRunning())
{
mTimer.Start(kTimerInterval);
}
}
void MlrManager::SendMulticastListenerRegistration(void)
{
otError error = OT_ERROR_NONE;
Mle::MleRouter & mle = Get<Mle::MleRouter>();
Coap::Message * message = nullptr;
Ip6::MessageInfo messageInfo;
IPv6AddressesTlv addressesTlv;
uint8_t num;
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)
{
num = kIPv6AddressesNumMax;
}
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(message->SetToken(Coap::Message::kDefaultTokenLength));
SuccessOrExit(message->AppendUriPathOptions(OT_URI_PATH_MLR));
SuccessOrExit(message->SetPayloadMarker());
addressesTlv.Init();
addressesTlv.SetLength(sizeof(Ip6::Address) * num);
SuccessOrExit(error = message->Append(&addressesTlv, sizeof(addressesTlv)));
for (Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress().IsMulticastLargerThanRealmLocal() && addr.GetMlrState() == kMlrStateToRegister)
{
SuccessOrExit(error = message->Append(&addr.GetAddress(), sizeof(Ip6::Address)));
addr.SetMlrState(kMlrStateRegistering);
if (--num == 0)
{
break;
}
}
}
if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
{
uint8_t pbbrServiceId;
SuccessOrExit(error = Get<BackboneRouter::Leader>().GetServiceId(pbbrServiceId));
SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()));
}
else
{
messageInfo.GetPeerAddr().SetToRoutingLocator(mle.GetMeshLocalPrefix(),
Get<BackboneRouter::Leader>().GetServer16());
}
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetSockAddr(mle.GetMeshLocal16());
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(
*message, messageInfo, &MlrManager::HandleMulticastListenerRegistrationResponse, this));
mMlrPending = true;
// TODO: not enable fast polls for SSED
if (!Get<Mle::Mle>().IsRxOnWhenIdle())
{
Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
}
exit:
if (error != OT_ERROR_NONE)
{
if (message != nullptr)
{
message->Free();
}
SetNetifMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
if (error == OT_ERROR_NO_BUFS)
{
ScheduleSend(1);
}
}
otLogInfoMlr("Send MLR.req: %s", otThreadErrorToString(error));
LogMulticastAddresses();
}
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
const Ip6::MessageInfo *aMessageInfo,
otError aResult)
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
otError error;
mMlrPending = false;
VerifyOrExit(aResult == OT_ERROR_NONE && aMessage != nullptr, error = OT_ERROR_PARSE);
VerifyOrExit(aMessage->GetCode() == OT_COAP_CODE_CHANGED, error = OT_ERROR_PARSE);
SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, status));
exit:
if (status == ThreadStatusTlv::MlrStatus::kMlrSuccess)
{
SetNetifMulticastAddressMlrState(kMlrStateRegistering, kMlrStateRegistered);
// keep sending until all multicast addresses are registered.
ScheduleSend(0);
}
else
{
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
// TODO (MLR): reregister only failed addresses.
if (Get<BackboneRouter::Leader>().GetConfig(config) == OT_ERROR_NONE)
{
reregDelay = config.mReregistrationDelay > 1
? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
: 1;
ScheduleSend(reregDelay);
}
}
otLogInfoMlr("Receive MLR.rsp, result=%s, status=%d, error=%s", otThreadErrorToString(aResult), status,
otThreadErrorToString(error));
}
void MlrManager::HandleTimer(void)
{
if (mSendDelay > 0 && --mSendDelay == 0)
{
SendMulticastListenerRegistration();
}
if (mReregistrationDelay > 0 && --mReregistrationDelay == 0)
{
Reregister();
}
ResetTimer();
}
void MlrManager::Reregister(void)
{
SetNetifMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
ScheduleSend(0);
// Schedule for the next renewing.
UpdateReregistrationDelay(false);
}
void MlrManager::UpdateReregistrationDelay(bool aRereg)
{
Mle::MleRouter &mle = Get<Mle::MleRouter>();
bool needSendMlr = (mle.IsFullThreadDevice() || mle.GetParent().IsThreadVersion1p1()) &&
Get<BackboneRouter::Leader>().HasPrimary();
if (!needSendMlr)
{
mReregistrationDelay = 0;
}
else
{
BackboneRouter::BackboneRouterConfig config;
uint32_t reregDelay;
uint32_t effectiveMlrTimeout;
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
if (aRereg)
{
reregDelay = config.mReregistrationDelay > 1
? Random::NonCrypto::GetUint16InRange(1, config.mReregistrationDelay)
: 1;
}
else
{
// Calculate renewing period according to Thread Spec. 5.24.2.3.2
// The random time t SHOULD be chosen such that (0.5* MLR-Timeout) < t < (MLR-Timeout 9 seconds).
effectiveMlrTimeout = config.mMlrTimeout > Mle::kMlrTimeoutMin ? config.mMlrTimeout
: static_cast<uint32_t>(Mle::kMlrTimeoutMin);
reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9);
}
if (mReregistrationDelay == 0 || mReregistrationDelay > reregDelay)
{
mReregistrationDelay = reregDelay;
}
}
ResetTimer();
otLogDebgMlr("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
mReregistrationDelay);
}
void MlrManager::LogMulticastAddresses(void)
{
#if OPENTHREAD_CONFIG_LOG_BBR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
otLogDebgMlr("-------- Multicast Addresses --------");
for (const Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress().IsMulticastLargerThanRealmLocal())
{
MlrState state = addr.GetMlrState();
otLogDebgMlr("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[state]);
}
}
#endif
}
uint16_t MlrManager::CountNetifMulticastAddressesToRegister(void) const
{
uint16_t count = 0;
for (const Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress().IsMulticastLargerThanRealmLocal() && addr.GetMlrState() == kMlrStateToRegister)
{
count++;
}
}
return count;
}
void MlrManager::SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
for (Ip6::ExternalNetifMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
{
if (addr.GetAddress().IsMulticastLargerThanRealmLocal() && addr.GetMlrState() == aFromState)
{
addr.SetMlrState(aToState);
}
}
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for managing Multicast Listener Registration feature defined in Thread 1.2.
*/
#ifndef MLR_MANAGER_HPP_
#define MLR_MANAGER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_MLR_ENABLE
#include "backbone_router/bbr_leader.hpp"
#include "coap/coap_message.hpp"
#include "common/locator.hpp"
#include "common/notifier.hpp"
#include "common/timer.hpp"
#include "net/netif.hpp"
namespace ot {
/**
* @addtogroup core-mlr
*
* @brief
* This module includes definitions for Multicast Listener Registration.
*
* @{
*
* @defgroup core-mlr Mlr
*
* @}
*
*/
/**
* This class implements MLR management.
*
*/
class MlrManager : public InstanceLocator, public Notifier::Receiver
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit MlrManager(Instance &aInstance);
/**
* This method notifies Primary Backbone Router status.
*
* @param[in] aState The state or state change of Primary Backbone Router.
* @param[in] aConfig The Primary Backbone Router service.
*
*/
void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState,
const BackboneRouter::BackboneRouterConfig &aConfig);
private:
enum
{
kTimerInterval = 1000,
};
static void HandleNotifierEvents(Notifier::Receiver &aReceiver, Events aEvents)
{
static_cast<MlrManager &>(aReceiver).HandleNotifierEvents(aEvents);
}
void HandleNotifierEvents(Events aEvents);
void SendMulticastListenerRegistration(void);
static void HandleMulticastListenerRegistrationResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
otError aResult)
{
static_cast<MlrManager *>(aContext)->HandleMulticastListenerRegistrationResponse(
static_cast<Coap::Message *>(aMessage), static_cast<const Ip6::MessageInfo *>(aMessageInfo), aResult);
}
void HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
const Ip6::MessageInfo *aMessageInfo,
otError aResult);
uint16_t CountNetifMulticastAddressesToRegister(void) const;
void SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
void ScheduleSend(uint16_t aDelay);
void ResetTimer(void);
void UpdateReregistrationDelay(bool aRereg);
void Reregister(void);
static void HandleTimer(Timer &aTimer) { aTimer.GetOwner<MlrManager>().HandleTimer(); }
void HandleTimer(void);
void LogMulticastAddresses(void);
TimerMilli mTimer;
uint32_t mReregistrationDelay;
uint16_t mSendDelay;
bool mMlrPending : 1;
};
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
#endif // MLR_MANAGER_HPP_
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for Multicast Listener Registration states of multicast addresses.
*/
#ifndef MLR_TYPES_HPP_
#define MLR_TYPES_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_MLR_ENABLE
namespace ot {
/**
* @addtogroup core-mlr
*
* @{
*
*/
/**
* Multicast Listener Registration state for multicast addresses.
*
*/
typedef 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 // MLR_TYPES_HPP_
+4
View File
@@ -109,9 +109,13 @@ ThreadNetif::ThreadNetif(Instance &aInstance)
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
, mBackboneRouterLocal(aInstance)
, mBackboneRouterManager(aInstance)
#endif
#if OPENTHREAD_CONFIG_DUA_ENABLE
, mDuaManager(aInstance)
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE
, mMlrManager(aInstance)
#endif
, mChildSupervisor(aInstance)
, mSupervisionListener(aInstance)
+10 -1
View File
@@ -52,12 +52,17 @@
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include "backbone_router/bbr_local.hpp"
#include "backbone_router/bbr_manager.hpp"
#endif
#if OPENTHREAD_CONFIG_DUA_ENABLE
#include "thread/dua_manager.hpp"
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE
#include "thread/mlr_manager.hpp"
#endif
#include "meshcop/dataset_manager.hpp"
#if OPENTHREAD_CONFIG_JOINER_ENABLE
@@ -253,10 +258,14 @@ private:
BackboneRouter::Leader mBackboneRouterLeader;
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
BackboneRouter::Local mBackboneRouterLocal;
BackboneRouter::Local mBackboneRouterLocal;
BackboneRouter::Manager mBackboneRouterManager;
#endif
#if OPENTHREAD_CONFIG_DUA_ENABLE
DuaManager mDuaManager;
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE
MlrManager mMlrManager;
#endif
Utils::ChildSupervisor mChildSupervisor;
Utils::SupervisionListener mSupervisionListener;
+64
View File
@@ -51,6 +51,10 @@ using ot::Encoding::BigEndian::HostSwap32;
enum
{
kCoapUdpPort = 61631,
// Thread 1.2.0 5.19.13 limits the number of IPv6 addresses should be [1, 15].
kIPv6AddressesNumMin = 1,
kIPv6AddressesNumMax = 15,
};
/**
@@ -77,6 +81,7 @@ public:
kNDOption = 8, ///< ND Option TLV
kNDData = 9, ///< ND Data TLV
kThreadNetworkData = 10, ///< Thread Network Data TLV
kIPv6Addresses = 14, ///< IPv6 Addresses TLV
};
/**
@@ -116,6 +121,20 @@ public:
kHaveChildIdRequest = 3, ///< Address Solicit due to child ID request.
kParentPartitionChange = 4, ///< Address Solicit due to parent partition change
};
/**
* Multicast Listener Registration (MLR) Status values
*
*/
enum MlrStatus
{
kMlrSuccess = 0, ///< Successful (de)registration of all IPv6 addresses.
kMlrInvalid = 2, ///< Invalid IPv6 address(es) in request.
kMlrNoPersistent = 3, ///< This device does not support persistent registrations.
kMlrNoResources = 4, ///< BBR resource shortage.
kMlrBbrNotPrimary = 5, ///< BBR is not Primary at this moment.
kMlrGeneralFailure = 6, ///< Reason(s) for failure are not further specified.
};
};
/**
@@ -225,6 +244,51 @@ private:
uint8_t mTlvs[kMaxSize];
} OT_TOOL_PACKED_END;
#if OPENTHREAD_CONFIG_MLR_ENABLE
/**
* This class implements IPv6 Addresses TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class IPv6AddressesTlv : public ThreadTlv
{
public:
/**
* This method initializes the TLV.
*
*/
void Init(void) { SetType(kIPv6Addresses); }
/**
* This method indicates whether or not the TLV appears to be well-formed.
*
* @retval TRUE If the TLV appears to be well-formed.
* @retval FALSE If the TLV does not appear to be well-formed.
*
*/
bool IsValid(void) const
{
return GetLength() >= sizeof(Ip6::Address) * kIPv6AddressesNumMin &&
GetLength() <= sizeof(Ip6::Address) * kIPv6AddressesNumMax && (GetLength() % sizeof(Ip6::Address)) == 0;
}
/**
* This method returns a pointer to the IPv6 address entry.
*
* @param[in] aIndex The index into the IPv6 address list.
*
* @returns A reference to the IPv6 address.
*
*/
const Ip6::Address &GetIp6Address(uint8_t aIndex) const
{
return *reinterpret_cast<const Ip6::Address *>(GetValue() + (aIndex * sizeof(Ip6::Address)));
}
} OT_TOOL_PACKED_END;
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
} // namespace ot
#endif // THREAD_TLVS_HPP_
+8
View File
@@ -292,6 +292,14 @@ namespace ot {
*/
#define OT_URI_PATH_DIAGNOSTIC_RESET "d/dr"
/**
* @def OT_URI_PATH_MLR
*
* The URI Path for Multicast Listener Registration.
*
*/
#define OT_URI_PATH_MLR "n/mr"
} // namespace ot
#endif // THREAD_URIS_HPP_
+3
View File
@@ -336,6 +336,8 @@ def create_deafult_network_tlvs_factories():
# which is in fact MLE Route64 TLV. Thread specificaton v1.1. - Chapter 5.20
network_layer.TlvType.MLE_ROUTING:
create_default_mle_tlv_route64_factory(),
network_layer.TlvType.IPv6_ADDRESSES:
network_layer.IPv6AddressesFactory(),
}
@@ -488,6 +490,7 @@ def create_default_uri_path_based_payload_factories():
'/d/dg': network_diag_tlvs_factory,
'/d/dq': network_diag_tlvs_factory,
'/d/dr': network_diag_tlvs_factory,
'/n/mr': network_layer_tlvs_factory,
}
+19 -1
View File
@@ -27,8 +27,8 @@
# POSSIBILITY OF SUCH DAMAGE.
#
import ipaddress
import struct
from binascii import hexlify
from enum import IntEnum
@@ -47,6 +47,7 @@ class TlvType(IntEnum):
ND_DATA = 9
THREAD_NETWORK_DATA = 10
MLE_ROUTING = 11
IPv6_ADDRESSES = 14
XTAL_ACCURACY = 254
@@ -318,3 +319,20 @@ class ThreadNetworkDataFactory(object):
def parse(self, data, message_info):
tlvs = self._network_data_tlvs_factory.parse(data, message_info)
return ThreadNetworkData(tlvs)
class IPv6Addresses(list):
pass
class IPv6AddressesFactory(object):
def parse(self, data, message_info):
data = bytes(data.read())
assert len(data) % 16 == 0, data
addrs = IPv6Addresses()
for i in range(0, len(data), 16):
addr = ipaddress.IPv6Address(data[i:i + 16])
addrs.append(addr)
return addrs
+5
View File
@@ -727,6 +727,11 @@ class Node:
self.send_command(cmd)
self._expect('Done')
def del_ipmaddr(self, ipmaddr):
cmd = 'ipmaddr del %s' % ipmaddr
self.send_command(cmd)
self._expect('Done')
def get_addrs(self):
self.send_command('ipaddr')
+1 -1
View File
@@ -135,7 +135,7 @@ class VirtualTime(BaseSimulator):
BASE_PORT = 9000
MAX_NODES = 34
MAX_MESSAGE = 1024
END_OF_TIME = 0x7FFFFFFF
END_OF_TIME = float('inf')
PORT_OFFSET = int(os.getenv('PORT_OFFSET', '0'))
BLOCK_TIMEOUT = 10
@@ -0,0 +1,400 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import ipaddress
import unittest
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)
WAIT_ATTACH = 5
WAIT_REDUNDANCE = 3
ROUTER_SELECTION_JITTER = 1
BBR_REGISTRATION_JITTER = 5
SED_POLL_PERIOD = 1000 # 1s
REREG_DELAY = 10
MLR_TIMEOUT = 300
MA1 = 'ff04::1234:777a:1'
MA1g = 'ff0e::1234:777a:1'
MA2 = 'ff05::1234:777a:2'
MA3 = 'ff0e::1234:777a:3'
MA4 = 'ff05::1234:777a:4'
MA5 = 'ff03::1234:777a:5'
MA6 = 'ff02::10'
"""
Initial topology:
BBR_1---BBR_2
| / \ |
| / \ |
ROUTER_1_2 ROUTER_1_1
| | \ |
| | \ |
SED_1 MED_1 FED_1
"""
class TestMulticastListenerRegistration(thread_cert.TestCase):
TOPOLOGY = {
BBR_1: {
'version': '1.2',
'whitelist': [BBR_2, ROUTER_1_2],
'is_bbr': True,
'router_selection_jitter': 1,
},
BBR_2: {
'version': '1.2',
'whitelist': [BBR_1, ROUTER_1_2, ROUTER_1_1],
'is_bbr': True,
'router_selection_jitter': 1,
},
ROUTER_1_2: {
'version': '1.2',
'whitelist': [BBR_1, BBR_2, SED_1, MED_1, FED_1],
'router_selection_jitter': 1,
},
ROUTER_1_1: {
'version': '1.1',
'whitelist': [BBR_2],
'router_selection_jitter': 1,
},
MED_1: {
'mode': 'rsn',
'version': '1.2',
'whitelist': [ROUTER_1_2],
'timeout': config.DEFAULT_CHILD_TIMEOUT,
},
SED_1: {
'mode': 'sn',
'version': '1.2',
'whitelist': [ROUTER_1_2],
'timeout': config.DEFAULT_CHILD_TIMEOUT,
},
FED_1: {
'mode': 'rsdn',
'version': '1.2',
'whitelist': [ROUTER_1_2],
'router_upgrade_threshold': 0,
'timeout': config.DEFAULT_CHILD_TIMEOUT,
},
}
"""All nodes are created with default configurations"""
def test(self):
# starting context id
context_id = 1
# Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router
self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_1].set_backbone_router(seqno=1,
reg_delay=REREG_DELAY,
mlr_timeout=MLR_TIMEOUT)
self.nodes[BBR_1].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
self.nodes[BBR_1].enable_backbone_router()
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(),
'Primary')
self.pbbr_seq = 1
# Bring up BBR_2, BBR_2 becomes Router and Secondary Backbone Router
self.nodes[BBR_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_2].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_2].set_backbone_router(seqno=2,
reg_delay=REREG_DELAY,
mlr_timeout=MLR_TIMEOUT)
self.nodes[BBR_2].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_state(), 'router')
self.nodes[BBR_2].enable_backbone_router()
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(),
'Secondary')
self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX)
domain_prefix_cid = context_id
# Bring up ROUTER_1_2
self.nodes[ROUTER_1_2].set_router_selection_jitter(
ROUTER_SELECTION_JITTER)
self.nodes[ROUTER_1_2].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
# Bring up ROUTER_1_1
self.nodes[ROUTER_1_1].set_router_selection_jitter(
ROUTER_SELECTION_JITTER)
self.nodes[ROUTER_1_1].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
# Bring up FED_1
self.nodes[FED_1].start()
self.simulator.go(WAIT_ATTACH)
self.assertEqual(self.nodes[FED_1].get_state(), 'child')
# Bring up MED_1
self.nodes[MED_1].start()
self.simulator.go(WAIT_ATTACH)
self.assertEqual(self.nodes[MED_1].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')
# 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)
self.__check_mlr_ok(MED_1, is_ftd=False)
self.__check_mlr_ok(SED_1, is_ftd=False)
# Switch to parent 1.1
self.__switch_to_1_1_parent()
# Verify MLR.req for each device when parent is 1.1
self.__check_mlr_ok(FED_1, is_ftd=True, is_parent_1p1=True)
self.__check_mlr_ok(MED_1, is_ftd=False, is_parent_1p1=True)
self.__check_mlr_ok(SED_1, is_ftd=False, is_parent_1p1=True)
# Switch to parent 1.2
self.__switch_to_1_2_parent()
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
print(
"======== checking MLR: Node%d (%s), Parent=%s ========" %
(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
for addr in [MA1, MA1g, MA2, MA3, MA4]:
self.__check_ipmaddr_add(id, addr, expect_mlr_req=expect_mlr_req)
for addr in [MA5, MA6]:
self.__check_ipmaddr_add(id, addr, expect_mlr_req=False)
print('=' * 120)
def __check_ipmaddr_add(self, id, addr, expect_mlr_req=True):
"""Check MLR works for the added multicast address"""
self.flush_all()
self.nodes[id].add_ipmaddr(addr)
self.assertTrue(self.nodes[id].has_ipmaddr(addr))
self.simulator.go(3)
reg_mas = self.__get_registered_MAs(id)
if expect_mlr_req:
assert ipaddress.IPv6Address(addr) in reg_mas, (addr, reg_mas)
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.nodes[id].del_ipmaddr(addr)
self.simulator.go(1)
def __get_registered_MAs(self, id):
"""Get MAs registered via MLR.req by the node"""
messages = self.simulator.get_messages_sent_by(id)
reg_mas = []
while True:
msg = messages.next_coap_message('0.02',
'/n/mr',
assert_enabled=False)
if not msg:
break
addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses)
reg_mas.extend(addrs)
print('Node %d registered MAs: %s' % (id, reg_mas))
return reg_mas
def __check_renewing(self, id, addr):
"""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)
def __check_rereg(self, id, addr):
"""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)
def __check_rereg_seqno(self, id, addr):
"""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()
self.pbbr_seq = (self.pbbr_seq + 10) % 256
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)
def __check_rereg_pbbr_change(self, id, addr):
"""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()
self.nodes[BBR_1].disable_backbone_router()
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
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)
# Restore BBR_1 to be Primary and BBR_2 to be Secondary
self.nodes[BBR_2].disable_backbone_router()
self.nodes[BBR_1].enable_backbone_router()
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(),
'Primary')
self.nodes[BBR_2].enable_backbone_router()
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(),
'Secondary')
def __switch_to_1_1_parent(self):
"""Check if MLR works when nodes are switching to a 1.1 parent"""
# Add MA1 to EDs to prepare for parent switching
print("=" * 10, "switching to 1.1 parent", '=' * 10)
self.flush_all()
self.nodes[FED_1].add_ipmaddr(MA1)
self.nodes[MED_1].add_ipmaddr(MA1)
self.nodes[SED_1].add_ipmaddr(MA1)
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(FED_1))
self.assertNotIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(MED_1))
self.assertNotIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(SED_1))
self.flush_all()
# Turn off Router 1.2 and turn on Router 1.1
self.nodes[ROUTER_1_2].stop()
for id in [FED_1, MED_1, SED_1]:
self.nodes[ROUTER_1_1].add_whitelist(self.nodes[id].get_addr64())
self.nodes[id].add_whitelist(self.nodes[ROUTER_1_1].get_addr64())
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE)
self.assertEqual(self.nodes[id].get_state(), 'child')
self.assertEqual(self.nodes[id].get_router_id(),
self.nodes[ROUTER_1_1].get_router_id())
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
# Verify all FED send MLR.req within REREG_DELAY when parent is 1.1
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(FED_1))
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(MED_1))
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(SED_1))
self.nodes[FED_1].del_ipmaddr(MA1)
self.nodes[MED_1].del_ipmaddr(MA1)
self.nodes[SED_1].del_ipmaddr(MA1)
self.simulator.go(WAIT_REDUNDANCE)
def __switch_to_1_2_parent(self):
"""Check if MLR works when nodes are switching to a 1.2 parent"""
# Add MA1 to EDs to prepare for parent switching
print("=" * 10, "switching to 1.2 parent", '=' * 10)
self.flush_all()
self.nodes[FED_1].add_ipmaddr(MA1)
self.nodes[MED_1].add_ipmaddr(MA1)
self.nodes[SED_1].add_ipmaddr(MA1)
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(FED_1))
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(MED_1))
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(SED_1))
self.flush_all()
# Turn off Router 1.1 and turn on Router 1.2
self.nodes[ROUTER_1_1].stop()
self.nodes[ROUTER_1_2].start()
for id in [FED_1, MED_1, SED_1]:
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE)
self.assertEqual(self.nodes[id].get_state(), 'child')
self.assertEqual(self.nodes[id].get_router_id(),
self.nodes[ROUTER_1_2].get_router_id())
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
# Verify only FTD sends MLR.req within REREG_DELAY when parent is 1.2
self.assertIn(ipaddress.IPv6Address(MA1),
self.__get_registered_MAs(FED_1))
# MED and SED might still send MLR.req during this period because it could be sending to it's 1.2 parent.
self.nodes[FED_1].del_ipmaddr(MA1)
self.nodes[MED_1].del_ipmaddr(MA1)
self.nodes[SED_1].del_ipmaddr(MA1)
self.simulator.go(WAIT_REDUNDANCE)
if __name__ == '__main__':
unittest.main()