Files
openthread/tests/nexus/test_nat64_translator.cpp
T
Abtin Keshavarzian ab3c6600a0 [icmp6] use Icmp6Header instead of Icmp::Header (#13194)
This commit updates the codebase to use the `Icmp6Header` type
directly, replacing the nested `Ip6::Icmp::Header` definition.
This change aligns the ICMPv6 header type definition with the
conventions used for other network protocol headers and simplifies
type references across the network, border router, and utility
modules.
2026-06-03 14:39:21 -07:00

1332 lines
50 KiB
C++

/*
* 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "core/net/ip4_types.hpp"
#include "core/net/nat64_translator.hpp"
#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<Mle::Mle>().IsLeader());
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
SuccessOrQuit(node.Get<Notifier>().RegisterCallback(HandleNotifierEvent, nullptr));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check NAT64 Translator's initial state");
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateDisabled);
VerifyOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr) == kErrorNotFound);
VerifyOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix) == kErrorNotFound);
iterator.Init(node.GetInstance());
VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable NAT64");
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateNotRunning);
VerifyOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr) == kErrorNotFound);
VerifyOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix) == kErrorNotFound);
iterator.Init(node.GetInstance());
VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set an invalid CIDR");
testCidr.Clear();
VerifyOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(testCidr) == kErrorInvalidArgs);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateNotRunning);
VerifyOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr) == kErrorNotFound);
VerifyOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix) == kErrorNotFound);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set a valid CIDR");
SuccessOrQuit(testCidr.FromString("192.168.100.0/8"));
SuccessOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(testCidr));
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateNotRunning);
VerifyOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix) == kErrorNotFound);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set a IPv6 NAT64 prefix");
SuccessOrQuit(testPrefix.FromString("fd01::/96"));
node.Get<Nat64::Translator>().SetNat64Prefix(testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check that NAT64 is now active");
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
VerifyOrQuit(sNotifierCallbackInvoked);
VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().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<Nat64::Translator>().GetState() == Nat64::kStateActive);
sNotifierCallbackInvoked = false;
node.Get<Nat64::Translator>().SetEnabled(false);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateDisabled);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
nexus.AdvanceTime(1);
VerifyOrQuit(sNotifierCallbackInvoked);
VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged);
// Re-enable
sNotifierCallbackInvoked = false;
node.Get<Nat64::Translator>().SetEnabled(true);
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
VerifyOrQuit(sNotifierCallbackInvoked);
VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set the NAT64 prefix to the same value");
nexus.AdvanceTime(1000);
node.Get<Nat64::Translator>().SetNat64Prefix(testPrefix);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Clear NAT64 prefix and ensure NAT64 is stopped");
nexus.AdvanceTime(1000);
sNotifierCallbackInvoked = false;
node.Get<Nat64::Translator>().ClearNat64Prefix();
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateNotRunning);
VerifyOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix) == kErrorNotFound);
SuccessOrQuit(node.Get<Nat64::Translator>().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<Nat64::Translator>().SetNat64Prefix(testPrefix);
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().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<Nat64::Translator>().ClearIp4Cidr();
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateNotRunning);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
VerifyOrQuit(node.Get<Nat64::Translator>().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<Nat64::Translator>().SetIp4Cidr(testCidr));
nexus.AdvanceTime(1);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
SuccessOrQuit(node.Get<Nat64::Translator>().GetNat64Prefix(prefix));
VerifyOrQuit(prefix == testPrefix);
SuccessOrQuit(node.Get<Nat64::Translator>().GetIp4Cidr(cidr));
VerifyOrQuit(cidr == testCidr);
VerifyOrQuit(sNotifierCallbackInvoked);
VerifyOrQuit(sNotifierEvents & kEventNat64TranslatorStateChanged);
Log("End of TestNat64StateChanges");
}
Message *PrepareUdpMessage(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::UdpHeader udpHeader;
message = aNode.Get<MessagePool>().Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr);
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.SetSource(aSrcIp6Address);
SuccessOrQuit(aNode.Get<Nat64::Translator>().GetNat64Prefix(nat64Prefix));
ip6Header.GetDestination().SynthesizeFromIp4Address(nat64Prefix, aDstIp4Address);
ip6Header.SetNextHeader(Ip6::kProtoUdp);
ip6Header.SetPayloadLength(sizeof(Ip6::UdpHeader) + 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<uint8_t>(static_cast<uint8_t>(i & 0xff)));
}
return message;
}
Message *PrepareTcpMessage(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::TcpHeader tcpHeader;
message = aNode.Get<MessagePool>().Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr);
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.SetSource(aSrcIp6Address);
SuccessOrQuit(aNode.Get<Nat64::Translator>().GetNat64Prefix(nat64Prefix));
ip6Header.GetDestination().SynthesizeFromIp4Address(nat64Prefix, aDstIp4Address);
ip6Header.SetNextHeader(Ip6::kProtoTcp);
ip6Header.SetPayloadLength(sizeof(Ip6::TcpHeader) + aPayloadLen);
SuccessOrQuit(message->Append(ip6Header));
tcpHeader.Clear();
tcpHeader.SetSourcePort(aSrcPort);
tcpHeader.SetDestinationPort(aDstPort);
SuccessOrQuit(message->Append(tcpHeader));
for (uint16_t i = 0; i < aPayloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(static_cast<uint8_t>(i & 0xff)));
}
return message;
}
Message *PrepareIcmp6Message(Node &aNode,
const Ip6::Address &aSrcIp6Address,
const Ip4::Address &aDstIp4Address,
uint16_t aPayloadLen = 10)
{
Message *message = nullptr;
Ip6::Prefix nat64Prefix;
Ip6::Header ip6Header;
Ip6::Icmp6Header icmpHeader;
message = aNode.Get<MessagePool>().Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr);
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.SetSource(aSrcIp6Address);
SuccessOrQuit(aNode.Get<Nat64::Translator>().GetNat64Prefix(nat64Prefix));
ip6Header.GetDestination().SynthesizeFromIp4Address(nat64Prefix, aDstIp4Address);
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
ip6Header.SetPayloadLength(sizeof(Ip6::Icmp6Header) + aPayloadLen);
SuccessOrQuit(message->Append(ip6Header));
icmpHeader.Clear();
icmpHeader.SetType(Ip6::Icmp6Header::kTypeEchoRequest);
SuccessOrQuit(message->Append(icmpHeader));
for (uint16_t i = 0; i < aPayloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(static_cast<uint8_t>(i & 0xff)));
}
message->SetOffset(sizeof(Ip6::Header));
Checksum::UpdateMessageChecksum(*message, ip6Header.GetSource(), ip6Header.GetDestination(), Ip6::kProtoIcmp6);
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 = 2 * 610 * 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> message;
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<Mle::Mle>().IsLeader());
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable NAT64 translator");
SuccessOrQuit(prefix.FromString("fd01::/96"));
SuccessOrQuit(cidr.FromString("192.168.100.0/24"));
node.Get<Nat64::Translator>().SetNat64Prefix(prefix);
SuccessOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(cidr));
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().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(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength * 2));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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(PrepareUdpMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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(PrepareUdpMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
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<Nat64::Translator>().SetIp4Cidr(cidr));
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
iterator.Init(node.GetInstance());
VerifyOrQuit(iterator.GetNext(mapping) == kErrorNotFound);
}
void TestNat64CidrAddressReuse(const char *aCidr)
{
static constexpr uint32_t kExpireTimeout = 600 * 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;
uint16_t numIp4Addrs;
Nat64::Translator::AddressMappingIterator iterator;
Nat64::Translator::AddressMapping mapping;
OwnedPtr<Message> message;
Ip6::Address ip6Addr;
Ip4::Address ip4Addr;
Ip4::Headers ip4Headers;
uint16_t count;
Log("------------------------------------------------------------------------------------------------------");
Log("TestNat64CidrAddressReuse(%s)", aCidr);
nexus.AdvanceTime(0);
node.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable NAT64 translator");
SuccessOrQuit(prefix.FromString("fd01::/96"));
SuccessOrQuit(cidr.FromString(aCidr));
node.Get<Nat64::Translator>().SetNat64Prefix(prefix);
SuccessOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(cidr));
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Determine number of available IPv4 addresses");
switch (cidr.GetLength())
{
case 32:
numIp4Addrs = 1;
break;
case 31:
numIp4Addrs = 2;
break;
default:
numIp4Addrs = (1 << (32 - cidr.GetLength())) - 2;
break;
}
Log("Number of available IPv4 addresses: %u", numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Translate IPv6 messages to use all IPv4 addresses");
for (uint16_t i = 0; i < numIp4Addrs; i++)
{
SuccessOrQuit(ip6Addr.FromString("fd02::0"));
SuccessOrQuit(ip4Addr.FromString("200.100.1.1"));
ip6Addr.mFields.m8[15] = i;
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
SuccessOrQuit(ip4Headers.ParseFrom(*message));
VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr);
VerifyOrQuit(ip4Headers.IsUdp());
VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort);
VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort);
VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength);
nexus.AdvanceTime(1000);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
}
VerifyOrQuit(count == numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that next translation fails since all IPv4 addresses are in use");
SuccessOrQuit(ip6Addr.FromString("fd02::100"));
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for mapping entries to expire");
nexus.AdvanceTime(kExpireTimeout);
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Translate IPv6 messages again and check that IPv4 addresses are now reused");
for (uint16_t i = 0; i < numIp4Addrs; i++)
{
SuccessOrQuit(ip6Addr.FromString("fd02::200:0"));
SuccessOrQuit(ip4Addr.FromString("200.100.3.3"));
ip6Addr.mFields.m8[15] = i + 100;
message.Reset(PrepareTcpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
SuccessOrQuit(ip4Headers.ParseFrom(*message));
VerifyOrQuit(ip4Headers.GetDestinationAddress() == ip4Addr);
VerifyOrQuit(ip4Headers.IsTcp());
VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort);
VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort);
nexus.AdvanceTime(1000);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
}
VerifyOrQuit(count == numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that next translation fails since all IPv4 addresses are in use");
SuccessOrQuit(ip6Addr.FromString("fd02::200:100"));
message.Reset(PrepareTcpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
Log("End of TestNat64CidrAddressReuse(%s)", aCidr);
}
void TestNat64Evict(void)
{
static constexpr uint32_t kEvictTimeout = (120 + 10) * 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;
uint16_t numIp4Addrs;
Nat64::Translator::AddressMappingIterator iterator;
Nat64::Translator::AddressMapping mapping;
OwnedPtr<Message> message;
Ip6::Address ip6Addr;
Ip4::Address ip4Addr;
uint16_t count;
Log("------------------------------------------------------------------------------------------------------");
Log("TestNat64Evict()");
nexus.AdvanceTime(0);
node.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Enable NAT64 translator");
SuccessOrQuit(prefix.FromString("fd00:abba::/96"));
SuccessOrQuit(cidr.FromString("192.168.107.0/29"));
numIp4Addrs = 6;
node.Get<Nat64::Translator>().SetNat64Prefix(prefix);
SuccessOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(cidr));
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Translate IPv6 messages to use all IPv4 addresses");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::0"));
SuccessOrQuit(ip4Addr.FromString("200.100.2.1"));
for (uint16_t i = 0; i < numIp4Addrs; i++)
{
ip6Addr.mFields.m8[15] = i;
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
}
VerifyOrQuit(count == numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that next translation fails since all IPv4 addresses are in use and none can be evicted");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::ff"));
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for longer than min evict time");
nexus.AdvanceTime(kEvictTimeout);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Now a new transaction should evict the oldest mapping");
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings and that oldest entry is evicted");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
VerifyOrQuit(mapping.mIp6.mFields.m8[15] != 0);
}
VerifyOrQuit(count == numIp4Addrs);
Log("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
node.Get<Nat64::Translator>().SetEnabled(false);
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Translate IPv6 TCP messages to use all IPv4 addresses");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::0"));
SuccessOrQuit(ip4Addr.FromString("200.100.2.1"));
for (uint16_t i = 1; i < numIp4Addrs; i++)
{
ip6Addr.mFields.m8[15] = i;
message.Reset(PrepareTcpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
}
Log("Translate one final IPv6 UDP message");
ip6Addr.mFields.m8[15] = 0;
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
}
VerifyOrQuit(count == numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that next translation fails since all IPv4 addresses are in use and none can be evicted");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::ff"));
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for longer than min evict time");
nexus.AdvanceTime(kEvictTimeout);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Now a new transaction should evict the UDP entry");
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings and that correct entry is evicted");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
VerifyOrQuit(mapping.mIp6.mFields.m8[15] != 0);
}
VerifyOrQuit(count == numIp4Addrs);
Log("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
node.Get<Nat64::Translator>().SetEnabled(false);
node.Get<Nat64::Translator>().SetEnabled(true);
VerifyOrQuit(node.Get<Nat64::Translator>().GetState() == Nat64::kStateActive);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Translate various TCP/UDP messages to use all IPv4 addresses");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::0"));
SuccessOrQuit(ip4Addr.FromString("200.100.2.1"));
for (uint16_t i = 1; i < numIp4Addrs - 1; i++)
{
ip6Addr.mFields.m8[15] = i;
message.Reset(PrepareTcpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
}
ip6Addr.mFields.m8[15] = numIp4Addrs - 1;
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
Log("Translate one final ICMPv6 message");
ip6Addr.mFields.m8[15] = 0;
message.Reset(PrepareIcmp6Message(node, ip6Addr, ip4Addr));
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
nexus.AdvanceTime(1000);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
}
VerifyOrQuit(count == numIp4Addrs);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Validate that next translation fails since all IPv4 addresses are in use and none can be evicted");
SuccessOrQuit(ip6Addr.FromString("fd00:cdef::ff"));
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Wait for longer than min evict time");
nexus.AdvanceTime(kEvictTimeout);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Now a new transaction should evict the ICMP entry");
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Check the Address Mappings and that correct entry is evicted");
iterator.Init(node.GetInstance());
for (count = 0; iterator.GetNext(mapping) == kErrorNone; count++)
{
LogAddressMapping(mapping);
VerifyOrQuit(mapping.mRemainingTimeMs > 0);
VerifyOrQuit(mapping.mIp6.mFields.m8[15] != 0);
}
VerifyOrQuit(count == numIp4Addrs);
Log("End of TestNat64Evict()");
}
void TestNat64IhlBypass(void)
{
Core nexus;
Node &node = nexus.CreateNode();
Ip6::Prefix prefix;
Ip4::Cidr cidr;
Ip6::Address ip6Addr;
Ip4::Address ip4Addr;
uint16_t srcPort = 5678;
uint16_t dstPort = 1234;
uint16_t payloadLen = 10;
Log("------------------------------------------------------------------------------------------------------");
Log("TestNat64IhlBypass");
nexus.AdvanceTime(0);
node.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
SuccessOrQuit(Instance::SetGlobalLogLevel(kLogLevelInfo));
SuccessOrQuit(prefix.FromString("fd00:7d03:7d03:7d03::/96"));
SuccessOrQuit(cidr.FromString("192.168.255.0/24"));
node.Get<Nat64::Translator>().SetNat64Prefix(prefix);
SuccessOrQuit(node.Get<Nat64::Translator>().SetIp4Cidr(cidr));
node.Get<Nat64::Translator>().SetEnabled(true);
// Create a mapping by sending a packet from IPv6 to IPv4
SuccessOrQuit(ip6Addr.FromString("fd00::1"));
SuccessOrQuit(ip4Addr.FromString("1.2.3.4"));
{
Log("Translate an IPv6 message to create mapping");
OwnedPtr<Message> message;
message.Reset(node.Get<MessagePool>().Allocate(Message::kTypeIp6));
VerifyOrQuit(message != nullptr);
Ip6::Header ip6Header;
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.SetSource(ip6Addr);
ip6Header.GetDestination().SynthesizeFromIp4Address(prefix, ip4Addr);
ip6Header.SetNextHeader(Ip6::kProtoUdp);
ip6Header.SetPayloadLength(sizeof(Ip6::UdpHeader) + payloadLen);
SuccessOrQuit(message->Append(ip6Header));
Ip6::UdpHeader udpHeader;
udpHeader.Clear();
udpHeader.SetSourcePort(srcPort);
udpHeader.SetDestinationPort(dstPort);
udpHeader.SetLength(sizeof(Ip6::UdpHeader) + payloadLen);
SuccessOrQuit(message->Append(udpHeader));
for (uint16_t i = 0; i < payloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(0));
}
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
}
// Now test IPv4 to IPv6 translation with IHL=6 (options)
Log("Test 2 (IHL=6, options)");
{
OwnedPtr<Message> message;
message.Reset(node.Get<MessagePool>().Allocate(Message::kTypeIp4));
VerifyOrQuit(message != nullptr);
Ip4::Header ip4Header;
ip4Header.Clear();
// IHL=6 means header length is 6*4 = 24 bytes.
ip4Header.SetVersionIhl(0x46);
ip4Header.SetTotalLength(24 + sizeof(Ip4::UdpHeader) + payloadLen);
ip4Header.SetProtocol(Ip4::kProtoUdp);
ip4Header.SetTtl(64);
ip4Header.SetSource(ip4Addr);
// Find mapped IPv4 address
Nat64::Translator::AddressMappingIterator iterator;
Nat64::Translator::AddressMapping mapping;
iterator.Init(node.GetInstance());
SuccessOrQuit(iterator.GetNext(mapping));
ip4Header.SetDestination(AsCoreType(&mapping.mIp4));
SuccessOrQuit(message->Append(ip4Header));
// Append 4 bytes of options (NOPs)
uint8_t options[] = {0x01, 0x01, 0x01, 0x00};
SuccessOrQuit(message->Append(options));
Ip4::UdpHeader udpHeader;
udpHeader.Clear();
udpHeader.SetSourcePort(srcPort);
udpHeader.SetDestinationPort(dstPort);
udpHeader.SetLength(sizeof(Ip4::UdpHeader) + payloadLen);
SuccessOrQuit(message->Append(udpHeader));
for (uint16_t i = 0; i < payloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(0));
}
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp4ToIp6(*message));
// Check the translated IPv6 packet
Ip6::Headers ip6Headers;
SuccessOrQuit(ip6Headers.ParseFrom(*message));
Log("PayloadLength=%d, SrcPort=%d, DstPort=%d", ip6Headers.GetIp6Header().GetPayloadLength(),
ip6Headers.GetSourcePort(), ip6Headers.GetDestinationPort());
VerifyOrQuit(ip6Headers.GetSourcePort() == srcPort, "Source port corrupted!");
VerifyOrQuit(ip6Headers.GetDestinationPort() == dstPort, "Destination port corrupted!");
}
// Test 3: Source route options should be discarded
Log("Test 3 (IHL=7, LSRR option)");
{
OwnedPtr<Message> message;
message.Reset(node.Get<MessagePool>().Allocate(Message::kTypeIp4));
VerifyOrQuit(message != nullptr);
Ip4::Header ip4Header;
ip4Header.Clear();
// IHL=7 means header length is 7*4 = 28 bytes.
ip4Header.SetVersionIhl(0x47);
ip4Header.SetTotalLength(28 + sizeof(Ip4::UdpHeader) + payloadLen);
ip4Header.SetProtocol(Ip4::kProtoUdp);
ip4Header.SetTtl(64);
ip4Header.SetSource(ip4Addr);
// Find mapped IPv4 address
Nat64::Translator::AddressMappingIterator iterator;
Nat64::Translator::AddressMapping mapping;
iterator.Init(node.GetInstance());
SuccessOrQuit(iterator.GetNext(mapping));
ip4Header.SetDestination(AsCoreType(&mapping.mIp4));
SuccessOrQuit(message->Append(ip4Header));
// Append LSRR option (Type 131, Length 8, Pointer 4, Address 1.1.1.1)
uint8_t options[] = {0x83, 0x08, 0x04, 0x01, 0x01, 0x01, 0x01, 0x00};
SuccessOrQuit(message->Append(options));
Ip4::UdpHeader udpHeader;
udpHeader.Clear();
udpHeader.SetSourcePort(srcPort);
udpHeader.SetDestinationPort(dstPort);
udpHeader.SetLength(sizeof(Ip4::UdpHeader) + payloadLen);
SuccessOrQuit(message->Append(udpHeader));
for (uint16_t i = 0; i < payloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(0));
}
// This should return kErrorDrop
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp4ToIp6(*message) == kErrorDrop,
"LSRR option not discarded!");
}
// Test 4: SSRR option should be discarded
Log("Test 4 (IHL=7, SSRR option)");
{
OwnedPtr<Message> message;
message.Reset(node.Get<MessagePool>().Allocate(Message::kTypeIp4));
VerifyOrQuit(message != nullptr);
Ip4::Header ip4Header;
ip4Header.Clear();
// IHL=7 means header length is 7*4 = 28 bytes.
ip4Header.SetVersionIhl(0x47);
ip4Header.SetTotalLength(28 + sizeof(Ip4::UdpHeader) + payloadLen);
ip4Header.SetProtocol(Ip4::kProtoUdp);
ip4Header.SetTtl(64);
ip4Header.SetSource(ip4Addr);
// Find mapped IPv4 address
Nat64::Translator::AddressMappingIterator iterator;
Nat64::Translator::AddressMapping mapping;
iterator.Init(node.GetInstance());
SuccessOrQuit(iterator.GetNext(mapping));
ip4Header.SetDestination(AsCoreType(&mapping.mIp4));
SuccessOrQuit(message->Append(ip4Header));
// Append SSRR option (Type 137, Length 8, Pointer 4, Address 1.1.1.1)
uint8_t options[] = {0x89, 0x08, 0x04, 0x01, 0x01, 0x01, 0x01, 0x00};
SuccessOrQuit(message->Append(options));
Ip4::UdpHeader udpHeader;
udpHeader.Clear();
udpHeader.SetSourcePort(srcPort);
udpHeader.SetDestinationPort(dstPort);
udpHeader.SetLength(sizeof(Ip4::UdpHeader) + payloadLen);
SuccessOrQuit(message->Append(udpHeader));
for (uint16_t i = 0; i < payloadLen; i++)
{
SuccessOrQuit(message->Append<uint8_t>(0));
}
// This should return kErrorDrop
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp4ToIp6(*message) == kErrorDrop,
"SSRR option not discarded!");
}
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::TestNat64StateChanges();
ot::Nexus::TestNat64Mapping();
ot::Nexus::TestNat64CidrAddressReuse("192.168.101.133/32");
ot::Nexus::TestNat64CidrAddressReuse("192.168.102.178/31");
ot::Nexus::TestNat64CidrAddressReuse("192.168.103.0/30");
ot::Nexus::TestNat64CidrAddressReuse("192.168.104.0/27");
ot::Nexus::TestNat64Evict();
ot::Nexus::TestNat64IhlBypass();
printf("All tests passed\n");
return 0;
}