mirror of
https://github.com/espressif/openthread.git
synced 2026-08-12 13:47:47 +00:00
[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:
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/locator-getters.hpp"
|
||||
|
||||
using namespace ot;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user