mirror of
https://github.com/espressif/openthread.git
synced 2026-07-08 05:10:25 +00:00
[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:
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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[]);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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 = \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user