[nat64-translator] enhance and harmonize code style (#11847)

This commit updates the `Translator` class to improve code style,
readability, and consistency with the OpenThread coding conventions.
This is a pure refactoring commit with no intended logic changes. Key
changes include:

- Renamed types and variables for brevity (e.g., `AddressMapping` to
  `Mapping`).
- Renamed members within the `Mapping` struct for clarity(e.g., `mIp4`
  to `mIp4Address`).
- Standardized local variable names (e.g., `err` to `error`).
- Added `const` to methods that do not modify the class state.
- Improved and reformatted Doxygen and inline comments.
This commit is contained in:
Abtin Keshavarzian
2025-08-22 14:31:39 -07:00
committed by GitHub
parent afb52ba3a7
commit 2c697309b6
2 changed files with 306 additions and 310 deletions
+227 -184
View File
@@ -66,13 +66,13 @@ const char *StateToString(State aState)
Translator::Translator(Instance &aInstance)
: InstanceLocator(aInstance)
, mState(State::kStateDisabled)
, mMappingExpirerTimer(aInstance)
, mTimer(aInstance)
{
Random::NonCrypto::Fill(mNextMappingId);
mNat64Prefix.Clear();
mIp4Cidr.Clear();
mMappingExpirerTimer.Start(kAddressMappingIdleTimeoutMsec);
mTimer.Start(kIdleTimeout);
}
Message *Translator::NewIp4Message(const Message::Settings &aSettings)
@@ -109,38 +109,40 @@ exit:
Translator::Result Translator::TranslateFromIp6(Message &aMessage)
{
Result res = kDrop;
ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
Ip6::Headers ip6Headers;
Ip4::Header ip4Header;
uint16_t srcPortOrId = 0;
AddressMapping *mapping = nullptr;
Result result = kDrop;
DropReason dropReason = kReasonUnknown;
Ip6::Headers ip6Headers;
Ip4::Header ip4Header;
uint16_t srcPortOrId = 0;
Mapping *mapping = nullptr;
if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64())
{
ExitNow(res = kNotTranslated);
ExitNow(result = kNotTranslated);
}
// ParseFrom will do basic checks for the message, including the message length and IP protocol version.
// `ParseFrom()` will do basic checks for the message, including
// the message length and IP protocol version.
if (ip6Headers.ParseFrom(aMessage) != kErrorNone)
{
LogWarn("outgoing datagram is not a valid IPv6 datagram, drop");
dropReason = ErrorCounters::Reason::kIllegalPacket;
ExitNow(res = kDrop);
LogWarn("Outgoing datagram is not a valid IPv6 datagram, drop");
dropReason = kReasonIllegalPacket;
ExitNow(result = kDrop);
}
if (!ip6Headers.GetDestinationAddress().MatchesPrefix(mNat64Prefix))
{
ExitNow(res = kNotTranslated);
ExitNow(result = kNotTranslated);
}
mapping = FindOrAllocateMapping(ip6Headers);
if (mapping == nullptr)
{
LogWarn("failed to get a mapping for %s (mapping pool full?)",
LogWarn("Failed to get a mapping for %s (mapping pool full?)",
ip6Headers.GetSourceAddress().ToString().AsCString());
dropReason = ErrorCounters::Reason::kNoMapping;
ExitNow(res = kDrop);
dropReason = kReasonNoMapping;
ExitNow(result = kDrop);
}
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
@@ -153,100 +155,106 @@ Translator::Result Translator::TranslateFromIp6(Message &aMessage)
ip4Header.Clear();
ip4Header.InitVersionIhl();
ip4Header.SetSource(mapping->mIp4);
ip4Header.SetSource(mapping->mIp4Address);
ip4Header.GetDestination().ExtractFromIp6Address(mNat64Prefix.mLength, ip6Headers.GetDestinationAddress());
ip4Header.SetTtl(ip6Headers.GetIpHopLimit());
ip4Header.SetIdentification(0);
switch (ip6Headers.GetIpProto())
{
// The IP header is consumed , so the next header is at offset 0.
// The IP header is consumed, so the next header is at offset 0.
case Ip6::kProtoUdp:
ip4Header.SetProtocol(Ip4::kProtoUdp);
ip6Headers.SetSourcePort(srcPortOrId);
aMessage.Write(0, ip6Headers.GetUdpHeader());
res = kForward;
result = kForward;
break;
case Ip6::kProtoTcp:
ip4Header.SetProtocol(Ip4::kProtoTcp);
ip6Headers.SetSourcePort(srcPortOrId);
aMessage.Write(0, ip6Headers.GetTcpHeader());
res = kForward;
result = kForward;
break;
case Ip6::kProtoIcmp6:
ip4Header.SetProtocol(Ip4::kProtoIcmp);
SuccessOrExit(TranslateIcmp6(aMessage, srcPortOrId));
res = kForward;
result = kForward;
break;
default:
dropReason = ErrorCounters::Reason::kUnsupportedProto;
ExitNow(res = kDrop);
dropReason = kReasonUnsupportedProto;
ExitNow(result = kDrop);
}
// res here must be kForward based on the switch above.
// `result` here must be kForward based on the switch above.
// TODO: Implement the logic for replying ICMP messages.
ip4Header.SetTotalLength(sizeof(Ip4::Header) + aMessage.GetLength() - aMessage.GetOffset());
Checksum::UpdateMessageChecksum(aMessage, ip4Header.GetSource(), ip4Header.GetDestination(),
ip4Header.GetProtocol());
Checksum::UpdateIp4HeaderChecksum(ip4Header);
if (aMessage.Prepend(ip4Header) != kErrorNone)
{
// This should never happen since the IPv4 header is shorter than the IPv6 header.
// This should never happen since the IPv4 header is shorter
// than the IPv6 header.
LogCrit("failed to prepend IPv4 head to translated message");
ExitNow(res = kDrop);
ExitNow(result = kDrop);
}
aMessage.SetType(Message::kTypeIp4);
mCounters.Count6To4Packet(ip6Headers.GetIpProto(), ip6Headers.GetIpLength());
mapping->mCounters.Count6To4Packet(ip6Headers.GetIpProto(), ip6Headers.GetIpLength());
exit:
if (res == Result::kDrop)
if (result == kDrop)
{
mErrorCounters.Count6To4(dropReason);
}
return res;
return result;
}
Translator::Result Translator::TranslateToIp6(Message &aMessage)
{
Result res = Result::kDrop;
ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
Ip6::Header ip6Header;
Ip4::Headers ip4Headers;
uint16_t dstPortOrId = 0;
AddressMapping *mapping = nullptr;
Result result = kDrop;
DropReason dropReason = kReasonUnknown;
Ip6::Header ip6Header;
Ip4::Headers ip4Headers;
uint16_t dstPortOrId = 0;
Mapping *mapping = nullptr;
// Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram.
// If the message is already an IPv6 datagram, forward it directly.
VerifyOrExit(ip6Header.ParseFrom(aMessage) != kErrorNone, res = kNotTranslated);
// `ParseFrom()` may return an error value when the incoming
// message is an IPv4 datagram. If the message is already an IPv6
// datagram, forward it directly.
VerifyOrExit(ip6Header.ParseFrom(aMessage) != kErrorNone, result = kNotTranslated);
if (mIp4Cidr.mLength == 0)
{
// The NAT64 translation is bypassed (will be handled externally)
LogWarn("incoming message is an IPv4 datagram but no IPv4 CIDR for NAT64 configured, drop");
ExitNow(res = kForward);
LogWarn("Incoming message is an IPv4 datagram but no IPv4 CIDR for NAT64 configured, drop");
ExitNow(result = kForward);
}
if (!mNat64Prefix.IsValidNat64())
{
LogWarn("incoming message is an IPv4 datagram but no NAT64 prefix configured, drop");
ExitNow(res = kDrop);
LogWarn("Incoming message is an IPv4 datagram but no NAT64 prefix configured, drop");
ExitNow(result = kDrop);
}
if (ip4Headers.ParseFrom(aMessage) != kErrorNone)
{
LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop");
dropReason = ErrorCounters::Reason::kIllegalPacket;
ExitNow(res = kDrop);
LogWarn("Incoming message is neither IPv4 nor an IPv6 datagram, drop");
dropReason = kReasonIllegalPacket;
ExitNow(result = kDrop);
}
mapping = FindMapping(ip4Headers);
if (mapping == nullptr)
{
LogWarn("no mapping found for the IPv4 address");
dropReason = ErrorCounters::Reason::kNoMapping;
ExitNow(res = kDrop);
LogWarn("No mapping found for the IPv4 address");
dropReason = kReasonNoMapping;
ExitNow(result = kDrop);
}
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
@@ -260,12 +268,14 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.GetSource().SynthesizeFromIp4Address(mNat64Prefix, ip4Headers.GetSourceAddress());
ip6Header.SetDestination(mapping->mIp6);
ip6Header.SetDestination(mapping->mIp6Address);
ip6Header.SetFlow(0);
ip6Header.SetHopLimit(ip4Headers.GetIpTtl());
// Note: TCP and UDP are the same for both IPv4 and IPv6 except for the checksum calculation, we will update the
// checksum in the payload later. However, we need to translate ICMPv6 messages to ICMP messages in IPv4.
// Note: TCP and UDP are the same for both IPv4 and IPv6 except
// for the checksum calculation, we will update the checksum in
// the payload later. However, we need to translate ICMPv6
// messages to ICMP messages in IPv4.
switch (ip4Headers.GetIpProto())
{
// The IP header is consumed , so the next header is at offset 0.
@@ -273,95 +283,99 @@ Translator::Result Translator::TranslateToIp6(Message &aMessage)
ip6Header.SetNextHeader(Ip6::kProtoUdp);
ip4Headers.SetDestinationPort(dstPortOrId);
aMessage.Write(0, ip4Headers.GetUdpHeader());
res = kForward;
result = kForward;
break;
case Ip4::kProtoTcp:
ip6Header.SetNextHeader(Ip6::kProtoTcp);
ip4Headers.SetDestinationPort(dstPortOrId);
aMessage.Write(0, ip4Headers.GetTcpHeader());
res = kForward;
result = kForward;
break;
case Ip4::kProtoIcmp:
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
SuccessOrExit(TranslateIcmp4(aMessage, dstPortOrId));
res = kForward;
result = kForward;
break;
default:
dropReason = ErrorCounters::Reason::kUnsupportedProto;
ExitNow(res = kDrop);
dropReason = kReasonUnsupportedProto;
ExitNow(result = kDrop);
}
// res here must be kForward based on the switch above.
// result here must be kForward based on the switch above.
// TODO: Implement the logic for replying ICMP datagrams.
ip6Header.SetPayloadLength(aMessage.GetLength() - aMessage.GetOffset());
Checksum::UpdateMessageChecksum(aMessage, ip6Header.GetSource(), ip6Header.GetDestination(),
ip6Header.GetNextHeader());
if (aMessage.Prepend(ip6Header) != kErrorNone)
{
// This might happen when the platform failed to reserve enough space before the original IPv4 datagram.
LogWarn("failed to prepend IPv6 head to translated message");
ExitNow(res = kDrop);
// This might happen when the platform failed to reserve
// enough space before the original IPv4 datagram.
LogWarn("Failed to prepend IPv6 head to translated message");
ExitNow(result = kDrop);
}
aMessage.SetType(Message::kTypeIp6);
mCounters.Count4To6Packet(ip4Headers.GetIpProto(), ip4Headers.GetIpLength() - sizeof(Ip4::Header));
mapping->mCounters.Count4To6Packet(ip4Headers.GetIpProto(), ip4Headers.GetIpLength() - sizeof(Ip4::Header));
exit:
if (res == Result::kDrop)
if (result == kDrop)
{
mErrorCounters.Count4To6(dropReason);
}
return res;
return result;
}
Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void) const
Translator::Mapping::InfoString Translator::Mapping::ToString(void) const
{
InfoString string;
string.Append("%s -> %s", mIp6.ToString().AsCString(), mIp4.ToString().AsCString());
string.Append("%s -> %s", mIp6Address.ToString().AsCString(), mIp4Address.ToString().AsCString());
return string;
}
void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const
void Translator::Mapping::CopyTo(AddressMapping &aMapping, TimeMilli aNow) const
{
aMapping.mId = mId;
aMapping.mIp4 = mIp4;
aMapping.mIp6 = mIp6;
aMapping.mIp4 = mIp4Address;
aMapping.mIp6 = mIp6Address;
aMapping.mSrcPortOrId = mSrcPortOrId;
aMapping.mTranslatedPortOrId = mTranslatedPortOrId;
aMapping.mCounters = mCounters;
// We are removing expired mappings lazily, and an expired mapping might become active again before actually
// removed. Report the mapping to be "just expired" to avoid confusion.
if (mExpiry < aNow)
{
aMapping.mRemainingTimeMs = 0;
}
else
{
aMapping.mRemainingTimeMs = mExpiry - aNow;
}
// We are removing expired mappings lazily, and an expired mapping
// might become active again before actually removed. Report the
// mapping to be "just expired" to avoid confusion.
aMapping.mRemainingTimeMs = (mExpiry < aNow) ? 0 : mExpiry - aNow;
}
void Translator::ReleaseMapping(AddressMapping &aMapping)
void Translator::ReleaseMapping(Mapping &aMapping)
{
if (mIp4Cidr.mLength <= kMaxCidrLenForValidAddrPool)
{
// IPv4 addresses are allocated from the pool only when the pool size is above a minimum value.
// Otherwise use just the first address from the list and we are not removing it from the array.
IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4));
// IPv4 addresses are allocated from the pool only when the
// pool size is above a minimum value. Otherwise use just the
// first address from the list and we are not removing it
// from the array.
IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4Address));
}
mAddressMappingPool.Free(aMapping);
LogInfo("mapping removed: %s", aMapping.ToString().AsCString());
mMappingPool.Free(aMapping);
LogInfo("Mapping removed: %s", aMapping.ToString().AsCString());
}
uint16_t Translator::ReleaseMappings(LinkedList<AddressMapping> &aMappings)
uint16_t Translator::ReleaseMappings(LinkedList<Mapping> &aMappings)
{
uint16_t numRemoved = 0;
for (AddressMapping *mapping = aMappings.Pop(); mapping != nullptr; mapping = aMappings.Pop())
for (Mapping *mapping = aMappings.Pop(); mapping != nullptr; mapping = aMappings.Pop())
{
numRemoved++;
ReleaseMapping(*mapping);
@@ -372,46 +386,57 @@ uint16_t Translator::ReleaseMappings(LinkedList<AddressMapping> &aMappings)
uint16_t Translator::ReleaseExpiredMappings(void)
{
LinkedList<AddressMapping> idleMappings;
LinkedList<Mapping> idleMappings;
mActiveAddressMappings.RemoveAllMatching(idleMappings, TimerMilli::GetNow());
mActiveMappings.RemoveAllMatching(idleMappings, TimerMilli::GetNow());
return ReleaseMappings(idleMappings);
}
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
uint16_t Translator::AllocateSourcePort(uint16_t aSrcPort)
{
// The translated port is randomly allocated from the range of dynamic or private ports (RFC 7605 section 4).
// In this way, we will not pick a random port that could be a well-known port preventing an unknown situation on
// the receiver side.
uint16_t retPort;
// The translated port is randomly allocated from the range of
// dynamic or private ports (RFC 7605 section 4). In this way, we
// will not pick a random port that could be a well-known port
// preventing an unknown situation on the receiver side.
uint16_t port;
do
{
retPort = Random::NonCrypto::GetUint16InRange(kTranslationPortRangeStart, kTranslationPortRangeEnd);
// The NAT64 SHOULD preserve the port parity (odd/even), as per Section 4.2.2 of [RFC4787]).
// Determine if original and allocated port have different parity
if (((aSrcPort ^ retPort) & 1) == 1)
{
retPort++;
}
} while (mActiveAddressMappings.ContainsMatching(retPort));
port = Random::NonCrypto::GetUint16InRange(kMinTranslationPort, kMaxTranslationPort);
return retPort;
// The NAT64 SHOULD preserve the port parity (odd/even), as
// per Section 4.2.2 of [RFC4787]). Determine if original and
// allocated port have different parity
if (((aSrcPort ^ port) & 1) == 1)
{
port++;
}
} while (mActiveMappings.ContainsMatching(port));
return port;
}
#endif
Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Headers &aIp6Headers)
Translator::Mapping *Translator::AllocateMapping(const Ip6::Headers &aIp6Headers)
{
AddressMapping *mapping = nullptr;
Ip4::Address ip4Addr;
Mapping *mapping = nullptr;
Ip4::Address ip4Addr;
// The NAT64 translator can work in 2 ways, either with a single
// IPv4 address or a larger pool of addresses. There is also the
// corner case where the address pool is generated from a big
// CIDR length and the number of available IPv4 addresses is not
// big enough to apply a 1 to 1 translation from IPv6 to IPv4
// address. When operating in the first case, there is no need to
// manage the address pool and all active mappings will use 1
// single address (or the limited number alternatively). If a
// larger pool is available each active mapping will use a
// separate IPv4 address.
// The NAT64 translator can work in 2 ways, either with a single IPv4 address or a larger pool of addresses. There
// is also the corner case where the address pool is generated from a big CIDR length and the number of available
// IPv4 addresses is not big enough to apply a 1 to 1 translation from IPv6 to IPv4 address. When operating in the
// first case, there is no need to manage the address pool and all active mappings will use 1 single address (or the
// limited number alternatively). If a larger pool is available each active mapping will use a separate IPv4
// address.
if (mIp4Cidr.mLength > kMaxCidrLenForValidAddrPool)
{
// TODO: add logic to cycle between available IPv4 addresses
@@ -421,44 +446,47 @@ Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Headers &aIp6
{
if (mIp4AddressPool.IsEmpty())
{
// ReleaseExpiredMappings returns the number of mappings removed.
// `ReleaseExpiredMappings()` returns the number of
// mappings removed.
VerifyOrExit(ReleaseExpiredMappings() > 0);
}
ip4Addr = *mIp4AddressPool.PopBack();
}
mapping = mAddressMappingPool.Allocate();
// We should get a valid item, there is enough space in the mapping pool. Otherwise return null and fail the
// translation.
mapping = mMappingPool.Allocate();
// We should get a valid item, there is enough space in the
// mapping pool. Otherwise return null and fail the translation.
VerifyOrExit(mapping != nullptr);
mActiveAddressMappings.Push(*mapping);
mActiveMappings.Push(*mapping);
mapping->mCounters.Clear();
mapping->mId = ++mNextMappingId;
mapping->mIp6 = aIp6Headers.GetSourceAddress();
mapping->mIp4 = ip4Addr;
mapping->mId = ++mNextMappingId;
mapping->mIp6Address = aIp6Headers.GetSourceAddress();
mapping->mIp4Address = ip4Addr;
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
mapping->mSrcPortOrId = aIp6Headers.IsIcmp6() ? aIp6Headers.GetIcmpHeader().GetId() : aIp6Headers.GetSourcePort();
// Allocate a unique source port or ICMP Id
mapping->mTranslatedPortOrId = AllocateSourcePort(mapping->mSrcPortOrId);
#else
mapping->mSrcPortOrId = 0;
mapping->mTranslatedPortOrId = 0;
#endif
mapping->Touch(TimerMilli::GetNow(), aIp6Headers.GetIpProto());
LogInfo("mapping created: %s", mapping->ToString().AsCString());
LogInfo("Mapping created: %s", mapping->ToString().AsCString());
exit:
return mapping;
}
Translator::AddressMapping *Translator::FindOrAllocateMapping(const Ip6::Headers &aIp6Headers)
Translator::Mapping *Translator::FindOrAllocateMapping(const Ip6::Headers &aIp6Headers)
{
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
uint16_t srcPortOrId = aIp6Headers.IsIcmp6() ? aIp6Headers.GetIcmpHeader().GetId() : aIp6Headers.GetSourcePort();
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Headers.GetSourceAddress(), srcPortOrId);
uint16_t srcPortOrId = aIp6Headers.IsIcmp6() ? aIp6Headers.GetIcmpHeader().GetId() : aIp6Headers.GetSourcePort();
Mapping *mapping = mActiveMappings.FindMatching(aIp6Headers.GetSourceAddress(), srcPortOrId);
#else
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Headers.GetSourceAddress());
Mapping *mapping = mActiveMappings.FindMatching(aIp6Headers.GetSourceAddress());
#endif
// Exit if we found a valid mapping.
@@ -470,15 +498,15 @@ exit:
return mapping;
}
Translator::AddressMapping *Translator::FindMapping(const Ip4::Headers &aIp4Headers)
Translator::Mapping *Translator::FindMapping(const Ip4::Headers &aIp4Headers)
{
uint16_t dstPortOrId =
aIp4Headers.IsIcmp4() ? aIp4Headers.GetIcmpHeader().GetId() : aIp4Headers.GetDestinationPort();
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Headers.GetDestinationAddress(), dstPortOrId);
Mapping *mapping = mActiveMappings.FindMatching(aIp4Headers.GetDestinationAddress(), dstPortOrId);
#else
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Headers.GetDestinationAddress());
Mapping *mapping = mActiveMappings.FindMatching(aIp4Headers.GetDestinationAddress());
OT_UNUSED_VARIABLE(dstPortOrId);
#endif
@@ -489,93 +517,108 @@ Translator::AddressMapping *Translator::FindMapping(const Ip4::Headers &aIp4Head
return mapping;
}
void Translator::AddressMapping::Touch(TimeMilli aNow, uint8_t aProtocol)
void Translator::Mapping::Touch(TimeMilli aNow, uint8_t aProtocol)
{
if ((aProtocol == Ip6::kProtoIcmp6) || (aProtocol == Ip4::kProtoIcmp))
{
mExpiry = aNow + kAddressMappingIcmpIdleTimeoutMsec;
mExpiry = aNow + kIcmpTimeout;
}
else
{
mExpiry = aNow + kAddressMappingIdleTimeoutMsec;
mExpiry = aNow + kIdleTimeout;
}
}
bool Translator::Mapping::Matches(const Ip6::Address &aIp6Address, const uint16_t aPort) const
{
return ((mIp6Address == aIp6Address) && (mSrcPortOrId == aPort));
}
bool Translator::Mapping::Matches(const Ip4::Address &aIp4Address, const uint16_t aPort) const
{
return ((mIp4Address == aIp4Address) && (mTranslatedPortOrId == aPort));
}
Error Translator::TranslateIcmp4(Message &aMessage, uint16_t aOriginalId)
{
Error err = kErrorNone;
Error error = kErrorNone;
Ip4::Icmp::Header icmp4Header;
Ip6::Icmp::Header icmp6Header;
// TODO: Implement the translation of other ICMP messages.
// Note: The caller consumed the IP header, so the ICMP header is at offset 0.
SuccessOrExit(err = aMessage.Read(0, icmp4Header));
// Note: The caller consumed the IP header, so the ICMP header is
// at offset 0.
SuccessOrExit(error = aMessage.Read(0, icmp4Header));
switch (icmp4Header.GetType())
{
case Ip4::Icmp::Header::Type::kTypeEchoReply:
{
// The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
// ICMP6 header and set the message type.
SuccessOrExit(err = aMessage.Read(0, icmp6Header));
icmp6Header.SetType(Ip6::Icmp::Header::Type::kTypeEchoReply);
// The only difference between ICMPv6 echo and ICMP4 echo is
// the message type field, so we can reinterpret it as ICMP6
// header and set the message type.
SuccessOrExit(error = aMessage.Read(0, icmp6Header));
icmp6Header.SetType(Ip6::Icmp::Header::kTypeEchoReply);
icmp6Header.SetId(aOriginalId);
aMessage.Write(0, icmp6Header);
break;
}
default:
err = kErrorInvalidArgs;
error = kErrorInvalidArgs;
break;
}
exit:
return err;
return error;
}
Error Translator::TranslateIcmp6(Message &aMessage, uint16_t aTranslatedId)
{
Error err = kErrorNone;
Error error = kErrorNone;
Ip4::Icmp::Header icmp4Header;
Ip6::Icmp::Header icmp6Header;
// TODO: Implement the translation of other ICMP messages.
// Note: The caller have consumed the IP header, so the ICMP header is at offset 0.
SuccessOrExit(err = aMessage.Read(0, icmp6Header));
// Note: The caller have consumed the IP header, so the ICMP
// header is at offset 0.
SuccessOrExit(error = aMessage.Read(0, icmp6Header));
switch (icmp6Header.GetType())
{
case Ip6::Icmp::Header::Type::kTypeEchoRequest:
{
// The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
// ICMP6 header and set the message type.
SuccessOrExit(err = aMessage.Read(0, icmp4Header));
case Ip6::Icmp::Header::kTypeEchoRequest:
// The only difference between ICMPv6 echo and ICMP4 echo is
// the message type field, so we can reinterpret it as ICMP6
// header and set the message type.
SuccessOrExit(error = aMessage.Read(0, icmp4Header));
icmp4Header.SetType(Ip4::Icmp::Header::Type::kTypeEchoRequest);
icmp4Header.SetId(aTranslatedId);
aMessage.Write(0, icmp4Header);
break;
}
default:
err = kErrorInvalidArgs;
error = kErrorInvalidArgs;
break;
}
exit:
return err;
return error;
}
Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
{
Error err = kErrorNone;
Error error = kErrorNone;
uint32_t numberOfHosts;
uint32_t hostIdBegin;
VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, err = kErrorInvalidArgs);
VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, error = kErrorInvalidArgs);
VerifyOrExit(mIp4Cidr != aCidr);
// Avoid using the 0s and 1s in the host id of an address, but what if the user provides us with /32 or /31
// addresses?
// Avoid using the 0s and 1s in the host id of an address, but
// what if the user provides us with /32 or /31 addresses?
if (aCidr.mLength == 32)
{
hostIdBegin = 0;
@@ -591,10 +634,11 @@ Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
hostIdBegin = 1;
numberOfHosts = static_cast<uint32_t>((1 << (Ip4::Address::kSize * 8 - aCidr.mLength)) - 2);
}
numberOfHosts = OT_MIN(numberOfHosts, kAddressMappingPoolSize);
mAddressMappingPool.FreeAll();
mActiveAddressMappings.Clear();
numberOfHosts = OT_MIN(numberOfHosts, kPoolSize);
mMappingPool.FreeAll();
mActiveMappings.Clear();
mIp4AddressPool.Clear();
for (uint32_t i = 0; i < numberOfHosts; i++)
@@ -608,22 +652,22 @@ Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
LogInfo("IPv4 CIDR for NAT64: %s (actual address pool: %s - %s, %lu addresses)", aCidr.ToString().AsCString(),
mIp4AddressPool.Front()->ToString().AsCString(), mIp4AddressPool.Back()->ToString().AsCString(),
ToUlong(numberOfHosts));
mIp4Cidr = aCidr;
UpdateState();
// Notify the platform when the CIDR is changed.
Get<Notifier>().Signal(kEventNat64TranslatorStateChanged);
exit:
return err;
return error;
}
void Translator::ClearIp4Cidr(void)
{
mIp4Cidr.Clear();
mAddressMappingPool.FreeAll();
mActiveAddressMappings.Clear();
mMappingPool.FreeAll();
mActiveMappings.Clear();
mIp4AddressPool.Clear();
UpdateState();
@@ -654,58 +698,57 @@ exit:
return;
}
void Translator::HandleMappingExpirerTimer(void)
void Translator::HandleTimer(void)
{
uint16_t numReleased = ReleaseExpiredMappings();
LogInfo("Released %u expired mappings", numReleased);
mMappingExpirerTimer.Start(Min(kAddressMappingIcmpIdleTimeoutMsec, kAddressMappingIdleTimeoutMsec));
mTimer.Start(Min(kIcmpTimeout, kIdleTimeout));
OT_UNUSED_VARIABLE(numReleased);
}
void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator)
{
aIterator.mPtr = mActiveAddressMappings.GetHead();
aIterator.mPtr = mActiveMappings.GetHead();
}
Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping)
Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, AddressMapping &aMapping)
{
Error err = kErrorNotFound;
TimeMilli now = TimerMilli::GetNow();
AddressMapping *item = static_cast<AddressMapping *>(aIterator.mPtr);
Error error = kErrorNotFound;
Mapping *mapping = static_cast<Mapping *>(aIterator.mPtr);
VerifyOrExit(item != nullptr);
VerifyOrExit(mapping != nullptr);
item->CopyTo(aMapping, now);
aIterator.mPtr = item->GetNext();
err = kErrorNone;
mapping->CopyTo(aMapping, TimerMilli::GetNow());
aIterator.mPtr = mapping->GetNext();
error = kErrorNone;
exit:
return err;
return error;
}
Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr)
Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr) const
{
Error err = kErrorNone;
Error error = kErrorNone;
VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound);
VerifyOrExit(mIp4Cidr.mLength > 0, error = kErrorNotFound);
aCidr = mIp4Cidr;
exit:
return err;
return error;
}
Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix)
Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix) const
{
Error err = kErrorNone;
Error error = kErrorNone;
VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound);
VerifyOrExit(mNat64Prefix.mLength > 0, error = kErrorNotFound);
aPrefix = mNat64Prefix;
exit:
return err;
return error;
}
void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize)
@@ -786,7 +829,7 @@ void Translator::SetEnabled(bool aEnabled)
if (!aEnabled)
{
ReleaseMappings(mActiveAddressMappings);
ReleaseMappings(mActiveMappings);
}
UpdateState();
+79 -126
View File
@@ -72,32 +72,18 @@ const char *StateToString(State aState);
class Translator : public InstanceLocator, private NonCopyable
{
public:
static constexpr uint32_t kAddressMappingIdleTimeoutMsec =
OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
// ICMP mappings can expire fast since the identifier field will usually be the same only for
// a ping sessing that can have multiple ping requests. Once a new session is started the
// identifier will change.
static constexpr uint32_t kAddressMappingIcmpIdleTimeoutMsec =
OPENTHREAD_CONFIG_NAT64_ICMP_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
static constexpr uint32_t kAddressMappingPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS;
static constexpr uint16_t kTranslationPortRangeStart = 49152;
static constexpr uint16_t kTranslationPortRangeEnd = 65535;
// The maximum value the CIDR len can have in order to have a big enough pool to support a
// minimal number of devices
static constexpr uint8_t kMaxCidrLenForValidAddrPool = 28;
typedef otNat64AddressMapping AddressMapping; ///< Address mapping.
typedef otNat64AddressMappingIterator AddressMappingIterator; ///< Address mapping Iterator.
typedef otNat64DropReason DropReason; ///< Drop reason.
/**
* The possible results of NAT64 translation.
*/
enum Result : uint8_t
{
kNotTranslated, ///< The message is not translated, it might be sending to an non-nat64 prefix (for outgoing
///< datagrams), or it is already an IPv6 message (for incoming datagrams).
kForward, ///< Message is successfully translated, the caller should continue forwarding the translated
///< datagram.
kDrop, ///< The caller should drop the datagram silently.
kNotTranslated, ///< Not translated (e.g., Outgoing msg using a non-NAT64 prefix, or incoming is already IPv6).
kForward, ///< Successfully translated and the translated message should be forwarded.
kDrop, ///< Silently drop the message.
};
/**
@@ -105,21 +91,10 @@ public:
*/
class ProtocolCounters : public otNat64ProtocolCounters, public Clearable<ProtocolCounters>
{
public:
/**
* Adds the packet to the counter for the given IPv6 protocol.
*
* @param[in] aProtocol The protocol of the packet.
* @param[in] aPacketSize The size of the packet.
*/
void Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize);
friend class Translator;
/**
* Adds the packet to the counter for the given IPv4 protocol.
*
* @param[in] aProtocol The protocol of the packet.
* @param[in] aPacketSize The size of the packet.
*/
private:
void Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize);
void Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize);
};
@@ -128,32 +103,17 @@ public:
*/
class ErrorCounters : public otNat64ErrorCounters, public Clearable<otNat64ErrorCounters>
{
friend class Translator;
public:
enum Reason : uint8_t
{
kUnknown = OT_NAT64_DROP_REASON_UNKNOWN,
kIllegalPacket = OT_NAT64_DROP_REASON_ILLEGAL_PACKET,
kUnsupportedProto = OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO,
kNoMapping = OT_NAT64_DROP_REASON_NO_MAPPING,
};
/**
* Adds the counter for the given reason when translating an IPv4 datagram.
*
* @param[in] aReason The reason of packet drop.
*/
void Count4To6(Reason aReason) { mCount4To6[aReason]++; }
/**
* Adds the counter for the given reason when translating an IPv6 datagram.
*
* @param[in] aReason The reason of packet drop.
*/
void Count6To4(Reason aReason) { mCount6To4[aReason]++; }
void Count4To6(DropReason aReason) { mCount4To6[aReason]++; }
void Count6To4(DropReason aReason) { mCount6To4[aReason]++; }
};
/**
* Initializes the NAT64 translator.
*
* @param[in] aInstance The OpenThread instance.
*/
explicit Translator(Instance &aInstance);
@@ -266,7 +226,7 @@ public:
void ClearNat64Prefix(void);
/**
* Initializes an `otNat64AddressMappingIterator`.
* Initializes an `AddressMappingIterator`.
*
* An iterator MUST be initialized before it is used.
*
@@ -279,17 +239,14 @@ public:
/**
* Gets the next AddressMapping info (using an iterator).
*
* @param[in,out] aIterator The iterator. On success the iterator will be updated to point to next NAT64
* address mapping record. To get the first entry the iterator should be set to
* OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT.
* @param[out] aMapping An `otNat64AddressMapping` where information of next NAT64 address mapping record
* is placed (on success).
* @param[in,out] aIterator The iterator.
* @param[out] aMapping An `AddressMapping` to output to next NAT64 address mapping.
*
* @retval kErrorNone Successfully found the next NAT64 address mapping info (@p aMapping was successfully
* updated).
* @retval kErrorNone Successfully found the next NAT64 address mapping info (@p aMapping and @p aIterator
* are updated.
* @retval kErrorNotFound No subsequent NAT64 address mapping info was found.
*/
Error GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping);
Error GetNextAddressMapping(AddressMappingIterator &aIterator, AddressMapping &aMapping);
/**
* Gets the NAT64 translator counters.
@@ -317,7 +274,7 @@ public:
* @retval kErrorNone @p aCidr is set to the configured CIDR.
* @retval kErrorNotFound The translator is not configured with an IPv4 CIDR.
*/
Error GetIp4Cidr(Ip4::Cidr &aCidr);
Error GetIp4Cidr(Ip4::Cidr &aCidr) const;
/**
* Gets the configured IPv6 prefix in the NAT64 translator.
@@ -327,83 +284,79 @@ public:
* @retval kErrorNone @p aPrefix is set to the configured prefix.
* @retval kErrorNotFound The translator is not configured with an IPv6 prefix.
*/
Error GetIp6Prefix(Ip6::Prefix &aPrefix);
Error GetIp6Prefix(Ip6::Prefix &aPrefix) const;
private:
class AddressMapping : public LinkedListEntry<AddressMapping>
{
public:
friend class LinkedListEntry<AddressMapping>;
friend class LinkedList<AddressMapping>;
// Timeouts are in milliseconds
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;
typedef String<Ip6::Address::kInfoStringSize + Ip4::Address::kAddressStringSize + 4> InfoString;
static constexpr uint32_t kPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS;
static constexpr uint16_t kMinTranslationPort = 49152;
static constexpr uint16_t kMaxTranslationPort = 65535;
// The maximum value the CIDR len can have in order to have a big
// enough pool to support a minimal number of devices
static constexpr uint8_t kMaxCidrLenForValidAddrPool = 28;
static constexpr DropReason kReasonUnknown = OT_NAT64_DROP_REASON_UNKNOWN;
static constexpr DropReason kReasonIllegalPacket = OT_NAT64_DROP_REASON_ILLEGAL_PACKET;
static constexpr DropReason kReasonUnsupportedProto = OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO;
static constexpr DropReason kReasonNoMapping = OT_NAT64_DROP_REASON_NO_MAPPING;
struct Mapping : public LinkedListEntry<Mapping>
{
static constexpr uint16_t kInfoStringSize = 70;
typedef String<kInfoStringSize> InfoString;
void Touch(TimeMilli aNow, uint8_t aProtocol);
InfoString ToString(void) const;
void CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const;
uint64_t mId; // The unique id for a mapping session.
Ip4::Address mIp4;
Ip6::Address mIp6;
uint16_t mSrcPortOrId;
uint16_t mTranslatedPortOrId;
TimeMilli mExpiry; // The timestamp when this mapping expires, in milliseconds.
void CopyTo(AddressMapping &aMapping, TimeMilli aNow) const;
bool Matches(const Ip4::Address &aIp4Address) const { return mIp4Address == aIp4Address; }
bool Matches(const Ip6::Address &aIp6Address) const { return mIp6Address == aIp6Address; }
bool Matches(const uint16_t aPort) const { return mTranslatedPortOrId == aPort; }
bool Matches(const TimeMilli aNow) const { return mExpiry < aNow; }
bool Matches(const Ip6::Address &aIp6Address, const uint16_t aPort) const;
bool Matches(const Ip4::Address &aIp4Address, const uint16_t aPort) const;
Mapping *mNext;
uint64_t mId;
Ip4::Address mIp4Address;
Ip6::Address mIp6Address;
uint16_t mSrcPortOrId;
uint16_t mTranslatedPortOrId;
TimeMilli mExpiry;
ProtocolCounters mCounters;
private:
bool Matches(const Ip4::Address &aIp4) const { return mIp4 == aIp4; }
bool Matches(const Ip6::Address &aIp6) const { return mIp6 == aIp6; }
bool Matches(const uint16_t aPort) const { return mTranslatedPortOrId == aPort; }
bool Matches(const TimeMilli aNow) const { return mExpiry < aNow; }
bool Matches(const Ip6::Address &aIp6, const uint16_t aPort) const
{
return ((mIp6 == aIp6) && (mSrcPortOrId == aPort));
}
bool Matches(const Ip4::Address &aIp4, const uint16_t aPort) const
{
return ((mIp4 == aIp4) && (mTranslatedPortOrId == aPort));
}
AddressMapping *mNext;
};
Error TranslateIcmp4(Message &aMessage, uint16_t aOriginalId);
Error TranslateIcmp6(Message &aMessage, uint16_t aTranslatedId);
Error TranslateIcmp4(Message &aMessage, uint16_t aOriginalId);
Error TranslateIcmp6(Message &aMessage, uint16_t aTranslatedId);
uint16_t ReleaseMappings(LinkedList<Mapping> &aMappings);
void ReleaseMapping(Mapping &aMapping);
uint16_t ReleaseExpiredMappings(void);
Mapping *AllocateMapping(const Ip6::Headers &aIp6Headers);
Mapping *FindOrAllocateMapping(const Ip6::Headers &aIp6Headers);
Mapping *FindMapping(const Ip4::Headers &aIp4Headers);
void HandleTimer(void);
void UpdateState(void);
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
uint16_t AllocateSourcePort(uint16_t aSrcPort);
#endif
uint16_t ReleaseMappings(LinkedList<AddressMapping> &aMappings);
void ReleaseMapping(AddressMapping &aMapping);
uint16_t ReleaseExpiredMappings(void);
AddressMapping *AllocateMapping(const Ip6::Headers &aIp6Headers);
AddressMapping *FindOrAllocateMapping(const Ip6::Headers &aIp6Headers);
AddressMapping *FindMapping(const Ip4::Headers &aIp4Headers);
void HandleMappingExpirerTimer(void);
using MappingTimer = TimerMilliIn<Translator, &Translator::HandleMappingExpirerTimer>;
using TranslatorTimer = TimerMilliIn<Translator, &Translator::HandleTimer>;
void UpdateState(void);
bool mEnabled;
State mState;
uint64_t mNextMappingId;
Array<Ip4::Address, kAddressMappingPoolSize> mIp4AddressPool;
Pool<AddressMapping, kAddressMappingPoolSize> mAddressMappingPool;
LinkedList<AddressMapping> mActiveAddressMappings;
Ip6::Prefix mNat64Prefix;
Ip4::Cidr mIp4Cidr;
MappingTimer mMappingExpirerTimer;
ProtocolCounters mCounters;
ErrorCounters mErrorCounters;
bool mEnabled;
State mState;
uint64_t mNextMappingId;
Array<Ip4::Address, kPoolSize> mIp4AddressPool;
Pool<Mapping, kPoolSize> mMappingPool;
LinkedList<Mapping> mActiveMappings;
Ip6::Prefix mNat64Prefix;
Ip4::Cidr mIp4Cidr;
TranslatorTimer mTimer;
ProtocolCounters mCounters;
ErrorCounters mErrorCounters;
};
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE