mirror of
https://github.com/espressif/openthread.git
synced 2026-08-20 09:29:51 +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_leader.cpp \
|
||||||
src/core/backbone_router/bbr_local.cpp \
|
src/core/backbone_router/bbr_local.cpp \
|
||||||
src/core/backbone_router/bbr_manager.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.cpp \
|
||||||
src/core/coap/coap_message.cpp \
|
src/core/coap/coap_message.cpp \
|
||||||
src/core/coap/coap_secure.cpp \
|
src/core/coap/coap_secure.cpp \
|
||||||
|
|||||||
@@ -193,6 +193,114 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *
|
|||||||
const otIp6InterfaceIdentifier *aMlIid,
|
const otIp6InterfaceIdentifier *aMlIid,
|
||||||
uint8_t aStatus);
|
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.
|
* @note This number versions both OpenThread platform and user APIs.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define OPENTHREAD_API_VERSION (24)
|
#define OPENTHREAD_API_VERSION (25)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @addtogroup api-instance
|
* @addtogroup api-instance
|
||||||
|
|||||||
+19
-5
@@ -138,16 +138,30 @@ do_clean()
|
|||||||
rm -rfv "${OT_BUILDDIR}" || sudo rm -rfv "${OT_BUILDDIR}"
|
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
|
if [[ ! -d ${builddir} ]]; then
|
||||||
echo "Cannot find build directory!"
|
echo "Cannot find build directory: ${builddir}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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()
|
do_cert()
|
||||||
|
|||||||
@@ -142,6 +142,49 @@ known status value:
|
|||||||
Done
|
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
|
### bbr state
|
||||||
|
|
||||||
Show local Backbone state ([`Disabled`,`Primary`, `Secondary`]) for Thread 1.2 FTD.
|
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;
|
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)
|
if (strcmp(aArgs[1], "dua") == 0)
|
||||||
{
|
{
|
||||||
otIp6InterfaceIdentifier *mlIid = nullptr;
|
otIp6InterfaceIdentifier *mlIid = nullptr;
|
||||||
otIp6InterfaceIdentifier iid;
|
otIp6InterfaceIdentifier iid;
|
||||||
|
|
||||||
|
VerifyOrExit((aArgsLength == 3 || aArgsLength == 4), error = OT_ERROR_INVALID_ARGS);
|
||||||
|
|
||||||
SuccessOrExit(error = ParseUnsignedLong(aArgs[2], value));
|
SuccessOrExit(error = ParseUnsignedLong(aArgs[2], value));
|
||||||
|
|
||||||
if (aArgsLength == 4)
|
if (aArgsLength == 4)
|
||||||
@@ -546,6 +548,11 @@ void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
|
|||||||
otBackboneRouterConfigNextDuaRegistrationResponse(mInstance, mlIid, static_cast<uint8_t>(value));
|
otBackboneRouterConfigNextDuaRegistrationResponse(mInstance, mlIid, static_cast<uint8_t>(value));
|
||||||
ExitNow();
|
ExitNow();
|
||||||
}
|
}
|
||||||
|
else if (strcmp(aArgs[1], "mlr") == 0)
|
||||||
|
{
|
||||||
|
error = ProcessBackboneRouterMgmtMlr(aArgsLength - 2, aArgs + 2);
|
||||||
|
ExitNow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||||
SuccessOrExit(error = ProcessBackboneRouterLocal(aArgsLength, aArgs));
|
SuccessOrExit(error = ProcessBackboneRouterLocal(aArgsLength, aArgs));
|
||||||
@@ -553,11 +560,70 @@ void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
||||||
|
|
||||||
AppendResult(error);
|
AppendResult(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
#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 Interpreter::ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[])
|
||||||
{
|
{
|
||||||
otError error = OT_ERROR_NONE;
|
otError error = OT_ERROR_NONE;
|
||||||
|
|||||||
@@ -275,6 +275,10 @@ private:
|
|||||||
|
|
||||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
||||||
otError ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[]);
|
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
|
#endif
|
||||||
|
|
||||||
void ProcessDomainName(uint8_t aArgsLength, char *aArgs[]);
|
void ProcessDomainName(uint8_t aArgsLength, char *aArgs[]);
|
||||||
|
|||||||
@@ -325,6 +325,8 @@ openthread_core_files = [
|
|||||||
"backbone_router/bbr_local.hpp",
|
"backbone_router/bbr_local.hpp",
|
||||||
"backbone_router/bbr_manager.cpp",
|
"backbone_router/bbr_manager.cpp",
|
||||||
"backbone_router/bbr_manager.hpp",
|
"backbone_router/bbr_manager.hpp",
|
||||||
|
"backbone_router/multicast_listeners_table.cpp",
|
||||||
|
"backbone_router/multicast_listeners_table.hpp",
|
||||||
"coap/coap.cpp",
|
"coap/coap.cpp",
|
||||||
"coap/coap.hpp",
|
"coap/coap.hpp",
|
||||||
"coap/coap_message.cpp",
|
"coap/coap_message.cpp",
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ set(COMMON_SOURCES
|
|||||||
backbone_router/bbr_leader.cpp
|
backbone_router/bbr_leader.cpp
|
||||||
backbone_router/bbr_local.cpp
|
backbone_router/bbr_local.cpp
|
||||||
backbone_router/bbr_manager.cpp
|
backbone_router/bbr_manager.cpp
|
||||||
|
backbone_router/multicast_listeners_table.cpp
|
||||||
coap/coap.cpp
|
coap/coap.cpp
|
||||||
coap/coap_message.cpp
|
coap/coap_message.cpp
|
||||||
coap/coap_secure.cpp
|
coap/coap_secure.cpp
|
||||||
|
|||||||
+306
-304
@@ -107,152 +107,153 @@ libopenthread_mtd_a_CPPFLAGS = \
|
|||||||
# unit"
|
# unit"
|
||||||
#
|
#
|
||||||
|
|
||||||
SOURCES_COMMON = \
|
SOURCES_COMMON = \
|
||||||
api/backbone_router_api.cpp \
|
api/backbone_router_api.cpp \
|
||||||
api/backbone_router_ftd_api.cpp \
|
api/backbone_router_ftd_api.cpp \
|
||||||
api/border_router_api.cpp \
|
api/border_router_api.cpp \
|
||||||
api/channel_manager_api.cpp \
|
api/channel_manager_api.cpp \
|
||||||
api/channel_monitor_api.cpp \
|
api/channel_monitor_api.cpp \
|
||||||
api/child_supervision_api.cpp \
|
api/child_supervision_api.cpp \
|
||||||
api/coap_api.cpp \
|
api/coap_api.cpp \
|
||||||
api/coap_secure_api.cpp \
|
api/coap_secure_api.cpp \
|
||||||
api/commissioner_api.cpp \
|
api/commissioner_api.cpp \
|
||||||
api/crypto_api.cpp \
|
api/crypto_api.cpp \
|
||||||
api/dataset_api.cpp \
|
api/dataset_api.cpp \
|
||||||
api/dataset_ftd_api.cpp \
|
api/dataset_ftd_api.cpp \
|
||||||
api/diags_api.cpp \
|
api/diags_api.cpp \
|
||||||
api/dns_api.cpp \
|
api/dns_api.cpp \
|
||||||
api/entropy_api.cpp \
|
api/entropy_api.cpp \
|
||||||
api/heap_api.cpp \
|
api/heap_api.cpp \
|
||||||
api/icmp6_api.cpp \
|
api/icmp6_api.cpp \
|
||||||
api/instance_api.cpp \
|
api/instance_api.cpp \
|
||||||
api/ip6_api.cpp \
|
api/ip6_api.cpp \
|
||||||
api/jam_detection_api.cpp \
|
api/jam_detection_api.cpp \
|
||||||
api/joiner_api.cpp \
|
api/joiner_api.cpp \
|
||||||
api/link_api.cpp \
|
api/link_api.cpp \
|
||||||
api/link_raw_api.cpp \
|
api/link_raw_api.cpp \
|
||||||
api/logging_api.cpp \
|
api/logging_api.cpp \
|
||||||
api/message_api.cpp \
|
api/message_api.cpp \
|
||||||
api/netdata_api.cpp \
|
api/netdata_api.cpp \
|
||||||
api/netdiag_api.cpp \
|
api/netdiag_api.cpp \
|
||||||
api/network_time_api.cpp \
|
api/network_time_api.cpp \
|
||||||
api/random_crypto_api.cpp \
|
api/random_crypto_api.cpp \
|
||||||
api/random_noncrypto_api.cpp \
|
api/random_noncrypto_api.cpp \
|
||||||
api/server_api.cpp \
|
api/server_api.cpp \
|
||||||
api/sntp_api.cpp \
|
api/sntp_api.cpp \
|
||||||
api/tasklet_api.cpp \
|
api/tasklet_api.cpp \
|
||||||
api/thread_api.cpp \
|
api/thread_api.cpp \
|
||||||
api/thread_ftd_api.cpp \
|
api/thread_ftd_api.cpp \
|
||||||
api/udp_api.cpp \
|
api/udp_api.cpp \
|
||||||
backbone_router/bbr_leader.cpp \
|
backbone_router/bbr_leader.cpp \
|
||||||
backbone_router/bbr_local.cpp \
|
backbone_router/bbr_local.cpp \
|
||||||
backbone_router/bbr_manager.cpp \
|
backbone_router/bbr_manager.cpp \
|
||||||
coap/coap.cpp \
|
backbone_router/multicast_listeners_table.cpp \
|
||||||
coap/coap_message.cpp \
|
coap/coap.cpp \
|
||||||
coap/coap_secure.cpp \
|
coap/coap_message.cpp \
|
||||||
common/crc16.cpp \
|
coap/coap_secure.cpp \
|
||||||
common/instance.cpp \
|
common/crc16.cpp \
|
||||||
common/logging.cpp \
|
common/instance.cpp \
|
||||||
common/message.cpp \
|
common/logging.cpp \
|
||||||
common/notifier.cpp \
|
common/message.cpp \
|
||||||
common/random_manager.cpp \
|
common/notifier.cpp \
|
||||||
common/settings.cpp \
|
common/random_manager.cpp \
|
||||||
common/string.cpp \
|
common/settings.cpp \
|
||||||
common/tasklet.cpp \
|
common/string.cpp \
|
||||||
common/time_ticker.cpp \
|
common/tasklet.cpp \
|
||||||
common/timer.cpp \
|
common/time_ticker.cpp \
|
||||||
common/tlvs.cpp \
|
common/timer.cpp \
|
||||||
common/trickle_timer.cpp \
|
common/tlvs.cpp \
|
||||||
crypto/aes_ccm.cpp \
|
common/trickle_timer.cpp \
|
||||||
crypto/aes_ecb.cpp \
|
crypto/aes_ccm.cpp \
|
||||||
crypto/ecdsa.cpp \
|
crypto/aes_ecb.cpp \
|
||||||
crypto/hmac_sha256.cpp \
|
crypto/ecdsa.cpp \
|
||||||
crypto/mbedtls.cpp \
|
crypto/hmac_sha256.cpp \
|
||||||
crypto/pbkdf2_cmac.cpp \
|
crypto/mbedtls.cpp \
|
||||||
crypto/sha256.cpp \
|
crypto/pbkdf2_cmac.cpp \
|
||||||
diags/factory_diags.cpp \
|
crypto/sha256.cpp \
|
||||||
mac/channel_mask.cpp \
|
diags/factory_diags.cpp \
|
||||||
mac/data_poll_handler.cpp \
|
mac/channel_mask.cpp \
|
||||||
mac/data_poll_sender.cpp \
|
mac/data_poll_handler.cpp \
|
||||||
mac/link_raw.cpp \
|
mac/data_poll_sender.cpp \
|
||||||
mac/mac.cpp \
|
mac/link_raw.cpp \
|
||||||
mac/mac_filter.cpp \
|
mac/mac.cpp \
|
||||||
mac/mac_frame.cpp \
|
mac/mac_filter.cpp \
|
||||||
mac/mac_types.cpp \
|
mac/mac_frame.cpp \
|
||||||
mac/sub_mac.cpp \
|
mac/mac_types.cpp \
|
||||||
mac/sub_mac_callbacks.cpp \
|
mac/sub_mac.cpp \
|
||||||
meshcop/announce_begin_client.cpp \
|
mac/sub_mac_callbacks.cpp \
|
||||||
meshcop/border_agent.cpp \
|
meshcop/announce_begin_client.cpp \
|
||||||
meshcop/commissioner.cpp \
|
meshcop/border_agent.cpp \
|
||||||
meshcop/dataset.cpp \
|
meshcop/commissioner.cpp \
|
||||||
meshcop/dataset_local.cpp \
|
meshcop/dataset.cpp \
|
||||||
meshcop/dataset_manager.cpp \
|
meshcop/dataset_local.cpp \
|
||||||
meshcop/dataset_manager_ftd.cpp \
|
meshcop/dataset_manager.cpp \
|
||||||
meshcop/dtls.cpp \
|
meshcop/dataset_manager_ftd.cpp \
|
||||||
meshcop/energy_scan_client.cpp \
|
meshcop/dtls.cpp \
|
||||||
meshcop/joiner.cpp \
|
meshcop/energy_scan_client.cpp \
|
||||||
meshcop/joiner_router.cpp \
|
meshcop/joiner.cpp \
|
||||||
meshcop/meshcop.cpp \
|
meshcop/joiner_router.cpp \
|
||||||
meshcop/meshcop_leader.cpp \
|
meshcop/meshcop.cpp \
|
||||||
meshcop/meshcop_tlvs.cpp \
|
meshcop/meshcop_leader.cpp \
|
||||||
meshcop/panid_query_client.cpp \
|
meshcop/meshcop_tlvs.cpp \
|
||||||
meshcop/timestamp.cpp \
|
meshcop/panid_query_client.cpp \
|
||||||
net/dhcp6_client.cpp \
|
meshcop/timestamp.cpp \
|
||||||
net/dhcp6_server.cpp \
|
net/dhcp6_client.cpp \
|
||||||
net/dns_client.cpp \
|
net/dhcp6_server.cpp \
|
||||||
net/icmp6.cpp \
|
net/dns_client.cpp \
|
||||||
net/ip6.cpp \
|
net/icmp6.cpp \
|
||||||
net/ip6_address.cpp \
|
net/ip6.cpp \
|
||||||
net/ip6_filter.cpp \
|
net/ip6_address.cpp \
|
||||||
net/ip6_headers.cpp \
|
net/ip6_filter.cpp \
|
||||||
net/ip6_mpl.cpp \
|
net/ip6_headers.cpp \
|
||||||
net/netif.cpp \
|
net/ip6_mpl.cpp \
|
||||||
net/sntp_client.cpp \
|
net/netif.cpp \
|
||||||
net/udp6.cpp \
|
net/sntp_client.cpp \
|
||||||
radio/radio.cpp \
|
net/udp6.cpp \
|
||||||
radio/radio_callbacks.cpp \
|
radio/radio.cpp \
|
||||||
radio/radio_platform.cpp \
|
radio/radio_callbacks.cpp \
|
||||||
thread/address_resolver.cpp \
|
radio/radio_platform.cpp \
|
||||||
thread/announce_begin_server.cpp \
|
thread/address_resolver.cpp \
|
||||||
thread/announce_sender.cpp \
|
thread/announce_begin_server.cpp \
|
||||||
thread/child_table.cpp \
|
thread/announce_sender.cpp \
|
||||||
thread/csl_tx_scheduler.cpp \
|
thread/child_table.cpp \
|
||||||
thread/discover_scanner.cpp \
|
thread/csl_tx_scheduler.cpp \
|
||||||
thread/dua_manager.cpp \
|
thread/discover_scanner.cpp \
|
||||||
thread/energy_scan_server.cpp \
|
thread/dua_manager.cpp \
|
||||||
thread/indirect_sender.cpp \
|
thread/energy_scan_server.cpp \
|
||||||
thread/key_manager.cpp \
|
thread/indirect_sender.cpp \
|
||||||
thread/link_quality.cpp \
|
thread/key_manager.cpp \
|
||||||
thread/lowpan.cpp \
|
thread/link_quality.cpp \
|
||||||
thread/mesh_forwarder.cpp \
|
thread/lowpan.cpp \
|
||||||
thread/mesh_forwarder_ftd.cpp \
|
thread/mesh_forwarder.cpp \
|
||||||
thread/mesh_forwarder_mtd.cpp \
|
thread/mesh_forwarder_ftd.cpp \
|
||||||
thread/mle.cpp \
|
thread/mesh_forwarder_mtd.cpp \
|
||||||
thread/mle_router.cpp \
|
thread/mle.cpp \
|
||||||
thread/mle_types.cpp \
|
thread/mle_router.cpp \
|
||||||
thread/mlr_manager.cpp \
|
thread/mle_types.cpp \
|
||||||
thread/neighbor_table.cpp \
|
thread/mlr_manager.cpp \
|
||||||
thread/network_data.cpp \
|
thread/neighbor_table.cpp \
|
||||||
thread/network_data_leader.cpp \
|
thread/network_data.cpp \
|
||||||
thread/network_data_leader_ftd.cpp \
|
thread/network_data_leader.cpp \
|
||||||
thread/network_data_local.cpp \
|
thread/network_data_leader_ftd.cpp \
|
||||||
thread/network_data_notifier.cpp \
|
thread/network_data_local.cpp \
|
||||||
thread/network_diagnostic.cpp \
|
thread/network_data_notifier.cpp \
|
||||||
thread/panid_query_server.cpp \
|
thread/network_diagnostic.cpp \
|
||||||
thread/router_table.cpp \
|
thread/panid_query_server.cpp \
|
||||||
thread/src_match_controller.cpp \
|
thread/router_table.cpp \
|
||||||
thread/thread_netif.cpp \
|
thread/src_match_controller.cpp \
|
||||||
thread/time_sync_service.cpp \
|
thread/thread_netif.cpp \
|
||||||
thread/topology.cpp \
|
thread/time_sync_service.cpp \
|
||||||
utils/channel_manager.cpp \
|
thread/topology.cpp \
|
||||||
utils/channel_monitor.cpp \
|
utils/channel_manager.cpp \
|
||||||
utils/child_supervision.cpp \
|
utils/channel_monitor.cpp \
|
||||||
utils/flash.cpp \
|
utils/child_supervision.cpp \
|
||||||
utils/heap.cpp \
|
utils/flash.cpp \
|
||||||
utils/jam_detector.cpp \
|
utils/heap.cpp \
|
||||||
utils/otns.cpp \
|
utils/jam_detector.cpp \
|
||||||
utils/parse_cmdline.cpp \
|
utils/otns.cpp \
|
||||||
utils/slaac_address.cpp \
|
utils/parse_cmdline.cpp \
|
||||||
|
utils/slaac_address.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
@@ -318,164 +319,165 @@ nodist_libopenthread_radio_a_SOURCES = \
|
|||||||
|
|
||||||
endif # OPENTHREAD_ENABLE_VENDOR_EXTENSION
|
endif # OPENTHREAD_ENABLE_VENDOR_EXTENSION
|
||||||
|
|
||||||
HEADERS_COMMON = \
|
HEADERS_COMMON = \
|
||||||
openthread-core-config.h \
|
openthread-core-config.h \
|
||||||
backbone_router/bbr_leader.hpp \
|
backbone_router/bbr_leader.hpp \
|
||||||
backbone_router/bbr_local.hpp \
|
backbone_router/bbr_local.hpp \
|
||||||
backbone_router/bbr_manager.hpp \
|
backbone_router/bbr_manager.hpp \
|
||||||
coap/coap.hpp \
|
backbone_router/multicast_listeners_table.hpp \
|
||||||
coap/coap_message.hpp \
|
coap/coap.hpp \
|
||||||
coap/coap_secure.hpp \
|
coap/coap_message.hpp \
|
||||||
common/bit_vector.hpp \
|
coap/coap_secure.hpp \
|
||||||
common/clearable.hpp \
|
common/bit_vector.hpp \
|
||||||
common/code_utils.hpp \
|
common/clearable.hpp \
|
||||||
common/crc16.hpp \
|
common/code_utils.hpp \
|
||||||
common/debug.hpp \
|
common/crc16.hpp \
|
||||||
common/encoding.hpp \
|
common/debug.hpp \
|
||||||
common/equatable.hpp \
|
common/encoding.hpp \
|
||||||
common/extension.hpp \
|
common/equatable.hpp \
|
||||||
common/instance.hpp \
|
common/extension.hpp \
|
||||||
common/linked_list.hpp \
|
common/instance.hpp \
|
||||||
common/locator.hpp \
|
common/linked_list.hpp \
|
||||||
common/locator-getters.hpp \
|
common/locator.hpp \
|
||||||
common/logging.hpp \
|
common/locator-getters.hpp \
|
||||||
common/message.hpp \
|
common/logging.hpp \
|
||||||
common/new.hpp \
|
common/message.hpp \
|
||||||
common/non_copyable.hpp \
|
common/new.hpp \
|
||||||
common/notifier.hpp \
|
common/non_copyable.hpp \
|
||||||
common/pool.hpp \
|
common/notifier.hpp \
|
||||||
common/random.hpp \
|
common/pool.hpp \
|
||||||
common/random_manager.hpp \
|
common/random.hpp \
|
||||||
common/settings.hpp \
|
common/random_manager.hpp \
|
||||||
common/string.hpp \
|
common/settings.hpp \
|
||||||
common/tasklet.hpp \
|
common/string.hpp \
|
||||||
common/time.hpp \
|
common/tasklet.hpp \
|
||||||
common/time_ticker.hpp \
|
common/time.hpp \
|
||||||
common/timer.hpp \
|
common/time_ticker.hpp \
|
||||||
common/tlvs.hpp \
|
common/timer.hpp \
|
||||||
common/trickle_timer.hpp \
|
common/tlvs.hpp \
|
||||||
config/announce_sender.h \
|
common/trickle_timer.hpp \
|
||||||
config/backbone_router.h \
|
config/announce_sender.h \
|
||||||
config/border_router.h \
|
config/backbone_router.h \
|
||||||
config/channel_manager.h \
|
config/border_router.h \
|
||||||
config/channel_monitor.h \
|
config/channel_manager.h \
|
||||||
config/child_supervision.h \
|
config/channel_monitor.h \
|
||||||
config/coap.h \
|
config/child_supervision.h \
|
||||||
config/commissioner.h \
|
config/coap.h \
|
||||||
config/dhcp6_client.h \
|
config/commissioner.h \
|
||||||
config/dhcp6_server.h \
|
config/dhcp6_client.h \
|
||||||
config/diag.h \
|
config/dhcp6_server.h \
|
||||||
config/dns_client.h \
|
config/diag.h \
|
||||||
config/ip6.h \
|
config/dns_client.h \
|
||||||
config/joiner.h \
|
config/ip6.h \
|
||||||
config/link_quality.h \
|
config/joiner.h \
|
||||||
config/link_raw.h \
|
config/link_quality.h \
|
||||||
config/logging.h \
|
config/link_raw.h \
|
||||||
config/mac.h \
|
config/logging.h \
|
||||||
config/mle.h \
|
config/mac.h \
|
||||||
config/openthread-core-config-check.h \
|
config/mle.h \
|
||||||
config/openthread-core-default-config.h \
|
config/openthread-core-config-check.h \
|
||||||
config/parent_search.h \
|
config/openthread-core-default-config.h \
|
||||||
config/platform.h \
|
config/parent_search.h \
|
||||||
config/sntp_client.h \
|
config/platform.h \
|
||||||
config/time_sync.h \
|
config/sntp_client.h \
|
||||||
config/tmf.h \
|
config/time_sync.h \
|
||||||
crypto/aes_ccm.hpp \
|
config/tmf.h \
|
||||||
crypto/aes_ecb.hpp \
|
crypto/aes_ccm.hpp \
|
||||||
crypto/ecdsa.hpp \
|
crypto/aes_ecb.hpp \
|
||||||
crypto/hmac_sha256.hpp \
|
crypto/ecdsa.hpp \
|
||||||
crypto/mbedtls.hpp \
|
crypto/hmac_sha256.hpp \
|
||||||
crypto/pbkdf2_cmac.h \
|
crypto/mbedtls.hpp \
|
||||||
crypto/sha256.hpp \
|
crypto/pbkdf2_cmac.h \
|
||||||
diags/factory_diags.hpp \
|
crypto/sha256.hpp \
|
||||||
mac/channel_mask.hpp \
|
diags/factory_diags.hpp \
|
||||||
mac/data_poll_handler.hpp \
|
mac/channel_mask.hpp \
|
||||||
mac/data_poll_sender.hpp \
|
mac/data_poll_handler.hpp \
|
||||||
mac/link_raw.hpp \
|
mac/data_poll_sender.hpp \
|
||||||
mac/mac.hpp \
|
mac/link_raw.hpp \
|
||||||
mac/mac_filter.hpp \
|
mac/mac.hpp \
|
||||||
mac/mac_frame.hpp \
|
mac/mac_filter.hpp \
|
||||||
mac/mac_types.hpp \
|
mac/mac_frame.hpp \
|
||||||
mac/sub_mac.hpp \
|
mac/mac_types.hpp \
|
||||||
meshcop/announce_begin_client.hpp \
|
mac/sub_mac.hpp \
|
||||||
meshcop/border_agent.hpp \
|
meshcop/announce_begin_client.hpp \
|
||||||
meshcop/commissioner.hpp \
|
meshcop/border_agent.hpp \
|
||||||
meshcop/dataset.hpp \
|
meshcop/commissioner.hpp \
|
||||||
meshcop/dataset_local.hpp \
|
meshcop/dataset.hpp \
|
||||||
meshcop/dataset_manager.hpp \
|
meshcop/dataset_local.hpp \
|
||||||
meshcop/dtls.hpp \
|
meshcop/dataset_manager.hpp \
|
||||||
meshcop/energy_scan_client.hpp \
|
meshcop/dtls.hpp \
|
||||||
meshcop/joiner.hpp \
|
meshcop/energy_scan_client.hpp \
|
||||||
meshcop/joiner_router.hpp \
|
meshcop/joiner.hpp \
|
||||||
meshcop/meshcop.hpp \
|
meshcop/joiner_router.hpp \
|
||||||
meshcop/meshcop_leader.hpp \
|
meshcop/meshcop.hpp \
|
||||||
meshcop/meshcop_tlvs.hpp \
|
meshcop/meshcop_leader.hpp \
|
||||||
meshcop/panid_query_client.hpp \
|
meshcop/meshcop_tlvs.hpp \
|
||||||
meshcop/timestamp.hpp \
|
meshcop/panid_query_client.hpp \
|
||||||
net/dhcp6.hpp \
|
meshcop/timestamp.hpp \
|
||||||
net/dhcp6_client.hpp \
|
net/dhcp6.hpp \
|
||||||
net/dhcp6_server.hpp \
|
net/dhcp6_client.hpp \
|
||||||
net/dns_client.hpp \
|
net/dhcp6_server.hpp \
|
||||||
net/dns_headers.hpp \
|
net/dns_client.hpp \
|
||||||
net/icmp6.hpp \
|
net/dns_headers.hpp \
|
||||||
net/ip6.hpp \
|
net/icmp6.hpp \
|
||||||
net/ip6_address.hpp \
|
net/ip6.hpp \
|
||||||
net/ip6_filter.hpp \
|
net/ip6_address.hpp \
|
||||||
net/ip6_headers.hpp \
|
net/ip6_filter.hpp \
|
||||||
net/ip6_mpl.hpp \
|
net/ip6_headers.hpp \
|
||||||
net/netif.hpp \
|
net/ip6_mpl.hpp \
|
||||||
net/sntp_client.hpp \
|
net/netif.hpp \
|
||||||
net/socket.hpp \
|
net/sntp_client.hpp \
|
||||||
net/tcp.hpp \
|
net/socket.hpp \
|
||||||
net/udp6.hpp \
|
net/tcp.hpp \
|
||||||
radio/radio.hpp \
|
net/udp6.hpp \
|
||||||
thread/address_resolver.hpp \
|
radio/radio.hpp \
|
||||||
thread/announce_begin_server.hpp \
|
thread/address_resolver.hpp \
|
||||||
thread/announce_sender.hpp \
|
thread/announce_begin_server.hpp \
|
||||||
thread/child_mask.hpp \
|
thread/announce_sender.hpp \
|
||||||
thread/child_table.hpp \
|
thread/child_mask.hpp \
|
||||||
thread/csl_tx_scheduler.hpp \
|
thread/child_table.hpp \
|
||||||
thread/discover_scanner.hpp \
|
thread/csl_tx_scheduler.hpp \
|
||||||
thread/dua_manager.hpp \
|
thread/discover_scanner.hpp \
|
||||||
thread/energy_scan_server.hpp \
|
thread/dua_manager.hpp \
|
||||||
thread/indirect_sender.hpp \
|
thread/energy_scan_server.hpp \
|
||||||
thread/indirect_sender_frame_context.hpp \
|
thread/indirect_sender.hpp \
|
||||||
thread/key_manager.hpp \
|
thread/indirect_sender_frame_context.hpp \
|
||||||
thread/link_quality.hpp \
|
thread/key_manager.hpp \
|
||||||
thread/lowpan.hpp \
|
thread/link_quality.hpp \
|
||||||
thread/mesh_forwarder.hpp \
|
thread/lowpan.hpp \
|
||||||
thread/mle.hpp \
|
thread/mesh_forwarder.hpp \
|
||||||
thread/mle_router.hpp \
|
thread/mle.hpp \
|
||||||
thread/mle_tlvs.hpp \
|
thread/mle_router.hpp \
|
||||||
thread/mle_types.hpp \
|
thread/mle_tlvs.hpp \
|
||||||
thread/mlr_manager.hpp \
|
thread/mle_types.hpp \
|
||||||
thread/mlr_types.hpp \
|
thread/mlr_manager.hpp \
|
||||||
thread/neighbor_table.hpp \
|
thread/mlr_types.hpp \
|
||||||
thread/network_data.hpp \
|
thread/neighbor_table.hpp \
|
||||||
thread/network_data_leader.hpp \
|
thread/network_data.hpp \
|
||||||
thread/network_data_leader_ftd.hpp \
|
thread/network_data_leader.hpp \
|
||||||
thread/network_data_local.hpp \
|
thread/network_data_leader_ftd.hpp \
|
||||||
thread/network_data_notifier.hpp \
|
thread/network_data_local.hpp \
|
||||||
thread/network_data_tlvs.hpp \
|
thread/network_data_notifier.hpp \
|
||||||
thread/network_diagnostic.hpp \
|
thread/network_data_tlvs.hpp \
|
||||||
thread/network_diagnostic_tlvs.hpp \
|
thread/network_diagnostic.hpp \
|
||||||
thread/panid_query_server.hpp \
|
thread/network_diagnostic_tlvs.hpp \
|
||||||
thread/router_table.hpp \
|
thread/panid_query_server.hpp \
|
||||||
thread/src_match_controller.hpp \
|
thread/router_table.hpp \
|
||||||
thread/thread_netif.hpp \
|
thread/src_match_controller.hpp \
|
||||||
thread/thread_tlvs.hpp \
|
thread/thread_netif.hpp \
|
||||||
thread/thread_uri_paths.hpp \
|
thread/thread_tlvs.hpp \
|
||||||
thread/time_sync_service.hpp \
|
thread/thread_uri_paths.hpp \
|
||||||
thread/topology.hpp \
|
thread/time_sync_service.hpp \
|
||||||
utils/channel_manager.hpp \
|
thread/topology.hpp \
|
||||||
utils/channel_monitor.hpp \
|
utils/channel_manager.hpp \
|
||||||
utils/child_supervision.hpp \
|
utils/channel_monitor.hpp \
|
||||||
utils/flash.hpp \
|
utils/child_supervision.hpp \
|
||||||
utils/heap.hpp \
|
utils/flash.hpp \
|
||||||
utils/jam_detector.hpp \
|
utils/heap.hpp \
|
||||||
utils/otns.hpp \
|
utils/jam_detector.hpp \
|
||||||
utils/parse_cmdline.hpp \
|
utils/otns.hpp \
|
||||||
utils/slaac_address.hpp \
|
utils/parse_cmdline.hpp \
|
||||||
|
utils/slaac_address.hpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
|||||||
@@ -111,6 +111,15 @@ otError otBackboneRouterGetDomainPrefix(otInstance *aInstance, otBorderRouterCon
|
|||||||
*static_cast<NetworkData::OnMeshPrefixConfig *>(aConfig));
|
*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
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||||
void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * aInstance,
|
void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * aInstance,
|
||||||
const otIp6InterfaceIdentifier *aMlIid,
|
const otIp6InterfaceIdentifier *aMlIid,
|
||||||
@@ -121,6 +130,46 @@ void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *
|
|||||||
instance.Get<BackboneRouter::Manager>().ConfigNextDuaRegistrationResponse(
|
instance.Get<BackboneRouter::Manager>().ConfigNextDuaRegistrationResponse(
|
||||||
static_cast<const Ip6::InterfaceIdentifier *>(aMlIid), aStatus);
|
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
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ void Leader::UpdateBackboneRouterPrimary(void)
|
|||||||
{
|
{
|
||||||
BackboneRouterConfig config;
|
BackboneRouterConfig config;
|
||||||
State state;
|
State state;
|
||||||
|
uint32_t origMlrTimeout;
|
||||||
|
|
||||||
IgnoreError(Get<NetworkData::Leader>().GetBackboneRouterPrimary(config));
|
IgnoreError(Get<NetworkData::Leader>().GetBackboneRouterPrimary(config));
|
||||||
|
|
||||||
@@ -217,6 +218,23 @@ void Leader::UpdateBackboneRouterPrimary(void)
|
|||||||
state = kStateUnchanged;
|
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;
|
mConfig = config;
|
||||||
LogBackboneRouterPrimary(state, mConfig);
|
LogBackboneRouterPrimary(state, mConfig);
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ otError Local::SetConfig(const BackboneRouterConfig &aConfig)
|
|||||||
otError error = OT_ERROR_NONE;
|
otError error = OT_ERROR_NONE;
|
||||||
bool update = false;
|
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:
|
// 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."
|
// "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);
|
VerifyOrExit(aConfig.mReregistrationDelay >= 1, error = OT_ERROR_INVALID_ARGS);
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ Manager::Manager(Instance &aInstance)
|
|||||||
: InstanceLocator(aInstance)
|
: InstanceLocator(aInstance)
|
||||||
, mMulticastListenerRegistration(OT_URI_PATH_MLR, Manager::HandleMulticastListenerRegistration, this)
|
, mMulticastListenerRegistration(OT_URI_PATH_MLR, Manager::HandleMulticastListenerRegistration, this)
|
||||||
, mDuaRegistration(OT_URI_PATH_DUA_REGISTRATION_REQUEST, Manager::HandleDuaRegistration, this)
|
, mDuaRegistration(OT_URI_PATH_DUA_REGISTRATION_REQUEST, Manager::HandleDuaRegistration, this)
|
||||||
|
, mMulticastListenersTable(aInstance)
|
||||||
|
, mTimer(aInstance, Manager::HandleTimer, this)
|
||||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||||
, mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess)
|
, mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess)
|
||||||
, mDuaResponseIsSpecified(false)
|
, mDuaResponseIsSpecified(false)
|
||||||
@@ -68,20 +70,39 @@ void Manager::HandleNotifierEvents(Events aEvents)
|
|||||||
{
|
{
|
||||||
Get<Coap::Coap>().RemoveResource(mMulticastListenerRegistration);
|
Get<Coap::Coap>().RemoveResource(mMulticastListenerRegistration);
|
||||||
Get<Coap::Coap>().RemoveResource(mDuaRegistration);
|
Get<Coap::Coap>().RemoveResource(mDuaRegistration);
|
||||||
|
mTimer.Stop();
|
||||||
|
mMulticastListenersTable.Clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Get<Coap::Coap>().AddResource(mMulticastListenerRegistration);
|
Get<Coap::Coap>().AddResource(mMulticastListenerRegistration);
|
||||||
Get<Coap::Coap>().AddResource(mDuaRegistration);
|
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)
|
void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||||
{
|
{
|
||||||
otError error = OT_ERROR_NONE;
|
otError error = OT_ERROR_NONE;
|
||||||
bool isPrimary = Get<BackboneRouter::Local>().IsPrimary();
|
bool isPrimary = Get<BackboneRouter::Local>().IsPrimary();
|
||||||
ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess;
|
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);
|
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) send configured MLR response for Reference Device
|
||||||
// TODO: (MLR) handle Commissioner Session TLV
|
// TODO: (MLR) handle Commissioner Session TLV
|
||||||
// TODO: (MLR) handle Timeout 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:
|
exit:
|
||||||
if (error == OT_ERROR_NONE)
|
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,
|
void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
|
||||||
const Ip6::MessageInfo & aMessageInfo,
|
const Ip6::MessageInfo & aMessageInfo,
|
||||||
ThreadStatusTlv::MlrStatus aStatus)
|
ThreadStatusTlv::MlrStatus aStatus,
|
||||||
|
Ip6::Address * aFailedAddresses,
|
||||||
|
uint8_t aFailedAddressNum)
|
||||||
{
|
{
|
||||||
otError error = OT_ERROR_NONE;
|
otError error = OT_ERROR_NONE;
|
||||||
Coap::Message *message = nullptr;
|
Coap::Message *message = nullptr;
|
||||||
@@ -113,7 +179,20 @@ void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message &
|
|||||||
|
|
||||||
SuccessOrExit(Tlv::AppendUint8Tlv(*message, ThreadTlv::kStatus, aStatus));
|
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));
|
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo));
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <openthread/backbone_router_ftd.h>
|
#include <openthread/backbone_router_ftd.h>
|
||||||
|
|
||||||
#include "backbone_router/bbr_leader.hpp"
|
#include "backbone_router/bbr_leader.hpp"
|
||||||
|
#include "backbone_router/multicast_listeners_table.hpp"
|
||||||
#include "common/locator.hpp"
|
#include "common/locator.hpp"
|
||||||
#include "net/netif.hpp"
|
#include "net/netif.hpp"
|
||||||
#include "thread/network_data.hpp"
|
#include "thread/network_data.hpp"
|
||||||
@@ -81,7 +82,20 @@ public:
|
|||||||
void ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus);
|
void ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gets the Multicast Listeners Table.
|
||||||
|
*
|
||||||
|
* @returns The Multicast Listeners Table.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
MulticastListenersTable &GetMulticastListenersTable(void) { return mMulticastListenersTable; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kTimerInterval = 1000,
|
||||||
|
};
|
||||||
|
|
||||||
static void HandleMulticastListenerRegistration(void * aContext,
|
static void HandleMulticastListenerRegistration(void * aContext,
|
||||||
otMessage * aMessage,
|
otMessage * aMessage,
|
||||||
const otMessageInfo *aMessageInfo)
|
const otMessageInfo *aMessageInfo)
|
||||||
@@ -92,7 +106,9 @@ private:
|
|||||||
void HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
void HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||||
void SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
|
void SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage,
|
||||||
const Ip6::MessageInfo & aMessageInfo,
|
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)
|
static void HandleDuaRegistration(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
|
||||||
{
|
{
|
||||||
@@ -104,11 +120,18 @@ private:
|
|||||||
const Ip6::MessageInfo & aMessageInfo,
|
const Ip6::MessageInfo & aMessageInfo,
|
||||||
const Ip6::Address & aTarget,
|
const Ip6::Address & aTarget,
|
||||||
ThreadStatusTlv::DuaStatus aStatus);
|
ThreadStatusTlv::DuaStatus aStatus);
|
||||||
|
|
||||||
void HandleNotifierEvents(Events aEvents);
|
void HandleNotifierEvents(Events aEvents);
|
||||||
|
|
||||||
|
static void HandleTimer(Timer &aTimer) { aTimer.GetOwner<Manager>().HandleTimer(); }
|
||||||
|
void HandleTimer(void);
|
||||||
|
|
||||||
Coap::Resource mMulticastListenerRegistration;
|
Coap::Resource mMulticastListenerRegistration;
|
||||||
Coap::Resource mDuaRegistration;
|
Coap::Resource mDuaRegistration;
|
||||||
|
|
||||||
|
MulticastListenersTable mMulticastListenersTable;
|
||||||
|
TimerMilli mTimer;
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||||
Ip6::InterfaceIdentifier mDuaResponseTargetMlIid;
|
Ip6::InterfaceIdentifier mDuaResponseTargetMlIid;
|
||||||
ThreadStatusTlv::DuaStatus mDuaResponseStatus;
|
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;
|
return mThreadNetif.mBackboneRouterManager;
|
||||||
}
|
}
|
||||||
|
template <> inline BackboneRouter::MulticastListenersTable &Instance::Get(void)
|
||||||
|
{
|
||||||
|
return mThreadNetif.mBackboneRouterManager.GetMulticastListenersTable();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_MLR_ENABLE || OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
|||||||
@@ -45,4 +45,19 @@
|
|||||||
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
|
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
|
||||||
#endif
|
#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_
|
#endif // CONFIG_BACKBONE_ROUTER_H_
|
||||||
|
|||||||
@@ -257,22 +257,24 @@ enum
|
|||||||
*/
|
*/
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
kRegistrationDelayDefault = 1200, ///< In seconds.
|
kRegistrationDelayDefault = 1200, ///< In seconds.
|
||||||
kMlrTimeoutDefault = 3600, ///< In seconds.
|
kMlrTimeoutDefault = 3600, ///< In seconds.
|
||||||
kMlrTimeoutMin = 300, ///< In seconds.
|
kMlrTimeoutMin = 300, ///< In seconds.
|
||||||
kBackboneRouterRegistrationJitter = 5, ///< In seconds.
|
kMlrTimeoutMax = 0x7fffffff / 1000, ///< In seconds (about 24 days).
|
||||||
kParentAggregateDelay = 5, ///< In seconds.
|
kBackboneRouterRegistrationJitter = 5, ///< In seconds.
|
||||||
kNoBufDelay = 5, ///< In seconds.
|
kParentAggregateDelay = 5, ///< In seconds.
|
||||||
kImmediateReRegisterDelay = 1, ///< In seconds.
|
kNoBufDelay = 5, ///< In seconds.
|
||||||
KResponseTimeoutDelay = 30, ///< In seconds.
|
kImmediateReRegisterDelay = 1, ///< In seconds.
|
||||||
kDuaDadPeriod = 100, ///< In seconds. Time period after which the address
|
KResponseTimeoutDelay = 30, ///< In seconds.
|
||||||
///< becomes "Preferred" if no duplicate address error.
|
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");
|
"kMlrTimeoutDefault must be larger than or equal to kMlrTimeoutMin");
|
||||||
|
|
||||||
static_assert(Mle::kParentAggregateDelay > 1, "kParentAggregateDelay should be larger than 1 second");
|
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
|
* State change of Child's DUA
|
||||||
|
|||||||
+175
-37
@@ -96,6 +96,7 @@ void MlrManager::UpdateLocalSubscriptions(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CheckInvariants();
|
||||||
ScheduleSend(0);
|
ScheduleSend(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,17 +118,6 @@ exit:
|
|||||||
return ret;
|
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
|
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
@@ -183,6 +173,7 @@ void MlrManager::UpdateProxiedSubscriptions(Child & aChild,
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
LogMulticastAddresses();
|
LogMulticastAddresses();
|
||||||
|
CheckInvariants();
|
||||||
|
|
||||||
if (aChild.HasAnyMlrToRegisterAddress())
|
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
|
#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
|
|
||||||
void MlrManager::ScheduleSend(uint16_t aDelay)
|
void MlrManager::ScheduleSend(uint16_t aDelay)
|
||||||
@@ -360,6 +338,7 @@ exit:
|
|||||||
|
|
||||||
otLogInfoMlr("Send MLR.req: %s", otThreadErrorToString(error));
|
otLogInfoMlr("Send MLR.req: %s", otThreadErrorToString(error));
|
||||||
LogMulticastAddresses();
|
LogMulticastAddresses();
|
||||||
|
CheckInvariants();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
|
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
|
||||||
@@ -368,25 +347,40 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message *
|
|||||||
{
|
{
|
||||||
OT_UNUSED_VARIABLE(aMessageInfo);
|
OT_UNUSED_VARIABLE(aMessageInfo);
|
||||||
|
|
||||||
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
|
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
|
||||||
otError error;
|
otError error = OT_ERROR_NONE;
|
||||||
|
uint16_t addressesOffset, addressesLength;
|
||||||
mMlrPending = false;
|
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
|
||||||
|
uint8_t failedAddressNum = 0;
|
||||||
|
|
||||||
VerifyOrExit(aResult == OT_ERROR_NONE && aMessage != nullptr, error = OT_ERROR_PARSE);
|
VerifyOrExit(aResult == OT_ERROR_NONE && aMessage != nullptr, error = OT_ERROR_PARSE);
|
||||||
VerifyOrExit(aMessage->GetCode() == OT_COAP_CODE_CHANGED, error = OT_ERROR_PARSE);
|
VerifyOrExit(aMessage->GetCode() == OT_COAP_CODE_CHANGED, error = OT_ERROR_PARSE);
|
||||||
|
|
||||||
SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, status));
|
SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, status));
|
||||||
|
|
||||||
exit:
|
if (ThreadTlv::FindTlvValueOffset(*aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset, addressesLength) ==
|
||||||
SetMulticastAddressMlrState(kMlrStateRegistering, status == ThreadStatusTlv::MlrStatus::kMlrSuccess
|
OT_ERROR_NONE)
|
||||||
? kMlrStateRegistered
|
{
|
||||||
: kMlrStateToRegister);
|
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = OT_ERROR_PARSE);
|
||||||
otLogInfoBbr("Receive MLR.rsp, result=%s, status=%d, error=%s", otThreadErrorToString(aResult), status,
|
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax, error = OT_ERROR_PARSE);
|
||||||
otThreadErrorToString(error));
|
|
||||||
LogMulticastAddresses();
|
|
||||||
|
|
||||||
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.
|
// keep sending until all multicast addresses are registered.
|
||||||
ScheduleSend(0);
|
ScheduleSend(0);
|
||||||
@@ -399,7 +393,6 @@ exit:
|
|||||||
// The Device has just attempted a Multicast Listener Registration which failed, and it retries the same
|
// 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].
|
// 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
|
// 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)
|
if (Get<BackboneRouter::Leader>().GetConfig(config) == OT_ERROR_NONE)
|
||||||
{
|
{
|
||||||
reregDelay = config.mReregistrationDelay > 1
|
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)
|
void MlrManager::HandleTimeTick(void)
|
||||||
{
|
{
|
||||||
if (mSendDelay > 0 && --mSendDelay == 0)
|
if (mSendDelay > 0 && --mSendDelay == 0)
|
||||||
@@ -428,6 +486,7 @@ void MlrManager::HandleTimeTick(void)
|
|||||||
void MlrManager::Reregister(void)
|
void MlrManager::Reregister(void)
|
||||||
{
|
{
|
||||||
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
|
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
|
||||||
|
CheckInvariants();
|
||||||
|
|
||||||
ScheduleSend(0);
|
ScheduleSend(0);
|
||||||
|
|
||||||
@@ -529,6 +588,85 @@ exit:
|
|||||||
return;
|
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
|
} // namespace ot
|
||||||
|
|
||||||
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
|
#endif // OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
|
|||||||
@@ -127,12 +127,10 @@ private:
|
|||||||
|
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||||
void UpdateLocalSubscriptions(void);
|
void UpdateLocalSubscriptions(void);
|
||||||
void SetNetifMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
|
|
||||||
bool IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const;
|
bool IsAddressMlrRegisteredByNetif(const Ip6::Address &aAddress) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||||
void SetChildMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
|
|
||||||
bool IsAddressMlrRegisteredByAnyChild(const Ip6::Address &aAddress) const
|
bool IsAddressMlrRegisteredByAnyChild(const Ip6::Address &aAddress) const
|
||||||
{
|
{
|
||||||
return IsAddressMlrRegisteredByAnyChildExcept(aAddress, nullptr);
|
return IsAddressMlrRegisteredByAnyChildExcept(aAddress, nullptr);
|
||||||
@@ -140,19 +138,17 @@ private:
|
|||||||
bool IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const;
|
bool IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAddress, const Child *aExceptChild) const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
|
void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState);
|
||||||
{
|
void FinishMulticastListenerRegistration(bool aSuccess,
|
||||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
const Ip6::Address *aFailedAddresses,
|
||||||
SetNetifMulticastAddressMlrState(aFromState, aToState);
|
uint8_t aFailedAddressNum);
|
||||||
#endif
|
|
||||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
|
||||||
SetChildMulticastAddressMlrState(aFromState, aToState);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax],
|
void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIPv6AddressesNumMax],
|
||||||
uint8_t & aAddressNum,
|
uint8_t & aAddressNum,
|
||||||
const Ip6::Address &aAddress);
|
const Ip6::Address &aAddress);
|
||||||
|
static bool AddressListContains(const Ip6::Address *aAddressList,
|
||||||
|
uint8_t aAddressListSize,
|
||||||
|
const Ip6::Address &aAddress);
|
||||||
|
|
||||||
void ScheduleSend(uint16_t aDelay);
|
void ScheduleSend(uint16_t aDelay);
|
||||||
void UpdateTimeTickerRegistration(void);
|
void UpdateTimeTickerRegistration(void);
|
||||||
@@ -161,6 +157,12 @@ private:
|
|||||||
void HandleTimeTick(void);
|
void HandleTimeTick(void);
|
||||||
|
|
||||||
void LogMulticastAddresses(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;
|
uint32_t mReregistrationDelay;
|
||||||
uint16_t mSendDelay;
|
uint16_t mSendDelay;
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ private:
|
|||||||
uint8_t mTlvs[kMaxSize];
|
uint8_t mTlvs[kMaxSize];
|
||||||
} OT_TOOL_PACKED_END;
|
} 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.
|
* This class implements IPv6 Addresses TLV generation and parsing.
|
||||||
@@ -302,7 +302,7 @@ public:
|
|||||||
}
|
}
|
||||||
} OT_TOOL_PACKED_END;
|
} 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
|
} // namespace ot
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ import time
|
|||||||
import unittest
|
import unittest
|
||||||
import binascii
|
import binascii
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
|
|
||||||
@@ -528,6 +530,35 @@ class Node:
|
|||||||
self.send_command(cmd)
|
self.send_command(cmd)
|
||||||
self._expect('Done')
|
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):
|
def set_link_quality(self, addr, lqi):
|
||||||
cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
|
cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
|
||||||
self.send_command(cmd)
|
self.send_command(cmd)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
from typing import Union, List
|
from typing import Union, List
|
||||||
|
|
||||||
@@ -35,6 +37,8 @@ import config
|
|||||||
import network_layer
|
import network_layer
|
||||||
import thread_cert
|
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)
|
_, BBR_1, BBR_2, ROUTER_1_2, ROUTER_1_1, SED_1, MED_1, MED_2, FED_1 = range(9)
|
||||||
|
|
||||||
WAIT_ATTACH = 5
|
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
|
assert (turn_on_bbr_2 or not turn_on_router_1_1) # ROUTER_1_1 needs BBR_2
|
||||||
|
|
||||||
# starting context id
|
# starting context id
|
||||||
|
t0 = time.time()
|
||||||
context_id = 1
|
context_id = 1
|
||||||
|
|
||||||
# Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router
|
# 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.simulator.go(WAIT_TIME)
|
||||||
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
|
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:
|
if turn_on_bbr_2:
|
||||||
# Bring up BBR_2, BBR_2 becomes Router and Secondary Backbone Router
|
# 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.simulator.go(WAIT_ATTACH)
|
||||||
self.assertEqual(self.nodes[SED_1].get_state(), 'child')
|
self.assertEqual(self.nodes[SED_1].get_state(), 'child')
|
||||||
|
|
||||||
|
logging.info("bootstrap takes %f seconds", time.time() - t0)
|
||||||
|
|
||||||
def test(self):
|
def test(self):
|
||||||
self._bootstrap()
|
self._bootstrap()
|
||||||
|
|
||||||
@@ -257,11 +265,171 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
# Expect MLR.req sent by ROUTER_1_2 and FED_1
|
# 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)
|
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):
|
def __check_mlr_ok(self, id, is_ftd, is_parent_1p1=False):
|
||||||
"""Check if MLR works for the node"""
|
"""Check if MLR works for the node"""
|
||||||
# Add MA1 and send MLR.req
|
# Add MA1 and send MLR.req
|
||||||
print("======== checking MLR: Node%d (%s), Parent=%s ========" %
|
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'))
|
(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
|
expect_mlr_req = is_ftd or is_parent_1p1
|
||||||
|
|
||||||
if id == ROUTER_1_2:
|
if id == ROUTER_1_2:
|
||||||
@@ -278,11 +446,11 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
|
|
||||||
for addr in [MA5, MA6]:
|
for addr in [MA5, MA6]:
|
||||||
self.__check_ipmaddr_add(id, parent_id, addr, expect_mlr_req=False, expect_mlr_req_proxied=False)
|
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):
|
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"""
|
"""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.flush_all()
|
||||||
self.nodes[id].add_ipmaddr(addr)
|
self.nodes[id].add_ipmaddr(addr)
|
||||||
self.assertTrue(self.nodes[id].has_ipmaddr(addr))
|
self.assertTrue(self.nodes[id].has_ipmaddr(addr))
|
||||||
@@ -315,12 +483,14 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
addrs: Union[List[str], str],
|
addrs: Union[List[str], str],
|
||||||
should_send=True,
|
should_send=True,
|
||||||
expect_mlr_rsp=False,
|
expect_mlr_rsp=False,
|
||||||
|
expect_mlr_rsp_status=0,
|
||||||
expect_mlr_req_num=None,
|
expect_mlr_req_num=None,
|
||||||
expect_unique_reg=False):
|
expect_unique_reg=False):
|
||||||
if isinstance(addrs, str):
|
if isinstance(addrs, str):
|
||||||
addrs = [addrs]
|
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:
|
if should_send:
|
||||||
for addr in addrs:
|
for addr in addrs:
|
||||||
self.assertIn(ipaddress.IPv6Address(addr), reg_mas)
|
self.assertIn(ipaddress.IPv6Address(addr), reg_mas)
|
||||||
@@ -329,13 +499,37 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
|
|
||||||
# BBR should send MLR.rsp ACK
|
# BBR should send MLR.rsp ACK
|
||||||
if expect_mlr_rsp:
|
if expect_mlr_rsp:
|
||||||
messages = self.simulator.get_messages_sent_by(BBR_1)
|
message_id = message_ids[0]
|
||||||
messages.next_coap_message('2.04')
|
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:
|
else:
|
||||||
for addr in addrs:
|
for addr in addrs:
|
||||||
self.assertNotIn(ipaddress.IPv6Address(addr), reg_mas)
|
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"""
|
"""Get MAs registered via MLR.req by the node"""
|
||||||
messages = self.simulator.get_messages_sent_by(id)
|
messages = self.simulator.get_messages_sent_by(id)
|
||||||
reg_mas = []
|
reg_mas = []
|
||||||
@@ -343,10 +537,14 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
msg = messages.next_coap_message('0.02', '/n/mr', assert_enabled=False)
|
msg = messages.next_coap_message('0.02', '/n/mr', assert_enabled=False)
|
||||||
if not msg:
|
if not msg:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
logging.info("MLR.req: %s %s" % (msg.coap.message_id, msg.coap.payload))
|
||||||
addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses)
|
addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses)
|
||||||
reg_mas.append(addrs)
|
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:
|
if expect_mlr_req_num is not None:
|
||||||
self.assertEqual(len(reg_mas), expect_mlr_req_num)
|
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):
|
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"""
|
"""Check if MLR works that a node can renew it's registered MAs"""
|
||||||
|
assert self.pbbr_id == BBR_1
|
||||||
self.flush_all()
|
self.flush_all()
|
||||||
self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
|
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):
|
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"""
|
"""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
|
# Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY
|
||||||
|
assert self.pbbr_id == BBR_1
|
||||||
|
|
||||||
self.flush_all()
|
self.flush_all()
|
||||||
self.nodes[BBR_1].disable_backbone_router()
|
self.nodes[BBR_1].disable_backbone_router()
|
||||||
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
|
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
|
||||||
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Primary')
|
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Primary')
|
||||||
|
self.pbbr_id = BBR_2
|
||||||
|
|
||||||
self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
|
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)
|
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.nodes[BBR_2].enable_backbone_router()
|
||||||
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
|
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
|
||||||
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
|
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
|
||||||
|
self.pbbr_id = BBR_1
|
||||||
|
|
||||||
def __switch_to_1_1_parent(self):
|
def __switch_to_1_1_parent(self):
|
||||||
"""Check if MLR works when nodes are switching to a 1.1 parent"""
|
"""Check if MLR works when nodes are switching to a 1.1 parent"""
|
||||||
# Add MA1 to EDs to prepare for parent switching
|
# 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()
|
self.flush_all()
|
||||||
|
|
||||||
@@ -468,7 +672,7 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
|||||||
def __switch_to_1_2_parent(self):
|
def __switch_to_1_2_parent(self):
|
||||||
"""Check if MLR works when nodes are switching to a 1.2 parent"""
|
"""Check if MLR works when nodes are switching to a 1.2 parent"""
|
||||||
# Add MA1 to EDs to prepare for parent switching
|
# 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()
|
self.flush_all()
|
||||||
|
|
||||||
|
|||||||
@@ -336,6 +336,29 @@ target_link_libraries(test-message-queue
|
|||||||
|
|
||||||
add_test(NAME test-message-queue COMMAND 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
|
add_executable(test-netif
|
||||||
${COMMON_SOURCES}
|
${COMMON_SOURCES}
|
||||||
test_netif.cpp
|
test_netif.cpp
|
||||||
@@ -513,7 +536,7 @@ target_link_libraries(test-timer
|
|||||||
add_test(NAME test-timer COMMAND test-timer)
|
add_test(NAME test-timer COMMAND test-timer)
|
||||||
|
|
||||||
set_target_properties(
|
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
|
PROPERTIES
|
||||||
C_STANDARD 99
|
C_STANDARD 99
|
||||||
CXX_STANDARD 11
|
CXX_STANDARD 11
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ check_PROGRAMS += \
|
|||||||
test-mac-frame \
|
test-mac-frame \
|
||||||
test-message \
|
test-message \
|
||||||
test-message-queue \
|
test-message-queue \
|
||||||
|
test-multicast-listeners-table \
|
||||||
test-netif \
|
test-netif \
|
||||||
test-network-data \
|
test-network-data \
|
||||||
test-pool \
|
test-pool \
|
||||||
@@ -209,6 +210,9 @@ test_message_SOURCES = $(COMMON_SOURCES) test_message.cpp
|
|||||||
test_message_queue_LDADD = $(COMMON_LDADD)
|
test_message_queue_LDADD = $(COMMON_LDADD)
|
||||||
test_message_queue_SOURCES = $(COMMON_SOURCES) test_message_queue.cpp
|
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_LDADD = $(COMMON_LDADD)
|
||||||
test_spinel_buffer_SOURCES = $(COMMON_SOURCES) test_spinel_buffer.cpp
|
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