[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.
This commit is contained in:
Abtin Keshavarzian
2023-02-28 23:01:43 -08:00
committed by GitHub
parent 0491c8ff2c
commit 091f68ed70
53 changed files with 541 additions and 425 deletions
-1
View File
@@ -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)")
-1
View File
@@ -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
-1
View File
@@ -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. |
-5
View File
@@ -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
+24 -12
View File
@@ -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);
/**
* @}
*
+1 -1
View File
@@ -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
+1
View File
@@ -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
-1
View File
@@ -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"
-1
View File
@@ -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"
-2
View File
@@ -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"
-1
View File
@@ -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"
-1
View File
@@ -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"
-1
View File
@@ -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'
+26 -4
View File
@@ -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.
+19 -16
View File
@@ -37,6 +37,7 @@
#include <stdlib.h>
#include <string.h>
#include <openthread/child_supervision.h>
#include <openthread/diag.h>
#include <openthread/dns.h>
#include <openthread/icmp6.h>
@@ -58,9 +59,6 @@
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#include <openthread/server.h>
#endif
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#include <openthread/child_supervision.h>
#endif
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
#include <openthread/platform/misc.h>
#endif
@@ -1894,12 +1892,12 @@ template <> otError Interpreter::Process<Cmd("child")>(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<Cmd("child")>(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<Cmd("child")>(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<Cmd("childmax")>(Arg aArgs[])
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
@@ -2127,7 +2126,6 @@ template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
*/
error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout);
}
#if OPENTHREAD_FTD
/**
* @cli childsupervision interval
* @code
@@ -2135,8 +2133,6 @@ template <> otError Interpreter::Process<Cmd("childsupervision")>(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<Cmd("childsupervision")>(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"),
+2 -6
View File
@@ -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",
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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 \
+13 -11
View File
@@ -33,8 +33,6 @@
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#include <openthread/child_supervision.h>
#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<Utils::ChildSupervisor>().GetSupervisionInterval();
return AsCoreType(aInstance).Get<SupervisionListener>().GetInterval();
}
void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval)
{
AsCoreType(aInstance).Get<Utils::ChildSupervisor>().SetSupervisionInterval(aInterval);
AsCoreType(aInstance).Get<SupervisionListener>().SetInterval(aInterval);
}
#endif
uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Utils::SupervisionListener>().GetTimeout();
return AsCoreType(aInstance).Get<SupervisionListener>().GetTimeout();
}
void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout)
{
AsCoreType(aInstance).Get<Utils::SupervisionListener>().SetTimeout(aTimeout);
AsCoreType(aInstance).Get<SupervisionListener>().SetTimeout(aTimeout);
}
#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<SupervisionListener>().GetCounter();
}
void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance)
{
AsCoreType(aInstance).Get<SupervisionListener>().ResetCounter();
}
-3
View File
@@ -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)
+5 -9
View File
@@ -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; }
+1 -3
View File
@@ -133,9 +133,7 @@ void Notifier::EmitEvents(void)
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
Get<BackboneRouter::Manager>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
Get<Utils::ChildSupervisor>().HandleNotifierEvents(events);
#endif
Get<ChildSupervisor>().HandleNotifierEvents(events);
#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
Get<MeshCoP::DatasetUpdater>().HandleNotifierEvents(events);
#endif
+1 -3
View File
@@ -96,12 +96,10 @@ void TimeTicker::HandleTimer(void)
}
#endif
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
if (mReceivers & Mask(kChildSupervisor))
{
Get<Utils::ChildSupervisor>().HandleTimeTick();
Get<ChildSupervisor>().HandleTimeTick();
}
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
+1 -1
View File
@@ -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`
+15 -20
View File
@@ -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_
@@ -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_
@@ -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<ChildTable>().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<Mle::MleRouter>().IsDisabled() &&
Get<ChildTable>().HasChildren(Child::kInStateValid));
bool shouldRun = (!Get<Mle::Mle>().IsDisabled() && Get<ChildTable>().HasChildren(Child::kInStateValid));
if (shouldRun && !Get<TimeTicker>().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<Mle::Mle>().IsChild())
{
IgnoreError(Get<Mle::Mle>().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<Mle::MleRouter>().IsChild() && !Get<MeshForwarder>().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<Mle::MleRouter>().SendChildUpdateRequest());
@@ -209,7 +222,4 @@ exit:
RestartTimer();
}
#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
} // namespace Utils
} // namespace ot
@@ -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<SupervisionListener, &SupervisionListener::HandleTimer>;
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_
+2 -4
View File
@@ -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<Utils::ChildSupervisor>().UpdateOnSend(aChild);
Get<ChildSupervisor>().UpdateOnSend(aChild);
}
#endif
// A zero `nextOffset` indicates that the sent frame is an empty
// frame generated by `PrepareFrameForChild()` when there was no
-6
View File
@@ -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);
+3 -9
View File
@@ -663,16 +663,12 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
if (aRxOnWhenIdle)
{
mDataPollSender.StopPolling();
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
Get<Utils::SupervisionListener>().Stop();
#endif
Get<SupervisionListener>().Stop();
}
else
{
mDataPollSender.StartPolling();
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
Get<Utils::SupervisionListener>().Start();
#endif
Get<SupervisionListener>().Start();
}
}
@@ -1301,9 +1297,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame)
frameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength());
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
Get<Utils::SupervisionListener>().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled());
#endif
Get<SupervisionListener>().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled());
switch (aFrame.GetType())
{
+1 -3
View File
@@ -122,15 +122,13 @@ Error MeshForwarder::SendMessage(Message &aMessage)
break;
}
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
case Message::kTypeSupervision:
{
Child *child = Get<Utils::ChildSupervisor>().GetDestination(aMessage);
Child *child = Get<ChildSupervisor>().GetDestination(aMessage);
OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle());
mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
break;
}
#endif
default:
aMessage.SetDirectTransmission();
+11
View File
@@ -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<SupervisionListener>().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<SupervisionListener>().GetInterval()));
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().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<SupervisionListener>().GetInterval()));
break;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case Tlv::kCslTimeout:
if (Get<Mac::Mac>().IsCslEnabled())
@@ -4707,6 +4713,11 @@ exit:
return error;
}
Error Mle::TxMessage::AppendSupervisionIntervalTlv(uint16_t aInterval)
{
return Tlv::Append<SupervisionIntervalTlv>(*this, aInterval);
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
Error Mle::TxMessage::AppendTimeRequestTlv(void)
{
+15 -1
View File
@@ -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.
+53
View File
@@ -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<TimeoutTlv>(aRxInfo.mMessage, timeout));
// Supervision interval
switch (Tlv::Find<SupervisionIntervalTlv>(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<SupervisionIntervalTlv>(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<SupervisionIntervalTlv>(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())
+3
View File
@@ -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);
+7
View File
@@ -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<Tlv::kActiveTimestamp, MeshCoP::Timestamp> ActiveTimestamp
*/
typedef SimpleTlvInfo<Tlv::kPendingTimestamp, MeshCoP::Timestamp> PendingTimestampTlv;
/**
* This class defines Timeout TLV constants and types.
*
*/
typedef UintTlvInfo<Tlv::kSupervisionInterval, uint16_t> SupervisionIntervalTlv;
/**
* This class defines CSL Timeout TLV constants and types.
*
+18 -17
View File
@@ -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
+17 -6
View File
@@ -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!");
};
-2
View File
@@ -1867,9 +1867,7 @@ template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CAPS>(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));
-4
View File
@@ -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),
-6
View File
@@ -39,9 +39,7 @@
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
#include <openthread/channel_manager.h>
#endif
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#include <openthread/child_supervision.h>
#endif
#include <openthread/dataset.h>
#include <openthread/dataset_ftd.h>
#include <openthread/diag.h>
@@ -1223,8 +1221,6 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHILD_SUPERVISION_INTERVAL>(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<SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL>(void)
-6
View File
@@ -40,9 +40,7 @@
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
#include <openthread/channel_monitor.h>
#endif
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#include <openthread/child_supervision.h>
#endif
#include <openthread/diag.h>
#include <openthread/icmp6.h>
#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<SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT>(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<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_INTERVAL>(void)
-1
View File
@@ -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
-1
View File
@@ -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 \
+3 -3
View File
@@ -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"
+2
View File
@@ -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 \
+1
View File
@@ -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
+29 -4
View File
@@ -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
+189
View File
@@ -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()
@@ -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))
@@ -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
*
-1
View File
@@ -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