[routing-manager] indicate peer BRs in discovered infra-if routers (#10448)

This commit updates the public API `otBorderRoutingGetNextRouterEntry`
and `otBorderRoutingRouterEntry` structure to indicate whether a
discovered router on an infrastructure link is likely a peer Thread
Border Router (BR) connected to the same Thread mesh. The related CLI
commands are also updated.

Additionally, this commit adds new tests to validate the discovery and
tracking of peer BRs.
This commit is contained in:
Abtin Keshavarzian
2024-07-10 14:47:18 -07:00
committed by GitHub
parent 32fe4c7cdd
commit 4c84e4d642
8 changed files with 157 additions and 5 deletions
+6
View File
@@ -94,6 +94,11 @@ typedef struct otBorderRoutingPrefixTableIterator
/**
* Represents a discovered router on the infrastructure link.
*
* The `mIsPeerBr` field requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`. Routing Manager
* determines whether the router is a peer BR (connected to the same Thread mesh network) by comparing its advertised
* PIO/RIO prefixes with the entries in the Thread Network Data. While this method is generally effective, it may not
* be 100% accurate in all scenarios, so the `mIsPeerBr` flag should be used with caution.
*
*/
typedef struct otBorderRoutingRouterEntry
{
@@ -105,6 +110,7 @@ typedef struct otBorderRoutingRouterEntry
bool mStubRouterFlag : 1; ///< The router's Stub Router flag.
bool mIsLocalDevice : 1; ///< This router is the local device (this BR).
bool mIsReachable : 1; ///< This router is reachable.
bool mIsPeerBr : 1; ///< This router is (likely) a peer BR.
} otBorderRoutingRouterEntry;
/**
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (426)
#define OPENTHREAD_API_VERSION (427)
/**
* @addtogroup api-instance
+1
View File
@@ -355,6 +355,7 @@ Info per router:
- Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation probes.
- Age: Duration interval since this router was first discovered. 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.
- `(this BR)` is appended when the router is the local device itself.
- `(peer BR)` is appended when the router is likely a peer BR connected to the same Thread mesh. This requires `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.
```bash
> br routers
+9
View File
@@ -536,6 +536,8 @@ exit:
* 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.
* - `(this BR)` is appended when the router is the local device itself.
* - `(peer BR)` is appended when the router is likely a peer BR connected to the same Thread mesh. This requires
* `OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE`.
* @sa otBorderRoutingGetNextRouterEntry
*/
template <> otError Br::Process<Cmd("routers")>(Arg aArgs[])
@@ -576,6 +578,13 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry, RouterOutput
{
OutputFormat(" (this BR)");
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_TRACK_PEER_BR_INFO_ENABLE
if (aEntry.mIsPeerBr)
{
OutputFormat(" (peer BR)");
}
#endif
}
OutputNewLine();
@@ -1916,6 +1916,17 @@ bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker)
return !hasFlags && mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty();
}
bool RoutingManager::RxRaTracker::Router::IsPeerBr(void) const
{
// Determines whether the router is a peer BR (connected to the
// same Thread mesh network). It must have at least one entry
// (on-link or route) and all entries should be marked to be
// disregarded. While this model is generally effective to detect
// peer BRs, it may not be 100% accurate in all scenarios.
return mAllEntriesDisregarded && !(mOnLinkPrefixes.IsEmpty() && mRoutePrefixes.IsEmpty());
}
void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const
{
aEntry.mAddress = mAddress;
@@ -1926,6 +1937,7 @@ void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMi
aEntry.mStubRouterFlag = mStubRouterFlag;
aEntry.mIsLocalDevice = mIsLocalDevice;
aEntry.mIsReachable = IsReachable();
aEntry.mIsPeerBr = IsPeerBr();
}
//---------------------------------------------------------------------------------------------------------------------
@@ -841,6 +841,7 @@ private:
void DetermineReachabilityTimeout(void);
bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; }
bool Matches(const EmptyChecker &aChecker);
bool IsPeerBr(void) const;
void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow, uint32_t aUptime) const;
using OnLinkPrefixList = OwningList<Entry<OnLinkPrefix>>;
@@ -97,11 +97,16 @@ verify(br2_local_onlink != br2_favored_onlink)
verify(br1_favored_onlink == br2_favored_onlink)
br1_routers = br1.br_get_routers()
br2_routers = br2.br_get_routers()
# Check that the two BRs discover and track each other (not as peer BR since
# connected to different networks).
verify(len(br1_routers) > 0)
verify(len(br2_routers) > 0)
for br in [br1, br1]:
routers = br.br_get_routers()
verify(len(routers) > 0)
for router in routers:
verify('reachable:yes' in router)
verify('Stub:1' in router)
verify(not router.endswith('(peer BR)'))
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024, 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.
from cli import verify
from cli import verify_within
import cli
import time
# -----------------------------------------------------------------------------------------------------------------------
# Test description:
#
# Check tracking of peer BR (connected to same Thread mesh).
#
# ______________
# / \
# br1 --- br2 --- br3
#
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
print('-' * 120)
print('Starting \'{}\''.format(test_name))
# -----------------------------------------------------------------------------------------------------------------------
# Creating `cli.Nodes` instances
speedup = 60
cli.Node.set_time_speedup_factor(speedup)
br1 = cli.Node()
br2 = cli.Node()
br3 = cli.Node()
IF_INDEX = 1
# -----------------------------------------------------------------------------------------------------------------------
# Form topology
br1.form("peer-brs")
br2.join(br1)
br3.join(br2)
verify(br1.get_state() == 'leader')
verify(br2.get_state() == 'router')
verify(br3.get_state() == 'router')
# -----------------------------------------------------------------------------------------------------------------------
# Test implementation
# Start first border router `br1`
br1.srp_server_set_addr_mode('unicast')
br1.srp_server_auto_enable()
br1.br_init(IF_INDEX, 1)
br1.br_enable()
time.sleep(1)
verify(br1.br_get_state() == 'running')
br1_local_omr = br1.br_get_local_omrprefix()
br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
verify(br1_local_omr == br1_favored_omr)
br1_local_onlink = br1.br_get_local_onlinkprefix()
br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
verify(br1_local_onlink == br1_favored_onlink)
# Start `br2` and `br3` together
br2.br_init(IF_INDEX, 1)
br2.br_enable()
br3.br_init(IF_INDEX, 1)
br3.br_enable()
time.sleep(1)
verify(br2.br_get_state() == 'running')
verify(br3.br_get_state() == 'running')
# Validate that all BRs discovered the other ones as peer BR
for br in [br1, br2, br3]:
routers = br.br_get_routers()
verify(len(routers) == 2)
for router in routers:
verify(router.endswith('(peer BR)'))
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
cli.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))