From 51df484ac882b015031bff0f372b0daf9679ef3a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 2 Sep 2025 07:58:17 -0700 Subject: [PATCH] [nexus] add test for nat64 translator (#11882) This commit introduces a new Nexus test to validate the functionality of the NAT64 translator. The test is divided into two main parts: - `TestNat64StateChanges`: Verifies the state management of the translator. It checks that the translator transitions correctly between `kStateDisabled`, `kStateNotRunning`, and `kStateActive` when the feature is enabled/disabled or when the IPv4 CIDR and NAT64 prefix are configured or cleared. This test also confirms that state change notifications are properly signaled. - `TestNat64Mapping`: Validates the address mapping and translation logic. It ensures that address mappings are correctly created for new IPv6-to-IPv4 traffic, reused for subsequent packets from the same IPv6 source, and eventually expire and are removed after a period of inactivity. It also verifies that the mapping table is cleared when the configured IPv4 CIDR is changed. --- tests/nexus/CMakeLists.txt | 1 + tests/nexus/openthread-core-nexus-config.h | 1 + tests/nexus/test_nat64_translator.cpp | 611 +++++++++++++++++++++ 3 files changed, 613 insertions(+) create mode 100644 tests/nexus/test_nat64_translator.cpp diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index b70224027..a5b3a6076 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -116,4 +116,5 @@ ot_nexus_test(dtls) ot_nexus_test(form_join) ot_nexus_test(full_network_reset) ot_nexus_test(large_network) +ot_nexus_test(nat64_translator) ot_nexus_test(trel) diff --git a/tests/nexus/openthread-core-nexus-config.h b/tests/nexus/openthread-core-nexus-config.h index 52ce2288f..f9902952a 100644 --- a/tests/nexus/openthread-core-nexus-config.h +++ b/tests/nexus/openthread-core-nexus-config.h @@ -100,6 +100,7 @@ #define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 1 #define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 1 #define OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 1 +#define OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS 120 #define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE OPENTHREAD_FTD #define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL "Nexus Simulation" #define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME "OpenThread by Google Nest" diff --git a/tests/nexus/test_nat64_translator.cpp b/tests/nexus/test_nat64_translator.cpp new file mode 100644 index 000000000..f18e16519 --- /dev/null +++ b/tests/nexus/test_nat64_translator.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2025, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +static bool sNotifierCallbackInvoked = false; +static otChangedFlags sNotifierEvents = 0; + +void HandleNotifierEvent(otChangedFlags aFlags, void *aContext) +{ + VerifyOrQuit(aContext == nullptr); + + sNotifierCallbackInvoked = true; + sNotifierEvents = aFlags; +} + +void TestNat64StateChanges(void) +{ + Core nexus; + Node &node = nexus.CreateNode(); + Ip6::Prefix prefix, testPrefix; + Ip4::Cidr cidr, testCidr; + Nat64::Translator::AddressMappingIterator iterator; + Nat64::Translator::AddressMapping mapping; + + Log("------------------------------------------------------------------------------------------------------"); + Log("TestNat64StateChanges"); + + nexus.AdvanceTime(0); + + node.Form(); + nexus.AdvanceTime(50 * Time::kOneSecondInMsec); + VerifyOrQuit(node.Get().IsLeader()); + + node.Get().SetLogLevel(kLogLevelInfo); + + SuccessOrQuit(node.Get().RegisterCallback(HandleNotifierEvent, nullptr)); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Check NAT64 Translator's initial state"); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateDisabled); + + VerifyOrQuit(node.Get().GetIp4Cidr(cidr) == kErrorNotFound); + VerifyOrQuit(node.Get().GetIp6Prefix(prefix) == kErrorNotFound); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Enable NAT64"); + + node.Get().SetEnabled(true); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateNotRunning); + + VerifyOrQuit(node.Get().GetIp4Cidr(cidr) == kErrorNotFound); + VerifyOrQuit(node.Get().GetIp6Prefix(prefix) == kErrorNotFound); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Set an invalid CIDR"); + + testCidr.Clear(); + VerifyOrQuit(node.Get().SetIp4Cidr(testCidr) == kErrorInvalidArgs); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateNotRunning); + + VerifyOrQuit(node.Get().GetIp4Cidr(cidr) == kErrorNotFound); + VerifyOrQuit(node.Get().GetIp6Prefix(prefix) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Set a valid CIDR"); + + SuccessOrQuit(testCidr.FromString("192.168.100.0/8")); + SuccessOrQuit(node.Get().SetIp4Cidr(testCidr)); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateNotRunning); + + VerifyOrQuit(node.Get().GetIp6Prefix(prefix) == kErrorNotFound); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Set a IPv6 NAT64 prefix"); + + SuccessOrQuit(testPrefix.FromString("fd01::/96")); + node.Get().SetNat64Prefix(testPrefix); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Check that NAT64 is now active"); + + nexus.AdvanceTime(1); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Disable and re-enable NAT64"); + + nexus.AdvanceTime(1000); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + sNotifierCallbackInvoked = false; + + node.Get().SetEnabled(false); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateDisabled); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + nexus.AdvanceTime(1); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + // Re-enable + + sNotifierCallbackInvoked = false; + + node.Get().SetEnabled(true); + + nexus.AdvanceTime(1); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Set the NAT64 prefix to the same value"); + + nexus.AdvanceTime(1000); + + node.Get().SetNat64Prefix(testPrefix); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Clear NAT64 prefix and ensure NAT64 is stopped"); + + nexus.AdvanceTime(1000); + + sNotifierCallbackInvoked = false; + node.Get().ClearNat64Prefix(); + + nexus.AdvanceTime(1); + VerifyOrQuit(node.Get().GetState() == Nat64::kStateNotRunning); + + VerifyOrQuit(node.Get().GetIp6Prefix(prefix) == kErrorNotFound); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Change NAT64 prefix and ensure NAT64 is again active"); + + sNotifierCallbackInvoked = false; + + SuccessOrQuit(testPrefix.FromString("fd02::/96")); + node.Get().SetNat64Prefix(testPrefix); + + nexus.AdvanceTime(1); + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Clear the configured CIDR and ensure NAT64 is stopped"); + + nexus.AdvanceTime(1000); + + sNotifierCallbackInvoked = false; + node.Get().ClearIp4Cidr(); + + nexus.AdvanceTime(1); + VerifyOrQuit(node.Get().GetState() == Nat64::kStateNotRunning); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + VerifyOrQuit(node.Get().GetIp4Cidr(cidr) == kErrorNotFound); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Set the CIDR again and ensure NAT64 becomes active"); + + sNotifierCallbackInvoked = false; + + SuccessOrQuit(testCidr.FromString("192.168.200.1/32")); + SuccessOrQuit(node.Get().SetIp4Cidr(testCidr)); + + nexus.AdvanceTime(1); + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + SuccessOrQuit(node.Get().GetIp6Prefix(prefix)); + VerifyOrQuit(prefix == testPrefix); + + SuccessOrQuit(node.Get().GetIp4Cidr(cidr)); + VerifyOrQuit(cidr == testCidr); + + VerifyOrQuit(sNotifierCallbackInvoked); + VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged); + + Log("End of TestNat64StateChanges"); +} + +Message *PrepareMessage(Node &aNode, + const Ip6::Address &aSrcIp6Address, + const Ip4::Address &aDstIp4Address, + uint16_t aSrcPort = 1234, + uint16_t aDstPort = 1235, + uint16_t aPayloadLen = 10) +{ + Message *message = nullptr; + Ip6::Prefix nat64Prefix; + Ip6::Header ip6Header; + Ip6::Udp::Header udpHeader; + + message = aNode.Get().Allocate(Message::kTypeIp6); + VerifyOrQuit(message != nullptr); + + ip6Header.Clear(); + ip6Header.InitVersionTrafficClassFlow(); + + ip6Header.SetSource(aSrcIp6Address); + + SuccessOrQuit(aNode.Get().GetIp6Prefix(nat64Prefix)); + ip6Header.GetDestination().SynthesizeFromIp4Address(nat64Prefix, aDstIp4Address); + + ip6Header.SetNextHeader(Ip6::kProtoUdp); + ip6Header.SetPayloadLength(sizeof(Ip6::Udp::Header) + aPayloadLen); + + SuccessOrQuit(message->Append(ip6Header)); + + udpHeader.Clear(); + udpHeader.SetSourcePort(aSrcPort); + udpHeader.SetDestinationPort(aDstPort); + udpHeader.SetLength(aPayloadLen); + + SuccessOrQuit(message->Append(udpHeader)); + + for (uint16_t i = 0; i < aPayloadLen; i++) + { + SuccessOrQuit(message->Append(static_cast(i & 0xff))); + } + + return message; +} + +void LogAddressMapping(const Nat64::Translator::AddressMapping &aMapping) +{ + Log("Mapping: %s -> %s, remaining-time:%lu", AsCoreType(&aMapping.mIp6).ToString().AsCString(), + AsCoreType(&aMapping.mIp4).ToString().AsCString(), ToUlong(aMapping.mRemainingTimeMs)); +} + +void TestNat64Mapping(void) +{ + static constexpr uint32_t kExpireTimeout = 250 * Time::kOneSecondInMsec; + + static constexpr uint16_t kSrcPort = 55387; + static constexpr uint16_t kDstPort = 55388; + static constexpr uint16_t kPayloadLength = 32; + + Core nexus; + Node &node = nexus.CreateNode(); + Ip6::Prefix prefix; + Ip4::Cidr cidr; + Nat64::Translator::AddressMappingIterator iterator; + Nat64::Translator::AddressMapping mapping; + OwnedPtr message; + Nat64::Translator::Result result; + Ip6::Address ip6Addr; + Ip6::Address ip6Addr2; + Ip4::Address ip4Addr; + Ip4::Headers ip4Headers; + uint16_t count; + + Log("------------------------------------------------------------------------------------------------------"); + Log("TestNat64Mapping"); + + nexus.AdvanceTime(0); + + node.Form(); + nexus.AdvanceTime(50 * Time::kOneSecondInMsec); + VerifyOrQuit(node.Get().IsLeader()); + + node.Get().SetLogLevel(kLogLevelInfo); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Enable NAT64 translator"); + + SuccessOrQuit(prefix.FromString("fd01::/96")); + SuccessOrQuit(cidr.FromString("192.168.100.0/24")); + + node.Get().SetNat64Prefix(prefix); + SuccessOrQuit(node.Get().SetIp4Cidr(cidr)); + + node.Get().SetEnabled(true); + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Translate an IPv6 message"); + + SuccessOrQuit(ip6Addr.FromString("fd02::1")); + SuccessOrQuit(ip4Addr.FromString("200.100.1.1")); + + message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Check the created Address Mapping"); + + iterator.Init(node.GetInstance()); + SuccessOrQuit(iterator.GetNext(mapping)); + LogAddressMapping(mapping); + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Translate another IPv6 message from the same IPv6 sender to a new IPv4 dest"); + + SuccessOrQuit(ip4Addr.FromString("200.100.1.2")); + + message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength * 2)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength * 2); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Ensure the previous Address Mapping is reused"); + + iterator.Init(node.GetInstance()); + SuccessOrQuit(iterator.GetNext(mapping)); + LogAddressMapping(mapping); + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Translate a new IPv6 message from a new IPv6 address"); + + nexus.AdvanceTime(30 * 1000); + + SuccessOrQuit(ip6Addr2.FromString("fd02::2")); + + message.Reset(PrepareMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Ensure a new Address Mapping is created"); + + iterator.Init(node.GetInstance()); + + for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++) + { + LogAddressMapping(mapping); + + if (AsCoreType(&mapping.mIp6) == ip6Addr) + { + continue; + } + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr2); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + } + + VerifyOrQuit(count == 2); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Translate another IPv6 message from with previous used addresses"); + + SuccessOrQuit(ip4Addr.FromString("200.100.1.5")); + + message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Ensure the previous Address Mapping is reused"); + + iterator.Init(node.GetInstance()); + + for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++) + { + LogAddressMapping(mapping); + + if (AsCoreType(&mapping.mIp6) == ip6Addr2) + { + continue; + } + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + } + + VerifyOrQuit(count == 2); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + message.Reset(PrepareMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength); + + iterator.Init(node.GetInstance()); + + for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++) + { + LogAddressMapping(mapping); + + if (AsCoreType(&mapping.mIp6) == ip6Addr) + { + continue; + } + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr2); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + } + + VerifyOrQuit(count == 2); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Check Address Mapping expiration and removal"); + + nexus.AdvanceTime(kExpireTimeout); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Translate a new IPv6 message and check that a new Address Mapping is created"); + + message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength)); + + result = node.Get().TranslateFromIp6(*message); + VerifyOrQuit(result == Nat64::Translator::kForward); + + SuccessOrQuit(ip4Headers.ParseFrom(*message)); + VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr); + VerifyOrQuit(ip4Headers.IsUdp()); + VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort); + VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort); + VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength); + + iterator.Init(node.GetInstance()); + SuccessOrQuit(iterator.GetNext(mapping)); + LogAddressMapping(mapping); + + VerifyOrQuit(AsCoreType(&mapping.mIp6) == ip6Addr); + VerifyOrQuit(AsCoreType(&mapping.mIp4) == ip4Headers.GetSourceAddress()); + VerifyOrQuit(mapping.mRemainingTimeMs > 0); + + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Log("Change the CIDR and check that mapping list is cleared"); + + SuccessOrQuit(cidr.FromString("192.168.200.0/24")); + SuccessOrQuit(node.Get().SetIp4Cidr(cidr)); + + VerifyOrQuit(node.Get().GetState() == Nat64::kStateActive); + + iterator.Init(node.GetInstance()); + VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestNat64StateChanges(); + ot::Nexus::TestNat64Mapping(); + + printf("All tests passed\n"); + return 0; +}