[mlr] add MulticastListenersTable on BBR (#5292)

This commit adds the Multicast Listener Table for BBR:

- Implements Multicast Listener Table on BBR
  - Adds registered MAs to MulticastListenerTable
  - Reject when the table is full or the multicast address is invalid
    and return failed status
  - Respond with unsuccessful status along with failed addresses
  - Expire Listeners from MulticastListenerTable per second (using
    MinHeap for better performance)
- MLR enhancement
  - Re-register only failed multicast addresses (used to register all
    registering multicast addresses)
- Reference Device only APIs
  - Some APIs are defined for reference devices and tests.
- Add tests
  - Add unit test: test_multicast_listener_table.cpp (runs for 1.2-bbr
    only)
  - Add functional tests
This commit is contained in:
Simon Lin
2020-08-21 23:40:58 +08:00
committed by GitHub
parent ec0373c354
commit c73320ff3e
28 changed files with 2002 additions and 397 deletions
+1
View File
@@ -179,6 +179,7 @@ LOCAL_SRC_FILES := \
src/core/backbone_router/bbr_leader.cpp \
src/core/backbone_router/bbr_local.cpp \
src/core/backbone_router/bbr_manager.cpp \
src/core/backbone_router/multicast_listeners_table.cpp \
src/core/coap/coap.cpp \
src/core/coap/coap_message.cpp \
src/core/coap/coap_secure.cpp \
+108
View File
@@ -193,6 +193,114 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *
const otIp6InterfaceIdentifier *aMlIid,
uint8_t aStatus);
/**
* Represents the Multicast Listener events.
*
*/
typedef enum
{
OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED = 0, ///< Multicast Listener was added.
OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED = 1, ///< Multicast Listener was removed or expired.
} otBackboneRouterMulticastListenerEvent;
/**
* This function pointer is called whenever the Multicast Listeners change.
*
* @param[in] aContext The user context pointer.
* @param[in] aEvent The Multicast Listener event.
* @param[in] aAddress The Ip6 multicast address of the Multicast Listener.
*
*/
typedef void (*otBackboneRouterMulticastListenerCallback)(void * aContext,
otBackboneRouterMulticastListenerEvent aEvent,
const otIp6Address * aAddress);
/**
* This method sets the Backbone Router Multicast Listener callback.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aCallback A pointer to the Multicast Listener callback.
* @param[in] aContext A user context pointer.
*
*/
void otBackboneRouterSetMulticastListenerCallback(otInstance * aInstance,
otBackboneRouterMulticastListenerCallback aCallback,
void * aContext);
/**
* This method clears the Multicast Listeners.
*
* Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
* Only used for test and certification.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @sa otBackboneRouterMulticastListenerAdd
* @sa otBackboneRouterMulticastListenerGetNext
*
*/
void otBackboneRouterMulticastListenerClear(otInstance *aInstance);
/**
* This method adds a Multicast Listener.
*
* Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
* Only used for test and certification.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aAddress The Multicast Listener address.
* @param[in] aTimeout The timeout (in seconds) of the Multicast Listener, or 0 to use the default MLR timeout.
*
* @retval OT_ERROR_NONE If the Multicast Listener was successfully added.
* @retval OT_ERROR_INVALID_ARGS If the Multicast Listener address was invalid.
* @retval OT_ERROR_NO_BUFS No space available to save the Multicast Listener.
*
* @sa otBackboneRouterMulticastListenerClear
* @sa otBackboneRouterMulticastListenerGetNext
*
*/
otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout);
#define OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT \
0 ///< Initializer for otBackboneRouterMulticastListenerIterator
typedef uint16_t otBackboneRouterMulticastListenerIterator; ///< Used to iterate through Multicast Listeners.
/**
* This structure represents a Backbone Router Multicast Listener info.
*
*/
typedef struct otBackboneRouterMulticastListenerInfo
{
otIp6Address mAddress; // Multicast Listener address.
uint32_t mTimeout; // Timeout (in seconds).
} otBackboneRouterMulticastListenerInfo;
/**
* This function gets the next Multicast Listener info (using an iterator).
*
* Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
* Only used for test and certification.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[inout] aIterator A pointer to the iterator. On success the iterator will be updated to point to next
* Multicast Listener. To get the first entry the iterator should be set to
* OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.
* @param[out] aListenerInfo A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next
* Multicast Listener is placed (on success).
*
* @retval OT_ERROR_NONE Successfully found the next Multicast Listener info (@p aListenerInfo was successfully
* updated).
* @retval OT_ERROR_NOT_FOUND No subsequent Multicast Listener info was found.
*
* @sa otBackboneRouterMulticastListenerClear
* @sa otBackboneRouterMulticastListenerAdd
*
*/
otError otBackboneRouterMulticastListenerGetNext(otInstance * aInstance,
otBackboneRouterMulticastListenerIterator *aIterator,
otBackboneRouterMulticastListenerInfo * aListenerInfo);
/**
* @}
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (24)
#define OPENTHREAD_API_VERSION (25)
/**
* @addtogroup api-instance
+19 -5
View File
@@ -138,16 +138,30 @@ do_clean()
rm -rfv "${OT_BUILDDIR}" || sudo rm -rfv "${OT_BUILDDIR}"
}
do_unit()
do_unit_version()
{
local builddir="${OT_BUILDDIR}/cmake/openthread-simulation-${THREAD_VERSION}"
local version=$1
local builddir="${OT_BUILDDIR}/cmake/openthread-simulation-${version}"
if [[ ! -d ${builddir} ]]; then
echo "Cannot find build directory!"
echo "Cannot find build directory: ${builddir}"
exit 1
fi
cd "${builddir}"
ninja test
(
cd "${builddir}"
ninja test
)
}
do_unit()
{
do_unit_version "${THREAD_VERSION}"
if [[ ${THREAD_VERSION} == "1.2" ]]; then
do_unit_version "1.2-bbr"
do_unit_version "1.1"
fi
}
do_cert()
+43
View File
@@ -142,6 +142,49 @@ known status value:
Done
```
### bbr mgmt mlr listener
Show the Multicast Listeners.
Only for testing/reference Backbone Router device.
```bash
> bbr mgmt mlr listener
ff04:0:0:0:0:0:0:abcd 3534000
ff04:0:0:0:0:0:0:eeee 3537610
Done
```
### bbr mgmt mlr listener add \<ipaddr\> \[\<timeout\>\]
Add a Multicast Listener with a given Ip6 multicast address and timeout (in seconds).
Only for testing/reference Backbone Router device.
```bash
> bbr mgmt mlr listener add ff04::1
Done
> bbr mgmt mlr listener add ff04::2 300
Done
> bbr mgmt mlr listener
ff04:0:0:0:0:0:0:2 261
ff04:0:0:0:0:0:0:1 3522
Done
```
### bbr mgmt mlr listener clear
Removes all the Multicast Listeners.
Only for testing/reference Backbone Router device.
```bash
> bbr mgmt mlr listener clear
Done
> bbr mgmt mlr listener
Done
```
### bbr state
Show local Backbone state ([`Disabled`,`Primary`, `Secondary`]) for Thread 1.2 FTD.
+68 -2
View File
@@ -527,13 +527,15 @@ void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
{
unsigned long value;
VerifyOrExit((aArgsLength == 3 || aArgsLength == 4), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(aArgsLength >= 2, error = OT_ERROR_INVALID_COMMAND);
if (strcmp(aArgs[1], "dua") == 0)
{
otIp6InterfaceIdentifier *mlIid = nullptr;
otIp6InterfaceIdentifier iid;
VerifyOrExit((aArgsLength == 3 || aArgsLength == 4), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseUnsignedLong(aArgs[2], value));
if (aArgsLength == 4)
@@ -546,6 +548,11 @@ void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
otBackboneRouterConfigNextDuaRegistrationResponse(mInstance, mlIid, static_cast<uint8_t>(value));
ExitNow();
}
else if (strcmp(aArgs[1], "mlr") == 0)
{
error = ProcessBackboneRouterMgmtMlr(aArgsLength - 2, aArgs + 2);
ExitNow();
}
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
SuccessOrExit(error = ProcessBackboneRouterLocal(aArgsLength, aArgs));
@@ -553,11 +560,70 @@ void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
exit:
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
AppendResult(error);
}
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otError Interpreter::ProcessBackboneRouterMgmtMlr(uint8_t aArgsLength, char **aArgs)
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_INVALID_COMMAND;
VerifyOrExit(aArgsLength >= 1, OT_NOOP);
if (!strcmp(aArgs[0], "listener"))
{
if (aArgsLength == 1)
{
PrintMulticastListenersTable();
error = OT_ERROR_NONE;
}
else if (!strcmp(aArgs[1], "clear"))
{
otBackboneRouterMulticastListenerClear(mInstance);
error = OT_ERROR_NONE;
}
else if (!strcmp(aArgs[1], "add"))
{
struct otIp6Address address;
unsigned long value;
uint32_t timeout = 0;
VerifyOrExit(aArgsLength == 3 || aArgsLength == 4, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[2], &address));
if (aArgsLength == 4)
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[3], value));
timeout = static_cast<uint32_t>(value);
}
error = otBackboneRouterMulticastListenerAdd(mInstance, &address, timeout);
}
}
exit:
return error;
}
void Interpreter::PrintMulticastListenersTable(void)
{
otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
otBackboneRouterMulticastListenerInfo listenerInfo;
while (otBackboneRouterMulticastListenerGetNext(mInstance, &iter, &listenerInfo) == OT_ERROR_NONE)
{
OutputIp6Address(listenerInfo.mAddress);
OutputFormat(" %u\r\n", listenerInfo.mTimeout);
}
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otError Interpreter::ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
+4
View File
@@ -275,6 +275,10 @@ private:
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
otError ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[]);
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otError ProcessBackboneRouterMgmtMlr(uint8_t aArgsLength, char **aArgs);
void PrintMulticastListenersTable(void);
#endif
#endif
void ProcessDomainName(uint8_t aArgsLength, char *aArgs[]);
+2
View File
@@ -325,6 +325,8 @@ openthread_core_files = [
"backbone_router/bbr_local.hpp",
"backbone_router/bbr_manager.cpp",
"backbone_router/bbr_manager.hpp",
"backbone_router/multicast_listeners_table.cpp",
"backbone_router/multicast_listeners_table.hpp",
"coap/coap.cpp",
"coap/coap.hpp",
"coap/coap_message.cpp",
+1
View File
@@ -105,6 +105,7 @@ set(COMMON_SOURCES
backbone_router/bbr_leader.cpp
backbone_router/bbr_local.cpp
backbone_router/bbr_manager.cpp
backbone_router/multicast_listeners_table.cpp
coap/coap.cpp
coap/coap_message.cpp
coap/coap_secure.cpp
+306 -304
View File
@@ -107,152 +107,153 @@ libopenthread_mtd_a_CPPFLAGS = \
# unit"
#
SOURCES_COMMON = \
api/backbone_router_api.cpp \
api/backbone_router_ftd_api.cpp \
api/border_router_api.cpp \
api/channel_manager_api.cpp \
api/channel_monitor_api.cpp \
api/child_supervision_api.cpp \
api/coap_api.cpp \
api/coap_secure_api.cpp \
api/commissioner_api.cpp \
api/crypto_api.cpp \
api/dataset_api.cpp \
api/dataset_ftd_api.cpp \
api/diags_api.cpp \
api/dns_api.cpp \
api/entropy_api.cpp \
api/heap_api.cpp \
api/icmp6_api.cpp \
api/instance_api.cpp \
api/ip6_api.cpp \
api/jam_detection_api.cpp \
api/joiner_api.cpp \
api/link_api.cpp \
api/link_raw_api.cpp \
api/logging_api.cpp \
api/message_api.cpp \
api/netdata_api.cpp \
api/netdiag_api.cpp \
api/network_time_api.cpp \
api/random_crypto_api.cpp \
api/random_noncrypto_api.cpp \
api/server_api.cpp \
api/sntp_api.cpp \
api/tasklet_api.cpp \
api/thread_api.cpp \
api/thread_ftd_api.cpp \
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 \
common/crc16.cpp \
common/instance.cpp \
common/logging.cpp \
common/message.cpp \
common/notifier.cpp \
common/random_manager.cpp \
common/settings.cpp \
common/string.cpp \
common/tasklet.cpp \
common/time_ticker.cpp \
common/timer.cpp \
common/tlvs.cpp \
common/trickle_timer.cpp \
crypto/aes_ccm.cpp \
crypto/aes_ecb.cpp \
crypto/ecdsa.cpp \
crypto/hmac_sha256.cpp \
crypto/mbedtls.cpp \
crypto/pbkdf2_cmac.cpp \
crypto/sha256.cpp \
diags/factory_diags.cpp \
mac/channel_mask.cpp \
mac/data_poll_handler.cpp \
mac/data_poll_sender.cpp \
mac/link_raw.cpp \
mac/mac.cpp \
mac/mac_filter.cpp \
mac/mac_frame.cpp \
mac/mac_types.cpp \
mac/sub_mac.cpp \
mac/sub_mac_callbacks.cpp \
meshcop/announce_begin_client.cpp \
meshcop/border_agent.cpp \
meshcop/commissioner.cpp \
meshcop/dataset.cpp \
meshcop/dataset_local.cpp \
meshcop/dataset_manager.cpp \
meshcop/dataset_manager_ftd.cpp \
meshcop/dtls.cpp \
meshcop/energy_scan_client.cpp \
meshcop/joiner.cpp \
meshcop/joiner_router.cpp \
meshcop/meshcop.cpp \
meshcop/meshcop_leader.cpp \
meshcop/meshcop_tlvs.cpp \
meshcop/panid_query_client.cpp \
meshcop/timestamp.cpp \
net/dhcp6_client.cpp \
net/dhcp6_server.cpp \
net/dns_client.cpp \
net/icmp6.cpp \
net/ip6.cpp \
net/ip6_address.cpp \
net/ip6_filter.cpp \
net/ip6_headers.cpp \
net/ip6_mpl.cpp \
net/netif.cpp \
net/sntp_client.cpp \
net/udp6.cpp \
radio/radio.cpp \
radio/radio_callbacks.cpp \
radio/radio_platform.cpp \
thread/address_resolver.cpp \
thread/announce_begin_server.cpp \
thread/announce_sender.cpp \
thread/child_table.cpp \
thread/csl_tx_scheduler.cpp \
thread/discover_scanner.cpp \
thread/dua_manager.cpp \
thread/energy_scan_server.cpp \
thread/indirect_sender.cpp \
thread/key_manager.cpp \
thread/link_quality.cpp \
thread/lowpan.cpp \
thread/mesh_forwarder.cpp \
thread/mesh_forwarder_ftd.cpp \
thread/mesh_forwarder_mtd.cpp \
thread/mle.cpp \
thread/mle_router.cpp \
thread/mle_types.cpp \
thread/mlr_manager.cpp \
thread/neighbor_table.cpp \
thread/network_data.cpp \
thread/network_data_leader.cpp \
thread/network_data_leader_ftd.cpp \
thread/network_data_local.cpp \
thread/network_data_notifier.cpp \
thread/network_diagnostic.cpp \
thread/panid_query_server.cpp \
thread/router_table.cpp \
thread/src_match_controller.cpp \
thread/thread_netif.cpp \
thread/time_sync_service.cpp \
thread/topology.cpp \
utils/channel_manager.cpp \
utils/channel_monitor.cpp \
utils/child_supervision.cpp \
utils/flash.cpp \
utils/heap.cpp \
utils/jam_detector.cpp \
utils/otns.cpp \
utils/parse_cmdline.cpp \
utils/slaac_address.cpp \
SOURCES_COMMON = \
api/backbone_router_api.cpp \
api/backbone_router_ftd_api.cpp \
api/border_router_api.cpp \
api/channel_manager_api.cpp \
api/channel_monitor_api.cpp \
api/child_supervision_api.cpp \
api/coap_api.cpp \
api/coap_secure_api.cpp \
api/commissioner_api.cpp \
api/crypto_api.cpp \
api/dataset_api.cpp \
api/dataset_ftd_api.cpp \
api/diags_api.cpp \
api/dns_api.cpp \
api/entropy_api.cpp \
api/heap_api.cpp \
api/icmp6_api.cpp \
api/instance_api.cpp \
api/ip6_api.cpp \
api/jam_detection_api.cpp \
api/joiner_api.cpp \
api/link_api.cpp \
api/link_raw_api.cpp \
api/logging_api.cpp \
api/message_api.cpp \
api/netdata_api.cpp \
api/netdiag_api.cpp \
api/network_time_api.cpp \
api/random_crypto_api.cpp \
api/random_noncrypto_api.cpp \
api/server_api.cpp \
api/sntp_api.cpp \
api/tasklet_api.cpp \
api/thread_api.cpp \
api/thread_ftd_api.cpp \
api/udp_api.cpp \
backbone_router/bbr_leader.cpp \
backbone_router/bbr_local.cpp \
backbone_router/bbr_manager.cpp \
backbone_router/multicast_listeners_table.cpp \
coap/coap.cpp \
coap/coap_message.cpp \
coap/coap_secure.cpp \
common/crc16.cpp \
common/instance.cpp \
common/logging.cpp \
common/message.cpp \
common/notifier.cpp \
common/random_manager.cpp \
common/settings.cpp \
common/string.cpp \
common/tasklet.cpp \
common/time_ticker.cpp \
common/timer.cpp \
common/tlvs.cpp \
common/trickle_timer.cpp \
crypto/aes_ccm.cpp \
crypto/aes_ecb.cpp \
crypto/ecdsa.cpp \
crypto/hmac_sha256.cpp \
crypto/mbedtls.cpp \
crypto/pbkdf2_cmac.cpp \
crypto/sha256.cpp \
diags/factory_diags.cpp \
mac/channel_mask.cpp \
mac/data_poll_handler.cpp \
mac/data_poll_sender.cpp \
mac/link_raw.cpp \
mac/mac.cpp \
mac/mac_filter.cpp \
mac/mac_frame.cpp \
mac/mac_types.cpp \
mac/sub_mac.cpp \
mac/sub_mac_callbacks.cpp \
meshcop/announce_begin_client.cpp \
meshcop/border_agent.cpp \
meshcop/commissioner.cpp \
meshcop/dataset.cpp \
meshcop/dataset_local.cpp \
meshcop/dataset_manager.cpp \
meshcop/dataset_manager_ftd.cpp \
meshcop/dtls.cpp \
meshcop/energy_scan_client.cpp \
meshcop/joiner.cpp \
meshcop/joiner_router.cpp \
meshcop/meshcop.cpp \
meshcop/meshcop_leader.cpp \
meshcop/meshcop_tlvs.cpp \
meshcop/panid_query_client.cpp \
meshcop/timestamp.cpp \
net/dhcp6_client.cpp \
net/dhcp6_server.cpp \
net/dns_client.cpp \
net/icmp6.cpp \
net/ip6.cpp \
net/ip6_address.cpp \
net/ip6_filter.cpp \
net/ip6_headers.cpp \
net/ip6_mpl.cpp \
net/netif.cpp \
net/sntp_client.cpp \
net/udp6.cpp \
radio/radio.cpp \
radio/radio_callbacks.cpp \
radio/radio_platform.cpp \
thread/address_resolver.cpp \
thread/announce_begin_server.cpp \
thread/announce_sender.cpp \
thread/child_table.cpp \
thread/csl_tx_scheduler.cpp \
thread/discover_scanner.cpp \
thread/dua_manager.cpp \
thread/energy_scan_server.cpp \
thread/indirect_sender.cpp \
thread/key_manager.cpp \
thread/link_quality.cpp \
thread/lowpan.cpp \
thread/mesh_forwarder.cpp \
thread/mesh_forwarder_ftd.cpp \
thread/mesh_forwarder_mtd.cpp \
thread/mle.cpp \
thread/mle_router.cpp \
thread/mle_types.cpp \
thread/mlr_manager.cpp \
thread/neighbor_table.cpp \
thread/network_data.cpp \
thread/network_data_leader.cpp \
thread/network_data_leader_ftd.cpp \
thread/network_data_local.cpp \
thread/network_data_notifier.cpp \
thread/network_diagnostic.cpp \
thread/panid_query_server.cpp \
thread/router_table.cpp \
thread/src_match_controller.cpp \
thread/thread_netif.cpp \
thread/time_sync_service.cpp \
thread/topology.cpp \
utils/channel_manager.cpp \
utils/channel_monitor.cpp \
utils/child_supervision.cpp \
utils/flash.cpp \
utils/heap.cpp \
utils/jam_detector.cpp \
utils/otns.cpp \
utils/parse_cmdline.cpp \
utils/slaac_address.cpp \
$(NULL)
EXTRA_DIST = \
@@ -318,164 +319,165 @@ nodist_libopenthread_radio_a_SOURCES = \
endif # OPENTHREAD_ENABLE_VENDOR_EXTENSION
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 \
common/bit_vector.hpp \
common/clearable.hpp \
common/code_utils.hpp \
common/crc16.hpp \
common/debug.hpp \
common/encoding.hpp \
common/equatable.hpp \
common/extension.hpp \
common/instance.hpp \
common/linked_list.hpp \
common/locator.hpp \
common/locator-getters.hpp \
common/logging.hpp \
common/message.hpp \
common/new.hpp \
common/non_copyable.hpp \
common/notifier.hpp \
common/pool.hpp \
common/random.hpp \
common/random_manager.hpp \
common/settings.hpp \
common/string.hpp \
common/tasklet.hpp \
common/time.hpp \
common/time_ticker.hpp \
common/timer.hpp \
common/tlvs.hpp \
common/trickle_timer.hpp \
config/announce_sender.h \
config/backbone_router.h \
config/border_router.h \
config/channel_manager.h \
config/channel_monitor.h \
config/child_supervision.h \
config/coap.h \
config/commissioner.h \
config/dhcp6_client.h \
config/dhcp6_server.h \
config/diag.h \
config/dns_client.h \
config/ip6.h \
config/joiner.h \
config/link_quality.h \
config/link_raw.h \
config/logging.h \
config/mac.h \
config/mle.h \
config/openthread-core-config-check.h \
config/openthread-core-default-config.h \
config/parent_search.h \
config/platform.h \
config/sntp_client.h \
config/time_sync.h \
config/tmf.h \
crypto/aes_ccm.hpp \
crypto/aes_ecb.hpp \
crypto/ecdsa.hpp \
crypto/hmac_sha256.hpp \
crypto/mbedtls.hpp \
crypto/pbkdf2_cmac.h \
crypto/sha256.hpp \
diags/factory_diags.hpp \
mac/channel_mask.hpp \
mac/data_poll_handler.hpp \
mac/data_poll_sender.hpp \
mac/link_raw.hpp \
mac/mac.hpp \
mac/mac_filter.hpp \
mac/mac_frame.hpp \
mac/mac_types.hpp \
mac/sub_mac.hpp \
meshcop/announce_begin_client.hpp \
meshcop/border_agent.hpp \
meshcop/commissioner.hpp \
meshcop/dataset.hpp \
meshcop/dataset_local.hpp \
meshcop/dataset_manager.hpp \
meshcop/dtls.hpp \
meshcop/energy_scan_client.hpp \
meshcop/joiner.hpp \
meshcop/joiner_router.hpp \
meshcop/meshcop.hpp \
meshcop/meshcop_leader.hpp \
meshcop/meshcop_tlvs.hpp \
meshcop/panid_query_client.hpp \
meshcop/timestamp.hpp \
net/dhcp6.hpp \
net/dhcp6_client.hpp \
net/dhcp6_server.hpp \
net/dns_client.hpp \
net/dns_headers.hpp \
net/icmp6.hpp \
net/ip6.hpp \
net/ip6_address.hpp \
net/ip6_filter.hpp \
net/ip6_headers.hpp \
net/ip6_mpl.hpp \
net/netif.hpp \
net/sntp_client.hpp \
net/socket.hpp \
net/tcp.hpp \
net/udp6.hpp \
radio/radio.hpp \
thread/address_resolver.hpp \
thread/announce_begin_server.hpp \
thread/announce_sender.hpp \
thread/child_mask.hpp \
thread/child_table.hpp \
thread/csl_tx_scheduler.hpp \
thread/discover_scanner.hpp \
thread/dua_manager.hpp \
thread/energy_scan_server.hpp \
thread/indirect_sender.hpp \
thread/indirect_sender_frame_context.hpp \
thread/key_manager.hpp \
thread/link_quality.hpp \
thread/lowpan.hpp \
thread/mesh_forwarder.hpp \
thread/mle.hpp \
thread/mle_router.hpp \
thread/mle_tlvs.hpp \
thread/mle_types.hpp \
thread/mlr_manager.hpp \
thread/mlr_types.hpp \
thread/neighbor_table.hpp \
thread/network_data.hpp \
thread/network_data_leader.hpp \
thread/network_data_leader_ftd.hpp \
thread/network_data_local.hpp \
thread/network_data_notifier.hpp \
thread/network_data_tlvs.hpp \
thread/network_diagnostic.hpp \
thread/network_diagnostic_tlvs.hpp \
thread/panid_query_server.hpp \
thread/router_table.hpp \
thread/src_match_controller.hpp \
thread/thread_netif.hpp \
thread/thread_tlvs.hpp \
thread/thread_uri_paths.hpp \
thread/time_sync_service.hpp \
thread/topology.hpp \
utils/channel_manager.hpp \
utils/channel_monitor.hpp \
utils/child_supervision.hpp \
utils/flash.hpp \
utils/heap.hpp \
utils/jam_detector.hpp \
utils/otns.hpp \
utils/parse_cmdline.hpp \
utils/slaac_address.hpp \
HEADERS_COMMON = \
openthread-core-config.h \
backbone_router/bbr_leader.hpp \
backbone_router/bbr_local.hpp \
backbone_router/bbr_manager.hpp \
backbone_router/multicast_listeners_table.hpp \
coap/coap.hpp \
coap/coap_message.hpp \
coap/coap_secure.hpp \
common/bit_vector.hpp \
common/clearable.hpp \
common/code_utils.hpp \
common/crc16.hpp \
common/debug.hpp \
common/encoding.hpp \
common/equatable.hpp \
common/extension.hpp \
common/instance.hpp \
common/linked_list.hpp \
common/locator.hpp \
common/locator-getters.hpp \
common/logging.hpp \
common/message.hpp \
common/new.hpp \
common/non_copyable.hpp \
common/notifier.hpp \
common/pool.hpp \
common/random.hpp \
common/random_manager.hpp \
common/settings.hpp \
common/string.hpp \
common/tasklet.hpp \
common/time.hpp \
common/time_ticker.hpp \
common/timer.hpp \
common/tlvs.hpp \
common/trickle_timer.hpp \
config/announce_sender.h \
config/backbone_router.h \
config/border_router.h \
config/channel_manager.h \
config/channel_monitor.h \
config/child_supervision.h \
config/coap.h \
config/commissioner.h \
config/dhcp6_client.h \
config/dhcp6_server.h \
config/diag.h \
config/dns_client.h \
config/ip6.h \
config/joiner.h \
config/link_quality.h \
config/link_raw.h \
config/logging.h \
config/mac.h \
config/mle.h \
config/openthread-core-config-check.h \
config/openthread-core-default-config.h \
config/parent_search.h \
config/platform.h \
config/sntp_client.h \
config/time_sync.h \
config/tmf.h \
crypto/aes_ccm.hpp \
crypto/aes_ecb.hpp \
crypto/ecdsa.hpp \
crypto/hmac_sha256.hpp \
crypto/mbedtls.hpp \
crypto/pbkdf2_cmac.h \
crypto/sha256.hpp \
diags/factory_diags.hpp \
mac/channel_mask.hpp \
mac/data_poll_handler.hpp \
mac/data_poll_sender.hpp \
mac/link_raw.hpp \
mac/mac.hpp \
mac/mac_filter.hpp \
mac/mac_frame.hpp \
mac/mac_types.hpp \
mac/sub_mac.hpp \
meshcop/announce_begin_client.hpp \
meshcop/border_agent.hpp \
meshcop/commissioner.hpp \
meshcop/dataset.hpp \
meshcop/dataset_local.hpp \
meshcop/dataset_manager.hpp \
meshcop/dtls.hpp \
meshcop/energy_scan_client.hpp \
meshcop/joiner.hpp \
meshcop/joiner_router.hpp \
meshcop/meshcop.hpp \
meshcop/meshcop_leader.hpp \
meshcop/meshcop_tlvs.hpp \
meshcop/panid_query_client.hpp \
meshcop/timestamp.hpp \
net/dhcp6.hpp \
net/dhcp6_client.hpp \
net/dhcp6_server.hpp \
net/dns_client.hpp \
net/dns_headers.hpp \
net/icmp6.hpp \
net/ip6.hpp \
net/ip6_address.hpp \
net/ip6_filter.hpp \
net/ip6_headers.hpp \
net/ip6_mpl.hpp \
net/netif.hpp \
net/sntp_client.hpp \
net/socket.hpp \
net/tcp.hpp \
net/udp6.hpp \
radio/radio.hpp \
thread/address_resolver.hpp \
thread/announce_begin_server.hpp \
thread/announce_sender.hpp \
thread/child_mask.hpp \
thread/child_table.hpp \
thread/csl_tx_scheduler.hpp \
thread/discover_scanner.hpp \
thread/dua_manager.hpp \
thread/energy_scan_server.hpp \
thread/indirect_sender.hpp \
thread/indirect_sender_frame_context.hpp \
thread/key_manager.hpp \
thread/link_quality.hpp \
thread/lowpan.hpp \
thread/mesh_forwarder.hpp \
thread/mle.hpp \
thread/mle_router.hpp \
thread/mle_tlvs.hpp \
thread/mle_types.hpp \
thread/mlr_manager.hpp \
thread/mlr_types.hpp \
thread/neighbor_table.hpp \
thread/network_data.hpp \
thread/network_data_leader.hpp \
thread/network_data_leader_ftd.hpp \
thread/network_data_local.hpp \
thread/network_data_notifier.hpp \
thread/network_data_tlvs.hpp \
thread/network_diagnostic.hpp \
thread/network_diagnostic_tlvs.hpp \
thread/panid_query_server.hpp \
thread/router_table.hpp \
thread/src_match_controller.hpp \
thread/thread_netif.hpp \
thread/thread_tlvs.hpp \
thread/thread_uri_paths.hpp \
thread/time_sync_service.hpp \
thread/topology.hpp \
utils/channel_manager.hpp \
utils/channel_monitor.hpp \
utils/child_supervision.hpp \
utils/flash.hpp \
utils/heap.hpp \
utils/jam_detector.hpp \
utils/otns.hpp \
utils/parse_cmdline.hpp \
utils/slaac_address.hpp \
$(NULL)
noinst_HEADERS = \
+50 -1
View File
@@ -111,6 +111,15 @@ otError otBackboneRouterGetDomainPrefix(otInstance *aInstance, otBorderRouterCon
*static_cast<NetworkData::OnMeshPrefixConfig *>(aConfig));
}
void otBackboneRouterSetMulticastListenerCallback(otInstance * aInstance,
otBackboneRouterMulticastListenerCallback aCallback,
void * aContext)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<BackboneRouter::MulticastListenersTable>().SetCallback(aCallback, aContext);
}
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * aInstance,
const otIp6InterfaceIdentifier *aMlIid,
@@ -121,6 +130,46 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *
instance.Get<BackboneRouter::Manager>().ConfigNextDuaRegistrationResponse(
static_cast<const Ip6::InterfaceIdentifier *>(aMlIid), aStatus);
}
#endif
void otBackboneRouterMulticastListenerClear(otInstance *aInstance)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<BackboneRouter::MulticastListenersTable>().Clear();
}
otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout)
{
Instance &instance = *static_cast<Instance *>(aInstance);
OT_ASSERT(aAddress != nullptr);
if (aTimeout == 0)
{
BackboneRouter::BackboneRouterConfig config;
instance.Get<BackboneRouter::Local>().GetConfig(config);
aTimeout = config.mMlrTimeout;
}
aTimeout =
aTimeout > static_cast<uint32_t>(Mle::kMlrTimeoutMax) ? static_cast<uint32_t>(Mle::kMlrTimeoutMax) : aTimeout;
aTimeout = Time::SecToMsec(aTimeout);
return instance.Get<BackboneRouter::MulticastListenersTable>().Add(static_cast<const Ip6::Address &>(*aAddress),
TimerMilli::GetNow() + aTimeout);
}
otError otBackboneRouterMulticastListenerGetNext(otInstance * aInstance,
otChildIp6AddressIterator * aIterator,
otBackboneRouterMulticastListenerInfo *aListenerInfo)
{
Instance &instance = *static_cast<Instance *>(aInstance);
OT_ASSERT(aIterator != nullptr);
OT_ASSERT(aListenerInfo != nullptr);
return instance.Get<BackboneRouter::MulticastListenersTable>().GetNext(*aIterator, *aListenerInfo);
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+18
View File
@@ -180,6 +180,7 @@ void Leader::UpdateBackboneRouterPrimary(void)
{
BackboneRouterConfig config;
State state;
uint32_t origMlrTimeout;
IgnoreError(Get<NetworkData::Leader>().GetBackboneRouterPrimary(config));
@@ -217,6 +218,23 @@ void Leader::UpdateBackboneRouterPrimary(void)
state = kStateUnchanged;
}
// Restrain the range of MLR timeout to be always valid
if (config.mServer16 != Mac::kShortAddrInvalid)
{
origMlrTimeout = config.mMlrTimeout;
config.mMlrTimeout = config.mMlrTimeout < static_cast<uint32_t>(Mle::kMlrTimeoutMin)
? static_cast<uint32_t>(Mle::kMlrTimeoutMin)
: config.mMlrTimeout;
config.mMlrTimeout = config.mMlrTimeout > static_cast<uint32_t>(Mle::kMlrTimeoutMax)
? static_cast<uint32_t>(Mle::kMlrTimeoutMax)
: config.mMlrTimeout;
if (config.mMlrTimeout != origMlrTimeout)
{
otLogNoteBbr("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout);
}
}
mConfig = config;
LogBackboneRouterPrimary(state, mConfig);
+2 -1
View File
@@ -135,7 +135,8 @@ otError Local::SetConfig(const BackboneRouterConfig &aConfig)
otError error = OT_ERROR_NONE;
bool update = false;
VerifyOrExit(aConfig.mMlrTimeout >= Mle::kMlrTimeoutMin, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(aConfig.mMlrTimeout >= Mle::kMlrTimeoutMin && aConfig.mMlrTimeout <= Mle::kMlrTimeoutMax,
error = OT_ERROR_INVALID_ARGS);
// Validate configuration according to Thread 1.2.1 Specification 5.21.3.3:
// "The Reregistration Delay in seconds MUST be lower than (0.5 * MLR Timeout). It MUST be at least 1."
VerifyOrExit(aConfig.mReregistrationDelay >= 1, error = OT_ERROR_INVALID_ARGS);
+83 -4
View File
@@ -53,6 +53,8 @@ Manager::Manager(Instance &aInstance)
: InstanceLocator(aInstance)
, mMulticastListenerRegistration(OT_URI_PATH_MLR, Manager::HandleMulticastListenerRegistration, this)
, mDuaRegistration(OT_URI_PATH_DUA_REGISTRATION_REQUEST, Manager::HandleDuaRegistration, this)
, mMulticastListenersTable(aInstance)
, mTimer(aInstance, Manager::HandleTimer, this)
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
, mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess)
, mDuaResponseIsSpecified(false)
@@ -68,20 +70,39 @@ void Manager::HandleNotifierEvents(Events aEvents)
{
Get<Coap::Coap>().RemoveResource(mMulticastListenerRegistration);
Get<Coap::Coap>().RemoveResource(mDuaRegistration);
mTimer.Stop();
mMulticastListenersTable.Clear();
}
else
{
Get<Coap::Coap>().AddResource(mMulticastListenerRegistration);
Get<Coap::Coap>().AddResource(mDuaRegistration);
if (!mTimer.IsRunning())
{
mTimer.Start(kTimerInterval);
}
}
}
}
void Manager::HandleTimer(void)
{
mMulticastListenersTable.Expire();
mTimer.Start(kTimerInterval);
}
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;
uint16_t addressesOffset, addressesLength;
Ip6::Address address;
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
uint8_t failedAddressNum = 0;
TimeMilli expireTime;
BackboneRouterConfig config;
VerifyOrExit(aMessage.IsConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_PARSE);
@@ -90,18 +111,63 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage,
// 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
VerifyOrExit(ThreadTlv::FindTlvValueOffset(aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset,
addressesLength) == OT_ERROR_NONE,
error = OT_ERROR_PARSE);
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure);
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax,
status = ThreadStatusTlv::kMlrGeneralFailure);
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(config.mMlrTimeout);
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
{
bool failed = true;
IgnoreReturnValue(aMessage.Read(addressesOffset + offset, sizeof(Ip6::Address), &address));
switch (mMulticastListenersTable.Add(address, expireTime))
{
case OT_ERROR_NONE:
failed = false;
break;
case OT_ERROR_INVALID_ARGS:
if (status == ThreadStatusTlv::kMlrSuccess)
{
status = ThreadStatusTlv::kMlrInvalid;
}
break;
case OT_ERROR_NO_BUFS:
if (status == ThreadStatusTlv::kMlrSuccess)
{
status = ThreadStatusTlv::kMlrNoResources;
}
break;
default:
OT_ASSERT(false);
}
if (failed)
{
failedAddresses[failedAddressNum++] = address;
}
}
exit:
if (error == OT_ERROR_NONE)
{
SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status);
SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status, failedAddresses, failedAddressNum);
// TODO: (MLR) send BMLR.req
}
}
void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
const Ip6::MessageInfo & aMessageInfo,
ThreadStatusTlv::MlrStatus aStatus)
ThreadStatusTlv::MlrStatus aStatus,
Ip6::Address * aFailedAddresses,
uint8_t aFailedAddressNum)
{
otError error = OT_ERROR_NONE;
Coap::Message *message = nullptr;
@@ -113,7 +179,20 @@ void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message &
SuccessOrExit(Tlv::AppendUint8Tlv(*message, ThreadTlv::kStatus, aStatus));
// TODO: (MLR) append the failed addresses
if (aFailedAddressNum > 0)
{
IPv6AddressesTlv addressesTlv;
addressesTlv.Init();
addressesTlv.SetLength(sizeof(Ip6::Address) * aFailedAddressNum);
SuccessOrExit(error = message->Append(&addressesTlv, sizeof(addressesTlv)));
for (uint8_t i = 0; i < aFailedAddressNum; i++)
{
SuccessOrExit(error = message->Append(aFailedAddresses + i, sizeof(Ip6::Address)));
}
}
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo));
exit:
+24 -1
View File
@@ -41,6 +41,7 @@
#include <openthread/backbone_router_ftd.h>
#include "backbone_router/bbr_leader.hpp"
#include "backbone_router/multicast_listeners_table.hpp"
#include "common/locator.hpp"
#include "net/netif.hpp"
#include "thread/network_data.hpp"
@@ -81,7 +82,20 @@ public:
void ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus);
#endif
/**
* This method gets the Multicast Listeners Table.
*
* @returns The Multicast Listeners Table.
*
*/
MulticastListenersTable &GetMulticastListenersTable(void) { return mMulticastListenersTable; }
private:
enum
{
kTimerInterval = 1000,
};
static void HandleMulticastListenerRegistration(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
@@ -92,7 +106,9 @@ private:
void HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
const Ip6::MessageInfo & aMessageInfo,
ThreadStatusTlv::MlrStatus aStatus);
ThreadStatusTlv::MlrStatus aStatus,
Ip6::Address * aFailedAddresses,
uint8_t aFailedAddressNum);
static void HandleDuaRegistration(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
@@ -104,11 +120,18 @@ private:
const Ip6::MessageInfo & aMessageInfo,
const Ip6::Address & aTarget,
ThreadStatusTlv::DuaStatus aStatus);
void HandleNotifierEvents(Events aEvents);
static void HandleTimer(Timer &aTimer) { aTimer.GetOwner<Manager>().HandleTimer(); }
void HandleTimer(void);
Coap::Resource mMulticastListenerRegistration;
Coap::Resource mDuaRegistration;
MulticastListenersTable mMulticastListenersTable;
TimerMilli mTimer;
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Ip6::InterfaceIdentifier mDuaResponseTargetMlIid;
ThreadStatusTlv::DuaStatus mDuaResponseStatus;
@@ -0,0 +1,318 @@
/*
* 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 the Multicast Listeners Table.
*/
#include "multicast_listeners_table.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 {
otError MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTime)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aAddress.IsMulticastLargerThanRealmLocal(), error = OT_ERROR_INVALID_ARGS);
for (uint16_t i = 0; i < mNumValidListeners; i++)
{
Listener &listener = mListeners[i];
if (listener.GetAddress() == aAddress)
{
listener.SetExpireTime(aExpireTime);
FixHeap(i);
ExitNow();
}
}
VerifyOrExit(mNumValidListeners < OT_ARRAY_LENGTH(mListeners), error = OT_ERROR_NO_BUFS);
mListeners[mNumValidListeners].SetAddress(aAddress);
mListeners[mNumValidListeners].SetExpireTime(aExpireTime);
mNumValidListeners++;
FixHeap(mNumValidListeners - 1);
if (mCallback != nullptr)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &aAddress);
}
exit:
LogMulticastListenersTable("Add", aAddress, aExpireTime, error);
CheckInvariants();
return error;
}
void MulticastListenersTable::Remove(const Ip6::Address &aAddress)
{
otError error = OT_ERROR_NOT_FOUND;
for (uint16_t i = 0; i < mNumValidListeners; i++)
{
Listener &listener = mListeners[i];
if (listener.GetAddress() == aAddress)
{
mNumValidListeners--;
if (i != mNumValidListeners)
{
listener = mListeners[mNumValidListeners];
FixHeap(i);
}
if (mCallback != nullptr)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &aAddress);
}
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
LogMulticastListenersTable("Remove", aAddress, TimeMilli(0), error);
CheckInvariants();
}
void MulticastListenersTable::Expire(void)
{
TimeMilli now = TimerMilli::GetNow();
Ip6::Address address;
while (mNumValidListeners > 0 && now >= mListeners[0].GetExpireTime())
{
LogMulticastListenersTable("Expire", mListeners[0].GetAddress(), mListeners[0].GetExpireTime(), OT_ERROR_NONE);
address = mListeners[0].GetAddress();
mNumValidListeners--;
if (mNumValidListeners > 0)
{
mListeners[0] = mListeners[mNumValidListeners];
FixHeap(0);
}
if (mCallback != nullptr)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &address);
}
}
CheckInvariants();
}
void MulticastListenersTable::LogMulticastListenersTable(const char * aAction,
const Ip6::Address &aAddress,
TimeMilli aExpireTime,
otError aError)
{
OT_UNUSED_VARIABLE(aAction);
OT_UNUSED_VARIABLE(aAddress);
OT_UNUSED_VARIABLE(aExpireTime);
OT_UNUSED_VARIABLE(aError);
otLogDebgBbr("MulticastListenersTable: %s %s expire %u: %s", aAction, aAddress.ToString().AsCString(),
aExpireTime.GetValue(), otThreadErrorToString(aError));
}
void MulticastListenersTable::FixHeap(uint16_t aIndex)
{
if (!SiftHeapElemDown(aIndex))
{
SiftHeapElemUp(aIndex);
}
}
void MulticastListenersTable::CheckInvariants(void) const
{
#if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
for (uint16_t child = 1; child < mNumValidListeners; ++child)
{
uint16_t parent = (child - 1) / 2;
OT_ASSERT(!(mListeners[child] < mListeners[parent]));
}
#endif
}
bool MulticastListenersTable::SiftHeapElemDown(uint16_t aIndex)
{
uint16_t index = aIndex;
Listener saveElem;
OT_ASSERT(aIndex < mNumValidListeners);
saveElem = mListeners[aIndex];
for (;;)
{
uint16_t child = 2 * index + 1;
if (child >= mNumValidListeners || child <= index) // child <= index after int overflow
{
break;
}
if (child + 1 < mNumValidListeners && mListeners[child + 1] < mListeners[child])
{
child++;
}
if (!(mListeners[child] < saveElem))
{
break;
}
mListeners[index] = mListeners[child];
index = child;
}
if (index > aIndex)
{
mListeners[index] = saveElem;
}
return index > aIndex;
}
void MulticastListenersTable::SiftHeapElemUp(uint16_t aIndex)
{
uint16_t index = aIndex;
Listener saveElem;
OT_ASSERT(aIndex < mNumValidListeners);
saveElem = mListeners[aIndex];
for (;;)
{
uint16_t parent = (index - 1) / 2;
if (index == 0 || !(saveElem < mListeners[parent]))
{
break;
}
mListeners[index] = mListeners[parent];
index = parent;
}
if (index < aIndex)
{
mListeners[index] = saveElem;
}
}
MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::begin(void)
{
return &Get<MulticastListenersTable>().mListeners[0];
}
MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::end(void)
{
return &Get<MulticastListenersTable>().mListeners[Get<MulticastListenersTable>().mNumValidListeners];
}
void MulticastListenersTable::Clear(void)
{
if (mCallback != nullptr)
{
for (uint16_t i = 0; i < mNumValidListeners; i++)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &mListeners[i].GetAddress());
}
}
mNumValidListeners = 0;
CheckInvariants();
}
void MulticastListenersTable::SetCallback(otBackboneRouterMulticastListenerCallback aCallback, void *aContext)
{
mCallback = aCallback;
mCallbackContext = aContext;
if (mCallback != nullptr)
{
for (uint16_t i = 0; i < mNumValidListeners; i++)
{
mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &mListeners[i].GetAddress());
}
}
}
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otError MulticastListenersTable::GetNext(otBackboneRouterMulticastListenerIterator &aIterator,
otBackboneRouterMulticastListenerInfo & aListenerInfo)
{
otError error = OT_ERROR_NONE;
TimeMilli now;
VerifyOrExit(aIterator < mNumValidListeners, error = OT_ERROR_NOT_FOUND);
now = TimerMilli::GetNow();
aListenerInfo.mAddress = mListeners[aIterator].mAddress;
aListenerInfo.mTimeout =
Time::MsecToSec(mListeners[aIterator].mExpireTime > now ? mListeners[aIterator].mExpireTime - now : 0);
aIterator++;
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
} // namespace BackboneRouter
} // namespace ot
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
@@ -0,0 +1,241 @@
/*
* 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 Listeners Table.
*/
#ifndef MULTICAST_LISTENERS_TABLE_HPP
#define MULTICAST_LISTENERS_TABLE_HPP
#include "openthread-core-config.h"
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <openthread/backbone_router_ftd.h>
#include "common/notifier.hpp"
#include "common/time.hpp"
#include "net/ip6_address.hpp"
namespace ot {
namespace BackboneRouter {
/**
* This class implements the definitions for Multicast Listeners Table.
*
*/
class MulticastListenersTable : public InstanceLocator, private NonCopyable
{
class IteratorBuilder;
public:
/**
* This class represents a Multicast Listener entry.
*
*/
class Listener : public Clearable<Listener>
{
friend class MulticastListenersTable;
public:
/**
* This constructor initializes the `Listener` object.
*
*/
Listener(void) { Clear(); }
/**
* This method returns the Multicast Listener address.
*
* @returns The Multicast Listener address.
*
*/
const Ip6::Address &GetAddress(void) const { return mAddress; }
/**
* This method returns the expire time of the Multicast Listener.
*
* @returns The Multicast Listener expire time.
*
*/
const TimeMilli GetExpireTime(void) const { return mExpireTime; }
private:
void SetAddress(const Ip6::Address &aAddress) { mAddress = aAddress; }
void SetExpireTime(TimeMilli aExpireTime) { mExpireTime = aExpireTime; }
bool operator<(const Listener &aOther) const { return GetExpireTime() < aOther.GetExpireTime(); }
Ip6::Address mAddress;
TimeMilli mExpireTime;
};
/**
* This constructor initializes the Multicast Listeners Table.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit MulticastListenersTable(Instance &aInstance)
: InstanceLocator(aInstance)
, mNumValidListeners(0)
, mCallback(nullptr)
, mCallbackContext(nullptr)
{
}
/**
* This method adds a Multicast Listener with given address and expire time.
*
* @param[in] aAddress The Multicast Listener address.
* @param[in] aExpireTime The Multicast Listener expire time.
*
* @retval OT_ERROR_NONE If the Multicast Listener was successfully added.
* @retval OT_ERROR_INVALID_ARGS If the Multicast Listener address was invalid.
* @retval OT_ERROR_NO_BUFS No space available to save the Multicast Listener.
*
*/
otError Add(const Ip6::Address &aAddress, TimeMilli aExpireTime);
/**
* This method removes a given Multicast Listener.
*
* @param[in] aAddress The Multicast Listener address.
*
*/
void Remove(const Ip6::Address &aAddress);
/**
* This method removes expired Multicast Listeners.
*
*/
void Expire(void);
/**
* This method counts the number of valid Multicast Listeners.
*
* @returns The number of valid Multicast Listeners.
*
*/
uint16_t Count(void) const { return mNumValidListeners; }
/**
* This method enables range-based `for` loop iteration over all Multicast Listeners.
*
* This method should be used as follows:
*
* for (MulticastListenersTable::Listener &listener : Get<MulticastListenersTable>().Iterate())
*
* @returns An IteratorBuilder instance.
*
*/
IteratorBuilder Iterate(void) { return IteratorBuilder(GetInstance()); }
/**
* This method removes all the Multicast Listeners.
*
*/
void Clear(void);
/**
* This method sets the Multicast Listener callback.
*
* @param[in] aCallback The callback function.
* @param[in] aContext A user context pointer.
*
*/
void SetCallback(otBackboneRouterMulticastListenerCallback aCallback, void *aContext);
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
/**
* This method gets the next Multicast Listener.
*
* @param[in] aIterator A pointer to the Multicast Listener Iterator.
* @param[out] aListenerInfo A pointer to where the Multicast Listener info is placed.
*
* @retval OT_ERROR_NONE Successfully found the next Multicast Listener info.
* @retval OT_ERROR_NOT_FOUND No subsequent Multicast Listener was found.
*
*/
otError GetNext(otBackboneRouterMulticastListenerIterator &aIterator,
otBackboneRouterMulticastListenerInfo & aListenerInfo);
#endif
private:
enum
{
kMulticastListenersTableSize = OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS,
};
static_assert(
kMulticastListenersTableSize >= 75,
"Thread 1.2 Conformance requires the Multicast Listener Table size to be larger than or equal to 75.");
class IteratorBuilder : InstanceLocator
{
public:
IteratorBuilder(Instance &aInstance)
: InstanceLocator(aInstance)
{
}
Listener *begin(void);
Listener *end(void);
};
void LogMulticastListenersTable(const char * aAction,
const Ip6::Address &aAddress,
TimeMilli aExpireTime,
otError aError);
void FixHeap(uint16_t aIndex);
bool SiftHeapElemDown(uint16_t aIndex);
void SiftHeapElemUp(uint16_t aIndex);
void CheckInvariants(void) const;
Listener mListeners[kMulticastListenersTableSize];
uint16_t mNumValidListeners;
otBackboneRouterMulticastListenerCallback mCallback;
void * mCallbackContext;
};
} // namespace BackboneRouter
/**
* @}
*/
} // namespace ot
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#endif // MULTICAST_LISTENERS_TABLE_HPP
+4 -1
View File
@@ -759,7 +759,10 @@ template <> inline BackboneRouter::Manager &Instance::Get(void)
{
return mThreadNetif.mBackboneRouterManager;
}
template <> inline BackboneRouter::MulticastListenersTable &Instance::Get(void)
{
return mThreadNetif.mBackboneRouterManager.GetMulticastListenersTable();
}
#endif
#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
+15
View File
@@ -45,4 +45,19 @@
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS
*
* This setting configures the maximum number of Multicast Listeners on a Backbone Router.
*
* Note: According to Thread Conformance v1.2.0, a Thread Border Router MUST be able to hold a Multicast Listeners Table
* in memory with at least seventy five (75) entries.
*
* @sa MulticastListenersTable
*
*/
#ifndef OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS
#define OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS 75
#endif
#endif // CONFIG_BACKBONE_ROUTER_H_
+13 -11
View File
@@ -257,22 +257,24 @@ enum
*/
enum
{
kRegistrationDelayDefault = 1200, ///< In seconds.
kMlrTimeoutDefault = 3600, ///< In seconds.
kMlrTimeoutMin = 300, ///< In seconds.
kBackboneRouterRegistrationJitter = 5, ///< In seconds.
kParentAggregateDelay = 5, ///< In seconds.
kNoBufDelay = 5, ///< In seconds.
kImmediateReRegisterDelay = 1, ///< In seconds.
KResponseTimeoutDelay = 30, ///< In seconds.
kDuaDadPeriod = 100, ///< In seconds. Time period after which the address
///< becomes "Preferred" if no duplicate address error.
kRegistrationDelayDefault = 1200, ///< In seconds.
kMlrTimeoutDefault = 3600, ///< In seconds.
kMlrTimeoutMin = 300, ///< In seconds.
kMlrTimeoutMax = 0x7fffffff / 1000, ///< In seconds (about 24 days).
kBackboneRouterRegistrationJitter = 5, ///< In seconds.
kParentAggregateDelay = 5, ///< In seconds.
kNoBufDelay = 5, ///< In seconds.
kImmediateReRegisterDelay = 1, ///< In seconds.
KResponseTimeoutDelay = 30, ///< In seconds.
kDuaDadPeriod = 100, ///< In seconds. Time period after which the address
///< becomes "Preferred" if no duplicate address error.
};
static_assert(kMlrTimeoutDefault >= kMlrTimeoutMin,
static_assert(kMlrTimeoutDefault >= kMlrTimeoutMin && kMlrTimeoutDefault <= kMlrTimeoutMax,
"kMlrTimeoutDefault must be larger than or equal to kMlrTimeoutMin");
static_assert(Mle::kParentAggregateDelay > 1, "kParentAggregateDelay should be larger than 1 second");
static_assert(kMlrTimeoutMax * 1000 > kMlrTimeoutMax, "SecToMsec(kMlrTimeoutMax) will overflow");
/**
* State change of Child's DUA
+175 -37
View File
@@ -96,6 +96,7 @@ void MlrManager::UpdateLocalSubscriptions(void)
}
#endif
CheckInvariants();
ScheduleSend(0);
}
@@ -117,17 +118,6 @@ exit:
return ret;
}
void MlrManager::SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
for (Ip6::ExternalNetifMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == aFromState)
{
addr.SetMlrState(aToState);
}
}
}
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
@@ -183,6 +173,7 @@ void MlrManager::UpdateProxiedSubscriptions(Child & aChild,
exit:
LogMulticastAddresses();
CheckInvariants();
if (aChild.HasAnyMlrToRegisterAddress())
{
@@ -190,19 +181,6 @@ exit:
}
}
void MlrManager::SetChildMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (child.GetAddressMlrState(address) == aFromState)
{
child.SetAddressMlrState(address, aToState);
}
}
}
}
#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
void MlrManager::ScheduleSend(uint16_t aDelay)
@@ -360,6 +338,7 @@ exit:
otLogInfoMlr("Send MLR.req: %s", otThreadErrorToString(error));
LogMulticastAddresses();
CheckInvariants();
}
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
@@ -368,25 +347,40 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message *
{
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
otError error;
mMlrPending = false;
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
otError error = OT_ERROR_NONE;
uint16_t addressesOffset, addressesLength;
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
uint8_t failedAddressNum = 0;
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:
SetMulticastAddressMlrState(kMlrStateRegistering, status == ThreadStatusTlv::MlrStatus::kMlrSuccess
? kMlrStateRegistered
: kMlrStateToRegister);
otLogInfoBbr("Receive MLR.rsp, result=%s, status=%d, error=%s", otThreadErrorToString(aResult), status,
otThreadErrorToString(error));
LogMulticastAddresses();
if (ThreadTlv::FindTlvValueOffset(*aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset, addressesLength) ==
OT_ERROR_NONE)
{
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = OT_ERROR_PARSE);
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax, error = OT_ERROR_PARSE);
if (status == ThreadStatusTlv::MlrStatus::kMlrSuccess)
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
{
IgnoreReturnValue(
aMessage->Read(addressesOffset + offset, sizeof(Ip6::Address), &failedAddresses[failedAddressNum]));
failedAddressNum++;
}
}
VerifyOrExit(failedAddressNum == 0 || status != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = OT_ERROR_PARSE);
exit:
LogMlrResponse(aResult, error, status, failedAddresses, failedAddressNum);
FinishMulticastListenerRegistration(error == OT_ERROR_NONE && status == ThreadStatusTlv::MlrStatus::kMlrSuccess,
failedAddresses, failedAddressNum);
if (error == OT_ERROR_NONE && status == ThreadStatusTlv::MlrStatus::kMlrSuccess)
{
// keep sending until all multicast addresses are registered.
ScheduleSend(0);
@@ -399,7 +393,6 @@ exit:
// 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
@@ -410,6 +403,71 @@ exit:
}
}
void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::ExternalNetifMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == aFromState)
{
addr.SetMlrState(aToState);
}
}
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (child.GetAddressMlrState(address) == aFromState)
{
child.SetAddressMlrState(address, aToState);
}
}
}
#endif
}
void MlrManager::FinishMulticastListenerRegistration(bool aSuccess,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum)
{
OT_ASSERT(mMlrPending);
mMlrPending = false;
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::ExternalNetifMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (addr.GetMlrState() == kMlrStateRegistering)
{
bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, addr.GetAddress());
addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister);
}
}
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
if (child.GetAddressMlrState(address) == kMlrStateRegistering)
{
bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, address);
child.SetAddressMlrState(address, success ? kMlrStateRegistered : kMlrStateToRegister);
}
}
}
#endif
LogMulticastAddresses();
CheckInvariants();
}
void MlrManager::HandleTimeTick(void)
{
if (mSendDelay > 0 && --mSendDelay == 0)
@@ -428,6 +486,7 @@ void MlrManager::HandleTimeTick(void)
void MlrManager::Reregister(void)
{
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
CheckInvariants();
ScheduleSend(0);
@@ -529,6 +588,85 @@ exit:
return;
}
bool MlrManager::AddressListContains(const Ip6::Address *aAddressList,
uint8_t aAddressListSize,
const Ip6::Address &aAddress)
{
bool contains = false;
// An empty address list is treated as if it contains all failed addresses.
VerifyOrExit(aAddressListSize > 0, contains = true);
for (uint8_t i = 0; i < aAddressListSize; i++)
{
if (aAddressList[i] == aAddress)
{
ExitNow(contains = true);
}
}
exit:
return contains;
}
void MlrManager::LogMlrResponse(otError aResult,
otError aError,
uint8_t aStatus,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum)
{
OT_UNUSED_VARIABLE(aResult);
OT_UNUSED_VARIABLE(aError);
OT_UNUSED_VARIABLE(aStatus);
OT_UNUSED_VARIABLE(aFailedAddresses);
OT_UNUSED_VARIABLE(aFailedAddressNum);
#if OPENTHREAD_CONFIG_LOG_BBR
if (aResult == OT_ERROR_NONE && aError == OT_ERROR_NONE && aStatus == ThreadStatusTlv::MlrStatus::kMlrSuccess)
{
otLogInfoBbr("Receive MLR.rsp OK", otThreadErrorToString(aResult));
}
else
{
otLogWarnBbr("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d",
otThreadErrorToString(aResult), otThreadErrorToString(aError), aStatus, aFailedAddressNum);
for (uint8_t i = 0; i < aFailedAddressNum; i++)
{
otLogWarnBbr("MLR failed: %s", aFailedAddresses[i].ToString().AsCString());
}
}
#endif
}
void MlrManager::CheckInvariants(void) const
{
#if OPENTHREAD_EXAMPLES_SIMULATION
uint16_t registeringNum = 0;
OT_ASSERT(!mMlrPending || mSendDelay == 0);
#if OPENTHREAD_CONFIG_MLR_ENABLE
for (Ip6::ExternalNetifMulticastAddress &addr :
Get<ThreadNetif>().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
registeringNum += (addr.GetMlrState() == kMlrStateRegistering);
}
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
{
registeringNum += (child.GetAddressMlrState(address) == kMlrStateRegistering);
}
}
#endif
OT_ASSERT(registeringNum == 0 || mMlrPending);
#endif // OPENTHREAD_EXAMPLES_SIMULATION
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
+16 -14
View File
@@ -127,12 +127,10 @@ private:
#if OPENTHREAD_CONFIG_MLR_ENABLE
void UpdateLocalSubscriptions(void);
void SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
bool IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const;
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
void SetChildMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
bool IsAddressMlrRegisteredByAnyChild(const Ip6::Address &aAddress) const
{
return IsAddressMlrRegisteredByAnyChildExcept(aAddress, nullptr);
@@ -140,19 +138,17 @@ private:
bool IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const;
#endif
void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
{
#if OPENTHREAD_CONFIG_MLR_ENABLE
SetNetifMulticastAddressMlrState(aFromState, aToState);
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
SetChildMulticastAddressMlrState(aFromState, aToState);
#endif
}
void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
void FinishMulticastListenerRegistration(bool aSuccess,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum);
void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax],
uint8_t & aAddressNum,
const Ip6::Address &aAddress);
void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax],
uint8_t & aAddressNum,
const Ip6::Address &aAddress);
static bool AddressListContains(const Ip6::Address *aAddressList,
uint8_t aAddressListSize,
const Ip6::Address &aAddress);
void ScheduleSend(uint16_t aDelay);
void UpdateTimeTickerRegistration(void);
@@ -161,6 +157,12 @@ private:
void HandleTimeTick(void);
void LogMulticastAddresses(void);
void CheckInvariants(void) const;
void LogMlrResponse(otError aResult,
otError aError,
uint8_t aStatus,
const Ip6::Address *aFailedAddresses,
uint8_t aFailedAddressNum);
uint32_t mReregistrationDelay;
uint16_t mSendDelay;
+2 -2
View File
@@ -259,7 +259,7 @@ private:
uint8_t mTlvs[kMaxSize];
} OT_TOOL_PACKED_END;
#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
/**
* This class implements IPv6 Addresses TLV generation and parsing.
@@ -302,7 +302,7 @@ public:
}
} OT_TOOL_PACKED_END;
#endif // OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
} // namespace ot
+31
View File
@@ -40,6 +40,8 @@ import time
import unittest
import binascii
from typing import Union
class Node:
@@ -528,6 +530,35 @@ class Node:
self.send_command(cmd)
self._expect('Done')
def multicast_listener_list(self):
cmd = 'bbr mgmt mlr listener'
self.send_command(cmd)
table = {}
for line in self._expect_results("\S+ \d+"):
line = line.split()
assert len(line) == 2, line
ip = ipaddress.IPv6Address(line[0])
timeout = int(line[1])
assert ip not in table
table[ip] = timeout
return table
def multicast_listener_clear(self):
cmd = f'bbr mgmt mlr listener clear'
self.send_command(cmd)
self._expect("Done")
def multicast_listener_add(self, ip: Union[ipaddress.IPv6Address, str], timeout: int = 0):
if not isinstance(ip, ipaddress.IPv6Address):
ip = ipaddress.IPv6Address(ip)
cmd = f'bbr mgmt mlr listener add {ip.compressed} {timeout}'
self.send_command(cmd)
self._expect(r"(Done|Error .*)")
def set_link_quality(self, addr, lqi):
cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
self.send_command(cmd)
@@ -28,6 +28,8 @@
#
import ipaddress
import logging
import time
import unittest
from typing import Union, List
@@ -35,6 +37,8 @@ import config
import network_layer
import thread_cert
logging.basicConfig(level=logging.DEBUG)
_, BBR_1, BBR_2, ROUTER_1_2, ROUTER_1_1, SED_1, MED_1, MED_2, FED_1 = range(9)
WAIT_ATTACH = 5
@@ -124,6 +128,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
assert (turn_on_bbr_2 or not turn_on_router_1_1) # ROUTER_1_1 needs BBR_2
# starting context id
t0 = time.time()
context_id = 1
# Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router
@@ -142,7 +147,8 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
self.pbbr_seq = 1
self.pbbr_seq = 1
self.pbbr_id = BBR_1
if turn_on_bbr_2:
# Bring up BBR_2, BBR_2 becomes Router and Secondary Backbone Router
@@ -197,6 +203,8 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
self.simulator.go(WAIT_ATTACH)
self.assertEqual(self.nodes[SED_1].get_state(), 'child')
logging.info("bootstrap takes %f seconds", time.time() - t0)
def test(self):
self._bootstrap()
@@ -257,11 +265,171 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
# Expect MLR.req sent by ROUTER_1_2 and FED_1
self.__check_send_mlr_req(ROUTER_1_2, router_reg, should_send=True, expect_mlr_rsp=True)
def testMulticastListenersTableAdd(self):
self._bootstrap()
self.__test_multicast_listeners_table_add()
def testMulticastListenersTableExpire(self):
self._bootstrap()
self.__test_multicast_listeners_table_expire()
def testMulticastListenersTableFull(self):
self._bootstrap()
self.__test_multicast_listeners_table_full()
def testMulticastListenersTableTwoFreeSlot(self):
self._bootstrap()
self.__test_multicast_listeners_table_two_free_slots()
def testMulticastListenerTableAPI(self):
self._bootstrap()
self.__test_multicast_listeners_table_api()
def __test_multicast_listeners_table_api(self):
self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
self.nodes[BBR_1].multicast_listener_add("ff04::1")
self.assertEqual(1, len(self.nodes[BBR_1].multicast_listener_list()))
self.nodes[BBR_1].multicast_listener_add("ff04::2", 300)
self.assertEqual(2, len(self.nodes[BBR_1].multicast_listener_list()))
self.nodes[BBR_1].multicast_listener_clear()
self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
def __test_multicast_listeners_table_add(self):
self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
all_mas = set()
CHECK_LIST = [(ROUTER_1_2, "ff04::1"), (FED_1, "ff04::2"), (MED_1, "ff04::3"), (SED_1, "ff04::4")]
for id, ip in CHECK_LIST:
self.nodes[id].add_ipmaddr(ip)
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
all_mas.add(ipaddress.IPv6Address(ip))
self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
# restore
for id, ip in CHECK_LIST:
self.nodes[id].del_ipmaddr(ip)
self.simulator.go(WAIT_REDUNDANCE)
def __test_multicast_listeners_table_expire(self):
self.assertEqual({}, self.nodes[BBR_1].multicast_listener_list())
all_mas = set()
CHECK_LIST = [(ROUTER_1_2, "ff04::1"), (FED_1, "ff04::2"), (MED_1, "ff04::3"), (SED_1, "ff04::4")]
for id, ip in CHECK_LIST:
self.nodes[id].add_ipmaddr(ip)
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
all_mas.add(ipaddress.IPv6Address(ip))
self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
# remove MAs from nodes, and wait for Multicast Listeners to expire on BBR_1
for id, ip in CHECK_LIST:
self.nodes[id].del_ipmaddr(ip)
# Wait for MLR_TIMEOUT/3, and expect Multicast Listeners not to expire.
self.simulator.go(MLR_TIMEOUT / 3)
self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
# Wait for MLR_TIMEOUT*2/3, and expect all Multicast Listeners to expire.
self.simulator.go(MLR_TIMEOUT * 2 / 3 + WAIT_REDUNDANCE)
self.assertEqual({}, self.nodes[BBR_1].multicast_listener_list())
def __test_multicast_listeners_table_full(self):
self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
table = set()
# Add to Multicast Listeners Table until it's full
for i in range(1, 76):
self.nodes[BBR_1].multicast_listener_add(f"ff04::{i}")
table.add(ipaddress.IPv6Address(f"ff04::{i}"))
self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
# Add when Multicast Listeners Table is full should not succeed
self.nodes[BBR_1].multicast_listener_add(f"ff05::1")
self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
self.flush_all()
# Expect PBBR to respond with MLR_NO_RESOURCES Multicast Listeners Table when it's full
self.nodes[ROUTER_1_2].add_ipmaddr("ff06::1")
self.simulator.go(WAIT_REDUNDANCE)
self.__check_send_mlr_req(ROUTER_1_2,
"ff06::1",
should_send=True,
expect_mlr_rsp=True,
expect_mlr_rsp_status=4)
self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
self.nodes[MED_1].add_ipmaddr("ff06::2")
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
self.__check_send_mlr_req(ROUTER_1_2,
"ff06::2",
should_send=True,
expect_mlr_rsp=True,
expect_mlr_rsp_status=4)
self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
# the ROUTER_1_2 should be resending both ff06::1 and ff06::2
for i in range(3):
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
self.__check_send_mlr_req(ROUTER_1_2, ['ff06::1', 'ff06::2'],
should_send=True,
expect_mlr_rsp=True,
expect_mlr_rsp_status=4)
# Restore
self.nodes[ROUTER_1_2].del_ipmaddr("ff06::1")
self.nodes[MED_1].del_ipmaddr("ff06::2")
self.simulator.go(WAIT_REDUNDANCE)
def __test_multicast_listeners_table_two_free_slots(self):
# Add to Multicast Listeners Table until there is only two free slots
for i in range(1, 74):
self.nodes[BBR_1].multicast_listener_add(f"ff04::{i}")
self.assertEqual(73, len(self.nodes[BBR_1].multicast_listener_list()))
self.nodes[MED_1].add_ipmaddr("ff05::1")
self.nodes[MED_1].add_ipmaddr("ff05::2")
self.nodes[MED_2].add_ipmaddr("ff05::3")
self.nodes[MED_2].add_ipmaddr("ff05::4")
self.nodes[SED_1].add_ipmaddr("ff05::5")
self.nodes[SED_1].add_ipmaddr("ff05::6")
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
self.__check_send_mlr_req(ROUTER_1_2, ["ff05::1", "ff05::2", "ff05::3", "ff05::4", "ff05::5", "ff05::6"])
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
self.flush_all()
# two addresses should be registered, others can not
for i in range(3):
self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
self.assertEqual(4, len(set(self.__get_registered_MAs(ROUTER_1_2))))
# Restore
self.nodes[MED_1].del_ipmaddr("ff05::1")
self.nodes[MED_1].del_ipmaddr("ff05::2")
self.nodes[MED_2].del_ipmaddr("ff05::3")
self.nodes[MED_2].del_ipmaddr("ff05::4")
self.nodes[SED_1].del_ipmaddr("ff05::5")
self.nodes[SED_1].del_ipmaddr("ff05::6")
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'))
logging.info("======== 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
if id == ROUTER_1_2:
@@ -278,11 +446,11 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
for addr in [MA5, MA6]:
self.__check_ipmaddr_add(id, parent_id, addr, expect_mlr_req=False, expect_mlr_req_proxied=False)
print('=' * 120)
logging.info('=' * 120)
def __check_ipmaddr_add(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
"""Check MLR works for the added multicast address"""
print("Node %d: ipmaddr %s" % (id, addr))
logging.info("Node %d: ipmaddr %s" % (id, addr))
self.flush_all()
self.nodes[id].add_ipmaddr(addr)
self.assertTrue(self.nodes[id].has_ipmaddr(addr))
@@ -315,12 +483,14 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
addrs: Union[List[str], str],
should_send=True,
expect_mlr_rsp=False,
expect_mlr_rsp_status=0,
expect_mlr_req_num=None,
expect_unique_reg=False):
if isinstance(addrs, str):
addrs = [addrs]
reg_mas = self.__get_registered_MAs(id, expect_mlr_req_num=expect_mlr_req_num)
message_ids = []
reg_mas = self.__get_registered_MAs(id, expect_mlr_req_num=expect_mlr_req_num, message_ids=message_ids)
if should_send:
for addr in addrs:
self.assertIn(ipaddress.IPv6Address(addr), reg_mas)
@@ -329,13 +499,37 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
# BBR should send MLR.rsp ACK
if expect_mlr_rsp:
messages = self.simulator.get_messages_sent_by(BBR_1)
messages.next_coap_message('2.04')
message_id = message_ids[0]
rsp = self.__expect_MLR_rsp(message_id)
logging.info('MLR.rsp %s uri_path=%s, payload=%s', rsp, rsp.coap.uri_path, rsp.coap.payload)
statusTlv = None
for tlv in rsp.coap.payload:
if isinstance(tlv, network_layer.Status):
statusTlv = tlv
break
self.assertIsNotNone(statusTlv)
self.assertEqual(expect_mlr_rsp_status, statusTlv.status)
else:
for addr in addrs:
self.assertNotIn(ipaddress.IPv6Address(addr), reg_mas)
def __get_registered_MAs(self, id, expect_mlr_req_num=None):
def __expect_MLR_rsp(self, message_id):
logging.info("Expecting MLR.rsp with message ID = %s", message_id)
messages = self.simulator.get_messages_sent_by(self.pbbr_id)
logging.info("PBBR %d messages: %s", self.pbbr_id, messages)
while True:
msg = messages.next_coap_message('2.04')
logging.info('Check ACK for %s: %s, %s, %s, %s', message_id, msg, msg.coap.message_id, msg.coap.uri_path,
msg.coap.payload)
if msg.coap.message_id == message_id:
return msg
def __get_registered_MAs(self, id, expect_mlr_req_num=None, message_ids=None):
"""Get MAs registered via MLR.req by the node"""
messages = self.simulator.get_messages_sent_by(id)
reg_mas = []
@@ -343,10 +537,14 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
msg = messages.next_coap_message('0.02', '/n/mr', assert_enabled=False)
if not msg:
break
logging.info("MLR.req: %s %s" % (msg.coap.message_id, msg.coap.payload))
addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses)
reg_mas.append(addrs)
if message_ids is not None:
message_ids.append(msg.coap.message_id)
print('Node %d registered MAs: %s' % (id, reg_mas))
logging.info('Node %d registered MAs: %s' % (id, reg_mas))
if expect_mlr_req_num is not None:
self.assertEqual(len(reg_mas), expect_mlr_req_num)
@@ -358,6 +556,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
def __check_renewing(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
"""Check if MLR works that a node can renew it's registered MAs"""
assert self.pbbr_id == BBR_1
self.flush_all()
self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
@@ -401,10 +600,14 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
def __check_rereg_pbbr_change(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
"""Check if MLR works that a node can do MLR reregistration when PBBR changes"""
# Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY
assert self.pbbr_id == BBR_1
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.pbbr_id = BBR_2
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
self.__check_send_mlr_req(id, addr, should_send=expect_mlr_req, expect_mlr_rsp=expect_mlr_req)
@@ -423,11 +626,12 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
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')
self.pbbr_id = BBR_1
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)
logging.info("=" * 10 + " switching to 1.1 parent " + '=' * 10)
self.flush_all()
@@ -468,7 +672,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
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)
logging.info("=" * 10, "switching to 1.2 parent", '=' * 10)
self.flush_all()
+24 -1
View File
@@ -336,6 +336,29 @@ target_link_libraries(test-message-queue
add_test(NAME test-message-queue COMMAND test-message-queue)
add_executable(test-multicast-listeners-table
${COMMON_SOURCES}
test_multicast_listeners_table.cpp
)
target_include_directories(test-multicast-listeners-table
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(test-multicast-listeners-table
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(test-multicast-listeners-table
PRIVATE
${COMMON_LIBS}
)
add_test(NAME test-multicast-listeners-table COMMAND test-multicast-listeners-table)
add_executable(test-netif
${COMMON_SOURCES}
test_netif.cpp
@@ -513,7 +536,7 @@ target_link_libraries(test-timer
add_test(NAME test-timer COMMAND test-timer)
set_target_properties(
test-aes test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-netif test-network-data test-pool test-priority-queue test-pskc test-steering-data test-string test-timer
test-aes test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-multicast-listeners-table test-netif test-network-data test-pool test-priority-queue test-pskc test-steering-data test-string test-timer
PROPERTIES
C_STANDARD 99
CXX_STANDARD 11
+4
View File
@@ -119,6 +119,7 @@ check_PROGRAMS += \
test-mac-frame \
test-message \
test-message-queue \
test-multicast-listeners-table \
test-netif \
test-network-data \
test-pool \
@@ -209,6 +210,9 @@ test_message_SOURCES = $(COMMON_SOURCES) test_message.cpp
test_message_queue_LDADD = $(COMMON_LDADD)
test_message_queue_SOURCES = $(COMMON_SOURCES) test_message_queue.cpp
test_multicast_listeners_table_LDADD = $(COMMON_LDADD)
test_multicast_listeners_table_SOURCES = $(COMMON_SOURCES) test_multicast_listeners_table.cpp
test_spinel_buffer_LDADD = $(COMMON_LDADD)
test_spinel_buffer_SOURCES = $(COMMON_SOURCES) test_spinel_buffer.cpp
@@ -0,0 +1,213 @@
/*
* 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.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include "test_platform.h"
#include <openthread/config.h>
#include <openthread/ip6.h>
#include "test_util.h"
#include "backbone_router/multicast_listeners_table.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
namespace ot {
static ot::Instance *sInstance;
using namespace ot::BackboneRouter;
static const otIp6Address MA201 = {
{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
static const otIp6Address MA301 = {
{{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
static const otIp6Address MA401 = {
{{0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
static const otIp6Address MA501 = {
{{0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
uint32_t sNow;
uint32_t testTimerAlarmGetNow(void)
{
return sNow;
}
void InitTestTimer(void)
{
g_testPlatAlarmGetNow = testTimerAlarmGetNow;
}
void testMulticastListenersTableAPIs(Instance *aInstance);
void TestMulticastListenersTable(void)
{
InitTestTimer();
sInstance = testInitInstance();
VerifyOrQuit(sInstance != nullptr, "Null OpenThread instance");
MulticastListenersTable &table = sInstance->Get<MulticastListenersTable>();
for (MulticastListenersTable::Listener &listener : table.Iterate())
{
VerifyOrQuit(false, "MulticastListenersTable should be empty when created");
}
// Removing from empty table should be OK
table.Remove(static_cast<const Ip6::Address &>(MA401));
sNow = 1;
// Add valid MAs should succeed
SuccessOrQuit(table.Add(static_cast<const Ip6::Address &>(MA401), TimerMilli::GetNow()), "Add failed");
SuccessOrQuit(table.Add(static_cast<const Ip6::Address &>(MA501), TimerMilli::GetNow()), "Add failed");
VerifyOrQuit(table.Count() == 2, "Table count is wrong");
// Add invalid MAs should fail with OT_ERROR_INVALID_ARGS
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA201), TimerMilli::GetNow()) == OT_ERROR_INVALID_ARGS,
"Add should fail");
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA301), TimerMilli::GetNow()) == OT_ERROR_INVALID_ARGS,
"Add should fail");
// Expire should expire outdated Listeners
sNow = 2;
table.Expire();
VerifyOrQuit(table.Count() == 0, "Table count is wrong");
// Add different addresses until the table is full
sNow = 10;
for (uint16_t i = 0; i < OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; i++)
{
Ip6::Address address;
address = static_cast<const Ip6::Address &>(MA401);
address.mFields.m16[7] = HostSwap16(i);
SuccessOrQuit(table.Add(address, TimerMilli::GetNow() + i), "Add failed");
VerifyOrQuit(table.Count() == i + 1, "Table count is wrong");
}
// Now the table is full, we can't add more addresses
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA501), TimerMilli::GetNow()) == OT_ERROR_NO_BUFS,
"Add should fail");
// Expire one Listener at a time
for (uint16_t i = 0; i < OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; i++)
{
table.Expire();
VerifyOrQuit(table.Count() == OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS - i - 1, "Table count is wrong");
sNow += 1;
}
// Now the table should be empty
VerifyOrQuit(table.Count() == 0, "Table count is wrong");
// Now test the APIs
testMulticastListenersTableAPIs(sInstance);
// Do some fuzzy test
for (uint16_t i = 0; i < 10000; i++)
{
Ip6::Address address;
sNow += 10;
table.Expire();
for (MulticastListenersTable::Listener &listener : table.Iterate())
{
OT_ASSERT(listener.GetAddress().IsMulticastLargerThanRealmLocal());
OT_ASSERT(listener.GetExpireTime() > TimerMilli::GetNow());
}
address = static_cast<const Ip6::Address &>(MA401);
address.mFields.m16[7] = Random::NonCrypto::GetUint16InRange(1, 1000);
IgnoreError(table.Add(address, TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, 100)));
address.mFields.m16[7] = Random::NonCrypto::GetUint16InRange(1, 1000);
if (Random::NonCrypto::GetUint16InRange(0, 2) == 0)
{
table.Remove(address);
}
}
}
void testMulticastListenersTableAPIs(Instance *aInstance)
{
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
otBackboneRouterMulticastListenerInfo info;
size_t table_size = 0;
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == OT_ERROR_NONE)
{
VerifyOrQuit(false, "Table should be empty");
}
SuccessOrQuit(otBackboneRouterMulticastListenerAdd(aInstance, &MA401, 30), "Add failed");
table_size = 0, iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == OT_ERROR_NONE)
{
table_size++;
VerifyOrQuit(memcmp(&info.mAddress, &MA401, sizeof(otIp6Address)) == 0, "bad address");
VerifyOrQuit(info.mTimeout == 30, "bad timeout");
}
VerifyOrQuit(table_size == 1, "Table size is wrong");
otBackboneRouterMulticastListenerClear(aInstance);
iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == OT_ERROR_NONE)
{
VerifyOrQuit(false, "Table should be empty");
}
#endif
}
} // namespace ot
int main(void)
{
ot::TestMulticastListenersTable();
printf("\nAll tests passed.\n");
return 0;
}
#else
int main(void)
{
return 0;
}
#endif