diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 38a8464a4..e09f1f165 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -119,6 +119,7 @@ * @defgroup api-border-agent-txt-data Border Agent TXT Data Parser * @defgroup api-border-router Border Router * @defgroup api-border-routing Border Routing Manager + * @defgroup api-multi-ail-detection Border Router Multi AIL Detection * @defgroup api-commissioner Commissioner * @defgroup api-thread-general General * @brief This module includes functions for all Thread roles. diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index fb79c13b3..d16aa45e0 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -78,6 +78,7 @@ source_set("openthread") { "mdns.h", "mesh_diag.h", "message.h", + "multi_ail_detection.h", "multi_radio.h", "nat64.h", "ncp.h", diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 9a1f0a245..be56351d3 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -41,6 +41,7 @@ #include #include #include +#include // IWYU pragma: keep #include #ifdef __cplusplus @@ -641,68 +642,6 @@ otError otBorderRoutingGetNextPeerBrEntry(otInstance * */ uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge); -/** - * A callback function pointer called when the multi-AIL detection state changes. - * - * This callback function is invoked by the OpenThread stack whenever the Routing Manager determines a change in - * whether Border Routers on the Thread mesh might be connected to different Adjacent Infrastructure Links (AILs). - * - * See `otBorderRoutingIsMultiAilDetected()` for more details. - * - * @param[in] aDetected `TRUE` if multiple AILs are now detected, `FALSE` otherwise. - * @param[in] aContext A pointer to arbitrary context information provided when the callback was registered - * using `otBorderRoutingSetMultiAilCallback()`. - */ -typedef void (*otBorderRoutingMultiAilCallback)(bool aDetected, void *aContext); - -/** - * Gets the current detected state regarding multiple Adjacent Infrastructure Links (AILs). - * - * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE`. - * - * It returns whether the Routing Manager currently believes that Border Routers (BRs) on the Thread mesh may be - * connected to different AILs. - * - * The detection mechanism operates as follows: The Routing Manager monitors the number of peer BRs listed in the - * Thread Network Data (see `otBorderRoutingCountPeerBrs()`) and compares this count with the number of peer BRs - * discovered by processing received Router Advertisement (RA) messages on its connected AIL. If the count derived from - * Network Data consistently exceeds the count derived from RAs for a detection duration of 10 minutes, it concludes - * that BRs are likely connected to different AILs. To clear state a shorter window of 1 minute is used. - * - * The detection window of 10 minutes helps to avoid false positives due to transient changes. The Routing Manager uses - * 200 seconds for reachability checks of peer BRs (sending Neighbor Solicitation). Stale Network Data entries are - * also expected to age out within a few minutes. So a 10-minute detection time accommodates both cases. - * - * While generally effective, this detection mechanism may get less reliable in scenarios with a large number of - * BRs, particularly exceeding ten. This is related to the "Network Data Publisher" mechanism, where BRs might refrain - * from publishing their external route information in the Network Data to conserve its limited size, potentially - * skewing the Network Data BR count. - * - * @param[in] aInstance A pointer to the OpenThread instance. - * - * @retval TRUE Has detected that BRs are likely connected to multiple AILs. - * @retval FALSE Has not detected (or no longer detects) that BRs are connected to multiple AILs. - */ -bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance); - -/** - * Sets a callback function to be notified of changes in the multi-AIL detection state. - * - * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE`. - * - * Subsequent calls to this function will overwrite the previous callback setting. Using `NULL` for @p aCallback will - * disable the callback. - * - * @param[in] aInstance A pointer to the OpenThread instance. - * @param[in] aCallback A pointer to the function (`otBorderRoutingMultiAilCallback`) to be called - * upon state changes, or `NULL` to unregister a previously set callback. - * @param[in] aContext A pointer to application-specific context that will be passed back - * in the `aCallback` function. This can be `NULL` if no context is needed. - */ -void otBorderRoutingSetMultiAilCallback(otInstance *aInstance, - otBorderRoutingMultiAilCallback aCallback, - void *aContext); - /** * Iterates over the Recursive DNS Server (RDNSS) address entries. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d67bcb8f9..3df8a9c71 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (549) +#define OPENTHREAD_API_VERSION (550) /** * @addtogroup api-instance diff --git a/include/openthread/multi_ail_detection.h b/include/openthread/multi_ail_detection.h new file mode 100644 index 000000000..967ac31ed --- /dev/null +++ b/include/openthread/multi_ail_detection.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2025, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines the OpenThread Border Router Multi-AIL Detection API. + */ + +#ifndef OPENTHREAD_MULTI_AIL_DETECTION_H_ +#define OPENTHREAD_MULTI_AIL_DETECTION_H_ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-multi-ail-detection + * + * @brief + * This module includes functions for the OpenThread Multi-AIL Detection feature. + * + * @{ + * + * All the functions in this module require both the `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` and + * `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE` to be enabled. + */ + +/** + * A callback function pointer called when the multi-AIL detection state changes. + * + * This callback function is invoked by the OpenThread stack whenever the detector determines a change in whether + * Border Routers on the Thread mesh might be connected to different Adjacent Infrastructure Links (AILs). + * + * See `otBorderRoutingIsMultiAilDetected()` for more details. + * + * @param[in] aDetected `TRUE` if multiple AILs are now detected, `FALSE` otherwise. + * @param[in] aContext A pointer to arbitrary context information provided when the callback was registered + * using `otBorderRoutingSetMultiAilCallback()`. + */ +typedef void (*otBorderRoutingMultiAilCallback)(bool aDetected, void *aContext); + +/** + * Enables or disables the Multi-AIL Detector. + * + * If `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE` is enabled, the detector is enabled + * by default and starts running when the infra-if network is initialized and becomes active (running). + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aEnable TRUE to enable the detector, FALSE to disable. + */ +void otBorderRoutingSetMultiAilDetectionEnabled(otInstance *aInstance, bool aEnable); + +/** + * Checks if the Multi-AIL Detector is enabled. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @retval TRUE If the detector is enabled. + * @retval FALSE If the detector is disabled. + */ +bool otBorderRoutingIsMultiAilDetectionEnabled(otInstance *aInstance); + +/** + * Checks if the Multi-AIL Detector is running. + * + * The detector runs when it is enabled and the infrastructure interface is also active. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @retval TRUE If the detector is running. + * @retval FALSE If the detector is not running. + */ +bool otBorderRoutingIsMultiAilDetectionRunning(otInstance *aInstance); + +/** + * Gets the current detected state regarding multiple Adjacent Infrastructure Links (AILs). + * + * It returns whether the detector currently believes that Border Routers (BRs) on the Thread mesh may be connected to + * different AILs. + * + * The detection mechanism operates as follows: The detector monitors the number of peer BRs listed in the + * Thread Network Data (see `otBorderRoutingCountPeerBrs()`) and compares this count with the number of peer BRs + * discovered by processing received Router Advertisement (RA) messages on its connected AIL. If the count derived from + * Network Data consistently exceeds the count derived from RAs for a detection duration of 10 minutes, it concludes + * that BRs are likely connected to different AILs. To clear state a shorter window of 1 minute is used. + * + * The detection window of 10 minutes helps to avoid false positives due to transient changes. The detector uses + * 200 seconds for reachability checks of peer BRs (sending Neighbor Solicitation). Stale Network Data entries are + * also expected to age out within a few minutes. So a 10-minute detection time accommodates both cases. + * + * While generally effective, this detection mechanism may get less reliable in scenarios with a large number of + * BRs, particularly exceeding ten. This is related to the "Network Data Publisher" mechanism, where BRs might refrain + * from publishing their external route information in the Network Data to conserve its limited size, potentially + * skewing the Network Data BR count. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @retval TRUE Has detected that BRs are likely connected to multiple AILs. + * @retval FALSE Has not detected (or no longer detects) that BRs are connected to multiple AILs. + */ +bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance); + +/** + * Sets a callback function to be notified of changes in the multi-AIL detection state. + * + * Subsequent calls to this function will overwrite the previous callback setting. Using `NULL` for @p aCallback will + * disable the callback. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aCallback A pointer to the function (`otBorderRoutingMultiAilCallback`) to be called + * upon state changes, or `NULL` to unregister a previously set callback. + * @param[in] aContext A pointer to application-specific context that will be passed back + * in the `aCallback` function. This can be `NULL` if no context is needed. + */ +void otBorderRoutingSetMultiAilCallback(otInstance *aInstance, + otBorderRoutingMultiAilCallback aCallback, + void *aContext); + +/** + * @} + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_MULTI_AIL_DETECTION_H_ diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index 7361b5d2e..b3214a5d1 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -198,6 +198,34 @@ Done BR multi AIL callback: cleared ``` +Usage: `br multiail state` + +Outputs full state of multi-AIL detector: + +- Whether the detector is enabled. +- Whether the detector is running (when it is enabled and the infra-if interface is also active). +- Whether multi-AIL was detected. + +```bash +> br multiail state +Enabled: yes +Running: yes +Detected: no +Done +``` + +Usage: `br multiail enable|disable` + +Enable or disable the multi-AIL detector. + +```bash +> br multiail enable +Done + +> br multiail disable +Done +``` + ### omrconfig Usage: `br omrconfig` diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index d980c3290..e20982085 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -197,6 +197,40 @@ template <> otError Br::Process(Arg aArgs[]) otBorderRoutingSetMultiAilCallback(GetInstancePtr(), callback, this); } + /** + * @cli br multiail state + * @code + * br multiail state + * Enabled: yes + * Running: yes + * Detected: no + * Done + * @endcode + * @par + * Outputs full state of multi-AIL detector: + * - Whether the detector is enabled. + * - Whether the detector is running (when it is enabled and the infra-if interface is also active). + * - Whether multi-AIL was detected. + */ + else if (aArgs[0] == "state") + { + OutputLine("Enabled: %s", otBorderRoutingIsMultiAilDetectionEnabled(GetInstancePtr()) ? "yes" : "no"); + OutputLine("Running: %s", otBorderRoutingIsMultiAilDetectionRunning(GetInstancePtr()) ? "yes" : "no"); + OutputLine("Detected: %s", otBorderRoutingIsMultiAilDetected(GetInstancePtr()) ? "yes" : "no"); + } + /** + * @cli br multiail (enable, disable) + * @code + * br multiail enable + * Done + * @endcode + * @cparam br multiail @ca{enable|disable} + * @par api_copy + * #otBorderRoutingSetMultiAilDetectionEnabled + */ + else if (ProcessEnableDisable(aArgs, otBorderRoutingSetMultiAilDetectionEnabled) == OT_ERROR_NONE) + { + } else { error = OT_ERROR_INVALID_ARGS; diff --git a/src/cli/cli_br.hpp b/src/cli/cli_br.hpp index 9659f8959..595e019ac 100644 --- a/src/cli/cli_br.hpp +++ b/src/cli/cli_br.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include +#include #include "cli/cli_config.h" #include "cli/cli_history.hpp" diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index bf1b7eebc..151626b22 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -343,6 +343,7 @@ openthread_core_files = [ "api/mdns_api.cpp", "api/mesh_diag_api.cpp", "api/message_api.cpp", + "api/multi_ail_detection_api.cpp", "api/multi_radio_api.cpp", "api/nat64_api.cpp", "api/netdata_api.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f830f374b..f17eda820 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -67,6 +67,7 @@ set(COMMON_SOURCES api/mdns_api.cpp api/mesh_diag_api.cpp api/message_api.cpp + api/multi_ail_detection_api.cpp api/multi_radio_api.cpp api/nat64_api.cpp api/netdata_api.cpp diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 8bd83e308..c8726b998 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -293,22 +293,6 @@ uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge) #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE - -bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsDetected(); -} - -void otBorderRoutingSetMultiAilCallback(otInstance *aInstance, - otBorderRoutingMultiAilCallback aCallback, - void *aContext) -{ - AsCoreType(aInstance).Get().SetCallback(aCallback, aContext); -} - -#endif - #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled) diff --git a/src/core/api/multi_ail_detection_api.cpp b/src/core/api/multi_ail_detection_api.cpp new file mode 100644 index 000000000..16e05a28e --- /dev/null +++ b/src/core/api/multi_ail_detection_api.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file implements the OpenThread Multi-AIL Detection API. + */ + +#include + +#include "instance/instance.hpp" + +using namespace ot; + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE + +void otBorderRoutingSetMultiAilDetectionEnabled(otInstance *aInstance, bool aEnable) +{ + AsCoreType(aInstance).Get().SetEnabled(aEnable); +} + +bool otBorderRoutingIsMultiAilDetectionEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsEnabled(); +} + +bool otBorderRoutingIsMultiAilDetectionRunning(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsRunning(); +} + +bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsDetected(); +} + +void otBorderRoutingSetMultiAilCallback(otInstance *aInstance, + otBorderRoutingMultiAilCallback aCallback, + void *aContext) +{ + AsCoreType(aInstance).Get().SetCallback(aCallback, aContext); +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE diff --git a/src/core/border_router/br_types.hpp b/src/core/border_router/br_types.hpp index 4b709825c..b26ff48ff 100644 --- a/src/core/border_router/br_types.hpp +++ b/src/core/border_router/br_types.hpp @@ -66,7 +66,6 @@ typedef otBorderRoutingPeerBorderRouterEntry PeerBrEntry; ///< Peer Bor typedef otBorderRoutingPrefixTableEntry Dhcp6PdPrefix; ///< DHCPv6 PD prefix. typedef otPdProcessedRaInfo Dhcp6PdCounters; ///< DHCPv6 PD counters. typedef otBorderRoutingRequestDhcp6PdCallback Dhcp6PdCallback; ///< DHCPv6 PD callback. -typedef otBorderRoutingMultiAilCallback MultiAilCallback; ///< Multi AIL detection callback. // IPv6 Neighbor Discovery (ND) types typedef Ip6::Nd::PrefixInfoOption PrefixInfoOption; ///< Prefix Info Option (PIO). diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp index eae389b08..ac13a2163 100644 --- a/src/core/border_router/infra_if.cpp +++ b/src/core/border_router/infra_if.cpp @@ -184,6 +184,11 @@ Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning) mIsRunning = aIsRunning; Get().HandleInfraIfStateChanged(); + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE + Get().HandleInfraIfStateChanged(); +#endif + Get().HandleInfraIfStateChanged(); #if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE diff --git a/src/core/border_router/multi_ail_detector.cpp b/src/core/border_router/multi_ail_detector.cpp index bc9993174..ce6db2a16 100644 --- a/src/core/border_router/multi_ail_detector.cpp +++ b/src/core/border_router/multi_ail_detector.cpp @@ -40,13 +40,14 @@ namespace ot { namespace BorderRouter { -RegisterLogModule("BorderRouting"); +RegisterLogModule("MultiAilDetect"); //--------------------------------------------------------------------------------------------------------------------- // MultiAilDetector MultiAilDetector::MultiAilDetector(Instance &aInstance) : InstanceLocator(aInstance) + , mState(kAutoEnableMode ? kStateStopped : kStateDisabled) , mDetected(false) , mNetDataPeerBrCount(0) , mReachablePeerBrCount(0) @@ -54,12 +55,88 @@ MultiAilDetector::MultiAilDetector(Instance &aInstance) { } +void MultiAilDetector::SetEnabled(bool aEnable) +{ + if (aEnable) + { + VerifyOrExit(mState == kStateDisabled); + SetState(kStateStopped); + UpdateState(); + } + else + { + VerifyOrExit(mState != kStateDisabled); + Stop(); + SetState(kStateDisabled); + } + +exit: + return; +} + +void MultiAilDetector::SetState(State aState) +{ + VerifyOrExit(mState != aState); + + LogInfo("State: %s -> %s", StateToString(mState), StateToString(aState)); + mState = aState; + +exit: + return; +} + +void MultiAilDetector::HandleNotifierEvents(ot::Events aEvents) +{ + if (aEvents.Contains(kEventThreadRoleChanged)) + { + UpdateState(); + } +} + +void MultiAilDetector::UpdateState(void) +{ + VerifyOrExit(mState != kStateDisabled); + + if (Get().IsRunning() && Get().IsAttached()) + { + Start(); + } + else + { + Stop(); + } + +exit: + return; +} + +void MultiAilDetector::Start(void) +{ + VerifyOrExit(mState == kStateStopped); + + SetState(kStateRunning); + + Get().SetEnabled(true, RxRaTracker::kRequesterMultiAilDetector); + Evaluate(); + +exit: + return; +} + void MultiAilDetector::Stop(void) { + VerifyOrExit(mState == kStateRunning); + mTimer.Stop(); mDetected = false; mNetDataPeerBrCount = 0; mReachablePeerBrCount = 0; + Get().SetEnabled(false, RxRaTracker::kRequesterMultiAilDetector); + + SetState(kStateStopped); + +exit: + return; } void MultiAilDetector::HandleRxRaTrackerEvents(const RxRaTracker::Events &aEvents) @@ -77,7 +154,7 @@ void MultiAilDetector::Evaluate(void) uint32_t minAge; bool detected; - VerifyOrExit(Get().IsRunning()); + VerifyOrExit(mState == kStateRunning); count = Get().CountBrs(NetDataBrTracker::kExcludeThisDevice, minAge); @@ -129,6 +206,29 @@ void MultiAilDetector::HandleTimer(void) mCallback.InvokeIfSet(mDetected); } +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + +const char *MultiAilDetector::StateToString(State aState) +{ + static const char *const kStateStrings[] = { + "Disabled", // (0) kStateDisabled + "Stopped", // (1) kStateStopped + "Running", // (2) kStateRunning + }; + + struct EnumCheck + { + InitEnumValidatorCounter(); + ValidateNextEnum(kStateDisabled); + ValidateNextEnum(kStateStopped); + ValidateNextEnum(kStateRunning); + }; + + return kStateStrings[aState]; +} + +#endif + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/multi_ail_detector.hpp b/src/core/border_router/multi_ail_detector.hpp index 68480180c..27e79a1c6 100644 --- a/src/core/border_router/multi_ail_detector.hpp +++ b/src/core/border_router/multi_ail_detector.hpp @@ -42,18 +42,21 @@ #error "MULTI_AIL_DETECTION_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE" #endif +#include + #include "border_router/br_types.hpp" #include "border_router/rx_ra_tracker.hpp" #include "common/callback.hpp" #include "common/error.hpp" #include "common/locator.hpp" +#include "common/notifier.hpp" #include "common/timer.hpp" namespace ot { namespace BorderRouter { +class InfraIf; class NetDataBrTracker; -class RoutingManager; /** * Implements Multi-AIL (Adjacent Infrastructure Link) detection. @@ -69,13 +72,52 @@ class RoutingManager; */ class MultiAilDetector : public InstanceLocator { + friend class InfraIf; friend class NetDataBrTracker; friend class RxRaTracker; - friend class RoutingManager; + friend class ot::Notifier; public: + typedef otBorderRoutingMultiAilCallback MultiAilCallback; ///< Multi AIL detection callback. + + /** + * Initializes the `MultiAilDetector`. + * + * @param[in] aInstance The OpenThread instance. + */ explicit MultiAilDetector(Instance &aInstance); + /** + * Enables or disables the Multi-AIL Detector. + * + * When enabled, the detector starts monitoring the infrastructure interface state and evaluates for multiple AILs. + * When disabled, the detector stops and resets its internal state. + * + * If `OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE` is enabled, the detector is enabled + * by default and starts running when the infra-if network is initialized and becomes active. + * + * @param[in] aEnable TRUE to enable the detector, FALSE to disable. + */ + void SetEnabled(bool aEnable); + + /** + * Checks if the Multi-AIL Detector is enabled. + * + * @retval TRUE If the detector is enabled. + * @retval FALSE If the detector is disabled. + */ + bool IsEnabled(void) const { return mState != kStateDisabled; } + + /** + * Checks if the Multi-AIL Detector is running. + * + * The detector runs when it is enabled and the infrastructure interface is also running. + * + * @retval TRUE If the detector is running. + * @retval FALSE If the detector is not running. + */ + bool IsRunning(void) const { return mState == kStateRunning; } + /** * Gets the current detection state regarding multiple Adjacent Infrastructure Links (AILs). * @@ -104,17 +146,37 @@ private: static constexpr uint32_t kDetectTime = 10 * Time::kOneMinuteInMsec; static constexpr uint32_t kClearTime = 1 * Time::kOneMinuteInMsec; - void Start(void) { Evaluate(); } + static constexpr bool kAutoEnableMode = OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE; + + enum State : uint8_t + { + kStateDisabled, // Disabled. + kStateStopped, // Enabled but stopped and waiting for `InfraIf` state change. + kStateRunning, // Enabled and running. + }; + + void SetState(State aState); + void UpdateState(void); + void Start(void); void Stop(void); void Evaluate(void); void HandleTimer(void); + // Callback from `Notifier` + void HandleNotifierEvents(ot::Events aEvents); + + // Callback from `InfraIf` + void HandleInfraIfStateChanged(void) { UpdateState(); } + // Callback from `RxRaTracker` void HandleRxRaTrackerEvents(const RxRaTracker::Events &aEvents); + static const char *StateToString(State aState); + using DetectCallback = Callback; using DetectTimer = TimerMilliIn; + State mState; bool mDetected; uint16_t mNetDataPeerBrCount; uint16_t mReachablePeerBrCount; diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 06dd00f94..4c2d6912f 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -265,9 +265,6 @@ void RoutingManager::Start(void) #endif #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mNat64PrefixManager.Start(); -#endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE - Get().Start(); #endif } } @@ -284,9 +281,6 @@ void RoutingManager::Stop(void) #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mNat64PrefixManager.Stop(); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE - Get().Stop(); -#endif SendRouterAdvertisement(kInvalidateAllPrevPrefixes); diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp index 3c0576dd9..e1ea00f6f 100644 --- a/src/core/common/notifier.cpp +++ b/src/core/common/notifier.cpp @@ -167,6 +167,9 @@ void Notifier::EmitEvents(void) #if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE Get().HandleNotifierEvents(events); #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE + Get().HandleNotifierEvents(events); +#endif #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE Get().HandleNotifierEvents(events); diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h index be2610672..5963ed4ba 100644 --- a/src/core/config/border_routing.h +++ b/src/core/config/border_routing.h @@ -86,13 +86,13 @@ /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE * - * Define to 1 to enable Routing Manager multiple Adjacent Infrastructure Links (AILs) detection feature. + * Define to 1 to enable Multiple Adjacent Infrastructure Links (AILs) detection feature. * - * The detection mechanism operates as follows: The Routing Manager monitors the number of peer BRs listed in the - * Thread Network Data (see `otBorderRoutingCountPeerBrs()`) and compares this count with the number of peer BRs - * discovered by processing received Router Advertisement (RA) messages on its connected AIL. If the count derived from - * Network Data consistently exceeds the count derived from RAs for a detection duration of 10 minutes, it concludes - * that BRs are likely connected to different AILs. To clear state a shorter window of 1 minute is used. + * The detection mechanism operates as follows: The detector monitors the number of peer BRs listed in the Thread + * Network Data (see `otBorderRoutingCountPeerBrs()`) and compares this count with the number of peer BRs discovered + * by processing received Router Advertisement (RA) messages on its connected AIL. If the count derived from Network + * Data consistently exceeds the count derived from RAs for a detection duration of 10 minutes, it concludes that BRs + * are likely connected to different AILs. To clear state a shorter window of 1 minute is used. * * See `otBorderRoutingIsMultiAilDetected()` for more details. */ @@ -100,6 +100,20 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE + * + * Specifies the "Auto Enable Mode" for Multi-AIL Detection feature. + * + * When "Auto Enable Mode" is set, the Multi-AIL Detector is enabled by default and starts running and monitoring + * when the infrastructure interface network is initialized and become active/running. If this mode is disabled, the + * detector will be in a disabled state initially and must be explicitly enabled using the public API + * `otBorderRoutingSetMultiAilDetectionEnabled()`. + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_AUTO_ENABLE_MODE 1 +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE * diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 2036bd70d..a398f4bcb 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -856,6 +856,9 @@ class Node(object): def br_get_multiail(self): return self._cli_single_output('br multiail') + def br_get_multiail_state(self): + return self.cli('br multiail state') + def br_get_ifaddrs(self): return self.cli('br ifaddrs') diff --git a/tests/toranj/cli/test-505-multi-ail-detection.py b/tests/toranj/cli/test-505-multi-ail-detection.py index 13323dcee..cec180198 100755 --- a/tests/toranj/cli/test-505-multi-ail-detection.py +++ b/tests/toranj/cli/test-505-multi-ail-detection.py @@ -36,9 +36,9 @@ import time # # Check multi AIL detection # -# -+- _______ -# | / \ -# br1 --- br2 --- br3 +# -+- _________________ +# | / \ \ +# br1 --- br2 --- br3 --- detector # test_name = __file__[:-3] if __file__.endswith('.py') else __file__ @@ -56,6 +56,7 @@ one_minute = 60 br1 = cli.Node() br2 = cli.Node() br3 = cli.Node() +detector = cli.Node() IF_INDEX_1 = 1 IF_INDEX_2 = 2 @@ -66,10 +67,12 @@ IF_INDEX_2 = 2 br1.form("multiail") br2.join(br1) br3.join(br2) +detector.join(br2) verify(br1.get_state() == 'leader') verify(br2.get_state() == 'router') verify(br3.get_state() == 'router') +verify(detector.get_state() == 'router') # ----------------------------------------------------------------------------------------------------------------------- # Test implementation @@ -86,6 +89,20 @@ time.sleep(one_minute / speedup) verify(br1.br_get_state() == 'running') verify(br1.br_get_multiail() == 'not detected') +# Init `detector` on different AIL without enabling it as a BR. + +detector.br_init(IF_INDEX_2, 1) +time.sleep(10 / speedup) + +# Check that multi-ail detection is auto-enabled and started +# on `detector`. + +state = detector.br_get_multiail_state() +verify(len(state) == 3) +verify(state[0].strip() == 'Enabled: yes') +verify(state[1].strip() == 'Running: yes') +verify(state[2].strip() == 'Detected: no') + # Start `br2` and `br3` together on a different AIL br2.br_init(IF_INDEX_2, 1) @@ -108,6 +125,7 @@ time.sleep(10.01 * one_minute / speedup) verify(br1.br_get_multiail() == 'detected') verify(br2.br_get_multiail() == 'detected') verify(br3.br_get_multiail() == 'detected') +verify(detector.br_get_multiail() == 'detected') # Disable `br1` @@ -118,8 +136,9 @@ time.sleep(9 * one_minute / speedup) verify(br2.br_get_multiail() == 'not detected') verify(br3.br_get_multiail() == 'not detected') +verify(detector.br_get_multiail() == 'not detected') -# Enable `br` again, wait for a short time (before detection) and disable it +# Enable `br1` again, wait for a short time (before detection) and disable it br1.br_enable() @@ -133,6 +152,18 @@ time.sleep(10 * one_minute / speedup) verify(br2.br_get_multiail() == 'not detected') verify(br3.br_get_multiail() == 'not detected') +verify(detector.br_get_multiail() == 'not detected') + +# `br1` is no longer enabled to act as BR, but its multi-ail detector +# should continue to operate and detect that it is on a different AIL. + +verify(br1.br_get_multiail() == 'detected') + +state = br1.br_get_multiail_state() +verify(len(state) == 3) +verify(state[0].strip() == 'Enabled: yes') +verify(state[1].strip() == 'Running: yes') +verify(state[2].strip() == 'Detected: yes') # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index e4f90601b..5b5f1853c 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -1416,6 +1416,18 @@ void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false) SuccessOrQuit(otIp6SetEnabled(sInstance, true)); SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE + // We explicitly disable the multi-AIL detector to prevent + // interference with the tests. The detector enables + // `RxRaTracker` and keeps it enabled even when Border Routing is + // disabled, which keeps prefix entries in the heap and can + // invalidate the heap allocation checks. It may also mess up the + // expected timing of RS (Router Solicitation) messages (starting + // the `RxRaTracker` early). + otBorderRoutingSetMultiAilDetectionEnabled(sInstance, false); +#endif + SuccessOrQuit(otBorderRoutingSetEnabled(sInstance, aEnablBorderRouting)); // Reset all test flags