From f4344792dafc2c11162e68062e32b4be433055ad Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 21 Apr 2021 22:19:27 -0700 Subject: [PATCH] [network-data] add support for NAT64 flag in HasRoute TLV (#6429) This commit adds a new flag in `NetworkData::HasRouteEntry` to indicate that an off-mesh route prefix in the Network Data is also a NAT64 prefix. It updates the public `otExternalRouteConfig` structure to include `mNat64` bit flag and adds support for it in the core modules. It also updates `test_network_data` unit test to cover the new flag and also adds some smaller style changes in this test. This commit updates the CLI modules to add support for the new flag. It also adds smaller enhancements in CLI related to network data, adding methods `NetworkData::OutputPreference()` and `OutputIp6Prefix()` to output common info (shared between on-mesh prefixes and off-mesh routes), also simplifying how the flags are mapped to char array string (avoiding extra space char in the output string). This commit also updates spinel and NCP code to add support for the new NAT64 flag. --- include/openthread/instance.h | 2 +- include/openthread/netdata.h | 5 + src/cli/cli.cpp | 4 + src/cli/cli_network_data.cpp | 125 +++++++++++++++-------- src/cli/cli_network_data.hpp | 2 + src/core/net/ip6_address.cpp | 12 ++- src/core/net/ip6_address.hpp | 11 ++ src/core/thread/network_data.cpp | 1 + src/core/thread/network_data_local.cpp | 16 ++- src/core/thread/network_data_local.hpp | 5 +- src/core/thread/network_data_tlvs.hpp | 30 ++++++ src/lib/spinel/spinel.h | 9 +- src/ncp/ncp_base_mtd.cpp | 29 +++--- tests/unit/test_network_data.cpp | 135 +++++++++++++++++-------- 14 files changed, 274 insertions(+), 112 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 46d8b4fbc..793afc76b 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 (103) +#define OPENTHREAD_API_VERSION (104) /** * @addtogroup api-instance diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h index 17c64945d..f06b480a6 100644 --- a/include/openthread/netdata.h +++ b/include/openthread/netdata.h @@ -143,6 +143,11 @@ typedef struct otExternalRouteConfig */ signed int mPreference : 2; + /** + * TRUE, if this is a NAT64 prefix, FALSE, otherwise. + */ + bool mNat64 : 1; + /** * TRUE, if this configuration is considered Stable Network Data. FALSE, otherwise. */ diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index ccb1d27c6..7efb58d59 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -3710,6 +3710,10 @@ otError Interpreter::ProcessRouteAdd(uint8_t aArgsLength, char *aArgs[]) { config.mStable = true; } + else if (strcmp(aArgs[argcur], "n") == 0) + { + config.mNat64 = true; + } else if (strcmp(aArgs[argcur], "high") == 0) { config.mPreference = OT_ROUTE_PREFERENCE_HIGH; diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 87c82537e..f25263471 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -55,57 +55,123 @@ NetworkData::NetworkData(Interpreter &aInterpreter) void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig) { - mInterpreter.OutputFormat("%x:%x:%x:%x::/%d ", HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[0]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[1]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[2]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[3]), aConfig.mPrefix.mLength); + enum + { + // BorderRouter flag is `uint16_t` (though some of the bits are + // reserved for future use), so we use 17 chars string (16 flags + // plus null char at end of string). + kMaxFlagStringSize = 17, + }; + + char flagsString[kMaxFlagStringSize]; + char *flagsPtr = &flagsString[0]; + + OutputIp6Prefix(aConfig.mPrefix); if (aConfig.mPreferred) { - mInterpreter.OutputFormat("p"); + *flagsPtr++ = 'p'; } if (aConfig.mSlaac) { - mInterpreter.OutputFormat("a"); + *flagsPtr++ = 'a'; } if (aConfig.mDhcp) { - mInterpreter.OutputFormat("d"); + *flagsPtr++ = 'd'; } if (aConfig.mConfigure) { - mInterpreter.OutputFormat("c"); + *flagsPtr++ = 'c'; } if (aConfig.mDefaultRoute) { - mInterpreter.OutputFormat("r"); + *flagsPtr++ = 'r'; } if (aConfig.mOnMesh) { - mInterpreter.OutputFormat("o"); + *flagsPtr++ = 'o'; } if (aConfig.mStable) { - mInterpreter.OutputFormat("s"); + *flagsPtr++ = 's'; } if (aConfig.mNdDns) { - mInterpreter.OutputFormat("n"); + *flagsPtr++ = 'n'; } if (aConfig.mDp) { - mInterpreter.OutputFormat("D"); + *flagsPtr++ = 'D'; } - switch (aConfig.mPreference) + *flagsPtr = '\0'; + + if (flagsPtr != &flagsString[0]) + { + mInterpreter.OutputFormat(" %s", flagsString); + } + + OutputPreference(aConfig.mPreference); + + mInterpreter.OutputLine(" %04x", aConfig.mRloc16); +} + +void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) +{ + enum + { + // ExternalRoute flag is `uint8_t` (though some of the bits are + // reserved for future use), so we use 9 chars string (8 flags + // plus null char at end of string). + kMaxFlagStringSize = 9, + }; + + char flagsString[kMaxFlagStringSize]; + char *flagsPtr = &flagsString[0]; + + OutputIp6Prefix(aConfig.mPrefix); + + if (aConfig.mStable) + { + *flagsPtr++ = 's'; + } + + if (aConfig.mNat64) + { + *flagsPtr++ = 'n'; + } + + *flagsPtr = '\0'; + + if (flagsPtr != &flagsString[0]) + { + mInterpreter.OutputFormat(" %s", flagsString); + } + + OutputPreference(aConfig.mPreference); + + mInterpreter.OutputLine(" %04x", aConfig.mRloc16); +} + +void NetworkData::OutputIp6Prefix(const otIp6Prefix &aPrefix) +{ + mInterpreter.OutputFormat("%x:%x:%x:%x::/%d", HostSwap16(aPrefix.mPrefix.mFields.m16[0]), + HostSwap16(aPrefix.mPrefix.mFields.m16[1]), HostSwap16(aPrefix.mPrefix.mFields.m16[2]), + HostSwap16(aPrefix.mPrefix.mFields.m16[3]), aPrefix.mLength); +} + +void NetworkData::OutputPreference(signed int aPreference) +{ + switch (aPreference) { case OT_ROUTE_PREFERENCE_LOW: mInterpreter.OutputFormat(" low"); @@ -118,39 +184,10 @@ void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig) case OT_ROUTE_PREFERENCE_HIGH: mInterpreter.OutputFormat(" high"); break; - } - mInterpreter.OutputLine(" %04x", aConfig.mRloc16); -} - -void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) -{ - mInterpreter.OutputFormat("%x:%x:%x:%x::/%d ", HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[0]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[1]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[2]), - HostSwap16(aConfig.mPrefix.mPrefix.mFields.m16[3]), aConfig.mPrefix.mLength); - - if (aConfig.mStable) - { - mInterpreter.OutputFormat("s "); - } - - switch (aConfig.mPreference) - { - case OT_ROUTE_PREFERENCE_LOW: - mInterpreter.OutputFormat("low"); - break; - - case OT_ROUTE_PREFERENCE_MED: - mInterpreter.OutputFormat("med"); - break; - - case OT_ROUTE_PREFERENCE_HIGH: - mInterpreter.OutputFormat("high"); + default: break; } - - mInterpreter.OutputLine(" %04x", aConfig.mRloc16); } void NetworkData::OutputService(const otServiceConfig &aConfig) diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index 6e37a2d69..7d8cad201 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -111,6 +111,8 @@ private: void OutputPrefixes(void); void OutputRoutes(void); void OutputServices(void); + void OutputIp6Prefix(const otIp6Prefix &aPrefix); + void OutputPreference(signed int aPreference); static constexpr Command sCommands[] = { {"help", &NetworkData::ProcessHelp}, diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index cc2bb1e7c..c0a35256d 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -118,6 +118,12 @@ uint8_t Prefix::MatchLength(const uint8_t *aPrefixA, const uint8_t *aPrefixB, ui return matchedLength; } +bool Prefix::IsValidNat64(void) const +{ + return (mLength == 32) || (mLength == 40) || (mLength == 48) || (mLength == 56) || (mLength == 64) || + (mLength == 96); +} + Prefix::InfoString Prefix::ToString(void) const { InfoString string; @@ -480,16 +486,14 @@ void Address::SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address kSkipIndex = 8, }; - uint8_t prefixLen = aPrefix.GetLength(); uint8_t ip6Index; - OT_ASSERT((prefixLen == 32) || (prefixLen == 40) || (prefixLen == 48) || (prefixLen == 56) || (prefixLen == 64) || - (prefixLen == 96)); + OT_ASSERT(aPrefix.IsValidNat64()); Clear(); SetPrefix(aPrefix); - ip6Index = prefixLen / CHAR_BIT; + ip6Index = aPrefix.GetLength() / CHAR_BIT; for (uint8_t i = 0; i < Ip4::Address::kSize; i++) { diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 95f776136..0da6d49db 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -261,6 +261,17 @@ public: */ static uint8_t MatchLength(const uint8_t *aPrefixA, const uint8_t *aPrefixB, uint8_t aMaxSize); + /** + * This method indicates whether or not the prefix has a valid length for use as a NAT64 prefix. + * + * A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6502). + * + * @retval TRUE If the prefix has a valid length for use as a NAT64 prefix. + * @retval FALSE If the prefix does not have a valid length for use as a NAT64 prefix. + * + */ + bool IsValidNat64(void) const; + /** * This method converts the prefix to a string. * diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 1a988e077..a0dc369e5 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -76,6 +76,7 @@ void ExternalRouteConfig::SetFrom(Instance & aInstance, aPrefixTlv.CopyPrefixTo(GetPrefix()); mPreference = aHasRouteEntry.GetPreference(); + mNat64 = aHasRouteEntry.IsNat64(); mStable = aHasRouteTlv.IsStable(); mRloc16 = aHasRouteEntry.GetRloc(); mNextHopIsThisDevice = (aHasRouteEntry.GetRloc() == aInstance.Get().GetRloc16()); diff --git a/src/core/thread/network_data_local.cpp b/src/core/thread/network_data_local.cpp index be3991816..88a1cb5eb 100644 --- a/src/core/thread/network_data_local.cpp +++ b/src/core/thread/network_data_local.cpp @@ -159,8 +159,19 @@ exit: Error Local::AddHasRoutePrefix(const ExternalRouteConfig &aConfig) { - return AddPrefix(aConfig.GetPrefix(), NetworkDataTlv::kTypeHasRoute, aConfig.mPreference, /* aFlags */ 0, - aConfig.mStable); + Error error; + uint8_t flags = 0; + + if (aConfig.mNat64) + { + VerifyOrExit(aConfig.GetPrefix().IsValidNat64(), error = kErrorInvalidArgs); + flags |= HasRouteEntry::kNat64Flag; + } + + error = AddPrefix(aConfig.GetPrefix(), NetworkDataTlv::kTypeHasRoute, aConfig.mPreference, flags, aConfig.mStable); + +exit: + return error; } Error Local::RemoveHasRoutePrefix(const Ip6::Prefix &aPrefix) @@ -208,6 +219,7 @@ Error Local::AddPrefix(const Ip6::Prefix & aPrefix, hasRouteTlv->SetLength(hasRouteTlv->GetLength() + sizeof(HasRouteEntry)); hasRouteTlv->GetEntry(0)->Init(); hasRouteTlv->GetEntry(0)->SetPreference(aPrf); + hasRouteTlv->GetEntry(0)->SetFlags(static_cast(aFlags)); } if (aStable) diff --git a/src/core/thread/network_data_local.hpp b/src/core/thread/network_data_local.hpp index 70f49e2a5..00f717ba5 100644 --- a/src/core/thread/network_data_local.hpp +++ b/src/core/thread/network_data_local.hpp @@ -110,8 +110,9 @@ public: * * @param[in] aConfig A reference to the external route configuration. * - * @retval kErrorNone Successfully added the Has Route entry. - * @retval kErrorNoBufs Insufficient space to add the Has Route entry. + * @retval kErrorNone Successfully added the Has Route entry. + * @retval kErrorInvalidArgs One or more parameters in @p aConfig were invalid. + * @retval kErrorNoBufs Insufficient space to add the Has Route entry. * */ Error AddHasRoutePrefix(const ExternalRouteConfig &aConfig); diff --git a/src/core/thread/network_data_tlvs.hpp b/src/core/thread/network_data_tlvs.hpp index ceac70893..4f66298df 100644 --- a/src/core/thread/network_data_tlvs.hpp +++ b/src/core/thread/network_data_tlvs.hpp @@ -227,6 +227,11 @@ OT_TOOL_PACKED_BEGIN class HasRouteEntry : public Equatable { public: + enum : uint8_t + { + kNat64Flag = 1 << 5, // NAT64 flag. + }; + /** * This method initializes the header. * @@ -273,6 +278,31 @@ public: mFlags = (mFlags & ~kPreferenceMask) | ((static_cast(aPrf) << kPreferenceOffset) & kPreferenceMask); } + /** + * This method gets the Flags value. + * + * @returns The Flags value. + * + */ + uint16_t GetFlags(void) const { return (mFlags & ~kPreferenceMask); } + + /** + * This method sets the Flags value. + * + * @param[in] aFlags The Flags value. + * + */ + void SetFlags(uint8_t aFlags) { mFlags = (mFlags & kPreferenceMask) | (aFlags & ~kPreferenceMask); } + + /** + * This method indicates whether or not the NAT64 flag is set. + * + * @retval TRUE If the NAT64 flag is set. + * @retval FALSE If the NAT64 flag is not set. + * + */ + bool IsNat64(void) const { return (mFlags & kNat64Flag) != 0; } + /** * This method returns a pointer to the next HasRouteEntry. * diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 4814ef314..fa0f226c1 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -632,6 +632,11 @@ enum SPINEL_ROUTE_PREFERENCE_LOW = (3 << SPINEL_NET_FLAG_PREFERENCE_OFFSET), }; +enum +{ + SPINEL_ROUTE_FLAG_NAT64 = (1 << 5), +}; + enum { SPINEL_THREAD_MODE_FULL_NETWORK_DATA = (1 << 0), @@ -2367,7 +2372,7 @@ enum * `6`: IPv6 Prefix * `C`: Prefix length in bits * `b`: Stable flag - * `C`: TLV flags + * `C`: TLV flags (SPINEL_NET_FLAG_* definition) * `b`: "Is defined locally" flag. Set if this network was locally * defined. Assumed to be true for set, insert and replace. Clear if * the on mesh network was defined by another node. @@ -2388,7 +2393,7 @@ enum * `6`: Route Prefix * `C`: Prefix length in bits * `b`: Stable flag - * `C`: Route preference flags + * `C`: Route flags (SPINEL_ROUTE_FLAG_* and SPINEL_ROUTE_PREFERNCE_* definitions) * `b`: "Is defined locally" flag. Set if this route info was locally * defined as part of local network data. Assumed to be true for set, * insert and replace. Clear if the route is part of partition's network diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 9e0a7453c..3793c8bf6 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -80,7 +80,7 @@ namespace Ncp { static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig) { - uint8_t flags(0); + uint8_t flags = 0; if (aConfig.mPreferred) { @@ -119,7 +119,7 @@ static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig) static uint8_t BorderRouterConfigToFlagByteExtended(const otBorderRouterConfig &aConfig) { - uint8_t flags(0); + uint8_t flags = 0; if (aConfig.mNdDns) { @@ -134,29 +134,31 @@ static uint8_t BorderRouterConfigToFlagByteExtended(const otBorderRouterConfig & return flags; } -static uint8_t ExternalRoutePreferenceToFlagByte(int aPreference) +static uint8_t ExternalRouteConfigToFlagByte(const otExternalRouteConfig &aConfig) { - uint8_t flags; + uint8_t flags = 0; - switch (aPreference) + switch (aConfig.mPreference) { case OT_ROUTE_PREFERENCE_LOW: - flags = SPINEL_ROUTE_PREFERENCE_LOW; + flags |= SPINEL_ROUTE_PREFERENCE_LOW; break; case OT_ROUTE_PREFERENCE_HIGH: - flags = SPINEL_ROUTE_PREFERENCE_HIGH; + flags |= SPINEL_ROUTE_PREFERENCE_HIGH; break; case OT_ROUTE_PREFERENCE_MED: - - OT_FALL_THROUGH; - default: - flags = SPINEL_ROUTE_PREFERENCE_MEDIUM; + flags |= SPINEL_ROUTE_PREFERENCE_MEDIUM; break; } + if (aConfig.mNat64) + { + flags |= SPINEL_ROUTE_FLAG_NAT64; + } + return flags; } @@ -2149,7 +2151,7 @@ template <> otError NcpBase::HandlePropertyGet otError NcpBase::HandlePropertyGet otError NcpBase::HandlePropertyInsert