[border-router] move MultiAilDetector to separate files (#12067)

This change moves the `MultiAilDetector` class from being a nested
class within `RoutingManager` to its own dedicated `.hpp` and `.cpp`
files.

An instance of `MultiAilDetector` is now owned by the top-level
`Instance` class, making it a sibling component to `RoutingManager`
and other core components.

This is purely a code organization change and introduces no functional
or logic changes. This prepares for future changes where
`MultiAilDetector` may operate independently of `RoutingManager`.
This commit is contained in:
Abtin Keshavarzian
2025-10-27 16:55:46 -07:00
committed by GitHub
parent 4fdd08111e
commit 5a39715e0c
11 changed files with 270 additions and 184 deletions
+2
View File
@@ -387,6 +387,8 @@ openthread_core_files = [
"border_router/dhcp6_pd_client.hpp",
"border_router/infra_if.cpp",
"border_router/infra_if.hpp",
"border_router/multi_ail_detector.cpp",
"border_router/multi_ail_detector.hpp",
"border_router/routing_manager.cpp",
"border_router/routing_manager.hpp",
"border_router/rx_ra_tracker.cpp",
+1
View File
@@ -102,6 +102,7 @@ set(COMMON_SOURCES
border_router/br_types.cpp
border_router/dhcp6_pd_client.cpp
border_router/infra_if.cpp
border_router/multi_ail_detector.cpp
border_router/routing_manager.cpp
border_router/rx_ra_tracker.cpp
coap/coap.cpp
+2 -2
View File
@@ -297,14 +297,14 @@ uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge)
bool otBorderRoutingIsMultiAilDetected(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().IsMultiAilDetected();
return AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().IsDetected();
}
void otBorderRoutingSetMultiAilCallback(otInstance *aInstance,
otBorderRoutingMultiAilCallback aCallback,
void *aContext)
{
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetMultiAilCallback(aCallback, aContext);
AsCoreType(aInstance).Get<BorderRouter::MultiAilDetector>().SetCallback(aCallback, aContext);
}
#endif
+1 -1
View File
@@ -154,7 +154,7 @@ void NetDataBrTracker::HandleNotifierEvents(Events aEvents)
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
Get<RoutingManager>().mMultiAilDetector.Evaluate();
Get<MultiAilDetector>().Evaluate();
#endif
exit:
-2
View File
@@ -45,8 +45,6 @@
namespace ot {
namespace BorderRouter {
class RoutingManager;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
/**
@@ -0,0 +1,126 @@
/*
* 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
* This file implements the multi AIL detector.
*/
#include "multi_ail_detector.hpp"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
#include "instance/instance.hpp"
namespace ot {
namespace BorderRouter {
RegisterLogModule("BorderRouting");
//---------------------------------------------------------------------------------------------------------------------
// MultiAilDetector
MultiAilDetector::MultiAilDetector(Instance &aInstance)
: InstanceLocator(aInstance)
, mDetected(false)
, mNetDataPeerBrCount(0)
, mReachablePeerBrCount(0)
, mTimer(aInstance)
{
}
void MultiAilDetector::Stop(void)
{
mTimer.Stop();
mDetected = false;
mNetDataPeerBrCount = 0;
mReachablePeerBrCount = 0;
}
void MultiAilDetector::Evaluate(void)
{
uint16_t count;
uint32_t minAge;
bool detected;
VerifyOrExit(Get<RoutingManager>().IsRunning());
count = Get<NetDataBrTracker>().CountBrs(NetDataBrTracker::kExcludeThisDevice, minAge);
if (count != mNetDataPeerBrCount)
{
LogInfo("Peer BR count from netdata: %u -> %u", mNetDataPeerBrCount, count);
mNetDataPeerBrCount = count;
}
count = Get<RxRaTracker>().GetReachablePeerBrCount();
if (count != mReachablePeerBrCount)
{
LogInfo("Reachable Peer BR count from RaTracker: %u -> %u", mReachablePeerBrCount, count);
mReachablePeerBrCount = count;
}
detected = (mNetDataPeerBrCount > mReachablePeerBrCount);
if (detected == mDetected)
{
mTimer.Stop();
}
else if (!mTimer.IsRunning())
{
mTimer.Start(detected ? kDetectTime : kClearTime);
}
exit:
return;
}
void MultiAilDetector::HandleTimer(void)
{
if (!mDetected)
{
LogNote("BRs on multi AIL detected - BRs are likely connected to different infra-links");
LogInfo("More peer BRs in netdata vs from rx RAs for past %lu seconds", ToUlong(Time::MsecToSec(kDetectTime)));
LogInfo("NetData Peer BR count: %u, RaTracker reachable Peer BR count: %u", mNetDataPeerBrCount,
mReachablePeerBrCount);
mDetected = true;
}
else
{
LogNote("BRs on multi AIL detection cleared");
mDetected = false;
}
mCallback.InvokeIfSet(mDetected);
}
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
@@ -0,0 +1,125 @@
/*
* 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
* This file includes definitions for the multi AIL detector.
*/
#ifndef MULTI_AIL_DETECTOR_HPP_
#define MULTI_AIL_DETECTOR_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
#if !OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
#error "MULTI_AIL_DETECTION_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE"
#endif
#include "border_router/br_types.hpp"
#include "common/callback.hpp"
#include "common/error.hpp"
#include "common/locator.hpp"
#include "common/timer.hpp"
namespace ot {
namespace BorderRouter {
class NetDataBrTracker;
class RoutingManager;
/**
* Implements Multi-AIL (Adjacent Infrastructure Link) detection.
*
* This class detects whether Border Routers (BRs) may be connected to different AILs by tracking the number of peer
* BRs from Network Data versus from `RxRaTracker`. If the Network Data count exceeds the RA-tracked count for more
* than `kDetectTime` (10 minutes), it notifies this using a callback. To clear the state, `kClearTime` (1 minute) is
* used.
*
* This longer detection window of 10 minutes helps to avoid false positives due to transient changes. `RxRaTracker`
* uses 200 seconds for reachability checks of peer BRs. Stale Network Data entries are also expected to age out
* within a few minutes, so a 10-minute detection time accommodates both.
*/
class MultiAilDetector : public InstanceLocator
{
friend class NetDataBrTracker;
friend class RoutingManager;
public:
explicit MultiAilDetector(Instance &aInstance);
/**
* Gets the current detection state regarding multiple Adjacent Infrastructure Links (AILs).
*
* It returns whether this detector currently believes that Border Routers (BRs) on the Thread mesh may be
* connected to different AILs.
*
* See `otBorderRoutingIsMultiAilDetected()` for more details about detection process.
*
* @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 IsDetected(void) const { return mDetected; }
/**
* 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] aCallback The callback function
* @param[in] aContext A pointer to application-specific context used with @p aCallback.
*/
void SetCallback(MultiAilCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); }
private:
static constexpr uint32_t kDetectTime = 10 * Time::kOneMinuteInMsec;
static constexpr uint32_t kClearTime = 1 * Time::kOneMinuteInMsec;
void Start(void) { Evaluate(); }
void Stop(void);
void Evaluate(void);
void HandleTimer(void);
using DetectCallback = Callback<MultiAilCallback>;
using DetectTimer = TimerMilliIn<MultiAilDetector, &MultiAilDetector::HandleTimer>;
bool mDetected;
uint16_t mNetDataPeerBrCount;
uint16_t mReachablePeerBrCount;
DetectTimer mTimer;
DetectCallback mCallback;
};
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
#endif // MULTI_AIL_DETECTOR_HPP_
+3 -89
View File
@@ -57,10 +57,6 @@ RoutingManager::RoutingManager(Instance &aInstance)
, mOmrPrefixManager(aInstance)
, mRioAdvertiser(aInstance)
, mOnLinkPrefixManager(aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
, mMultiAilDetector(aInstance)
#endif
, mRoutePublisher(aInstance)
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
, mNat64PrefixManager(aInstance)
@@ -271,7 +267,7 @@ void RoutingManager::Start(void)
mNat64PrefixManager.Start();
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
mMultiAilDetector.Start();
Get<MultiAilDetector>().Start();
#endif
}
}
@@ -289,7 +285,7 @@ void RoutingManager::Stop(void)
mNat64PrefixManager.Stop();
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
mMultiAilDetector.Stop();
Get<MultiAilDetector>().Stop();
#endif
SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
@@ -670,7 +666,7 @@ void RoutingManager::HandleRxRaTrackerDecisionFactorChanged(void)
mOnLinkPrefixManager.HandleRxRaTrackerChanged();
mRoutePublisher.Evaluate();
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
mMultiAilDetector.Evaluate();
Get<MultiAilDetector>().Evaluate();
#endif
exit:
@@ -794,88 +790,6 @@ const char *RoutingManager::RouterAdvOriginToString(RxRaTracker::RouterAdvOrigin
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
//---------------------------------------------------------------------------------------------------------------------
// MultiAilDetector
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
RoutingManager::MultiAilDetector::MultiAilDetector(Instance &aInstance)
: InstanceLocator(aInstance)
, mDetected(false)
, mNetDataPeerBrCount(0)
, mReachablePeerBrCount(0)
, mTimer(aInstance)
{
}
void RoutingManager::MultiAilDetector::Stop(void)
{
mTimer.Stop();
mDetected = false;
mNetDataPeerBrCount = 0;
mReachablePeerBrCount = 0;
}
void RoutingManager::MultiAilDetector::Evaluate(void)
{
uint16_t count;
uint32_t minAge;
bool detected;
VerifyOrExit(Get<RoutingManager>().IsRunning());
count = Get<NetDataBrTracker>().CountBrs(NetDataBrTracker::kExcludeThisDevice, minAge);
if (count != mNetDataPeerBrCount)
{
LogInfo("Peer BR count from netdata: %u -> %u", mNetDataPeerBrCount, count);
mNetDataPeerBrCount = count;
}
count = Get<RxRaTracker>().GetReachablePeerBrCount();
if (count != mReachablePeerBrCount)
{
LogInfo("Reachable Peer BR count from RaTracker: %u -> %u", mReachablePeerBrCount, count);
mReachablePeerBrCount = count;
}
detected = (mNetDataPeerBrCount > mReachablePeerBrCount);
if (detected == mDetected)
{
mTimer.Stop();
}
else if (!mTimer.IsRunning())
{
mTimer.Start(detected ? kDetectTime : kClearTime);
}
exit:
return;
}
void RoutingManager::MultiAilDetector::HandleTimer(void)
{
if (!mDetected)
{
LogNote("BRs on multi AIL detected - BRs are likely connected to different infra-links");
LogInfo("More peer BRs in netdata vs from rx RAs for past %lu seconds", ToUlong(Time::MsecToSec(kDetectTime)));
LogInfo("NetData Peer BR count: %u, RaTracker reachable Peer BR count: %u", mNetDataPeerBrCount,
mReachablePeerBrCount);
mDetected = true;
}
else
{
LogNote("BRs on multi AIL detection cleared");
mDetected = false;
}
mCallback.InvokeIfSet(mDetected);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
//---------------------------------------------------------------------------------------------------------------------
// OmrPrefixManager
@@ -50,11 +50,6 @@
#error "TRACK_PEER_BR_INFO_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE"
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE && \
!OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
#error "MULTI_AIL_DETECTION_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE"
#endif
#include <openthread/border_routing.h>
#include <openthread/nat64.h>
#include <openthread/netdata.h>
@@ -441,37 +436,6 @@ public:
*/
void HandleInfraIfStateChanged(void) { EvaluateState(); }
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
/**
* Gets the current detected state regarding multiple Adjacent Infrastructure Links (AILs).
*
* It returns whether the Routing Manager currently believes that Border Routers (BRs) on the Thread mesh may be
* connected to different AILs.
*
* See `otBorderRoutingIsMultiAilDetected()` for more details about detection process.
*
* @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 IsMultiAilDetected(void) const { return mMultiAilDetector.IsDetected(); }
/**
* 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] aCallback The callback function
* @param[in] aContext A pointer to application-specific context used with @p aCallback.
*/
void SetMultiAilCallback(MultiAilCallback aCallback, void *aContext)
{
mMultiAilDetector.SetCallback(aCallback, aContext);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
/**
* Determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server.
@@ -638,56 +602,6 @@ private:
//------------------------------------------------------------------------------------------------------------------
// Nested types
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
void HandleMultiAilDetectorTimer(void) { mMultiAilDetector.HandleTimer(); }
class MultiAilDetector : public InstanceLocator
{
// Detects whether BRs may be connected to different AILs by
// tracking the number of peer BRs from netdata versus from
// `RxRaTracker`. If the netdata count exceeds the RA-tracked
// count for more than `kDetectTime` (10 minutes), it notifies
// this using the provided callback. To clear the state,
// `kClearTime` (1 minute) is used.
//
// This longer detection window of 10 minutes helps to avoid
// false positives due to transient changes. `RxRaTracker` uses
// 200 seconds for reachability checks of peer BRs. Stale
// Network Data entries are also expected to age out within a
// few minutes. So 10-minute detection time accommodates both.
friend class RxRaTracker;
public:
explicit MultiAilDetector(Instance &aInstance);
void SetCallback(MultiAilCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); }
bool IsDetected(void) const { return mDetected; }
void Start(void) { Evaluate(); }
void Stop(void);
void Evaluate(void);
void HandleTimer(void);
private:
static constexpr uint32_t kDetectTime = 10 * Time::kOneMinuteInMsec;
static constexpr uint32_t kClearTime = 1 * Time::kOneMinuteInMsec;
using DetectCallback = Callback<MultiAilCallback>;
using DetectTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleMultiAilDetectorTimer>;
bool mDetected;
uint16_t mNetDataPeerBrCount;
uint16_t mReachablePeerBrCount;
DetectTimer mTimer;
DetectCallback mCallback;
};
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class OmrPrefixManager : public InstanceLocator
{
public:
@@ -1133,10 +1047,6 @@ private:
OnLinkPrefixManager mOnLinkPrefixManager;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
MultiAilDetector mMultiAilDetector;
#endif
RoutePublisher mRoutePublisher;
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
+3
View File
@@ -277,6 +277,9 @@ Instance::Instance(void)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
, mNetDataBrTracker(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
, mMultiAilDetector(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE
, mDhcp6PdClient(*this)
#endif
+7
View File
@@ -76,6 +76,7 @@
#include "backbone_router/bbr_manager.hpp"
#include "border_router/dhcp6_pd_client.hpp"
#include "border_router/infra_if.hpp"
#include "border_router/multi_ail_detector.hpp"
#include "border_router/routing_manager.hpp"
#include "border_router/rx_ra_tracker.hpp"
#include "coap/coap_secure.hpp"
@@ -725,6 +726,9 @@ private:
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
BorderRouter::NetDataBrTracker mNetDataBrTracker;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
BorderRouter::MultiAilDetector mMultiAilDetector;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE
BorderRouter::Dhcp6PdClient mDhcp6PdClient;
#endif
@@ -1111,6 +1115,9 @@ template <> inline BorderRouter::RoutingManager &Instance::Get(void) { return mR
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
template <> inline BorderRouter::NetDataBrTracker &Instance::Get(void) { return mNetDataBrTracker; }
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_MULTI_AIL_DETECTION_ENABLE
template <> inline BorderRouter::MultiAilDetector &Instance::Get(void) { return mMultiAilDetector; }
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE
template <> inline BorderRouter::Dhcp6PdClient &Instance::Get(void) { return mDhcp6PdClient; }
#endif