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