[routing-manager] track peer BRs and their age from Network Data (#10485)

This commit adds a new mechanism in `RoutingManager` to discover and
track peer BRs found in Network Data. The `NetDataPeerBrTracker`
class implements this functionality, tracking a list of peer BR
RLOC16s and their age (time since appearance in the Network Data).

This commit also implements public OT APIs to iterate over the peer BR
list or get the total count, along with corresponding CLI commands
(`br peers`), and a test case validating the behavior.
This commit is contained in:
Abtin Keshavarzian
2024-07-15 09:49:08 -07:00
committed by GitHub
parent b0790b3daa
commit f331472b73
8 changed files with 452 additions and 2 deletions
+62
View File
@@ -131,6 +131,16 @@ typedef struct otBorderRoutingPrefixTableEntry
uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink`.
} otBorderRoutingPrefixTableEntry;
/**
* Represents information about a peer Border Router found in the Network Data.
*
*/
typedef struct otBorderRoutingPeerBorderRouterEntry
{
uint16_t mRloc16; ///< The RLOC16 of BR.
uint32_t mAge; ///< Seconds since the BR appeared in the Network Data.
} otBorderRoutingPeerBorderRouterEntry;
/**
* Represents a group of data of platform-generated RA messages processed.
*
@@ -488,6 +498,58 @@ otError otBorderRoutingGetNextRouterEntry(otInstance *aI
otBorderRoutingPrefixTableIterator *aIterator,
otBorderRoutingRouterEntry *aEntry);
/**
* Iterates over the peer BRs found in the Network Data.
*
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.
*
* Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered
* to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data
* entries:
*
* - It has added at least one external route entry.
* - It has added at least one prefix entry with both the default-route and on-mesh flags set.
* - It has added at least one domain prefix (with both the domain and on-mesh flags set).
*
* The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR.
*
* @param[in] aInstance The OpenThread instance.
* @param[in,out] aIterator A pointer to the iterator.
* @param[out] aEntry A pointer to the entry to populate.
*
* @retval OT_ERROR_NONE Iterated to the next entry, @p aEntry and @p aIterator are updated.
* @retval OT_ERROR_NOT_FOUND No more entries.
*
*/
otError otBorderRoutingGetNextPeerBrEntry(otInstance *aInstance,
otBorderRoutingPrefixTableIterator *aIterator,
otBorderRoutingPeerBorderRouterEntry *aEntry);
/**
* Returns the number of peer BRs found in the Network Data.
*
* Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.
*
* Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered
* to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data
* entries:
*
* - It has added at least one external route entry.
* - It has added at least one prefix entry with both the default-route and on-mesh flags set.
* - It has added at least one domain prefix (with both the domain and on-mesh flags set).
*
* The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR.
*
* @param[in] aInstance The OpenThread instance.
* @param[out] aMinAge Pointer to an `uint32_t` to return the minimum age among all peer BRs.
* Can be NULL if the caller does not need this information.
* Age is represented as seconds since appearance of the BR entry in the Network Data.
*
* @returns The number of peer BRs.
*
*/
uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge);
/**
* Enables / Disables DHCPv6 Prefix Delegation.
*
+44
View File
@@ -13,6 +13,7 @@ Usage : `br [command] ...`
- [omrprefix](#omrprefix)
- [onlinkprefix](#onlinkprefix)
- [pd](#pd)
- [peers](#peers)
- [prefixtable](#prefixtable)
- [rioprf](#rioprf)
- [routeprf](#routeprf)
@@ -35,6 +36,7 @@ enable
omrprefix
onlinkprefix
pd
peers
prefixtable
raoptions
rioprf
@@ -221,6 +223,48 @@ Get the DHCPv6 Prefix Delegation (PD) provided off-mesh-routable (OMR) prefix.
Done
```
### peers
Usage: `br peers`
Get the list of peer BRs found in the Network Data.
`OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE` is required.
Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered to provide external IP connectivity if at least one of the following conditions is met regarding its Network Data entries:
- It has added at least one external route entry.
- It has added at least one prefix entry with both the default-route and on-mesh flags set.
- It has added at least one domain prefix (with both the domain and on-mesh flags set).
The list of peer BRs specifically excludes the current device, even if it is itself acting as a BR.
Info per BR entry:
- RLOC16 of the BR
- Age as the duration interval since this BR appeared in Network Data. It is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds.
```bash
> br peers
rloc16:0x5c00 age:00:00:49
rloc16:0xf800 age:00:01:51
Done
```
Usage: `br peers count`
Gets the number of peer BRs found in the Network Data.
The count does not include the current device, even if it is itself acting as a BR.
The output indicates the minimum age among all peer BRs. Age is formatted as `{hh}:{mm}:{ss}` for hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds.
```bash
> br peer count
2 min-age:00:00:49
Done
```
### prefixtable
Usage: `br prefixtable`
+81
View File
@@ -374,6 +374,84 @@ exit:
#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
template <> otError Br::Process<Cmd("peers")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
/**
* @cli br peers
* @code
* br peers
* rloc16:0x5c00 age:00:00:49
* rloc16:0xf800 age:00:01:51
* Done
* @endcode
* @par
* Get the list of peer BRs found in Network Data entries.
* `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE` is required.
* Peer BRs are other devices within the Thread mesh that provide external IP connectivity. A device is considered
* to provide external IP connectivity if at least one of the following conditions is met regarding its Network
* Data entries:
* - It has added at least one external route entry.
* - It has added at least one prefix entry with both the default-route and on-mesh flags set.
* - It has added at least one domain prefix (with both the domain and on-mesh flags set).
* The list of peer BRs specifically excludes the current device, even if its is itself acting as a BR.
* Info per BR entry:
* - RLOC16 of the BR
* - Age as the duration interval since this BR appeared in Network Data. It is formatted as `{hh}:{mm}:{ss}` for
* hours, minutes, seconds, if the duration is less than 24 hours. If the duration is 24 hours or more, the
* format is `{dd}d.{hh}:{mm}:{ss}` for days, hours, minutes, seconds.
* @sa otBorderRoutingGetNextPrefixTableEntry
*/
if (aArgs[0].IsEmpty())
{
otBorderRoutingPrefixTableIterator iterator;
otBorderRoutingPeerBorderRouterEntry peerBrEntry;
char ageString[OT_DURATION_STRING_SIZE];
otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator);
while (otBorderRoutingGetNextPeerBrEntry(GetInstancePtr(), &iterator, &peerBrEntry) == OT_ERROR_NONE)
{
otConvertDurationInSecondsToString(peerBrEntry.mAge, ageString, sizeof(ageString));
OutputLine("rloc16:0x%04x age:%s", peerBrEntry.mRloc16, ageString);
}
}
/**
* @cli br peers count
* @code
* br peers count
* 2 min-age:00:00:47
* Done
* @endcode
* @par api_copy
* #otBorderRoutingCountPeerBrs
*/
else if (aArgs[0] == "count")
{
uint32_t minAge;
uint16_t count;
char ageString[OT_DURATION_STRING_SIZE];
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
count = otBorderRoutingCountPeerBrs(GetInstancePtr(), &minAge);
otConvertDurationInSecondsToString(minAge, ageString, sizeof(ageString));
OutputLine("%u min-age:%s", count, ageString);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
/**
* @cli br prefixtable
* @code
@@ -791,6 +869,9 @@ otError Br::Process(Arg aArgs[])
CmdEntry("onlinkprefix"),
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
CmdEntry("pd"),
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
CmdEntry("peers"),
#endif
CmdEntry("prefixtable"),
CmdEntry("raoptions"),
+22
View File
@@ -194,6 +194,28 @@ otError otBorderRoutingGetNextRouterEntry(otInstance *aI
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetNextRouterEntry(*aIterator, *aEntry);
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
otError otBorderRoutingGetNextPeerBrEntry(otInstance *aInstance,
otBorderRoutingPrefixTableIterator *aIterator,
otBorderRoutingPeerBorderRouterEntry *aEntry)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aEntry);
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetNextPeerBrEntry(*aIterator, *aEntry);
}
uint16_t otBorderRoutingCountPeerBrs(otInstance *aInstance, uint32_t *aMinAge)
{
uint32_t minAge;
return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().CountPeerBrs((aMinAge != nullptr) ? *aMinAge
: minAge);
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled)
{
+118
View File
@@ -75,6 +75,9 @@ RoutingManager::RoutingManager(Instance &aInstance)
, mOmrPrefixManager(aInstance)
, mRioAdvertiser(aInstance)
, mOnLinkPrefixManager(aInstance)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
, mNetDataPeerBrTracker(aInstance)
#endif
, mRxRaTracker(aInstance)
, mRoutePublisher(aInstance)
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
@@ -438,6 +441,10 @@ void RoutingManager::HandleNotifierEvents(Events aEvents)
mRoutePublisher.HandleNotifierEvents(aEvents);
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
mNetDataPeerBrTracker.HandleNotifierEvents(aEvents);
#endif
VerifyOrExit(IsInitialized() && IsEnabled());
if (aEvents.Contains(kEventThreadRoleChanged))
@@ -982,6 +989,91 @@ void RoutingManager::RoutePrefix::CopyInfoTo(PrefixTableEntry &aEntry, TimeMilli
aEntry.mRoutePreference = static_cast<otRoutePreference>(GetRoutePreference());
}
//---------------------------------------------------------------------------------------------------------------------
// NetDataPeerBrTracker
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
RoutingManager::NetDataPeerBrTracker::NetDataPeerBrTracker(Instance &aInstance)
: InstanceLocator(aInstance)
{
}
uint16_t RoutingManager::NetDataPeerBrTracker::CountPeerBrs(uint32_t &aMinAge) const
{
uint32_t uptime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
uint16_t count = 0;
aMinAge = NumericLimits<uint16_t>::kMax;
for (const PeerBr &peerBr : mPeerBrs)
{
count++;
aMinAge = Min(aMinAge, peerBr.GetAge(uptime));
}
if (count == 0)
{
aMinAge = 0;
}
return count;
}
Error RoutingManager::NetDataPeerBrTracker::GetNext(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const
{
using Iterator = RxRaTracker::Iterator;
Iterator &iterator = static_cast<Iterator &>(aIterator);
Error error;
SuccessOrExit(error = iterator.AdvanceToNextPeerBr(mPeerBrs.GetHead()));
aEntry.mRloc16 = iterator.GetPeerBrEntry()->mRloc16;
aEntry.mAge = iterator.GetPeerBrEntry()->GetAge(iterator.GetInitUptime());
exit:
return error;
}
void RoutingManager::NetDataPeerBrTracker::HandleNotifierEvents(Events aEvents)
{
NetworkData::Rlocs rlocs;
VerifyOrExit(aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged));
Get<NetworkData::Leader>().FindRlocs(NetworkData::kBrProvidingExternalIpConn, NetworkData::kAnyRole, rlocs);
// Remove `PeerBr` entries no longer found in Network Data,
// or they match the device RLOC16. Then allocate and add
// entries for newly discovered peers.
mPeerBrs.RemoveAndFreeAllMatching(PeerBr::Filter(rlocs));
mPeerBrs.RemoveAndFreeAllMatching(Get<Mle::Mle>().GetRloc16());
for (uint16_t rloc16 : rlocs)
{
PeerBr *newEntry;
if (Get<Mle::Mle>().HasRloc16(rloc16) || mPeerBrs.ContainsMatching(rloc16))
{
continue;
}
newEntry = PeerBr::Allocate();
VerifyOrExit(newEntry != nullptr, LogWarn("Failed to allocate `PeerBr` entry"));
newEntry->mRloc16 = rloc16;
newEntry->mDiscoverTime = Uptime::MsecToSec(Get<Uptime>().GetUptime());
mPeerBrs.Push(*newEntry);
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
//---------------------------------------------------------------------------------------------------------------------
// RxRaTracker
@@ -1850,6 +1942,32 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextPeerBr(const PeerBr *aPeerBrsHead)
{
Error error = kErrorNone;
if (GetType() == kUnspecified)
{
SetType(kPeerBrIterator);
SetEntry(aPeerBrsHead);
}
else
{
VerifyOrExit(GetType() == kPeerBrIterator, error = kErrorInvalidArgs);
VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound);
SetEntry(GetPeerBrEntry()->GetNext());
}
VerifyOrExit(GetPeerBrEntry() != nullptr, error = kErrorNotFound);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
//---------------------------------------------------------------------------------------------------------------------
// RxRaTracker::Router
+96 -1
View File
@@ -51,6 +51,10 @@
#error "OPENTHREAD_CONFIG_UPTIME_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE"
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE && !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
#error "TRACK_PEER_BR_INFO_ENABLE feature requires OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE"
#endif
#include <openthread/border_routing.h>
#include <openthread/nat64.h>
#include <openthread/netdata.h>
@@ -78,7 +82,6 @@
#include "thread/network_data.hpp"
namespace ot {
namespace BorderRouter {
extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength);
@@ -108,6 +111,7 @@ public:
typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator.
typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry.
typedef otBorderRoutingRouterEntry RouterEntry; ///< Router Entry.
typedef otBorderRoutingPeerBorderRouterEntry PeerBrEntry; ///< Peer Border Router Entry.
typedef otPdProcessedRaInfo PdProcessedRaInfo; ///< Data of PdProcessedRaInfo.
typedef otBorderRoutingRequestDhcp6PdCallback PdCallback; ///< DHCPv6 PD callback.
@@ -501,6 +505,38 @@ public:
return mRxRaTracker.GetNextRouter(aIterator, aEntry);
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
/**
* Iterates over the peer BRs found in the Network Data.
*
* @param[in,out] aIterator An iterator.
* @param[out] aEntry A reference to the entry to populate.
*
* @retval kErrorNone Got the next peer BR info, @p aEntry is updated and @p aIterator is advanced.
* @retval kErrorNotFound No more PR beers in the list.
*
*/
Error GetNextPeerBrEntry(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const
{
return mNetDataPeerBrTracker.GetNext(aIterator, aEntry);
}
/**
* Returns the number of peer BRs found in the Network Data.
*
* The count does not include this device itself (when it itself is acting as a BR).
*
* @param[out] aMinAge Reference to an `uint32_t` to return the minimum age among all peer BRs.
* Age is represented as seconds since appearance of the BR entry in the Network Data.
*
* @returns The number of peer BRs.
*
*/
uint16_t CountPeerBrs(uint32_t &aMinAge) const { return mNetDataPeerBrTracker.CountPeerBrs(aMinAge); }
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
/**
* Determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server.
@@ -736,6 +772,51 @@ private:
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
class RxRaTracker;
class NetDataPeerBrTracker : public InstanceLocator
{
friend class RxRaTracker;
public:
explicit NetDataPeerBrTracker(Instance &aInstance);
uint16_t CountPeerBrs(uint32_t &aMinAge) const;
Error GetNext(PrefixTableIterator &aIterator, PeerBrEntry &aEntry) const;
void HandleNotifierEvents(Events aEvents);
private:
struct PeerBr : LinkedListEntry<PeerBr>, Heap::Allocatable<PeerBr>
{
struct Filter
{
Filter(const NetworkData::Rlocs &aRlocs)
: mExcludeRlocs(aRlocs)
{
}
const NetworkData::Rlocs &mExcludeRlocs;
};
uint32_t GetAge(uint32_t aUptime) const { return aUptime - mDiscoverTime; }
bool Matches(uint16_t aRloc16) const { return mRloc16 == aRloc16; }
bool Matches(const Filter &aFilter) const { return !aFilter.mExcludeRlocs.Contains(mRloc16); }
PeerBr *mNext;
uint16_t mRloc16;
uint32_t mDiscoverTime;
};
OwningList<PeerBr> mPeerBrs;
};
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HandleRxRaTrackerSignalTask(void) { mRxRaTracker.HandleSignalTask(); }
void HandleRxRaTrackerExpirationTimer(void) { mRxRaTracker.HandleExpirationTimer(); }
void HandleRxRaTrackerStaleTimer(void) { mRxRaTracker.HandleStaleTimer(); }
@@ -755,6 +836,8 @@ private:
// the same flow of execution, the callback is invoked after all the
// changes are processed.
friend class NetDataPeerBrTracker;
public:
explicit RxRaTracker(Instance &aInstance);
@@ -881,6 +964,7 @@ private:
kUnspecified,
kRouterIterator,
kPrefixIterator,
kPeerBrIterator,
};
enum EntryType : uint8_t
@@ -903,6 +987,13 @@ private:
return static_cast<const Entry<PrefixType> *>(mPtr2);
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
using PeerBr = NetDataPeerBrTracker::PeerBr;
Error AdvanceToNextPeerBr(const PeerBr *aPeerBrsHead);
const PeerBr *GetPeerBrEntry(void) const { return static_cast<const PeerBr *>(mPtr2); }
#endif
private:
void SetRouter(const Entry<Router> *aRouter) { mPtr1 = aRouter; }
void SetInitUptime(uint32_t aUptime) { mData0 = aUptime; }
@@ -1487,6 +1578,10 @@ private:
OnLinkPrefixManager mOnLinkPrefixManager;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
NetDataPeerBrTracker mNetDataPeerBrTracker;
#endif
RxRaTracker mRxRaTracker;
RoutePublisher mRoutePublisher;
+6
View File
@@ -805,6 +805,12 @@ class Node(object):
def br_get_routers(self):
return self.cli('br routers')
def br_get_peer_brs(self):
return self.cli('br peers')
def br_count_peers(self):
return self._cli_single_output('br peers count')
# ------------------------------------------------------------------------------------------------------------------
# Helper methods
@@ -104,11 +104,33 @@ verify(br3.br_get_state() == 'running')
# Validate that all BRs discovered the other ones as peer BR
for br in [br1, br2, br3]:
all_brs = [br1, br2, br3]
for br in all_brs:
routers = br.br_get_routers()
verify(len(routers) == 2)
for router in routers:
verify(router.endswith('(peer BR)'))
verify(int(br.br_count_peers().split()[0]) == 2)
peers = br.br_get_peer_brs()
verify(len(peers) == 2)
other_brs = all_brs
other_brs.remove(br)
for other_br in other_brs:
rloc16 = other_br.get_rloc16()
verify(any([rloc16 in peer for peer in peers]))
# Disable BR3 and validate that BR1 and BR2 detect this.
# BR3 itself should continue to detect BR1 and BR2
br3.br_disable()
time.sleep(0.5)
for br in [br1, br2]:
verify(len(br.br_get_peer_brs()) == 1)
verify(len(br3.br_get_peer_brs()) == 2)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished