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