mirror of
https://github.com/espressif/openthread.git
synced 2026-07-27 06:17:47 +00:00
[nat64] introduce eviction mechanism for stale mappings (#11949)
Previously, when the translator ran out of IPv4 addresses or mapping pool entries, any new NAT64 translation attempt would fail until earlier entries reached their expiration time. The default expiration time is long (7200 seconds / 2 hours). This commit introduces a mechanism to evict existing "stale" mappings (old but not yet expired) to make room for new ones. This is handled by the new `EvictStaleMapping()` method, which is called from `AllocateIp4Address()` and `AllocateMapping()` when resources are full. A mapping is only eligible for eviction after a minimum idle period (`kMinEvictTimeout`) which is set to 2 minutes (matching `UDP_MIN` in RFC 6146). The eviction logic prioritizes mappings based on protocol usage and last use time. The preference for eviction, from most to least preferred, is: 1. ICMP-only mappings 2. UDP (and possibly ICMP) mappings 3. TCP (and possibly other) mappings If two mappings are in the same category, the one that has been idle for the longest time is evicted. This commit also adds a detailed `TestNat64Evict` test case to validate the eviction logic and priority.
This commit is contained in:
@@ -76,7 +76,7 @@ Translator::Translator(Instance &aInstance)
|
||||
|
||||
mNat64Prefix.Clear();
|
||||
mIp4Cidr.Clear();
|
||||
mTimer.Start(kIdleTimeout);
|
||||
mTimer.Start(Min(kIdleTimeout, kIcmpTimeout));
|
||||
|
||||
mCounters.Clear();
|
||||
ClearAllBytes(mErrorCounters);
|
||||
@@ -351,7 +351,7 @@ void Translator::Mapping::CopyTo(AddressMapping &aMapping, TimeMilli aNow) const
|
||||
// might become active again before actually removed. Report the
|
||||
// mapping to be "just expired" to avoid confusion.
|
||||
|
||||
aMapping.mRemainingTimeMs = (mExpiry < aNow) ? 0 : mExpiry - aNow;
|
||||
aMapping.mRemainingTimeMs = (mExpirationTime < aNow) ? 0 : mExpirationTime - aNow;
|
||||
}
|
||||
|
||||
void Translator::Mapping::Free(void)
|
||||
@@ -416,8 +416,7 @@ Error Translator::AllocateIp4Address(Ip4::Address &aIp4Address)
|
||||
|
||||
if (mActiveMappings.CountAllEntries() >= numberOfHosts)
|
||||
{
|
||||
mActiveMappings.RemoveAndFreeAllMatching(TimerMilli::GetNow());
|
||||
|
||||
EvictStaleMapping();
|
||||
VerifyOrExit(mActiveMappings.CountAllEntries() < numberOfHosts, error = kErrorFailed);
|
||||
}
|
||||
|
||||
@@ -454,6 +453,12 @@ Translator::Mapping *Translator::AllocateMapping(const Ip6::Headers &aIp6Headers
|
||||
|
||||
mapping = mMappingPool.Allocate();
|
||||
|
||||
if (mapping == nullptr)
|
||||
{
|
||||
EvictStaleMapping();
|
||||
mapping = mMappingPool.Allocate();
|
||||
}
|
||||
|
||||
VerifyOrExit(mapping != nullptr);
|
||||
|
||||
mActiveMappings.Push(*mapping);
|
||||
@@ -472,6 +477,110 @@ exit:
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Translator::Mapping::ProtoCategory Translator::Mapping::DetermineProtocolCategory(void) const
|
||||
{
|
||||
ProtoCategory category;
|
||||
|
||||
if (!IsCounterZero(mCounters.mTcp))
|
||||
{
|
||||
category = kTcpAndMaybeOthers;
|
||||
}
|
||||
else if (!IsCounterZero(mCounters.mUdp))
|
||||
{
|
||||
category = kUdpAndMaybeIcmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
category = kIcmpOnly;
|
||||
}
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
bool Translator::Mapping::IsCounterZero(const ProtocolCounters::Counters &aCounters)
|
||||
{
|
||||
return (aCounters.m6To4Packets == 0) && (aCounters.m4To6Packets == 0);
|
||||
}
|
||||
|
||||
bool Translator::Mapping::IsEligibleForEviction(TimeMilli aNow) const
|
||||
{
|
||||
return DetermineDurationSinceUse(aNow) >= kMinEvictTimeout;
|
||||
}
|
||||
|
||||
bool Translator::Mapping::IsBetterEvictionCandidateOver(const Mapping &aOther, TimeMilli aNow) const
|
||||
{
|
||||
// Determines whether this mapping is a better candidate for
|
||||
// eviction compared to `aOther`. This method assumes both
|
||||
// mappings are already eligible for eviction.
|
||||
//
|
||||
// The eviction preference, from most to least preferred to be
|
||||
// evicted, is:
|
||||
//
|
||||
// 1) ICMP-only mappings.
|
||||
// 2) UDP mappings (maybe used with ICMP as well).
|
||||
// 3) TCP mappings (maybe used with UDP or ICMP).
|
||||
//
|
||||
// If both mappings are in the same protocol category, the one
|
||||
// that hasn't been used for the longest time is preferred.
|
||||
|
||||
bool isBetter = false;
|
||||
int compare;
|
||||
|
||||
// Smaller value for `DetermineProtocolCategory` is preferred.
|
||||
// (kIcmpOnly -> kUdpAndMaybeIcmp, -> kTcpAndMaybeOthers).
|
||||
|
||||
static_assert(kIcmpOnly < kUdpAndMaybeIcmp, "ProtoCategory enum constants are invalid");
|
||||
static_assert(kUdpAndMaybeIcmp < kTcpAndMaybeOthers, "ProtoCategory enum constants are invalid");
|
||||
|
||||
compare = ThreeWayCompare<uint8_t>(DetermineProtocolCategory(), aOther.DetermineProtocolCategory());
|
||||
|
||||
if (compare < 0)
|
||||
{
|
||||
isBetter = true;
|
||||
}
|
||||
else if (compare == 0)
|
||||
{
|
||||
isBetter = (DetermineDurationSinceUse(aNow) > aOther.DetermineDurationSinceUse(aNow));
|
||||
}
|
||||
|
||||
return isBetter;
|
||||
}
|
||||
|
||||
void Translator::EvictStaleMapping(void)
|
||||
{
|
||||
// First tries to remove expired mappings, if there is no expired
|
||||
// mapping, it will then try to evict a stale mapping.
|
||||
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
const Mapping *evictCandidate = nullptr;
|
||||
bool didRemoveAny;
|
||||
|
||||
didRemoveAny = mActiveMappings.RemoveAndFreeAllMatching(ExpirationChecker(now));
|
||||
|
||||
VerifyOrExit(!didRemoveAny);
|
||||
|
||||
for (const Mapping &mapping : mActiveMappings)
|
||||
{
|
||||
if (!mapping.IsEligibleForEviction(now))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((evictCandidate == nullptr) || mapping.IsBetterEvictionCandidateOver(*evictCandidate, now))
|
||||
{
|
||||
evictCandidate = &mapping;
|
||||
}
|
||||
}
|
||||
|
||||
if (evictCandidate != nullptr)
|
||||
{
|
||||
mActiveMappings.RemoveMatching(*evictCandidate);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Translator::Mapping::Touch(uint8_t aProtocol)
|
||||
{
|
||||
uint32_t timeout;
|
||||
@@ -485,7 +594,8 @@ void Translator::Mapping::Touch(uint8_t aProtocol)
|
||||
timeout = kIdleTimeout;
|
||||
}
|
||||
|
||||
mExpiry = TimerMilli::GetNow() + timeout;
|
||||
mLastUseTime = TimerMilli::GetNow();
|
||||
mExpirationTime = mLastUseTime + timeout;
|
||||
}
|
||||
|
||||
bool Translator::Mapping::Matches(const Ip6::Headers &aIp6Headers) const
|
||||
@@ -683,7 +793,7 @@ exit:
|
||||
|
||||
void Translator::HandleTimer(void)
|
||||
{
|
||||
mActiveMappings.RemoveAndFreeAllMatching(TimerMilli::GetNow());
|
||||
mActiveMappings.RemoveAndFreeAllMatching(ExpirationChecker(TimerMilli::GetNow()));
|
||||
mTimer.Start(Min(kIcmpTimeout, kIdleTimeout));
|
||||
}
|
||||
|
||||
|
||||
@@ -280,6 +280,11 @@ private:
|
||||
static constexpr uint32_t kIdleTimeout = OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
|
||||
static constexpr uint32_t kIcmpTimeout = OPENTHREAD_CONFIG_NAT64_ICMP_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
|
||||
|
||||
static constexpr uint32_t kMinUdpTimeout = 2 * Time::kOneMinuteInMsec; // UDP_MIN in RFC 6146 (2 min)
|
||||
static constexpr uint32_t kMinEvictTimeout = kMinUdpTimeout;
|
||||
|
||||
static_assert(kIdleTimeout >= kMinUdpTimeout, "OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS is too short");
|
||||
|
||||
static constexpr uint32_t kPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS;
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
|
||||
@@ -298,25 +303,40 @@ private:
|
||||
|
||||
typedef String<kInfoStringSize> InfoString;
|
||||
|
||||
void Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); }
|
||||
void Free(void);
|
||||
void Touch(uint8_t aProtocol);
|
||||
InfoString ToString(void) const;
|
||||
void CopyTo(AddressMapping &aMapping, TimeMilli aNow) const;
|
||||
bool Matches(const Ip6::Headers &aIp6Headers) const;
|
||||
bool Matches(const Ip4::Headers &aIp4Headers) const;
|
||||
bool Matches(const TimeMilli aNow) const { return mExpiry < aNow; }
|
||||
enum ProtoCategory : uint8_t
|
||||
{
|
||||
kIcmpOnly,
|
||||
kUdpAndMaybeIcmp,
|
||||
kTcpAndMaybeOthers,
|
||||
};
|
||||
|
||||
void Init(Instance &aInstance) { InstanceLocatorInit::Init(aInstance); }
|
||||
void Free(void);
|
||||
void Touch(uint8_t aProtocol);
|
||||
InfoString ToString(void) const;
|
||||
void CopyTo(AddressMapping &aMapping, TimeMilli aNow) const;
|
||||
uint32_t DetermineDurationSinceUse(TimeMilli aNow) const { return aNow - mLastUseTime; }
|
||||
ProtoCategory DetermineProtocolCategory(void) const;
|
||||
bool IsEligibleForEviction(TimeMilli aNow) const;
|
||||
bool IsBetterEvictionCandidateOver(const Mapping &aOther, TimeMilli aNow) const;
|
||||
bool Matches(const Ip6::Headers &aIp6Headers) const;
|
||||
bool Matches(const Ip4::Headers &aIp4Headers) const;
|
||||
bool Matches(const ExpirationChecker &aChecker) const { return aChecker.IsExpired(mExpirationTime); }
|
||||
bool Matches(const Mapping &aMapping) const { return this == &aMapping; }
|
||||
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
|
||||
bool Matches(const uint16_t aPort) const { return mTranslatedPortOrId == aPort; }
|
||||
#else
|
||||
bool Matches(const Ip4::Address &aIp4Address) const { return mIp4Address == aIp4Address; }
|
||||
#endif
|
||||
|
||||
static bool IsCounterZero(const ProtocolCounters::Counters &aCounters);
|
||||
|
||||
Mapping *mNext;
|
||||
uint64_t mId;
|
||||
TimeMilli mLastUseTime;
|
||||
TimeMilli mExpirationTime;
|
||||
Ip4::Address mIp4Address;
|
||||
Ip6::Address mIp6Address;
|
||||
TimeMilli mExpiry;
|
||||
ProtocolCounters mCounters;
|
||||
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
|
||||
uint16_t mSrcPortOrId;
|
||||
@@ -333,6 +353,7 @@ private:
|
||||
void GetNextIp4Address(Ip4::Address &aIp4Address);
|
||||
Error AllocateIp4Address(Ip4::Address &aIp4Address);
|
||||
Mapping *AllocateMapping(const Ip6::Headers &aIp6Headers);
|
||||
void EvictStaleMapping(void);
|
||||
void HandleTimer(void);
|
||||
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
|
||||
uint16_t AllocateSourcePort(uint16_t aSrcPort);
|
||||
|
||||
@@ -100,7 +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_NAT64_IDLE_TIMEOUT_SECONDS 600
|
||||
#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"
|
||||
|
||||
@@ -285,12 +285,12 @@ void TestNat64StateChanges(void)
|
||||
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 *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;
|
||||
@@ -328,6 +328,87 @@ Message *PrepareMessage(Node &aNode,
|
||||
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::Tcp::Header 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::Tcp::Header) + 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::Icmp::Header 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::Icmp::Header) + aPayloadLen);
|
||||
|
||||
SuccessOrQuit(message->Append(ip6Header));
|
||||
|
||||
icmpHeader.Clear();
|
||||
icmpHeader.SetType(Ip6::Icmp::Header::kTypeEchoRequest);
|
||||
|
||||
SuccessOrQuit(message->Append(icmpHeader));
|
||||
|
||||
for (uint16_t i = 0; i < aPayloadLen; i++)
|
||||
{
|
||||
SuccessOrQuit(message->Append<uint8_t>(static_cast<uint8_t>(i & 0xff)));
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
void LogAddressMapping(const Nat64::Translator::AddressMapping &aMapping)
|
||||
{
|
||||
Log("Mapping: %s -> %s, remaining-time:%lu", AsCoreType(&aMapping.mIp6).ToString().AsCString(),
|
||||
@@ -336,7 +417,7 @@ void LogAddressMapping(const Nat64::Translator::AddressMapping &aMapping)
|
||||
|
||||
void TestNat64Mapping(void)
|
||||
{
|
||||
static constexpr uint32_t kExpireTimeout = 250 * Time::kOneSecondInMsec;
|
||||
static constexpr uint32_t kExpireTimeout = 2 * 610 * Time::kOneSecondInMsec;
|
||||
|
||||
static constexpr uint16_t kSrcPort = 55387;
|
||||
static constexpr uint16_t kDstPort = 55388;
|
||||
@@ -387,7 +468,7 @@ void TestNat64Mapping(void)
|
||||
SuccessOrQuit(ip6Addr.FromString("fd02::1"));
|
||||
SuccessOrQuit(ip4Addr.FromString("200.100.1.1"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -416,7 +497,7 @@ void TestNat64Mapping(void)
|
||||
|
||||
SuccessOrQuit(ip4Addr.FromString("200.100.1.2"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength * 2));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength * 2));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -447,7 +528,7 @@ void TestNat64Mapping(void)
|
||||
|
||||
SuccessOrQuit(ip6Addr2.FromString("fd02::2"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -484,7 +565,7 @@ void TestNat64Mapping(void)
|
||||
|
||||
SuccessOrQuit(ip4Addr.FromString("200.100.1.5"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -518,7 +599,7 @@ void TestNat64Mapping(void)
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr2, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -558,7 +639,7 @@ void TestNat64Mapping(void)
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Translate a new IPv6 message and check that a new Address Mapping is created");
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -593,7 +674,7 @@ void TestNat64Mapping(void)
|
||||
|
||||
void TestNat64CidrAddressReuse(const char *aCidr)
|
||||
{
|
||||
static constexpr uint32_t kExpireTimeout = 120 * Time::kOneSecondInMsec;
|
||||
static constexpr uint32_t kExpireTimeout = 600 * Time::kOneSecondInMsec;
|
||||
|
||||
static constexpr uint16_t kSrcPort = 55387;
|
||||
static constexpr uint16_t kDstPort = 55388;
|
||||
@@ -663,7 +744,7 @@ void TestNat64CidrAddressReuse(const char *aCidr)
|
||||
|
||||
ip6Addr.mFields.m8[15] = i;
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
SuccessOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message));
|
||||
|
||||
@@ -695,7 +776,7 @@ void TestNat64CidrAddressReuse(const char *aCidr)
|
||||
|
||||
SuccessOrQuit(ip6Addr.FromString("fd02::100"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
message.Reset(PrepareUdpMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
|
||||
VerifyOrQuit(node.Get<Nat64::Translator>().TranslateIp6ToIp4(*message) == kErrorDrop);
|
||||
|
||||
@@ -721,16 +802,15 @@ void TestNat64CidrAddressReuse(const char *aCidr)
|
||||
|
||||
ip6Addr.mFields.m8[15] = i + 100;
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
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.IsUdp());
|
||||
VerifyOrQuit(ip4Headers.IsTcp());
|
||||
VerifyOrQuit(ip4Headers.GetSourcePort() == kSrcPort);
|
||||
VerifyOrQuit(ip4Headers.GetDestinationPort() == kDstPort);
|
||||
VerifyOrQuit(ip4Headers.GetUdpHeader().GetLength() == kPayloadLength);
|
||||
|
||||
nexus.AdvanceTime(1000);
|
||||
}
|
||||
@@ -753,13 +833,271 @@ void TestNat64CidrAddressReuse(const char *aCidr)
|
||||
|
||||
SuccessOrQuit(ip6Addr.FromString("fd02::200:100"));
|
||||
|
||||
message.Reset(PrepareMessage(node, ip6Addr, ip4Addr, kSrcPort, kDstPort, kPayloadLength));
|
||||
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());
|
||||
|
||||
node.Get<Instance>().SetLogLevel(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()");
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
@@ -771,6 +1109,7 @@ int main(void)
|
||||
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();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user