diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 7a9dbb7bb..1f24649c1 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (487) +#define OPENTHREAD_API_VERSION (488) /** * @addtogroup api-instance diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h index 934ec5b9e..a7a3dd4ac 100644 --- a/include/openthread/netdata.h +++ b/include/openthread/netdata.h @@ -76,9 +76,10 @@ typedef struct otBorderRouterConfig */ typedef struct otLowpanContextInfo { - uint8_t mContextId; ///< The 6LoWPAN Context ID. - bool mCompressFlag; ///< The compress flag. - otIp6Prefix mPrefix; ///< The associated IPv6 prefix. + uint8_t mContextId; ///< The 6LoWPAN Context ID. + bool mCompressFlag : 1; ///< The compress flag. + bool mStable : 1; ///< Whether the Context TLV is marked as Stable Network Data. + otIp6Prefix mPrefix; ///< The associated IPv6 prefix. } otLowpanContextInfo; /** diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md index 824b548ac..96bf00bb6 100644 --- a/src/cli/README_NETDATA.md +++ b/src/cli/README_NETDATA.md @@ -380,7 +380,11 @@ Service entries are listed under `Services` header: - The prefix - Context ID -- Compress flag (`c` if marked or `-` otherwise). +- Flags: + - s: Stable flag + - c: Compress flag + +When there are no other flags, `-` will be used. Commissioning Dataset information is printed under `Commissioning` header: @@ -403,7 +407,7 @@ fd00:4567:0:0::/64 s med 8000 Services: 44970 5d fddead00beef00007bad0069ce45948504d2 s a000 0 Contexts: -fd00:dead:beef:cafe::/64 1 c +fd00:dead:beef:cafe::/64 1 sc Commissioning: 1248 dc00 9988 00000000000120000000000000000000 e Done diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 1a85ddfcd..ef79f016c 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -173,6 +173,32 @@ void NetworkData::OutputService(const otServiceConfig &aConfig) OutputLine(" %04x %u", aConfig.mServerConfig.mRloc16, aConfig.mServiceId); } +void NetworkData::OutputContext(const otLowpanContextInfo &aConfig) +{ + FlagsString flagsString; + char *flagsPtr = &flagsString[0]; + + if (aConfig.mStable) + { + *flagsPtr++ = 's'; + } + + if (aConfig.mCompressFlag) + { + *flagsPtr++ = 'c'; + } + + if (flagsPtr == &flagsString[0]) + { + *flagsPtr++ = '-'; + } + + *flagsPtr = '\0'; + + OutputIp6Prefix(aConfig.mPrefix); + OutputLine(" %u %s", aConfig.mContextId, flagsString); +} + /** * @cli netdata length * @code @@ -639,8 +665,7 @@ void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16) while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &context) == OT_ERROR_NONE) { - OutputIp6Prefix(context.mPrefix); - OutputLine(" %u %c", context.mContextId, context.mCompressFlag ? 'c' : '-'); + OutputContext(context); } otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset); @@ -742,7 +767,7 @@ exit: * 44970 5d c000 s 4000 0 * 44970 01 9a04b000000e10 s 4000 1 * Contexts: - * fd00:dead:beef:cafe::/64 1 c + * fd00:dead:beef:cafe::/64 1 sc * Commissioning: * 1248 dc00 9988 00000000000120000000000000000000 e * Done @@ -759,6 +784,7 @@ exit: * Routes: * Services: * Done + * @endcode * @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. @@ -802,7 +828,10 @@ exit: * 6LoWPAN Context IDs are listed under `Contexts` header: * * The prefix * * Context ID - * * Compress flag (`c` if marked or `-` otherwise). + * * Flags + * * s: Stable flag + * * c: Compress flag + * * -: If there are no flags * @par * Commissioning Dataset information is printed under `Commissioning` header: * * Session ID if present in Dataset or `-` otherwise diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index 5ef7b101e..47d71de4c 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -129,6 +129,7 @@ private: otError GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal); otError GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal); + void OutputContext(const otLowpanContextInfo &aConfig); otError OutputBinary(bool aLocal); void OutputNetworkData(bool aLocal, uint16_t aRloc16); diff --git a/src/core/thread/network_data_types.cpp b/src/core/thread/network_data_types.cpp index 7607e0b2e..0799db1d3 100644 --- a/src/core/thread/network_data_types.cpp +++ b/src/core/thread/network_data_types.cpp @@ -258,6 +258,7 @@ void LowpanContextInfo::SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &a { mContextId = aContextTlv.GetContextId(); mCompressFlag = aContextTlv.IsCompress(); + mStable = aContextTlv.IsStable(); aPrefixTlv.CopyPrefixTo(GetPrefix()); GetPrefix().SetLength(aContextTlv.GetContextLength()); } diff --git a/tests/toranj/cli/test-019-netdata-context-id.py b/tests/toranj/cli/test-019-netdata-context-id.py index 68029da21..ca1a58fc0 100755 --- a/tests/toranj/cli/test-019-netdata-context-id.py +++ b/tests/toranj/cli/test-019-netdata-context-id.py @@ -107,13 +107,18 @@ def check_netdata_1(): verify_within(check_netdata_1, 5) + +def parse_context_flags(context): + return context.split()[-1] + + contexts = netdata['contexts'] verify(len(contexts) == 3) verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) for context in contexts: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Remove prefix on `r3`. Validate that Context compress flag @@ -140,9 +145,9 @@ verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) for context in contexts: if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) else: - verify(context.endswith('-')) + verify('c' not in parse_context_flags(context)) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Validate that the prefix context is removed within reuse delay @@ -200,7 +205,7 @@ verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) for context in contexts: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Remove prefixes on `r1` and `r2` and re-add them back quickly both @@ -236,9 +241,9 @@ verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) for context in contexts: if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): - verify(context.endswith('-')) + verify('c' not in parse_context_flags(context)) else: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) # Re-add both prefixes (now from `r2`) before CID remove delay time # is expired. @@ -264,7 +269,7 @@ verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) for context in contexts: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) if context.startswith('fd00:1:0:0::/64'): verify(int(context.split()[1]) == cid1) elif context.startswith('fd00:2:0:0::/64'): @@ -305,9 +310,9 @@ verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) for context in contexts: if context.startswith('fd00:3:0:0::/64') or context.startswith('fd00:4:0:0::/64'): - verify(context.endswith('-')) + verify('c' not in parse_context_flags(context)) else: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) # Add first one removed as route and add a new prefix. @@ -333,7 +338,7 @@ verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) verify(any([context.startswith('fd00:5:0:0::/64') for context in contexts])) for context in contexts: - verify(context.endswith('c')) + verify('c' in parse_context_flags(context)) if context.startswith('fd00:5:0:0::/64'): verify(not int(context.split()[1]) in [cid3, cid4]) @@ -363,7 +368,7 @@ verify_within(check_netdata_9, 5) contexts = netdata['contexts'] verify(len(contexts) == 1) verify(contexts[0].startswith('fd00:5:0:0::/64')) -verify(contexts[0].endswith('c')) +verify('c' in parse_context_flags(context)) # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/toranj/cli/test-035-context-id-change-addr-reg.py b/tests/toranj/cli/test-035-context-id-change-addr-reg.py index 9e7251431..8b336ba0d 100755 --- a/tests/toranj/cli/test-035-context-id-change-addr-reg.py +++ b/tests/toranj/cli/test-035-context-id-change-addr-reg.py @@ -86,8 +86,8 @@ time.sleep(0.5 / speedup) netdata = leader.get_netdata() contexts = netdata['contexts'] verify(len(contexts) == 2) -verify(any([context.startswith('fd00:1:0:0::/64 1 c') for context in contexts])) -verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts])) +verify(any([context.startswith('fd00:1:0:0::/64 1') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64 2') for context in contexts])) # Remove the first prefix. @@ -105,7 +105,7 @@ time.sleep(3.5 / speedup) netdata = leader.get_netdata() contexts = netdata['contexts'] verify(len(contexts) == 1) -verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64 2') for context in contexts])) # Have `sed` attach as a child of `leader`. @@ -122,7 +122,7 @@ verify(int(sed.get_child_timeout()) == 10) netdata = sed.get_netdata() contexts = netdata['contexts'] verify(len(contexts) == 1) -verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64 2') for context in contexts])) # Find the `sed` address associated with on-mesh prefix `fd00:2::`. @@ -173,7 +173,7 @@ time.sleep(0.5 / speedup) netdata = leader.get_netdata() contexts = netdata['contexts'] verify(len(contexts) == 1) -verify(any([context.startswith('fd00:2:0:0::/64 1 c') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64 1') for context in contexts])) # Make sure that child is timed out and removed on parent.