From 58b230fef0cfd58da830ca2ae44fa4f48bbb5b23 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 27 Mar 2024 15:25:02 -0700 Subject: [PATCH] [cli] `netdata show` to support filtering by RLOC16 (#9969) This commit updates the CLI `netdata show` command to allow an optional RLOC16 as input. Providing an RLOC16 filters the output to display prefix, route, and service entries associated specifically with the specified RLOC16. This is helpful for checking entries added by a specific border router. `test-011-network-data-timeout.py` is updated to validate the new CLI functionality. --- src/cli/README_NETDATA.md | 17 ++++++- src/cli/cli_network_data.cpp | 44 ++++++++++++++----- src/cli/cli_network_data.hpp | 4 +- tests/toranj/cli/cli.py | 18 +++++--- .../cli/test-011-network-data-timeout.py | 26 +++++++---- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md index ad33bb993..940288596 100644 --- a/src/cli/README_NETDATA.md +++ b/src/cli/README_NETDATA.md @@ -334,10 +334,12 @@ Done ### show -Usage: `netdata show [local] [-x]` +Usage: `netdata show [local] [-x] [\]` Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information. +If the optional `rloc16` input is specified, prints the entries associated with the given RLOC16 only. The RLOC16 filtering can be used when `-x` or `local` are not used. + On-mesh prefixes are listed under `Prefixes` header: - The on-mesh prefix @@ -406,6 +408,19 @@ Commissioning: Done ``` +Print Network Data entries from the Leader associated with `0xa00` RLOC16. + +```bash +> netdata show 0xa00 +Prefixes: +fd00:dead:beef:cafe::/64 paros med a000 +Routes: +fd00:1234:0:0::/64 s med a000 +Services: +44970 5d fddead00beef00007bad0069ce45948504d2 s a000 +Done +``` + Print Network Data received from the Leader as hex-encoded TLVs. ```bash diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index fc25c9b56..7aa0dd5ed 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -564,7 +564,7 @@ otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRou return error; } -void NetworkData::OutputNetworkData(bool aLocal) +void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16) { otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otBorderRouterConfig prefix; @@ -577,7 +577,10 @@ void NetworkData::OutputNetworkData(bool aLocal) while (GetNextPrefix(&iterator, &prefix, aLocal) == OT_ERROR_NONE) { - OutputPrefix(prefix); + if ((aRloc16 == kAnyRloc16) || (aRloc16 == prefix.mRloc16)) + { + OutputPrefix(prefix); + } } OutputLine("Routes:"); @@ -585,7 +588,10 @@ void NetworkData::OutputNetworkData(bool aLocal) while (GetNextRoute(&iterator, &route, aLocal) == OT_ERROR_NONE) { - OutputRoute(route); + if ((aRloc16 == kAnyRloc16) || (aRloc16 == route.mRloc16)) + { + OutputRoute(route); + } } OutputLine("Services:"); @@ -593,10 +599,14 @@ void NetworkData::OutputNetworkData(bool aLocal) while (GetNextService(&iterator, &service, aLocal) == OT_ERROR_NONE) { - OutputService(service); + if ((aRloc16 == kAnyRloc16) || (aRloc16 == service.mServerConfig.mRloc16)) + { + OutputService(service); + } } VerifyOrExit(!aLocal); + VerifyOrExit(aRloc16 == kAnyRloc16); OutputLine("Contexts:"); iterator = OT_NETWORK_DATA_ITERATOR_INIT; @@ -716,8 +726,16 @@ exit: * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140 * Done * @endcode - * @cparam netdata show [@ca{-x}] + * @code + * netdata show 0xdc00 + * Prefixes: + * fd00:dead:beef:cafe::/64 paros med dc00 + * Routes: + * Services: + * Done + * @cparam netdata show [@ca{-x}|@ca{rloc16}] * * The optional `-x` argument gets Network Data as hex-encoded TLVs. + * * The optional `rloc16` argument gets all prefix/route/service entries associated with a given RLOC16. * @par * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix, @@ -774,9 +792,10 @@ exit: */ template <> otError NetworkData::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; - bool local = false; - bool binary = false; + otError error = OT_ERROR_INVALID_ARGS; + uint16_t rloc16 = kAnyRloc16; + bool local = false; + bool binary = false; for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++) { @@ -811,17 +830,22 @@ template <> otError NetworkData::Process(Arg aArgs[]) } else { - ExitNow(error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = aArgs[i].ParseAsUint16(rloc16)); } } + if (local || binary) + { + VerifyOrExit(rloc16 == kAnyRloc16, error = OT_ERROR_INVALID_ARGS); + } + if (binary) { error = OutputBinary(local); } else { - OutputNetworkData(local); + OutputNetworkData(local, rloc16); error = OT_ERROR_NONE; } diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index 61cd81897..ba8f281c4 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -130,6 +130,8 @@ public: private: using Command = CommandEntry; + static constexpr uint16_t kAnyRloc16 = 0xffff; + template otError Process(Arg aArgs[]); otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal); @@ -137,7 +139,7 @@ private: otError GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal); otError OutputBinary(bool aLocal); - void OutputNetworkData(bool aLocal); + void OutputNetworkData(bool aLocal, uint16_t aRloc16); #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL static void HandleNetdataFull(void *aContext) { static_cast(aContext)->HandleNetdataFull(); } diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 78c0229f2..89be4701e 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -398,19 +398,23 @@ class Node(object): #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # netdata - def get_netdata(self): - outputs = self.cli('netdata show') + def get_netdata(self, rloc16=None): + outputs = self.cli('netdata show', rloc16) outputs = [line.strip() for line in outputs] routes_index = outputs.index('Routes:') services_index = outputs.index('Services:') - contexts_index = outputs.index('Contexts:') - commissioning_index = outputs.index('Commissioning:') + if rloc16 is None: + contexts_index = outputs.index('Contexts:') + commissioning_index = outputs.index('Commissioning:') result = {} result['prefixes'] = outputs[1:routes_index] result['routes'] = outputs[routes_index + 1:services_index] - result['services'] = outputs[services_index + 1:contexts_index] - result['contexts'] = outputs[contexts_index + 1:commissioning_index] - result['commissioning'] = outputs[commissioning_index + 1:] + if rloc16 is None: + result['services'] = outputs[services_index + 1:contexts_index] + result['contexts'] = outputs[contexts_index + 1:commissioning_index] + result['commissioning'] = outputs[commissioning_index + 1:] + else: + result['services'] = outputs[services_index + 1:] return result diff --git a/tests/toranj/cli/test-011-network-data-timeout.py b/tests/toranj/cli/test-011-network-data-timeout.py index a3a4086a7..bf524878f 100755 --- a/tests/toranj/cli/test-011-network-data-timeout.py +++ b/tests/toranj/cli/test-011-network-data-timeout.py @@ -89,11 +89,6 @@ nodes = [r1, r2, c2] # ----------------------------------------------------------------------------------------------------------------------- # Test Implementation -common_prefix = 'fd00:cafe::' -prefix1 = 'fd00:1::' -prefix2 = 'fd00:2::' -prefix3 = 'fd00:3::' - # Each node adds its own prefix. r1.add_prefix('fd00:1::/64', 'paros', 'med') r2.add_prefix('fd00:2::/64', 'paros', 'med') @@ -104,6 +99,9 @@ r1.add_prefix('fd00:abba::/64', 'paros', 'high') r2.add_prefix('fd00:abba::/64', 'paros', 'med') c2.add_prefix('fd00:abba::/64', 'paros', 'low') +r1.add_route('fd00:cafe::/64', 's', 'med') +r2.add_route('fd00:cafe::/64', 's', 'med') + r1.register_netdata() r2.register_netdata() c2.register_netdata() @@ -112,12 +110,19 @@ c2.register_netdata() def check_netdata_on_all_nodes(): for node in nodes: netdata = node.get_netdata() - prefixes = netdata['prefixes'] - verify(len(prefixes) == 6) + verify(len(netdata['prefixes']) == 6) + verify(len(netdata['routes']) == 2) verify_within(check_netdata_on_all_nodes, 10) +# Check netdata filtering for r1 entries only. + +r1_rloc = int(r1.get_rloc16(), 16) +netdata = r1.get_netdata(r1_rloc) +verify(len(netdata['prefixes']) == 2) +verify(len(netdata['routes']) == 1) + # Remove `r2`. This should trigger all the prefixes added by it or its # child to timeout and be removed. @@ -127,8 +132,11 @@ r2.interface_down() def check_netdata_on_r1(): netdata = r1.get_netdata() - prefixes = netdata['prefixes'] - verify(len(prefixes) == 2) + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) + netdata = r1.get_netdata(r1_rloc) + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) verify_within(check_netdata_on_r1, 120)