From 091f68ed706ce7a4831802408cdd0b0b4f309e3b Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 28 Feb 2023 23:01:43 -0800 Subject: [PATCH] [child-supervision] mechanism to inform interval from child to parent (#7993) This commit contains changes to child supervision feature. It adds a new mechanism for a child to inform its desired supervision interval to its parent. A new optional MLE TLV is added with type 27 and value of `uint16_t` indicating the supervision interval in seconds. This TLV is included in MLE Child ID Request and MLE Child Update Request (sent from the child). The parent echoes back this TLV in the corresponding responses to indicate that it supports supervision. This commit changes the parent implementation to track the supervision interval per child. This commit also updates the OT public APIs along with the related CLI commands: - API now allows the supervision interval to be set on a child. - New field in `otChildInfo` to indicate the child's supervision interval. - New counter is added to track the number of supervision check timeouts failures on a child (intended for testing and debugging). This commit adds a test `test_child_supervision` to cover behavior of child supervision and its new behaviors. This change allows backward compatibility: If the parent does not support child supervision, it ignores the new TLV in the MLE messages and the child would fall back to periodically exchanging MLE Child Update Request with parent. If the child does not support supervision and/or does not indicate its desired child supervision interval, the parent will fall back to use the configured default interval. --- etc/cmake/options.cmake | 1 - examples/Makefile-simulation | 1 - examples/README.md | 1 - examples/common-switches.mk | 5 - include/openthread/child_supervision.h | 36 ++-- include/openthread/instance.h | 2 +- include/openthread/thread_ftd.h | 1 + script/check-arm-build | 1 - script/check-scan-build | 1 - script/check-simulation-build-autotools | 2 - script/check-size | 1 - script/cmake-build | 1 - script/make-pretty | 1 - src/cli/README.md | 30 ++- src/cli/cli.cpp | 35 ++-- src/core/BUILD.gn | 8 +- src/core/CMakeLists.txt | 2 +- src/core/Makefile.am | 4 +- src/core/api/child_supervision_api.cpp | 24 ++- src/core/common/instance.cpp | 3 - src/core/common/instance.hpp | 14 +- src/core/common/notifier.cpp | 4 +- src/core/common/time_ticker.cpp | 4 +- src/core/common/time_ticker.hpp | 2 +- src/core/config/child_supervision.h | 35 ++-- .../config/openthread-core-config-check.h | 8 + .../{utils => thread}/child_supervision.cpp | 54 +++-- .../{utils => thread}/child_supervision.hpp | 69 +++---- src/core/thread/indirect_sender.cpp | 6 +- src/core/thread/indirect_sender.hpp | 6 - src/core/thread/mesh_forwarder.cpp | 12 +- src/core/thread/mesh_forwarder_ftd.cpp | 4 +- src/core/thread/mle.cpp | 11 + src/core/thread/mle.hpp | 16 +- src/core/thread/mle_router.cpp | 53 +++++ src/core/thread/mle_router.hpp | 3 + src/core/thread/mle_tlvs.hpp | 7 + src/core/thread/topology.cpp | 35 ++-- src/core/thread/topology.hpp | 23 ++- src/ncp/ncp_base.cpp | 2 - src/ncp/ncp_base_dispatcher.cpp | 4 - src/ncp/ncp_base_ftd.cpp | 6 - src/ncp/ncp_base_mtd.cpp | 6 - src/posix/Makefile-posix | 1 - tests/fuzz/oss-fuzz-build | 1 - tests/scripts/expect/cli-child.exp | 6 +- tests/scripts/thread-cert/Makefile.am | 2 + tests/scripts/thread-cert/mle.py | 1 + tests/scripts/thread-cert/node.py | 33 ++- .../thread-cert/test_child_supervision.py | 189 ++++++++++++++++++ .../toranj/ncp/test-018-child-supervision.py | 180 ----------------- tests/toranj/openthread-core-toranj-config.h | 8 - tests/toranj/start.sh | 1 - 53 files changed, 541 insertions(+), 425 deletions(-) rename src/core/{utils => thread}/child_supervision.cpp (85%) rename src/core/{utils => thread}/child_supervision.hpp (89%) create mode 100755 tests/scripts/thread-cert/test_child_supervision.py delete mode 100644 tests/toranj/ncp/test-018-child-supervision.py diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 73d491994..e3de0c8b2 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -88,7 +88,6 @@ ot_option(OT_BORDER_ROUTING OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE "border rout ot_option(OT_BORDER_ROUTING_COUNTERS OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE "border routing counters") ot_option(OT_CHANNEL_MANAGER OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE "channel manager") ot_option(OT_CHANNEL_MONITOR OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE "channel monitor") -ot_option(OT_CHILD_SUPERVISION OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE "child supervision") ot_option(OT_COAP OPENTHREAD_CONFIG_COAP_API_ENABLE "coap api") ot_option(OT_COAP_BLOCK OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE "coap block-wise transfer (RFC7959)") ot_option(OT_COAP_OBSERVE OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE "coap observe (RFC7641)") diff --git a/examples/Makefile-simulation b/examples/Makefile-simulation index a803486c6..f7577f723 100644 --- a/examples/Makefile-simulation +++ b/examples/Makefile-simulation @@ -45,7 +45,6 @@ COAPS ?= 1 COMMISSIONER ?= 1 CHANNEL_MANAGER ?= 1 CHANNEL_MONITOR ?= 1 -CHILD_SUPERVISION ?= 1 DATASET_UPDATER ?= 1 DHCP6_CLIENT ?= 1 DHCP6_SERVER ?= 1 diff --git a/examples/README.md b/examples/README.md index 9c05718ef..a6d282320 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,7 +16,6 @@ This page lists the available common switches with description. Unless stated ot | BUILTIN_MBEDTLS_MANAGEMENT | OT_BUILTIN_MBEDTLS_MANAGEMENT | Enables the built-in mbedTLS management. Enable this switch if the external mbedTLS is used, but mbedTLS memory allocation and debug config should be managed internally by OpenThread. | | CHANNEL_MANAGER | OT_CHANNEL_MANAGER | Enables support for channel manager. Enable this switch on devices that are supposed to request a Thread network channel change. This switch should be used only with an FTD build. | | CHANNEL_MONITOR | OT_CHANNEL_MONITOR | Enables support for channel monitor. Enable this switch on devices that are supposed to determine the cleaner channels. | -| CHILD_SUPERVISION | OT_CHILD_SUPERVISION | Enables support for [child supervision](https://openthread.io/guides/build/features/child-supervision). Enable this switch on a parent or child node with custom OpenThread application that manages the supervision, checks timeout intervals, and verifies connectivity between parent and child. | | COAP | OT_COAP | Enables support for the CoAP API. Enable this switch if you want to control Constrained Application Protocol communication. | | COAP_OBSERVE | OT_COAP_OBSERVE | Enables support for CoAP Observe (RFC7641) API. | | COAPS | OT_COAPS | Enables support for the secure CoAP API. Enable this switch if you want to control Constrained Application Protocol Secure (CoAP over DTLS) communication. | diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 0bf04fd8b..01f5f84b4 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -42,7 +42,6 @@ COMMISSIONER ?= 0 COVERAGE ?= 0 CHANNEL_MANAGER ?= 0 CHANNEL_MONITOR ?= 0 -CHILD_SUPERVISION ?= 0 DATASET_UPDATER ?= 0 DEBUG ?= 0 DHCP6_CLIENT ?= 0 @@ -159,10 +158,6 @@ ifeq ($(CHANNEL_MONITOR),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1 endif -ifeq ($(CHILD_SUPERVISION),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1 -endif - ifeq ($(CSL_RECEIVER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1 endif diff --git a/include/openthread/child_supervision.h b/include/openthread/child_supervision.h index 3d47ba1d6..100e0280c 100644 --- a/include/openthread/child_supervision.h +++ b/include/openthread/child_supervision.h @@ -29,7 +29,7 @@ /** * @file * @brief - * This file includes the OpenThread API for child supervision feature + * This file includes the OpenThread API for child supervision feature. */ #ifndef OPENTHREAD_CHILD_SUPERVISION_H_ @@ -47,38 +47,35 @@ extern "C" { * @brief * This module includes functions for child supervision feature. * - * The functions in this module are available when child supervision feature - * (`OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE`) is enabled. - * * @{ * */ /** - * Gets the child supervision interval (in seconds). + * Gets the child supervision interval (in seconds) on a child. * - * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child - * within the supervision interval. If there is no transmission to the child within the supervision interval, - * OpenThread enqueues and sends a supervision message (a data message with empty payload) to the child. + * Child supervision feature provides a mechanism for a sleepy child to ask its parent to ensure to send a message to + * it within the supervision interval. If there is no transmission to the child within the supervision interval, + * parent sends a supervision message (a data message with empty payload) to the child. * * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns The child supervision interval. Zero indicates that child supervision is disabled. + * @returns The child supervision interval. Zero indicates that supervision is disabled. * */ uint16_t otChildSupervisionGetInterval(otInstance *aInstance); /** - * Sets the child supervision interval (in seconds). + * Sets the child supervision interval (in seconds) on the child. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision on parent. + * @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision. * */ void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval); /** - * Gets the supervision check timeout interval (in seconds). + * Gets the supervision check timeout interval (in seconds) on the child. * * If the device is a sleepy child and it does not hear from its parent within the specified check timeout, it initiates * the re-attach process (MLE Child Update Request/Response exchange with its parent). @@ -99,6 +96,21 @@ uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance); */ void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout); +/** + * Get the value of supervision check timeout failure counter. + * + * The counter tracks the number of supervision check failures on the child. It is incremented when the child does + * not hear from its parent within the specified check timeout interval. + * + */ +uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance); + +/** + * Reset the supervision check timeout failure counter to zero. + * + */ +void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 5f536034d..aa4f01c82 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (291) +#define OPENTHREAD_API_VERSION (292) /** * @addtogroup api-instance diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index 994ab4851..37cd9fc50 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -68,6 +68,7 @@ typedef struct uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. uint16_t mQueuedMessageCnt; ///< Number of queued messages for the child. + uint16_t mSupervisionInterval; ///< Supervision interval (in seconds). uint8_t mVersion; ///< MLE version bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mFullThreadDevice : 1; ///< Full Thread Device diff --git a/script/check-arm-build b/script/check-arm-build index 955ccc80f..a2dff828d 100755 --- a/script/check-arm-build +++ b/script/check-arm-build @@ -44,7 +44,6 @@ build_nrf52840() "-DOT_BORDER_ROUTER=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_COAP=ON" "-DOT_COAPS=ON" "-DOT_COMMISSIONER=ON" diff --git a/script/check-scan-build b/script/check-scan-build index b126d1719..13c653e12 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -48,7 +48,6 @@ OT_BUILD_OPTIONS=( "-DOT_COVERAGE=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_DATASET_UPDATER=ON" "-DOT_DHCP6_CLIENT=ON" "-DOT_DHCP6_SERVER=ON" diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 075b20990..118a293a4 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -47,7 +47,6 @@ build_all_features() "-DOPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" - "-DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" "-DOPENTHREAD_CONFIG_COAP_API_ENABLE=1" "-DOPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE=1" "-DOPENTHREAD_CONFIG_COMMISSIONER_ENABLE=1" @@ -144,7 +143,6 @@ build_nest_common() "-DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" - "-DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" "-DOPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE=1" "-DOPENTHREAD_CONFIG_DIAG_ENABLE=1" "-DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1" diff --git a/script/check-size b/script/check-size index fef1682d8..482dddf1a 100755 --- a/script/check-size +++ b/script/check-size @@ -126,7 +126,6 @@ size_nrf52840_version() "-DOT_BORDER_ROUTER=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_COAP=ON" "-DOT_COAPS=ON" "-DOT_COMMISSIONER=ON" diff --git a/script/cmake-build b/script/cmake-build index 5aa172388..8d726831a 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -81,7 +81,6 @@ OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_COMMISSIONER=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_DATASET_UPDATER=ON" "-DOT_DHCP6_CLIENT=ON" "-DOT_DHCP6_SERVER=ON" diff --git a/script/make-pretty b/script/make-pretty index ecfe33d31..c61c442a9 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -95,7 +95,6 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_BORDER_ROUTING=ON' '-DOT_CHANNEL_MANAGER=ON' '-DOT_CHANNEL_MONITOR=ON' - '-DOT_CHILD_SUPERVISION=ON' '-DOT_COAP=ON' '-DOT_COAP_BLOCK=ON' '-DOT_COAP_OBSERVE=ON' diff --git a/src/cli/README.md b/src/cli/README.md index 9c8a9e0b8..616a3ab0d 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -623,10 +623,10 @@ Print table of attached children. ```bash > child table -| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | -+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ -| 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | -| 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | +| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC | ++-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+ +| 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 129 | 4ecede68435358ac | +| 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | 0 | a672a601d2ce37d8 | Done ``` @@ -646,6 +646,7 @@ Timeout: 100 Age: 0 Link Quality In: 3 RSSI: -20 +Supervision Interval: 129 Done ``` @@ -741,6 +742,27 @@ Set the Child Supervision Check Timeout value. Done ``` +### childsupervision failcounter + +Get the current value of supervision check timeout failure counter. + +The counter tracks the number of supervision check failures on the child. It is incremented when the child does not hear from its parent within the specified check timeout interval. + +```bash +> childsupervision failcounter +0 +Done +``` + +### childsupervision failcounter reset + +Reset the supervision check timeout failure counter to zero. + +```bash +> childsupervision failcounter reset +Done +``` + ### childtimeout Get the Thread Child Timeout value. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 3114ab77b..7b2a359f7 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -58,9 +59,6 @@ #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE -#include -#endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #include #endif @@ -1894,12 +1892,12 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (isTable) { static const char *const kChildTableTitles[] = { - "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R", - "D", "N", "Ver", "CSL", "QMsgCnt", "Extended MAC", + "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R", + "D", "N", "Ver", "CSL", "QMsgCnt", "Suprvsn", "Extended MAC", }; static const uint8_t kChildTableColumnWidths[] = { - 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 18, + 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 7, 18, }; OutputTableHeader(kChildTableTitles, kChildTableColumnWidths); @@ -1929,6 +1927,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputFormat("|%3u", childInfo.mVersion); OutputFormat("| %1d ", childInfo.mIsCslSynced); OutputFormat("| %5u ", childInfo.mQueuedMessageCnt); + OutputFormat("| %5u ", childInfo.mSupervisionInterval); OutputFormat("| "); OutputExtAddress(childInfo.mExtAddress); OutputLine(" |"); @@ -1991,6 +1990,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputLine("Age: %lu", ToUlong(childInfo.mAge)); OutputLine("Link Quality In: %u", childInfo.mLinkQualityIn); OutputLine("RSSI: %d", childInfo.mAverageRssi); + OutputLine("Supervision Interval: %d", childInfo.mSupervisionInterval); exit: return error; @@ -2099,7 +2099,6 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_FTD -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; @@ -2127,7 +2126,6 @@ template <> otError Interpreter::Process(Arg aArgs[]) */ error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout); } -#if OPENTHREAD_FTD /** * @cli childsupervision interval * @code @@ -2135,8 +2133,6 @@ template <> otError Interpreter::Process(Arg aArgs[]) * 30 * Done * @endcode - * @par - * This command can only be used with FTD devices. * @par api_copy * #otChildSupervisionGetInterval */ @@ -2149,18 +2145,27 @@ template <> otError Interpreter::Process(Arg aArgs[]) * Done * @endcode * @cparam childsupervision interval @ca{interval-seconds} - * @par - * This command can only be used with FTD devices. * @par api_copy * #otChildSupervisionSetInterval */ error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval); } -#endif + else if (aArgs[0] == "failcounter") + { + if (aArgs[1].IsEmpty()) + { + OutputLine("%u", otChildSupervisionGetCheckFailureCounter(GetInstancePtr())); + error = OT_ERROR_NONE; + } + else if (aArgs[1] == "reset") + { + otChildSupervisionResetCheckFailureCounter(GetInstancePtr()); + error = OT_ERROR_NONE; + } + } return error; } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE /** @cli childtimeout * @code @@ -7637,9 +7642,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) CmdEntry("childmax"), CmdEntry("childrouterlinks"), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE CmdEntry("childsupervision"), -#endif CmdEntry("childtimeout"), #if OPENTHREAD_CONFIG_COAP_API_ENABLE CmdEntry("coap"), diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index d6abf3b04..6ec802b03 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -98,10 +98,6 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" ] } - if (openthread_config_child_supervision_enable) { - defines += [ "OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" ] - } - if (openthread_config_coap_api_enable) { defines += [ "OPENTHREAD_CONFIG_COAP_API_ENABLE=1" ] } @@ -602,6 +598,8 @@ openthread_core_files = [ "thread/anycast_locator.cpp", "thread/anycast_locator.hpp", "thread/child_mask.hpp", + "thread/child_supervision.cpp", + "thread/child_supervision.hpp", "thread/child_table.cpp", "thread/child_table.hpp", "thread/csl_tx_scheduler.cpp", @@ -688,8 +686,6 @@ openthread_core_files = [ "utils/channel_manager.hpp", "utils/channel_monitor.cpp", "utils/channel_monitor.hpp", - "utils/child_supervision.cpp", - "utils/child_supervision.hpp", "utils/flash.cpp", "utils/flash.hpp", "utils/heap.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0f5521b42..26bfe37ea 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -192,6 +192,7 @@ set(COMMON_SOURCES thread/announce_begin_server.cpp thread/announce_sender.cpp thread/anycast_locator.cpp + thread/child_supervision.cpp thread/child_table.cpp thread/csl_tx_scheduler.cpp thread/discover_scanner.cpp @@ -233,7 +234,6 @@ set(COMMON_SOURCES thread/uri_paths.cpp utils/channel_manager.cpp utils/channel_monitor.cpp - utils/child_supervision.cpp utils/flash.cpp utils/heap.cpp utils/history_tracker.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index c0cdb6763..d88f5f7bb 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -282,6 +282,7 @@ SOURCES_COMMON = \ thread/announce_begin_server.cpp \ thread/announce_sender.cpp \ thread/anycast_locator.cpp \ + thread/child_supervision.cpp \ thread/child_table.cpp \ thread/csl_tx_scheduler.cpp \ thread/discover_scanner.cpp \ @@ -323,7 +324,6 @@ SOURCES_COMMON = \ thread/uri_paths.cpp \ utils/channel_manager.cpp \ utils/channel_monitor.cpp \ - utils/child_supervision.cpp \ utils/flash.cpp \ utils/heap.cpp \ utils/history_tracker.cpp \ @@ -602,6 +602,7 @@ HEADERS_COMMON = \ thread/announce_sender.hpp \ thread/anycast_locator.hpp \ thread/child_mask.hpp \ + thread/child_supervision.hpp \ thread/child_table.hpp \ thread/csl_tx_scheduler.hpp \ thread/discover_scanner.hpp \ @@ -647,7 +648,6 @@ HEADERS_COMMON = \ thread/version.hpp \ utils/channel_manager.hpp \ utils/channel_monitor.hpp \ - utils/child_supervision.hpp \ utils/flash.hpp \ utils/heap.hpp \ utils/history_tracker.hpp \ diff --git a/src/core/api/child_supervision_api.cpp b/src/core/api/child_supervision_api.cpp index ab2dcecc7..d24a17d33 100644 --- a/src/core/api/child_supervision_api.cpp +++ b/src/core/api/child_supervision_api.cpp @@ -33,8 +33,6 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #include #include "common/as_core_type.hpp" @@ -42,28 +40,32 @@ using namespace ot; -#if OPENTHREAD_FTD - uint16_t otChildSupervisionGetInterval(otInstance *aInstance) { - return AsCoreType(aInstance).Get().GetSupervisionInterval(); + return AsCoreType(aInstance).Get().GetInterval(); } void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval) { - AsCoreType(aInstance).Get().SetSupervisionInterval(aInterval); + AsCoreType(aInstance).Get().SetInterval(aInterval); } -#endif - uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance) { - return AsCoreType(aInstance).Get().GetTimeout(); + return AsCoreType(aInstance).Get().GetTimeout(); } void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout) { - AsCoreType(aInstance).Get().SetTimeout(aTimeout); + AsCoreType(aInstance).Get().SetTimeout(aTimeout); } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCounter(); +} + +void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ResetCounter(); +} diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 9b3c886fb..4672468f6 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -175,13 +175,10 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE , mSrpServer(*this) #endif - -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD , mChildSupervisor(*this) #endif , mSupervisionListener(*this) -#endif , mAnnounceBegin(*this) , mPanIdQuery(*this) , mEnergyScan(*this) diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 6c6ca1d44..9fe4de1f7 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -101,6 +101,7 @@ #include "thread/announce_begin_server.hpp" #include "thread/announce_sender.hpp" #include "thread/anycast_locator.hpp" +#include "thread/child_supervision.hpp" #include "thread/discover_scanner.hpp" #include "thread/dua_manager.hpp" #include "thread/energy_scan_server.hpp" @@ -123,7 +124,6 @@ #include "thread/tmf.hpp" #include "utils/channel_manager.hpp" #include "utils/channel_monitor.hpp" -#include "utils/child_supervision.hpp" #include "utils/heap.hpp" #include "utils/history_tracker.hpp" #include "utils/jam_detector.hpp" @@ -544,12 +544,10 @@ private: Srp::Server mSrpServer; #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD - Utils::ChildSupervisor mChildSupervisor; -#endif - Utils::SupervisionListener mSupervisionListener; + ChildSupervisor mChildSupervisor; #endif + SupervisionListener mSupervisionListener; AnnounceBeginServer mAnnounceBegin; PanIdQueryServer mPanIdQuery; @@ -849,12 +847,10 @@ template <> inline Utils::JamDetector &Instance::Get(void) { return mJamDetector template <> inline Sntp::Client &Instance::Get(void) { return mSntpClient; } #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD -template <> inline Utils::ChildSupervisor &Instance::Get(void) { return mChildSupervisor; } -#endif -template <> inline Utils::SupervisionListener &Instance::Get(void) { return mSupervisionListener; } +template <> inline ChildSupervisor &Instance::Get(void) { return mChildSupervisor; } #endif +template <> inline SupervisionListener &Instance::Get(void) { return mSupervisionListener; } #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE template <> inline Utils::PingSender &Instance::Get(void) { return mPingSender; } diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp index 8a60c664b..4cc5a9634 100644 --- a/src/core/common/notifier.cpp +++ b/src/core/common/notifier.cpp @@ -133,9 +133,7 @@ void Notifier::EmitEvents(void) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE Get().HandleNotifierEvents(events); #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().HandleNotifierEvents(events); -#endif + Get().HandleNotifierEvents(events); #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE Get().HandleNotifierEvents(events); #endif diff --git a/src/core/common/time_ticker.cpp b/src/core/common/time_ticker.cpp index 1d1d56224..21845c803 100644 --- a/src/core/common/time_ticker.cpp +++ b/src/core/common/time_ticker.cpp @@ -96,12 +96,10 @@ void TimeTicker::HandleTimer(void) } #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE if (mReceivers & Mask(kChildSupervisor)) { - Get().HandleTimeTick(); + Get().HandleTimeTick(); } -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE diff --git a/src/core/common/time_ticker.hpp b/src/core/common/time_ticker.hpp index 0ffebf1ae..0db6df238 100644 --- a/src/core/common/time_ticker.hpp +++ b/src/core/common/time_ticker.hpp @@ -67,7 +67,7 @@ public: kMeshForwarder, ///< `MeshForwarder` kMleRouter, ///< `Mle::MleRouter` kAddressResolver, ///< `AddressResolver` - kChildSupervisor, ///< `Utils::ChildSupervisor` + kChildSupervisor, ///< `ChildSupervisor` kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages) kDuaManager, ///< `DuaManager` kMlrManager, ///< `MlrManager` diff --git a/src/core/config/child_supervision.h b/src/core/config/child_supervision.h index 282e5a0f3..94e8ec63a 100644 --- a/src/core/config/child_supervision.h +++ b/src/core/config/child_supervision.h @@ -35,24 +35,12 @@ #ifndef CONFIG_CHILD_SUPERVISION_H_ #define CONFIG_CHILD_SUPERVISION_H_ -/** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - * - * Define to 1 to enable Child Supervision support. - * - */ -#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 0 -#endif - /** * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL * - * The default supervision interval in seconds used by parent. Set to zero to disable the supervision process on the - * parent. + * The default supervision interval in seconds to use when in child state. Zero indicates no supervision needed. * - * Applicable only if child supervision feature is enabled (i.e., - * `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE ` is set). + * The current supervision interval can be changed using `otChildSupervisionSetInterval()`. * * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child * within the supervision interval. If there is no transmission to the child within the supervision interval, child @@ -69,7 +57,7 @@ * The default supervision check timeout interval (in seconds) used by a device in child state. Set to zero to disable * the supervision check process on the child. * - * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE` is set). + * The check timeout interval can be changed using `otChildSupervisionSetCheckTimeout()`. * * If the sleepy child does not hear from its parent within the specified timeout interval, it initiates the re-attach * process (MLE Child Update Request/Response exchange with its parent). @@ -80,15 +68,22 @@ #endif /** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST + * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL * - * Define as 1 to clear/disable 15.4 ack request in the MAC header of a supervision message. + * Specifies the default supervision interval to use on parent for children that do not explicitly indicate their + * desired supervision internal (do not include a "Supervision Interval TLV") and are running older Thread versions + * (version <= 1.3.0). * - * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE` is set). + * This config is added to allow backward compatibility on parent with SED children that used Child Supervision + * feature in OT stack before adoption of it by Thread specification and addition of the "Supervision Interval TLV" as + * the mechanism for child to inform the parent of its desired supervision interval. + * + * The config can be set to zero to effectively disable it, i.e., if a child does not provide "Supervision Interval TLV" + * it indicates that it does not want to be supervised and then parent will use zero interval for the child. * */ -#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST 0 +#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL +#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL 129 #endif #endif // CONFIG_CHILD_SUPERVISION_H_ diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index 29fdac250..991214770 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -640,4 +640,12 @@ #error "OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE was replaced by OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE" #endif +#ifdef OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +#error "OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE is removed. The feature is now supported by default (on 1.3.0)" +#endif + +#ifdef OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST +#error "OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST is removed". +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/src/core/utils/child_supervision.cpp b/src/core/thread/child_supervision.cpp similarity index 85% rename from src/core/utils/child_supervision.cpp rename to src/core/thread/child_supervision.cpp index bec322689..51237a6aa 100644 --- a/src/core/utils/child_supervision.cpp +++ b/src/core/thread/child_supervision.cpp @@ -41,26 +41,16 @@ #include "thread/thread_netif.hpp" namespace ot { -namespace Utils { RegisterLogModule("ChildSupervsn"); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD ChildSupervisor::ChildSupervisor(Instance &aInstance) : InstanceLocator(aInstance) - , mSupervisionInterval(kDefaultSupervisionInterval) { } -void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval) -{ - mSupervisionInterval = aInterval; - CheckState(); -} - Child *ChildSupervisor::GetDestination(const Message &aMessage) const { Child *child = nullptr; @@ -108,9 +98,14 @@ void ChildSupervisor::HandleTimeTick(void) { for (Child &child : Get().Iterate(Child::kInStateValid)) { + if (child.IsRxOnWhenIdle() || (child.GetSupervisionInterval() == 0)) + { + continue; + } + child.IncrementSecondsSinceLastSupervision(); - if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && !child.IsRxOnWhenIdle()) + if (child.GetSecondsSinceLastSupervision() >= child.GetSupervisionInterval()) { SendMessage(child); } @@ -119,14 +114,11 @@ void ChildSupervisor::HandleTimeTick(void) void ChildSupervisor::CheckState(void) { - bool shouldRun = false; + // Child Supervision should run if Thread MLE operation is + // enabled, and there is at least one "valid" child in the + // child table. - // Child Supervision should run if `mSupervisionInterval` is not - // zero, Thread MLE operation is enabled, and there is at least one - // "valid" child in the child table. - - shouldRun = ((mSupervisionInterval != 0) && !Get().IsDisabled() && - Get().HasChildren(Child::kInStateValid)); + bool shouldRun = (!Get().IsDisabled() && Get().HasChildren(Child::kInStateValid)); if (shouldRun && !Get().IsReceiverRegistered(TimeTicker::kChildSupervisor)) { @@ -154,6 +146,7 @@ void ChildSupervisor::HandleNotifierEvents(Events aEvents) SupervisionListener::SupervisionListener(Instance &aInstance) : InstanceLocator(aInstance) , mTimeout(0) + , mInterval(kDefaultInterval) , mTimer(aInstance) { SetTimeout(kDefaultTimeout); @@ -163,10 +156,29 @@ void SupervisionListener::Start(void) { RestartTimer(); } void SupervisionListener::Stop(void) { mTimer.Stop(); } +void SupervisionListener::SetInterval(uint16_t aInterval) +{ + VerifyOrExit(mInterval != aInterval); + + LogInfo("Interval: %u -> %u", mInterval, aInterval); + + mInterval = aInterval; + + if (Get().IsChild()) + { + IgnoreError(Get().SendChildUpdateRequest()); + } + +exit: + return; +} + void SupervisionListener::SetTimeout(uint16_t aTimeout) { if (mTimeout != aTimeout) { + LogInfo("Timeout: %u -> %u", mTimeout, aTimeout); + mTimeout = aTimeout; RestartTimer(); } @@ -201,7 +213,8 @@ void SupervisionListener::HandleTimer(void) { VerifyOrExit(Get().IsChild() && !Get().GetRxOnWhenIdle()); - LogWarn("Supervision timeout. No frame from parent in %d sec", mTimeout); + LogWarn("Supervision timeout. No frame from parent in %u sec", mTimeout); + mCounter++; IgnoreError(Get().SendChildUpdateRequest()); @@ -209,7 +222,4 @@ exit: RestartTimer(); } -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - -} // namespace Utils } // namespace ot diff --git a/src/core/utils/child_supervision.hpp b/src/core/thread/child_supervision.hpp similarity index 89% rename from src/core/utils/child_supervision.hpp rename to src/core/thread/child_supervision.hpp index bbae3997e..c56291f69 100644 --- a/src/core/utils/child_supervision.hpp +++ b/src/core/thread/child_supervision.hpp @@ -53,8 +53,6 @@ namespace ot { class ThreadNetif; class Child; -namespace Utils { - /** * * Child supervision feature provides a mechanism for parent @@ -85,8 +83,6 @@ namespace Utils { * */ -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD /** @@ -119,25 +115,6 @@ public: */ void Stop(void); - /** - * This method sets the supervision interval. - * - * Setting the supervision interval to a non-zero value will ensure to start the supervision process (if not - * already started). - * - * @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision. - * - */ - void SetSupervisionInterval(uint16_t aInterval); - - /** - * This method returns the supervision interval. - * - * @returns The current supervision interval (seconds), or zero if supervision is disabled. - * - */ - uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; } - /** * This method returns the destination for a supervision message. * @@ -165,8 +142,6 @@ private: void CheckState(void); void HandleTimeTick(void); void HandleNotifierEvents(Events aEvents); - - uint16_t mSupervisionInterval; }; #endif // #if OPENTHREAD_FTD @@ -198,6 +173,22 @@ public: */ void Stop(void); + /** + * This method sets the supervision interval. + * + * @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision. + * + */ + void SetInterval(uint16_t aInterval); + + /** + * This method returns the supervision interval. + * + * @returns The current supervision interval (seconds), or zero if supervision is disabled. + * + */ + uint16_t GetInterval(void) const { return mInterval; } + /** * This method sets the supervision check timeout (in seconds). * @@ -221,6 +212,21 @@ public: */ uint16_t GetTimeout(void) const { return mTimeout; } + /** + * This method returns the value of supervision check timeout failure counter. + * + * The counter tracks the number of supervision check failures on the child. It is incremented when the child does + * not hear from its parent within the specified check timeout interval. + * + */ + uint16_t GetCounter(void) const { return mCounter; } + + /** + * This method reset the supervision check timeout failure counter. + * + */ + void ResetCounter(void) { mCounter = 0; } + /** * This method updates the supervision listener state. It informs the listener of a received frame. * @@ -231,7 +237,8 @@ public: void UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure); private: - static constexpr uint16_t kDefaultTimeout = OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT; // (seconds) + static constexpr uint16_t kDefaultTimeout = OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT; // (seconds) + static constexpr uint16_t kDefaultInterval = OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL; // (seconds) void RestartTimer(void); void HandleTimer(void); @@ -239,17 +246,11 @@ private: using ListenerTimer = TimerMilliIn; uint16_t mTimeout; + uint16_t mInterval; + uint16_t mCounter; ListenerTimer mTimer; }; -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - -/** - * @} - * - */ - -} // namespace Utils } // namespace ot #endif // CHILD_SUPERVISION_HPP_ diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index 08cc61f6f..697ce35d1 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -345,7 +345,7 @@ Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &a break; case Message::kTypeSupervision: - PrepareEmptyFrame(aFrame, aChild, kSupervisionMsgAckRequest); + PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true); aContext.mMessageNextOffset = message->GetLength(); break; @@ -418,12 +418,10 @@ void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame, VerifyOrExit(mEnabled); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE if (aError == kErrorNone) { - Get().UpdateOnSend(aChild); + Get().UpdateOnSend(aChild); } -#endif // A zero `nextOffset` indicates that the sent frame is an empty // frame generated by `PrepareFrameForChild()` when there was no diff --git a/src/core/thread/indirect_sender.hpp b/src/core/thread/indirect_sender.hpp index 4e5fdb9fc..f04b54f1c 100644 --- a/src/core/thread/indirect_sender.hpp +++ b/src/core/thread/indirect_sender.hpp @@ -203,12 +203,6 @@ public: void HandleChildModeChange(Child &aChild, Mle::DeviceMode aOldMode); private: - /** - * Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message. - * - */ - static constexpr bool kSupervisionMsgAckRequest = (OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST == 0); - // Callbacks from DataPollHandler Error PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild); void HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, Child &aChild); diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 2d8b6db52..07c676515 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -663,16 +663,12 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) if (aRxOnWhenIdle) { mDataPollSender.StopPolling(); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().Stop(); -#endif + Get().Stop(); } else { mDataPollSender.StartPolling(); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().Start(); -#endif + Get().Start(); } } @@ -1301,9 +1297,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) frameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled()); -#endif + Get().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled()); switch (aFrame.GetType()) { diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 589a60f3d..2df2775ed 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -122,15 +122,13 @@ Error MeshForwarder::SendMessage(Message &aMessage) break; } -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE case Message::kTypeSupervision: { - Child *child = Get().GetDestination(aMessage); + Child *child = Get().GetDestination(aMessage); OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle()); mIndirectSender.AddMessageForSleepyChild(aMessage, *child); break; } -#endif default: aMessage.SetDirectTransmission(); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 75b2a0b64..540b40be6 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1790,6 +1790,7 @@ Error Mle::SendChildIdRequest(void) SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); SuccessOrExit(error = message->AppendVersionTlv()); + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); if (!IsFullThreadDevice()) { @@ -2055,6 +2056,7 @@ Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); SuccessOrExit(error = message->AppendTimeoutTlv(aTimeout)); + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { @@ -2148,6 +2150,10 @@ Error Mle::SendChildUpdateResponse(const TlvList &aTlvList, const Challenge &aCh SuccessOrExit(error = message->AppendMleFrameCounterTlv()); break; + case Tlv::kSupervisionInterval: + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); + break; + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE case Tlv::kCslTimeout: if (Get().IsCslEnabled()) @@ -4707,6 +4713,11 @@ exit: return error; } +Error Mle::TxMessage::AppendSupervisionIntervalTlv(uint16_t aInterval) +{ + return Tlv::Append(*this, aInterval); +} + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE Error Mle::TxMessage::AppendTimeRequestTlv(void) { diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index c23d67a11..d7900e031 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -73,6 +73,8 @@ namespace ot { * @} */ +class SupervisionListener; + /** * @namespace ot::Mle * @@ -100,6 +102,7 @@ class Mle : public InstanceLocator, private NonCopyable { friend class DiscoverScanner; friend class ot::Notifier; + friend class ot::SupervisionListener; #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE friend class ot::LinkMetrics::LinkMetrics; #endif @@ -842,7 +845,7 @@ protected: #endif }; - static constexpr uint8_t kMaxTlvListSize = 16; ///< Maximum number of TLVs in a `TlvList`. + static constexpr uint8_t kMaxTlvListSize = 32; ///< Maximum number of TLVs in a `TlvList`. /** * This type represents a list of TLVs (array of TLV types). @@ -1117,6 +1120,17 @@ protected: */ Error AppendAddressRegistrationTlv(AddressRegistrationMode aMode = kAppendAllAddresses); + /** + * This method appends a Supervision Interval TLV to the message. + * + * @param[in] aInterval The interval value. + * + * @retval kErrorNone Successfully appended the Supervision Interval TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Supervision Interval TLV. + * + */ + Error AppendSupervisionIntervalTlv(uint16_t aInterval); + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE /** * This method appends a Time Request TLV to the message. diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 97a63eb4f..4a43c90bb 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1995,6 +1995,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) Child *child; Router *router; uint8_t numTlvs; + uint16_t supervisionInterval; Log(kMessageReceive, kTypeChildIdRequest, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -2033,6 +2034,18 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) // Timeout SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, timeout)); + // Supervision interval + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + break; + case kErrorNotFound: + supervisionInterval = (version <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0; + break; + default: + ExitNow(error = kErrorParse); + } + // TLV Request SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)); @@ -2114,6 +2127,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) child->SetVersion(version); child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); child->SetTimeout(timeout); + child->SetSupervisionInterval(supervisionInterval); #if OPENTHREAD_CONFIG_MULTI_RADIO child->ClearLastRxFragmentTag(); #endif @@ -2168,6 +2182,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) Challenge challenge; LeaderData leaderData; uint32_t timeout; + uint16_t supervisionInterval; Child *child; DeviceMode oldMode; TlvList requestedTlvList; @@ -2274,6 +2289,24 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } + // Supervision interval + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + tlvList.Add(Tlv::kSupervisionInterval); + break; + + case kErrorNotFound: + supervisionInterval = + (child->GetVersion() <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0; + break; + + default: + ExitNow(error = kErrorParse); + } + + child->SetSupervisionInterval(supervisionInterval); + // TLV Request switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)) { @@ -2473,6 +2506,21 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } + { + uint16_t supervisionInterval; + + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + child->SetSupervisionInterval(supervisionInterval); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } + } + // IPv6 Address if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addrOffset, addrLength) == kErrorNone) { @@ -3025,6 +3073,11 @@ void MleRouter::SendChildUpdateResponse(Child *aChild, case Tlv::kTimeout: SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout())); break; + + case Tlv::kSupervisionInterval: + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(aChild->GetSupervisionInterval())); + break; + #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE case Tlv::kCslClockAccuracy: if (!aChild->IsRxOnWhenIdle()) diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 547c5eaa0..f97885e46 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -558,6 +558,9 @@ private: static constexpr uint8_t kMinCriticalChildrenCount = 6; + static constexpr uint16_t kChildSupervisionDefaultIntervalForOlderVersion = + OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL; + void HandleDetachStart(void); void HandleChildStart(AttachMode aMode); void HandleLinkRequest(RxInfo &aRxInfo); diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index 1970df397..ef6d2f68f 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -102,6 +102,7 @@ public: kActiveDataset = 24, ///< Active Operational Dataset TLV kPendingDataset = 25, ///< Pending Operational Dataset TLV kDiscovery = 26, ///< Thread Discovery TLV + kSupervisionInterval = 27, ///< Supervision Interval TLV kCslChannel = 80, ///< CSL Channel TLV kCslTimeout = 85, ///< CSL Timeout TLV kCslClockAccuracy = 86, ///< CSL Clock Accuracy TLV @@ -230,6 +231,12 @@ typedef SimpleTlvInfo ActiveTimestamp */ typedef SimpleTlvInfo PendingTimestampTlv; +/** + * This class defines Timeout TLV constants and types. + * + */ +typedef UintTlvInfo SupervisionIntervalTlv; + /** * This class defines CSL Timeout TLV constants and types. * diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index b67fef42e..8b6e65880 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -243,23 +243,24 @@ const char *Neighbor::StateToString(State aState) void Child::Info::SetFrom(const Child &aChild) { Clear(); - mExtAddress = aChild.GetExtAddress(); - mTimeout = aChild.GetTimeout(); - mRloc16 = aChild.GetRloc16(); - mChildId = Mle::ChildIdFromRloc16(aChild.GetRloc16()); - mNetworkDataVersion = aChild.GetNetworkDataVersion(); - mAge = Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard()); - mLinkQualityIn = aChild.GetLinkQualityIn(); - mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); - mLastRssi = aChild.GetLinkInfo().GetLastRss(); - mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); - mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); - mQueuedMessageCnt = aChild.GetIndirectMessageCount(); - mVersion = ClampToUint8(aChild.GetVersion()); - mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); - mFullThreadDevice = aChild.IsFullThreadDevice(); - mFullNetworkData = (aChild.GetNetworkDataType() == NetworkData::kFullSet); - mIsStateRestoring = aChild.IsStateRestoring(); + mExtAddress = aChild.GetExtAddress(); + mTimeout = aChild.GetTimeout(); + mRloc16 = aChild.GetRloc16(); + mChildId = Mle::ChildIdFromRloc16(aChild.GetRloc16()); + mNetworkDataVersion = aChild.GetNetworkDataVersion(); + mAge = Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard()); + mLinkQualityIn = aChild.GetLinkQualityIn(); + mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); + mLastRssi = aChild.GetLinkInfo().GetLastRss(); + mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); + mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); + mQueuedMessageCnt = aChild.GetIndirectMessageCount(); + mVersion = ClampToUint8(aChild.GetVersion()); + mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); + mFullThreadDevice = aChild.IsFullThreadDevice(); + mFullNetworkData = (aChild.GetNetworkDataType() == NetworkData::kFullSet); + mIsStateRestoring = aChild.IsStateRestoring(); + mSupervisionInterval = aChild.GetSupervisionInterval(); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE mIsCslSynced = aChild.IsCslSynchronized(); #else diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 52be960a3..9f3f06738 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -1210,7 +1210,21 @@ public: */ void SetRequestTlv(uint8_t aIndex, uint8_t aType) { mRequestTlvs[aIndex] = aType; } -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE + /** + * This method returns the supervision interval (in seconds). + * + * @returns The supervision interval (in seconds). + * + */ + uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; } + + /** + * This method sets the supervision interval. + * + * @param[in] aInterval The supervision interval (in seconds). + * + */ + void SetSupervisionInterval(uint16_t aInterval) { mSupervisionInterval = aInterval; } /** * This method increments the number of seconds since last supervision of the child. @@ -1232,8 +1246,6 @@ public: */ void ResetSecondsSinceLastSupervision(void) { mSecondsSinceSupervision = 0; } -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** * This method returns MLR state of an IPv6 multicast address. @@ -1331,9 +1343,8 @@ private: uint8_t mAttachChallenge[Mle::kMaxChallengeSize]; ///< The challenge value }; -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - uint16_t mSecondsSinceSupervision; ///< Number of seconds since last supervision of the child. -#endif + uint16_t mSupervisionInterval; // Supervision interval for the child (in sec). + uint16_t mSecondsSinceSupervision; // Number of seconds since last supervision of the child. static_assert(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS < 8192, "mQueuedMessageCount cannot fit max required!"); }; diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index a7e13a13a..83b15a27a 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -1867,9 +1867,7 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_JAM_DETECT)); #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHILD_SUPERVISION)); -#endif #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MONITOR)); diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 361fcbea1..6fc426785 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -344,10 +344,8 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_PERIOD), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_INTERVAL), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT), -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_PLATFORM_POSIX OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_RCP_VERSION), @@ -606,10 +604,8 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_PERIOD), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_INTERVAL), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT), -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SLAAC_ENABLED), diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index b3fb5ec00..ffd42559c 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -39,9 +39,7 @@ #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #include -#endif #include #include #include @@ -1223,8 +1221,6 @@ exit: return error; } -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetInterval(mInstance)); @@ -1242,8 +1238,6 @@ exit: return error; } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index bd4119a0b..1220e15e5 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -40,9 +40,7 @@ #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #include -#endif #include #include #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE @@ -2391,8 +2389,6 @@ void NcpBase::HandleJamStateChange(bool aJamState) #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetCheckTimeout(mInstance)); @@ -2410,8 +2406,6 @@ exit: return error; } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE template <> otError NcpBase::HandlePropertyGet(void) diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 0a26c848a..98c43caab 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -44,7 +44,6 @@ COAPS ?= 1 COMMISSIONER ?= 1 CHANNEL_MANAGER ?= 1 CHANNEL_MONITOR ?= 1 -CHILD_SUPERVISION ?= 1 DAEMON ?= 0 DATASET_UPDATER ?= 1 DHCP6_CLIENT ?= 1 diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build index 4b2662cca..60782f6c3 100755 --- a/tests/fuzz/oss-fuzz-build +++ b/tests/fuzz/oss-fuzz-build @@ -45,7 +45,6 @@ set -euxo pipefail -DOT_BORDER_ROUTER=ON \ -DOT_CHANNEL_MANAGER=ON \ -DOT_CHANNEL_MONITOR=ON \ - -DOT_CHILD_SUPERVISION=ON \ -DOT_COAP=ON \ -DOT_COAPS=ON \ -DOT_COAP_BLOCK=ON \ diff --git a/tests/scripts/expect/cli-child.exp b/tests/scripts/expect/cli-child.exp index 1e7309966..af13b2ef9 100755 --- a/tests/scripts/expect/cli-child.exp +++ b/tests/scripts/expect/cli-child.exp @@ -38,9 +38,9 @@ set rloc [get_rloc16] switch_node 1 send "child table\n" -expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |" -expect "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+" -expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| *\\d+\\| \\d \\| +\\d+ \\| $extaddr \\|" +expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC |" +expect "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+" +expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| *\\d+\\| \\d \\| +\\d+ \\| +\\d+ \\| $extaddr \\|" set child_id $expect_out(1,string) expect_line "Done" send "child list\n" diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index c2288ae00..a3d997038 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -154,6 +154,7 @@ EXTRA_DIST = \ test_anycast.py \ test_anycast_locator.py \ test_br_upgrade_router_role.py \ + test_child_supervision.py \ test_coap.py \ test_coap_block.py \ test_coap_observe.py \ @@ -236,6 +237,7 @@ check_SCRIPTS = \ test_anycast.py \ test_anycast_locator.py \ test_br_upgrade_router_role.py \ + test_child_supervision.py \ test_coap.py \ test_coap_block.py \ test_coap_observe.py \ diff --git a/tests/scripts/thread-cert/mle.py b/tests/scripts/thread-cert/mle.py index 3c70b7f4d..0c826ad98 100755 --- a/tests/scripts/thread-cert/mle.py +++ b/tests/scripts/thread-cert/mle.py @@ -90,6 +90,7 @@ class TlvType(IntEnum): ACTIVE_OPERATIONAL_DATASET = 24 PENDING_OPERATIONAL_DATASET = 25 THREAD_DISCOVERY = 26 + SUPERVISION_INTERVAL = 27 CSL_CHANNEL = 80 CSL_SYNCHRONIZED_TIMEOUT = 85 CSL_CLOCK_ACCURACY = 86 diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index fe581e4c4..4d4550268 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1721,6 +1721,30 @@ class NodeImpl: self.send_command('pollperiod %d' % pollperiod) self._expect_done() + def get_child_supervision_interval(self): + self.send_command('childsupervision interval') + return self._expect_result(r'\d+') + + def set_child_supervision_interval(self, interval): + self.send_command('childsupervision interval %d' % interval) + self._expect_done() + + def get_child_supervision_check_timeout(self): + self.send_command('childsupervision checktimeout') + return self._expect_result(r'\d+') + + def set_child_supervision_check_timeout(self, timeout): + self.send_command('childsupervision checktimeout %d' % timeout) + self._expect_done() + + def get_child_supervision_check_failure_counter(self): + self.send_command('childsupervision failcounter') + return self._expect_result(r'\d+') + + def reset_child_supervision_check_failure_counter(self): + self.send_command('childsupervision failcounter reset') + self._expect_done() + def get_csl_info(self): self.send_command('csl') self._expect_done() @@ -1928,10 +1952,10 @@ class NodeImpl: # # Example output: - # | ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | - # +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ - # | 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | - # | 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | + # | ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC | + # +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+ + # | 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 129 | 4ecede68435358ac | + # | 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | 0 | a672a601d2ce37d8 | # Done # @@ -1962,6 +1986,7 @@ class NodeImpl: 'ver': int(col('Ver')), 'csl': bool(int(col('CSL'))), 'qmsgcnt': int(col('QMsgCnt')), + 'suprvsn': int(col('Suprvsn')) } return table diff --git a/tests/scripts/thread-cert/test_child_supervision.py b/tests/scripts/thread-cert/test_child_supervision.py new file mode 100755 index 000000000..0418f80de --- /dev/null +++ b/tests/scripts/thread-cert/test_child_supervision.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import unittest + +import command +import config +import thread_cert + +# Test description: +# +# This test verifies behavior child supervision. +# +# +# Topology: +# +# Parent (leader) +# | +# | +# Child (sleepy). + +PARENT = 1 +CHILD = 2 + + +class ChildSupervision(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + PARENT: { + 'name': 'PARENT', + 'mode': 'rdn', + }, + CHILD: { + 'name': 'CHILD', + 'is_mtd': True, + 'mode': 'n', + }, + } + + def test(self): + parent = self.nodes[PARENT] + child = self.nodes[CHILD] + + # Form the network. + + parent.start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(parent.get_state(), 'leader') + + child.start() + self.simulator.go(5) + self.assertEqual(child.get_state(), 'child') + child.set_pollperiod(500) + + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Check the parent's child table. + + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) + + # Change the supervision interval on child. This should trigger an + # MLE Child Update exchange from child to parent so to inform parent + # about the change. Verify that parent is notified by checking the + # parent's child table. + + child.set_child_supervision_interval(20) + + self.simulator.go(2) + + self.assertEqual(int(child.get_child_supervision_interval()), 20) + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) + + # Change supervision check timeout on the child. + + child.set_child_supervision_check_timeout(25) + self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) + + # Wait for multiple supervision intervals and ensure that child + # stays attached (child supervision working as expected). + + self.simulator.go(110) + + self.assertEqual(child.get_state(), 'child') + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Disable supervision check on child. + + child.set_child_supervision_check_timeout(0) + + # Enable allowlist on parent without adding the child. After child + # timeout expires, the parent should remove the child from its child + # table. + + parent.clear_allowlist() + parent.enable_allowlist() + + table = parent.get_child_table() + child_timeout = table[1]['timeout'] + + self.simulator.go(child_timeout + 1) + table = parent.get_child_table() + self.assertEqual(len(table), 0) + + # Since supervision check is disabled on the child, it should + # continue to stay attached to parent (since data polls are acked by + # radio driver). + + self.assertEqual(child.get_state(), 'child') + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Re-enable supervision check on child. After the check timeout the + # child must try to exchange "Child Update" messages with parent and + # then detect that parent is not responding and detach. + + child.set_child_supervision_check_timeout(25) + + self.simulator.go(35) + self.assertEqual(child.get_state(), 'detached') + self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) + + # Disable allowlist on parent. Child should be able to attach again. + + parent.disable_allowlist() + self.simulator.go(30) + self.assertEqual(child.get_state(), 'child') + child.reset_child_supervision_check_failure_counter() + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Set the supervision interval to zero on child (child is asking + # parent not to supervise it anymore). This practically behaves + # the same as if parent does not support child supervision + # feature. + + child.set_child_supervision_interval(0) + child.set_child_supervision_check_timeout(25) + self.simulator.go(2) + + self.assertEqual(int(child.get_child_supervision_interval()), 0) + self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) + + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[2]['suprvsn'], int(child.get_child_supervision_interval())) + + # Wait for multiple check timeouts. The child should still stay + # attached to parent. + + self.simulator.go(100) + self.assertEqual(child.get_state(), 'child') + self.assertEqual(len(parent.get_child_table()), 1) + self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/toranj/ncp/test-018-child-supervision.py b/tests/toranj/ncp/test-018-child-supervision.py deleted file mode 100644 index 2fe276739..000000000 --- a/tests/toranj/ncp/test-018-child-supervision.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import time -import wpan -from wpan import verify - -# ----------------------------------------------------------------------------------------------------------------------- -# Test description: Child Supervision feature -# -# This test covers the behavior of Child Supervision feature. -# -# This test uses MAC allowlisting to emulate the situation where a child is -# removed from parent's child table while the child continues to stay attached -# to the parent (since data polls from child are acked at radio platform layer). -# Specifically the test verifies that once supervision check is enabled on the -# child, the child detects that it is no longer present in the parent's table -# and tries to re-attach. - -# The test verifies the behavior of both parent and child, when supervision is -# enabled. It verifies that parent is periodically sending supervision messages -# to the child and that the child is monitoring the messages. -# -# This test also indirectly verifies the child timeout on parent. -# - -test_name = __file__[:-3] if __file__.endswith('.py') else __file__ -print('-' * 120) -print('Starting \'{}\''.format(test_name)) - -# ----------------------------------------------------------------------------------------------------------------------- -# Creating `wpan.Nodes` instances - -speedup = 2 -wpan.Node.set_time_speedup_factor(speedup) - -parent = wpan.Node() -child = wpan.Node() - -# ----------------------------------------------------------------------------------------------------------------------- -# Init all nodes - -wpan.Node.init_all_nodes() - -# ----------------------------------------------------------------------------------------------------------------------- -# Build network topology - -CHILD_TIMEOUT = 6 -CHILD_SUPERVISION_CHECK_TIMEOUT = 2 -PARENT_SUPERVISION_INTERVAL = 1 - -child.set(wpan.WPAN_POLL_INTERVAL, '500') -child.set(wpan.WPAN_THREAD_CHILD_TIMEOUT, str(CHILD_TIMEOUT)) - -parent.form("child-sup") -child.join_node(parent, wpan.JOIN_TYPE_SLEEPY_END_DEVICE) - -# ----------------------------------------------------------------------------------------------------------------------- -# Test implementation - -# Disable child supervision on child and parent -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, '0') -child.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, '0') -verify(int(parent.get(wpan.WPAN_CHILD_SUPERVISION_INTERVAL), 0) == 0) -verify(int(child.get(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT), 0) == 0) - -# Check that the child is associated and has correct timeout -verify(child.is_associated()) -verify(int(child.get(wpan.WPAN_THREAD_CHILD_TIMEOUT), 0) == CHILD_TIMEOUT) - -# Verify the child table on parent contains the child with correct timeout -child_table = wpan.parse_child_table_result(parent.get(wpan.WPAN_THREAD_CHILD_TABLE)) -verify(len(child_table) == 1) -verify(int(child_table[0].timeout, 0) == CHILD_TIMEOUT) - -time.sleep(1) - -# Enabling allowlisting on parent -# -# Since child is not in parent's allowlist, the data polls from child -# should be rejected and the child should be removed from parent's -# child table after timeout. The child however should continue to -# stay attached (since data polls are acked by radio driver) and -# supervision check is disabled on the child. - -parent.set(wpan.WPAN_MAC_ALLOWLIST_ENABLED, '1') - - -def check_child_is_removed_from_parent_child_table(): - child_table = wpan.parse_child_table_result(parent.get(wpan.WPAN_THREAD_CHILD_TABLE)) - verify(len(child_table) == 0) - - -# wait till child is removed from parent's child table -# after this child should still be associated -wpan.verify_within(check_child_is_removed_from_parent_child_table, CHILD_TIMEOUT / speedup + 2) -verify(child.is_associated()) - -# Enable supervision check on child and expect the child to -# become detached after the check timeout - -child.set( - wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, - str(CHILD_SUPERVISION_CHECK_TIMEOUT), -) - - -def check_child_is_detached(): - verify(not child.is_associated()) - - -wpan.verify_within(check_child_is_detached, CHILD_SUPERVISION_CHECK_TIMEOUT / speedup + 8) - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Enable child supervision on parent and disable allowlisting - -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL)) -parent.set(wpan.WPAN_MAC_ALLOWLIST_ENABLED, '0') - -# Wait for the child to attach back - - -def check_child_is_attached(): - verify(child.is_associated()) - - -wpan.verify_within(check_child_is_attached, 5) - -# MAC counters are used to verify the child supervision behavior. - -parent_unicast_tx_count = int(parent.get("NCP:Counter:TX_PKT_UNICAST"), 0) - -time.sleep(PARENT_SUPERVISION_INTERVAL * 1.2 / speedup) - -# To verify that the parent is indeed sending empty "supervision" -# messages to its child, MAC counter for number of unicast tx is -# used. Note that supervision interval on parent is set to 1 sec. - -verify(int(parent.get("NCP:Counter:TX_PKT_UNICAST"), 0) >= parent_unicast_tx_count + 1) - -verify(child.is_associated()) - -# Disable child supervision on parent -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, '0') - -time.sleep(CHILD_SUPERVISION_CHECK_TIMEOUT * 3 / speedup) -verify(child.is_associated()) - -# ----------------------------------------------------------------------------------------------------------------------- -# Test finished - -wpan.Node.finalize_all_nodes() - -print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index eda319da3..3a6e802a0 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -435,14 +435,6 @@ */ #define OPENTHREAD_CONFIG_CHANNEL_MANAGER_THRESHOLD_TO_CHANGE_CHANNEL (0xffff * 10 / 100) -/** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - * - * Define to 1 to enable Child Supervision support. - * - */ -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 1 - /** * @def OPENTHREAD_CONFIG_TMF_PENDING_DATASET_MINIMUM_DELAY * diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 04347f9f1..01fb3e47d 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -222,7 +222,6 @@ run ncp/test-014-ip6-address-add.py run ncp/test-015-same-prefix-on-multiple-nodes.py run ncp/test-016-neighbor-table.py run ncp/test-017-parent-reset-child-recovery.py -run ncp/test-018-child-supervision.py run ncp/test-019-inform-previous-parent.py run ncp/test-020-router-table.py run ncp/test-021-address-cache-table.py