diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index af5bc6a5c..4fd237aa1 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -681,25 +681,44 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif } #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - /** - * @cli nat64 cidr - * @code - * nat64 cidr - * 192.168.255.0/24 - * Done - * @endcode - * @par api_copy - * #otNat64GetCidr - * - */ else if (aArgs[0] == "cidr") { otIp4Cidr cidr; - char cidrString[OT_IP4_CIDR_STRING_SIZE]; - SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr)); - otIp4CidrToString(&cidr, cidrString, sizeof(cidrString)); - OutputLine("%s", cidrString); + /** + * @cli nat64 cidr + * @code + * nat64 cidr + * 192.168.255.0/24 + * Done + * @endcode + * @par api_copy + * #otNat64GetCidr + * + */ + if (aArgs[1].IsEmpty()) + { + char cidrString[OT_IP4_CIDR_STRING_SIZE]; + + SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr)); + otIp4CidrToString(&cidr, cidrString, sizeof(cidrString)); + OutputLine("%s", cidrString); + } + /** + * @cli nat64 cidr + * @code + * nat64 cidr 192.168.255.0/24 + * Done + * @endcode + * @par api_copy + * #otPlatNat64SetIp4Cidr + * + */ + else + { + SuccessOrExit(error = otIp4CidrFromString(aArgs[1].GetCString(), &cidr)); + error = otNat64SetIp4Cidr(GetInstancePtr(), &cidr); + } } /** * @cli nat64 mappings diff --git a/src/core/net/nat64_translator.cpp b/src/core/net/nat64_translator.cpp index 6513cb2df..bdee98ee6 100644 --- a/src/core/net/nat64_translator.cpp +++ b/src/core/net/nat64_translator.cpp @@ -36,6 +36,8 @@ #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +#include + #include "common/code_utils.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" @@ -507,7 +509,8 @@ Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr) numberOfHosts); mIp4Cidr = aCidr; - UpdateState(); + // Always notify the platform when the CIDR is changed. + UpdateState(true /* aAlwaysNotify */); exit: return err; @@ -631,7 +634,7 @@ void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t a mTotal.m4To6Bytes += aPacketSize; } -void Translator::UpdateState(void) +void Translator::UpdateState(bool aAlwaysNotify) { State newState; @@ -651,7 +654,14 @@ void Translator::UpdateState(void) newState = kStateDisabled; } - SuccessOrExit(Get().Update(mState, newState, kEventNat64TranslatorStateChanged)); + if (aAlwaysNotify) + { + Get().Signal(kEventNat64TranslatorStateChanged); + } + else + { + SuccessOrExit(Get().Update(mState, newState, kEventNat64TranslatorStateChanged)); + } LogInfo("NAT64 translator is now %s", StateToString(mState)); exit: diff --git a/src/core/net/nat64_translator.hpp b/src/core/net/nat64_translator.hpp index 2335e7a9f..bd29ff690 100644 --- a/src/core/net/nat64_translator.hpp +++ b/src/core/net/nat64_translator.hpp @@ -380,7 +380,7 @@ private: using MappingTimer = TimerMilliIn; - void UpdateState(void); + void UpdateState(bool aAlwaysNotify = false); bool mEnabled; State mState; diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 3b2e90830..073b46553 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -156,7 +156,9 @@ extern int unsigned int gNetifIndex = 0; char gNetifName[IFNAMSIZ]; -otIp4Cidr gNat64Cidr; +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +static otIp4Cidr sActiveNat64Cidr; +#endif const char *otSysGetThreadNetifName(void) { return gNetifName; } @@ -896,24 +898,55 @@ static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsA } #if defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE -void processNat64StateChange(otNat64State aNewState) +static bool isSameIp4Cidr(const otIp4Cidr &aCidr1, const otIp4Cidr &aCidr2) { - // If the interface is not up and we are enabling NAT64, the route will be added when we bring up the route. - // Also, the route will be deleted by the kernel when we shutting down the interface. - // We should try to add route first, since otNat64SetEnabled never fails. - if (otIp6IsEnabled(gInstance)) + bool res = true; + + VerifyOrExit(aCidr1.mLength == aCidr2.mLength, res = false); + + // The higher (32 - length) bits must be the same, host bits are ignored. + VerifyOrExit(((ntohl(aCidr1.mAddress.mFields.m32) ^ ntohl(aCidr2.mAddress.mFields.m32)) >> (32 - aCidr1.mLength)) == + 0, + res = false); + +exit: + return res; +} + +static void processNat64StateChange(void) +{ + otIp4Cidr translatorCidr; + + // Skip if NAT64 translator has not been configured with a CIDR. + SuccessOrExit(otNat64GetCidr(gInstance, &translatorCidr)); + + if (!isSameIp4Cidr(translatorCidr, sActiveNat64Cidr)) // Someone sets a new CIDR for NAT64. { - if (aNewState == OT_NAT64_STATE_ACTIVE) + char cidrString[OT_IP4_CIDR_STRING_SIZE]; + + if (sActiveNat64Cidr.mLength != 0) { - AddIp4Route(gNat64Cidr, kNat64RoutePriority); - otLogInfoPlat("[netif] Adding route for NAT64"); - } - else - { - DeleteIp4Route(gNat64Cidr); - otLogInfoPlat("[netif] Deleting route for NAT64"); + DeleteIp4Route(sActiveNat64Cidr); } + sActiveNat64Cidr = translatorCidr; + + otIp4CidrToString(&translatorCidr, cidrString, sizeof(cidrString)); + otLogInfoPlat("[netif] NAT64 CIDR updated to %s.", cidrString); } + + if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE) + { + AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority); + otLogInfoPlat("[netif] Adding route for NAT64"); + } + else if (sActiveNat64Cidr.mLength > 0) // Translator is not active. + { + DeleteIp4Route(sActiveNat64Cidr); + otLogInfoPlat("[netif] Deleting route for NAT64"); + } + +exit: + return; } #endif // defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE @@ -936,9 +969,9 @@ void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags) #endif } #if defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - if (OT_CHANGED_NAT64_TRANSLATOR_STATE & aFlags) + if ((OT_CHANGED_NAT64_TRANSLATOR_STATE | OT_CHANGED_THREAD_NETIF_STATE) & aFlags) { - processNat64StateChange(otNat64GetTranslatorState(aInstance)); + processNat64StateChange(); } #endif } @@ -1207,10 +1240,10 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli } #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - if (isUp && gNat64Cidr.mLength > 0) + if (isUp && sActiveNat64Cidr.mLength > 0) { - SuccessOrExit(error = otNat64SetIp4Cidr(gInstance, &gNat64Cidr)); - otLogInfoPlat("[netif] Succeeded to enable NAT64"); + // Recover NAT64 route. + AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority); } #endif @@ -1886,6 +1919,22 @@ void platformNetifInit(otPlatformConfig *aPlatformConfig) #endif } +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +void nat64Init(void) +{ + otIp4Cidr cidr; + + if (otIp4CidrFromString(OPENTHREAD_POSIX_CONFIG_NAT64_CIDR, &cidr) == OT_ERROR_NONE && cidr.mLength != 0) + { + otNat64SetIp4Cidr(gInstance, &cidr); + } + else + { + otLogInfoPlat("[netif] No default NAT64 CIDR provided."); + } +} +#endif + void platformNetifSetUp(void) { OT_ASSERT(gInstance != nullptr); @@ -1905,6 +1954,9 @@ void platformNetifSetUp(void) #if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED otIp6SetMulticastPromiscuousEnabled(aInstance, true); #endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + nat64Init(); +#endif #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE gResolver.Init(); #endif diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index d279a8507..08fb79084 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -49,7 +49,6 @@ #include #include #include -#include #include #include @@ -448,11 +447,6 @@ extern char gNetifName[IFNAMSIZ]; */ extern unsigned int gNetifIndex; -/** - * The CIDR for NAT64 - */ -extern otIp4Cidr gNat64Cidr; - /** * Initializes platform Backbone network. * diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index b6895bfcd..154cfc2de 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -148,13 +148,6 @@ void platformInit(otPlatformConfig *aPlatformConfig) gNetifName[0] = '\0'; -#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - if (otIp4CidrFromString(OPENTHREAD_POSIX_CONFIG_NAT64_CIDR, &gNat64Cidr) != OT_ERROR_NONE) - { - gNat64Cidr.mLength = 0; - } -#endif - #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifInit(aPlatformConfig); #endif diff --git a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py index 7aeea62b7..82f27ebd1 100755 --- a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py +++ b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py @@ -63,6 +63,9 @@ SMALL_NAT64_PREFIX = "fd00:00:00:01:00:00::/96" # So the BR will remove the random-generated one. LARGE_NAT64_PREFIX = "ff00:00:00:01:00:00::/96" +DEFAULT_NAT64_CIDR = ipaddress.IPv4Network("192.168.255.0/24") +UPDATED_NAT64_CIDR = ipaddress.IPv4Network("100.64.64.0/24") + class Nat64SingleBorderRouter(thread_cert.TestCase): USE_MESSAGE_FACTORY = False @@ -235,7 +238,16 @@ class Nat64SingleBorderRouter(thread_cert.TestCase): self.assertEqual(counters['protocol']['ICMP']['6to4']['packets'], 2) # - # Case 6. Disable and re-enable ethernet on the border router. + # Case 6. Change the CIDR used by NAT64 during runtime + # + self.assertEqual(br.nat64_cidr, DEFAULT_NAT64_CIDR) + + br.nat64_cidr = UPDATED_NAT64_CIDR + self.assertEqual(br.nat64_cidr, UPDATED_NAT64_CIDR) + self.assertTrue(router.ping(ipaddr=host_ip)) + + # + # Case 7. Disable and re-enable ethernet on the border router. # Note: disable_ether will remove default route but enable_ether won't add it back, # NAT64 connectivity tests will fail after this. # TODO: Add a default IPv4 route after enable_ether. diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 674cf6d16..7dd69a5dd 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -413,6 +413,19 @@ class OtbrDocker: def nat64_set_enabled(self, enable): return self.call_dbus_method('io.openthread.BorderRouter', 'SetNat64Enabled', enable) + @property + def nat64_cidr(self): + self.send_command('nat64 cidr') + cidr = self._expect_command_output()[0].strip() + return ipaddress.IPv4Network(cidr, strict=False) + + @nat64_cidr.setter + def nat64_cidr(self, cidr: ipaddress.IPv4Network): + if not isinstance(cidr, ipaddress.IPv4Network): + raise ValueError("cidr is expected to be an instance of ipaddress.IPv4Network") + self.send_command(f'nat64 cidr {cidr}') + self._expect_done() + @property def nat64_state(self): state = self.get_dbus_property('Nat64State')