[multi-ail-detector] allow detector to run independently of Border Routing (#12078)

This commit updates the Multi-AIL Detection feature to operate
independently of the Border Routing Manager. This fundamental change
allows the detector to be enabled/disabled on its own, rather than
being tied to the Border Routing Manager's state.

This change also moves the Multi-AIL detection API into a separate
`openthread/multi_ail_detection.h` header and introduces new APIs to
control the detector independently. Corresponding CLI commands are
also added.

The `test-505-multi-ail-detection.py` is also updated to validate
this new independent behavior. In particular that a device that is
not enabled to act as a BR can independently run multi-AIL detection
and determine whether, if it becomes a BR, it will cause multi-AIL
issues.
This commit is contained in:
Abtin Keshavarzian
2025-11-12 13:09:42 -08:00
committed by GitHub
parent 61e43cffb9
commit eb51d4be51
22 changed files with 541 additions and 101 deletions
+1
View File
@@ -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.
+1
View File
@@ -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",
+1 -62
View File
@@ -41,6 +41,7 @@
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/multi_ail_detection.h> // IWYU pragma: keep
#include <openthread/netdata.h>
#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.
*
+1 -1
View File
@@ -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
+157
View File
@@ -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 <stdbool.h>
#include <openthread/instance.h>
#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_
+28
View File
@@ -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`
+34
View File
@@ -197,6 +197,40 @@ template <> otError Br::Process<Cmd("multiail")>(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;
+1
View File
@@ -37,6 +37,7 @@
#include "openthread-core-config.h"
#include <openthread/border_routing.h>
#include <openthread/multi_ail_detection.h>
#include "cli/cli_config.h"
#include "cli/cli_history.hpp"
+1
View File
@@ -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",
+1
View File
@@ -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
-16
View File
@@ -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<BorderRouter::MultiAilDetector>().IsDetected();
}
void otBorderRoutingSetMultiAilCallback(otInstance *aInstance,
otBorderRoutingMultiAilCallback aCallback,
void *aContext)
{
AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().SetCallback(aCallback, aContext);
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled)
+70
View File
@@ -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 <openthread/multi_ail_detection.h>
#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<BorderRouter::MultiAilDetector>().SetEnabled(aEnable);
}
bool otBorderRoutingIsMultiAilDetectionEnabled(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().IsEnabled();
}
bool otBorderRoutingIsMultiAilDetectionRunning(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().IsRunning();
}
bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().IsDetected();
}
void otBorderRoutingSetMultiAilCallback(otInstance *aInstance,
otBorderRoutingMultiAilCallback aCallback,
void *aContext)
{
AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().SetCallback(aCallback, aContext);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
-1
View File
@@ -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).
+5
View File
@@ -184,6 +184,11 @@ Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning)
mIsRunning = aIsRunning;
Get<RxRaTracker>().HandleInfraIfStateChanged();
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
Get<MultiAilDetector>().HandleInfraIfStateChanged();
#endif
Get<RoutingManager>().HandleInfraIfStateChanged();
#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+102 -2
View File
@@ -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<InfraIf>().IsRunning() && Get<Mle::Mle>().IsAttached())
{
Start();
}
else
{
Stop();
}
exit:
return;
}
void MultiAilDetector::Start(void)
{
VerifyOrExit(mState == kStateStopped);
SetState(kStateRunning);
Get<RxRaTracker>().SetEnabled(true, RxRaTracker::kRequesterMultiAilDetector);
Evaluate();
exit:
return;
}
void MultiAilDetector::Stop(void)
{
VerifyOrExit(mState == kStateRunning);
mTimer.Stop();
mDetected = false;
mNetDataPeerBrCount = 0;
mReachablePeerBrCount = 0;
Get<RxRaTracker>().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<RoutingManager>().IsRunning());
VerifyOrExit(mState == kStateRunning);
count = Get<NetDataBrTracker>().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
+65 -3
View File
@@ -42,18 +42,21 @@
#error "MULTI_AIL_DETECTION_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE"
#endif
#include <openthread/multi_ail_detection.h>
#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<MultiAilCallback>;
using DetectTimer = TimerMilliIn<MultiAilDetector, &MultiAilDetector::HandleTimer>;
State mState;
bool mDetected;
uint16_t mNetDataPeerBrCount;
uint16_t mReachablePeerBrCount;
@@ -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<MultiAilDetector>().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<MultiAilDetector>().Stop();
#endif
SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
+3
View File
@@ -167,6 +167,9 @@ void Notifier::EmitEvents(void)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
Get<BorderRouter::NetDataBrTracker>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
Get<BorderRouter::MultiAilDetector>().HandleNotifierEvents(events);
#endif
#endif
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
Get<Srp::Client>().HandleNotifierEvents(events);
+20 -6
View File
@@ -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
*
+3
View File
@@ -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')
@@ -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
+12
View File
@@ -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