From af031f3b50e0df1551803b4ba7c025381ea3df1f Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 17 Mar 2023 21:39:59 -0700 Subject: [PATCH] [netdata] new API to get lowpan context IDs (#8870) This commit adds `otNetDataGetNextLowpanContextInfo()` to iterate through the list of LoWPAN Context entries in Thread Network Data providing info about the prefix, its LoWPAN Context ID and Compress flag (relating to `ContextTlv` sub-TLVs of `PrefixTlv`s in Network Data). This commit also updates CLI command `netdata show` to list the context IDs in addition to prefixes, routes, and services. --- include/openthread/instance.h | 2 +- include/openthread/netdata.h | 27 ++++++++++++ src/cli/README_NETDATA.md | 49 ++++++++++++++++++++- src/cli/cli_network_data.cpp | 60 +++++++++++++++++++++++++- src/cli/cli_network_data.hpp | 1 + src/core/api/netdata_api.cpp | 10 +++++ src/core/thread/network_data.cpp | 50 +++++++++++++++++++-- src/core/thread/network_data.hpp | 13 ++++++ src/core/thread/network_data_types.cpp | 8 ++++ src/core/thread/network_data_types.hpp | 24 +++++++++++ tests/scripts/thread-cert/node.py | 6 ++- tests/toranj/cli/cli.py | 8 +++- tools/harness-thci/OpenThread.py | 5 ++- tools/otci/otci/otci.py | 11 ++++- 14 files changed, 263 insertions(+), 11 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 8f13ff1be..9a93e48ec 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (301) +#define OPENTHREAD_API_VERSION (302) /** * @addtogroup api-instance diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h index c3c22c6c8..ba9992114 100644 --- a/include/openthread/netdata.h +++ b/include/openthread/netdata.h @@ -71,6 +71,17 @@ typedef struct otBorderRouterConfig uint16_t mRloc16; ///< The border router's RLOC16 (value ignored on config add). } otBorderRouterConfig; +/** + * This structure represents 6LoWPAN Context ID information associated with a prefix in Network Data. + * + */ +typedef struct otLowpanContextInfo +{ + uint8_t mContextId; ///< The 6LoWPAN Context ID. + bool mCompressFlag; ///< The compress flag. + otIp6Prefix mPrefix; ///< The associated IPv6 prefix. +} otLowpanContextInfo; + /** * This structure represents an External Route configuration. * @@ -214,6 +225,22 @@ otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIte */ otError otNetDataGetNextService(otInstance *aInstance, otNetworkDataIterator *aIterator, otServiceConfig *aConfig); +/** + * Get the next 6LoWPAN Context ID info in the partition's Network Data. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in,out] aIterator A pointer to the Network Data iterator. To get the first service entry + it should be set to OT_NETWORK_DATA_ITERATOR_INIT. + * @param[out] aContextInfo A pointer to where the retrieved 6LoWPAN Context ID information will be placed. + * + * @retval OT_ERROR_NONE Successfully found the next 6LoWPAN Context ID info. + * @retval OT_ERROR_NOT_FOUND No subsequent 6LoWPAN Context info exists in the partition's Network Data. + * + */ +otError otNetDataGetNextLowpanContextInfo(otInstance *aInstance, + otNetworkDataIterator *aIterator, + otLowpanContextInfo *aContextInfo); + /** * Get the Network Data Version. * diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md index ecef75e24..b5c8ff0c5 100644 --- a/src/cli/README_NETDATA.md +++ b/src/cli/README_NETDATA.md @@ -284,14 +284,61 @@ Done Usage: `netdata show [local] [-x]` +Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information. + +On-mesh prefixes are listed under `Prefixes` header: + +- The on-mesh prefix +- Flags + - p: Preferred flag + - a: Stateless IPv6 Address Autoconfiguration flag + - d: DHCPv6 IPv6 Address Configuration flag + - c: DHCPv6 Other Configuration flag + - r: Default Route flag + - o: On Mesh flag + - s: Stable flag + - n: Nd Dns flag + - D: Domain Prefix flag (only available for Thread 1.2). +- Preference `high`, `med`, or `low` +- RLOC16 of device which added the on-mesh prefix + +External Routes are listed under `Routes` header: + +- The route prefix +- Flags + - s: Stable flag + - n: NAT64 flag +- Preference `high`, `med`, or `low` +- RLOC16 of device which added the route prefix + +Service entries are listed under `Services` header: + +- Enterprise number +- Service data (as hex bytes) +- Server data (as hex bytes) +- Flags + - s: Stable flag +- RLOC16 of devices which added the service entry + +6LoWPAN Context IDs are listed under `Contexts` header: + +- The prefix +- Context ID +- Compress flag (`c` if marked or `-` otherwise). + Print Network Data received from the Leader. ```bash > netdata show Prefixes: -fd00:dead:beef:cafe::/64 paros med dc00 +fd00:dead:beef:cafe::/64 paros med a000 Routes: +fd00:1234:0:0::/64 s med a000 +fd00:4567:0:0::/64 s med 8000 Services: +44970 5d fddead00beef00007bad0069ce45948504d2 s a000 +Contexts: +fd00:dead:beef:cafe::/64 1 c Done ``` diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index c28deb71a..499a5fbdb 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -606,6 +606,25 @@ void NetworkData::OutputServices(bool aLocal) } } +void NetworkData::OutputLowpanContexts(bool aLocal) +{ + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + otLowpanContextInfo info; + + VerifyOrExit(!aLocal); + + OutputLine("Contexts:"); + + while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &info) == OT_ERROR_NONE) + { + OutputIp6Prefix(info.mPrefix); + OutputLine(" %u %c", info.mContextId, info.mCompressFlag ? 'c' : '-'); + } + +exit: + return; +} + otError NetworkData::OutputBinary(bool aLocal) { otError error; @@ -643,6 +662,8 @@ exit: * Services: * 44970 5d c000 s 4000 * 44970 01 9a04b000000e10 s 4000 + * Contexts: + * fd00:dead:beef:cafe::/64 1 c * Done * @endcode * @code @@ -655,7 +676,43 @@ exit: * @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, - * #otNetDataGetNextRoute, and #otNetDataGetNextService. + * #otNetDataGetNextRoute, #otNetDataGetNextService and #otNetDataGetNextLowpanContextInfo. + * @par + * On-mesh prefixes are listed under `Prefixes` header: + * * The on-mesh prefix + * * Flags + * * p: Preferred flag + * * a: Stateless IPv6 Address Autoconfiguration flag + * * d: DHCPv6 IPv6 Address Configuration flag + * * c: DHCPv6 Other Configuration flag + * * r: Default Route flag + * * o: On Mesh flag + * * s: Stable flag + * * n: Nd Dns flag + * * D: Domain Prefix flag (only available for Thread 1.2). + * * Preference `high`, `med`, or `low` + * * RLOC16 of device which added the on-mesh prefix + * @par + * External Routes are listed under `Routes` header: + * * The route prefix + * * Flags + * * s: Stable flag + * * n: NAT64 flag + * * Preference `high`, `med`, or `low` + * * RLOC16 of device which added the route prefix + * @par + * Service entries are listed under `Services` header: + * * Enterprise number + * * Service data (as hex bytes) + * * Server data (as hex bytes) + * * Flags + * * s: Stable flag + * * RLOC16 of devices which added the service entry + * @par + * 6LoWPAN Context IDs are listed under `Contexts` header: + * * The prefix + * * Context ID + * * Compress flag (`c` if marked or `-` otherwise). * @par * @moreinfo{@netdata}. * @csa{br omrprefix} @@ -714,6 +771,7 @@ template <> otError NetworkData::Process(Arg aArgs[]) OutputPrefixes(local); OutputRoutes(local); OutputServices(local); + OutputLowpanContexts(local); error = OT_ERROR_NONE; } diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index 2d2714c04..d751c4587 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -139,6 +139,7 @@ private: void OutputPrefixes(bool aLocal); void OutputRoutes(bool aLocal); void OutputServices(bool aLocal); + void OutputLowpanContexts(bool aLocal); }; } // namespace Cli diff --git a/src/core/api/netdata_api.cpp b/src/core/api/netdata_api.cpp index 9325ee255..28e06c7f5 100644 --- a/src/core/api/netdata_api.cpp +++ b/src/core/api/netdata_api.cpp @@ -94,6 +94,16 @@ otError otNetDataGetNextService(otInstance *aInstance, otNetworkDataIterator *aI return AsCoreType(aInstance).Get().GetNextService(*aIterator, AsCoreType(aConfig)); } +otError otNetDataGetNextLowpanContextInfo(otInstance *aInstance, + otNetworkDataIterator *aIterator, + otLowpanContextInfo *aContextInfo) +{ + AssertPointerIsNotNull(aIterator); + + return AsCoreType(aInstance).Get().GetNextLowpanContextInfo(*aIterator, + AsCoreType(aContextInfo)); +} + uint8_t otNetDataGetVersion(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetLeaderData().GetDataVersion(NetworkData::kFullSet); diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index d4d6bf67f..2ef2c33e7 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -92,6 +92,7 @@ Error NetworkData::GetNextOnMeshPrefix(Iterator &aIterator, uint16_t aRloc16, On config.mOnMeshPrefix = &aConfig; config.mExternalRoute = nullptr; config.mService = nullptr; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } @@ -108,6 +109,7 @@ Error NetworkData::GetNextExternalRoute(Iterator &aIterator, uint16_t aRloc16, E config.mOnMeshPrefix = nullptr; config.mExternalRoute = &aConfig; config.mService = nullptr; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } @@ -124,10 +126,23 @@ Error NetworkData::GetNextService(Iterator &aIterator, uint16_t aRloc16, Service config.mOnMeshPrefix = nullptr; config.mExternalRoute = nullptr; config.mService = &aConfig; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } +Error NetworkData::GetNextLowpanContextInfo(Iterator &aIterator, LowpanContextInfo &aContextInfo) const +{ + Config config; + + config.mOnMeshPrefix = nullptr; + config.mExternalRoute = nullptr; + config.mService = nullptr; + config.mLowpanContext = &aContextInfo; + + return Iterate(aIterator, Mac::kShortAddrBroadcast, config); +} + Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfig) const { // Iterate to the next entry in Network Data matching `aRloc16` @@ -152,7 +167,8 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi switch (cur->GetType()) { case NetworkDataTlv::kTypePrefix: - if ((aConfig.mOnMeshPrefix != nullptr) || (aConfig.mExternalRoute != nullptr)) + if ((aConfig.mOnMeshPrefix != nullptr) || (aConfig.mExternalRoute != nullptr) || + (aConfig.mLowpanContext != nullptr)) { subTlvs = As(cur)->GetSubTlvs(); } @@ -199,6 +215,7 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi aConfig.mExternalRoute = nullptr; aConfig.mService = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mOnMeshPrefix->SetFrom(*prefixTlv, *borderRouter, *borderRouterEntry); ExitNow(error = kErrorNone); @@ -223,8 +240,9 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi { const HasRouteEntry *hasRouteEntry = hasRoute->GetEntry(index); - aConfig.mOnMeshPrefix = nullptr; - aConfig.mService = nullptr; + aConfig.mOnMeshPrefix = nullptr; + aConfig.mService = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mExternalRoute->SetFrom(GetInstance(), *prefixTlv, *hasRoute, *hasRouteEntry); ExitNow(error = kErrorNone); @@ -234,6 +252,29 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi break; } + case NetworkDataTlv::kTypeContext: + { + const ContextTlv *contextTlv = As(subCur); + + if (aConfig.mLowpanContext == nullptr) + { + continue; + } + + if (iterator.IsNewEntry()) + { + aConfig.mOnMeshPrefix = nullptr; + aConfig.mExternalRoute = nullptr; + aConfig.mService = nullptr; + aConfig.mLowpanContext->SetFrom(*prefixTlv, *contextTlv); + + iterator.MarkEntryAsNotNew(); + ExitNow(error = kErrorNone); + } + + break; + } + default: break; } @@ -260,6 +301,7 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi { aConfig.mOnMeshPrefix = nullptr; aConfig.mExternalRoute = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mService->SetFrom(*service, *server); iterator.MarkEntryAsNotNew(); @@ -344,6 +386,7 @@ bool NetworkData::ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRlo config.mOnMeshPrefix = &prefix; config.mExternalRoute = &route; config.mService = &service; + config.mLowpanContext = nullptr; SuccessOrExit(aCompare.Iterate(iterator, aRloc16, config)); @@ -643,6 +686,7 @@ Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const config.mOnMeshPrefix = &prefixConfig; config.mExternalRoute = &routeConfig; config.mService = &serviceConfig; + config.mLowpanContext = nullptr; SuccessOrExit(error = Iterate(aIterator, Mac::kShortAddrBroadcast, config)); diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp index ff1825746..72e44a56a 100644 --- a/src/core/thread/network_data.hpp +++ b/src/core/thread/network_data.hpp @@ -266,6 +266,18 @@ public: */ Error GetNextService(Iterator &aIterator, uint16_t aRloc16, ServiceConfig &aConfig) const; + /** + * This method gets the next 6LoWPAN Context ID info in the Thread Network Data. + * + * @param[in,out] aIterator A reference to the Network Data iterator. + * @param[out] aContextInfo A reference to where the retrieved 6LoWPAN Context ID information will be placed. + * + * @retval kErrorNone Successfully found the next 6LoWPAN Context ID info. + * @retval kErrorNotFound No subsequent 6LoWPAN Context info exists in the partition's Network Data. + * + */ + Error GetNextLowpanContextInfo(Iterator &aIterator, LowpanContextInfo &aContextInfo) const; + /** * This method indicates whether or not the Thread Network Data contains a given on mesh prefix entry. * @@ -557,6 +569,7 @@ private: OnMeshPrefixConfig *mOnMeshPrefix; ExternalRouteConfig *mExternalRoute; ServiceConfig *mService; + LowpanContextInfo *mLowpanContext; }; Error Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfig) const; diff --git a/src/core/thread/network_data_types.cpp b/src/core/thread/network_data_types.cpp index 5085782db..724bf0a59 100644 --- a/src/core/thread/network_data_types.cpp +++ b/src/core/thread/network_data_types.cpp @@ -268,5 +268,13 @@ void ServiceConfig::SetFrom(const ServiceTlv &aServiceTlv, const ServerTlv &aSer GetServerConfig().SetFrom(aServerTlv); } +void LowpanContextInfo::SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &aContextTlv) +{ + mContextId = aContextTlv.GetContextId(); + mCompressFlag = aContextTlv.IsCompress(); + aPrefixTlv.CopyPrefixTo(GetPrefix()); + GetPrefix().SetLength(aContextTlv.GetContextLength()); +} + } // namespace NetworkData } // namespace ot diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp index 0907612f9..d9a3c1069 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -67,6 +67,7 @@ class HasRouteTlv; class HasRouteEntry; class ServiceTlv; class ServerTlv; +class ContextTlv; /** * This enumeration represents the Network Data type. @@ -292,6 +293,28 @@ private: void SetFromTlvFlags(uint8_t aFlags); }; +/** + * This class represents 6LoWPAN Context ID information associated with a prefix in Network Data. + * + */ +class LowpanContextInfo : public otLowpanContextInfo, public Clearable +{ + friend class NetworkData; + +public: + /** + * This method gets the prefix. + * + * @return The prefix. + * + */ + const Ip6::Prefix &GetPrefix(void) const { return AsCoreType(&mPrefix); } + +private: + Ip6::Prefix &GetPrefix(void) { return AsCoreType(&mPrefix); } + void SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &aContextTlv); +}; + /** * This class represents a Service Data. * @@ -392,6 +415,7 @@ private: DefineCoreType(otBorderRouterConfig, NetworkData::OnMeshPrefixConfig); DefineCoreType(otExternalRouteConfig, NetworkData::ExternalRouteConfig); +DefineCoreType(otLowpanContextInfo, NetworkData::LowpanContextInfo); DefineCoreType(otServiceConfig, NetworkData::ServiceConfig); DefineCoreType(otServerConfig, NetworkData::ServiceConfig::ServerConfig); diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 4d4550268..96975cead 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -2300,6 +2300,8 @@ class NodeImpl: for line in netdata: if line.startswith('Services:'): services_section = True + elif line.startswith('Contexts'): + services_section = False elif services_section: services.append(line.strip().split(' ')) return services @@ -2310,8 +2312,8 @@ class NodeImpl: def get_netdata(self): raw_netdata = self.netdata_show() - netdata = {'Prefixes': [], 'Routes': [], 'Services': []} - key_list = ['Prefixes', 'Routes', 'Services'] + netdata = {'Prefixes': [], 'Routes': [], 'Services': [], 'Contexts': []} + key_list = ['Prefixes', 'Routes', 'Services', 'Contexts'] key = None for i in range(0, len(raw_netdata)): diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index ea89bc365..db0a40bc6 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -359,10 +359,13 @@ class Node(object): outputs = [line.strip() for line in outputs] routes_index = outputs.index('Routes:') services_index = outputs.index('Services:') + contexts_index = outputs.index('Contexts:') result = {} result['prefixes'] = outputs[1:routes_index] result['routes'] = outputs[routes_index + 1:services_index] - result['services'] = outputs[services_index + 1:] + result['services'] = outputs[services_index + 1:contexts_index] + result['contexts'] = outputs[contexts_index + 1:] + return result def get_netdata_prefixes(self): @@ -374,6 +377,9 @@ class Node(object): def get_netdata_services(self): return self.get_netdata()['services'] + def get_netdata_contexts(self): + return self.get_netdata()['contexts'] + def get_netdata_versions(self): leaderdata = Node.parse_list(self.cli('leaderdata')) return (int(leaderdata['Data Version']), int(leaderdata['Stable Data Version'])) diff --git a/tools/harness-thci/OpenThread.py b/tools/harness-thci/OpenThread.py index 5942aeec4..2410bc49d 100644 --- a/tools/harness-thci/OpenThread.py +++ b/tools/harness-thci/OpenThread.py @@ -1694,7 +1694,7 @@ class OpenThreadTHCI(object): @watched def getNetworkData(self): lines = self.__executeCommand('netdata show') - prefixes, routes, services = [], [], [] + prefixes, routes, services, contexts = [], [], [], [] classify = None for line in lines: @@ -1704,6 +1704,8 @@ class OpenThreadTHCI(object): classify = routes elif line == 'Services:': classify = services + elif line == 'Contexts:': + classify = contexts elif line == 'Done': classify = None else: @@ -1713,6 +1715,7 @@ class OpenThreadTHCI(object): 'Prefixes': prefixes, 'Routes': routes, 'Services': services, + 'Contexts': contexts, } @API diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index e66b4aa06..b75ecc0a0 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -1570,7 +1570,16 @@ class OTCI(object): routes_output.append(line) netdata['routes'] = self.__parse_routes(routes_output) - netdata['services'] = self.__parse_services(output) + + services_output = [] + while True: + line = output.pop(0) + if line == 'Contexts:': + break + else: + services_output.append(line) + + netdata['services'] = self.__parse_services(services_output) return netdata