blob: 9769185055a675d0a4e1ef113befc2a1a95aaace [file]
/*
* Copyright (c) 2022, 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.
*/
/**
* @file
* This file includes definitions for the NAT64 translator.
*/
#ifndef OT_CORE_NET_NAT64_TRANSLATOR_HPP_
#define OT_CORE_NET_NAT64_TRANSLATOR_HPP_
#include "openthread-core-config.h"
#include "common/locator.hpp"
#include "common/owning_list.hpp"
#include "common/pool.hpp"
#include "common/timer.hpp"
#include "net/ip4_types.hpp"
#include "net/ip6.hpp"
namespace ot {
namespace Nat64 {
enum State : uint8_t
{
kStateDisabled = OT_NAT64_STATE_DISABLED, ///< The component is disabled.
kStateNotRunning = OT_NAT64_STATE_NOT_RUNNING, ///< The component is enabled, but is not running.
kStateIdle = OT_NAT64_STATE_IDLE, ///< NAT64 is enabled, but this BR is not an active NAT64 BR.
kStateActive = OT_NAT64_STATE_ACTIVE, ///< The component is running.
};
/**
* Converts a `State` into a string.
*
* @param[in] aState A state.
*
* @returns A string representation of @p aState.
*/
const char *StateToString(State aState);
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
/**
* Implements the NAT64 translator.
*/
class Translator : public InstanceLocator, private NonCopyable
{
struct Mapping;
public:
typedef otNat64AddressMapping AddressMapping; ///< Address mapping.
typedef otNat64DropReason DropReason; ///< Drop reason.
typedef otNat64ErrorCounters ErrorCounters; ///< Error counters.
/**
* An iterator to iterate over `AddressMapping` entries.
*/
class AddressMappingIterator : public otNat64AddressMappingIterator
{
public:
/**
* Initializes an `AddressMappingIterator`.
*
* An iterator MUST be initialized before it is used. An iterator can be initialized again to start from the
* beginning of the mapping info list.
*
* @param[in] aInstance The OpenThread instance.
*/
void Init(Instance &aInstance);
/**
* Gets the next `AddressMapping` info using the iterator.
*
* @param[out] aMapping An `AddressMapping` to output to next NAT64 address mapping.
*
* @retval kErrorNone Successfully found the next NAT64 address mapping info.
* @retval kErrorNotFound No subsequent NAT64 address mapping info was found.
*/
Error GetNext(AddressMapping &aMapping);
private:
void SetMapping(const Mapping *aMapping) { mPtr = aMapping; }
const Mapping *GetMapping(void) const { return static_cast<const Mapping *>(mPtr); }
void SetInitTime(TimeMilli aNow) { mData32 = aNow.GetValue(); }
TimeMilli GetInitTime(void) const { return TimeMilli(mData32); }
};
/**
* Represents the counters for the protocols supported by NAT64.
*/
class ProtocolCounters : public otNat64ProtocolCounters,
public Clearable<ProtocolCounters>,
public Equatable<ProtocolCounters>
{
friend class Translator;
private:
typedef otNat64Counters Counters;
void Count6To4Packet(const Ip6::Headers &aIp6Headers);
void Count4To6Packet(const Ip4::Headers &aIp4Headers);
static void Update6To4(Counters &aCounters, uint16_t aSize);
static void Update4To6(Counters &aCounters, uint16_t aSize);
};
/**
* Initializes the NAT64 translator.
*
* @param[in] aInstance The OpenThread instance.
*/
explicit Translator(Instance &aInstance);
/**
* Set the state of NAT64 translator.
*
* Note: Disabling the translator will invalidate all address mappings.
*
* @param[in] aEnabled A boolean to enable/disable NAT64 translator.
*/
void SetEnabled(bool aEnabled);
/**
* Gets the state of NAT64 translator.
*
* @retval kNat64StateDisabled The translator is disabled.
* @retval kNat64StateIdle The translator is not configured with a valid NAT64 prefix and a CIDR.
* @retval kNat64StateActive The translator is translating packets.
*/
State GetState(void) const { return mState; }
/**
* Translates an IPv4 datagram to an IPv6 datagram and sends it via Thread interface.
*
* @param[in] aMessagePtr An owned pointer to a message (ownership is transferred to the method).
*
* @retval kErrorNone Successfully processed the message.
* @retval kErrorDrop Message was well-formed but not fully processed due to datagram processing rules.
* @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram.
* @retval kErrorNoRoute No route to host.
* @retval kErrorParse Encountered a malformed header when processing the message.
*/
Error SendMessage(OwnedPtr<Message> aMessagePtr);
/**
* Allocate a new message buffer for sending an IPv4 message (which will be translated into an IPv6 datagram by
* NAT64 later). Message buffers allocated by this function will have 20 bytes (The differences between the size of
* IPv6 headers and the size of IPv4 headers) reserved.
*
* @param[in] aSettings The message settings.
*
* @returns A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.
*/
Message *NewIp4Message(const Message::Settings &aSettings);
/**
* Translates an IPv4 message to an IPv6 message.
*
* @param[in,out] aMessage The message to be processed.
*
* @retval kErrorNone The message was successfully translated from IPv4 to IPv6.
* @retval kErrorDrop The message should be dropped, e.g., it is malformed or translation failed.
*/
Error TranslateIp4ToIp6(Message &aMessage);
/**
* Translates an IPv6 message to an IPv4 message.
*
* @param[in,out] aMessage The message to be processed.
*
* @retval kErrorNone The message was successfully translated from IPv6 to IPv4.
* @retval kErrorDrop The message should be dropped, e.g., it is malformed or translation failed.
* @retval kErrorAbort No translation was performed or required (e.g., NAT64 is disabled, the message is not
* destined for a NAT64-mapped address). In this case, the message is not modified.
*/
Error TranslateIp6ToIp4(Message &aMessage);
/**
* Sets the CIDR used when setting the source address of the outgoing translated IPv4 datagrams. A valid CIDR must
* have a non-zero prefix length.
*
* @note The actual addresses pool is limited by the size of the mapping pool and the number of addresses available
* in the CIDR block. If the provided is a valid IPv4 CIDR for NAT64, and it is different from the one already
* configured, the NAT64 translator will be reset and all existing sessions will be expired.
*
* @param[in] aCidr the CIDR for the sources of the translated datagrams.
*
* @retval kErrorInvalidArgs The the given CIDR a valid CIDR for NAT64.
* @retval kErrorNone Successfully enabled/disabled the NAT64 translator.
*/
Error SetIp4Cidr(const Ip4::Cidr &aCidr);
/**
* Clears the CIDR used when setting the source address of the outgoing translated IPv4 datagrams.
*
* @note The NAT64 translator will be reset and all existing sessions will be expired when clearing the configured
* CIDR.
*/
void ClearIp4Cidr(void);
/**
* Gets the configured CIDR in the NAT64 translator.
*
* @param[out] aCidr The `Ip4::Cidr` Where the configured CIDR will be placed.
*
* @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) const;
/**
* Sets the prefix of NAT64-mapped addresses in the thread network. The address mapping table will not be cleared.
* Equals to `ClearNat64Prefix` when an empty prefix is provided.
*
* @param[in] aNat64Prefix The prefix of the NAT64-mapped addresses.
*/
void SetNat64Prefix(const Ip6::Prefix &aNat64Prefix);
/**
* Clear the prefix of NAT64-mapped addresses in the thread network. The address mapping table will not be cleared.
* The translator will return kNotTranslated for all IPv6 datagrams and kDrop for all IPv4 datagrams.
*/
void ClearNat64Prefix(void);
/**
* Gets the configured IPv6 prefix in the NAT64 translator.
*
* @param[out] aPrefix The `Ip6::Prefix` where the configured NAT64 prefix will be placed.
*
* @retval kErrorNone @p aPrefix is set to the configured prefix.
* @retval kErrorNotFound The translator is not configured with an IPv6 prefix.
*/
Error GetNat64Prefix(Ip6::Prefix &aPrefix) const;
/**
* Gets the NAT64 translator counters.
*
* The counters are initialized to zero when the OpenThread instance is initialized.
*
* @returns The protocol counters.
*/
const ProtocolCounters &GetCounters(void) const { return mCounters; }
/**
* Gets the NAT64 translator error counters.
*
* The counters are initialized to zero when the OpenThread instance is initialized.
*
* @returns The error counters.
*/
const ErrorCounters &GetErrorCounters(void) const { return mErrorCounters; }
private:
// 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;
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
static constexpr uint16_t kMinTranslationPort = 49152;
static constexpr uint16_t kMaxTranslationPort = 65535;
#endif
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 InstanceLocatorInit, public LinkedListEntry<Mapping>
{
static constexpr uint16_t kInfoStringSize = 80;
typedef String<kInfoStringSize> InfoString;
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;
ProtocolCounters mCounters;
#if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
uint16_t mSrcPortOrId;
uint16_t mTranslatedPortOrId;
#endif
};
bool IsEnabled(void) const { return mState != kStateDisabled; }
bool HasValidPrefixAndCidr(void) const;
void SetState(State aState);
void UpdateState(void);
Error TranslateIcmp4(Message &aMessage, uint16_t aOriginalId);
Error TranslateIcmp6(Message &aMessage, uint16_t aTranslatedId);
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);
#endif
static uint16_t GetSourcePortOrIcmp6Id(const Ip6::Headers &aIp6Headers);
static uint16_t GetDestinationPortOrIcmp4Id(const Ip4::Headers &aIp4Headers);
using TranslatorTimer = TimerMilliIn<Translator, &Translator::HandleTimer>;
State mState;
uint64_t mNextMappingId;
Pool<Mapping, kPoolSize> mMappingPool;
OwningList<Mapping> mActiveMappings;
Ip6::Prefix mNat64Prefix;
Ip4::Cidr mIp4Cidr;
uint32_t mMinHostId;
uint32_t mMaxHostId;
uint32_t mNextHostId;
TranslatorTimer mTimer;
ProtocolCounters mCounters;
ErrorCounters mErrorCounters;
};
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
} // namespace Nat64
DefineMapEnum(otNat64State, Nat64::State);
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
DefineCoreType(otNat64AddressMappingIterator, Nat64::Translator::AddressMappingIterator);
DefineCoreType(otNat64ProtocolCounters, Nat64::Translator::ProtocolCounters);
#endif
} // namespace ot
#endif // OT_CORE_NET_NAT64_TRANSLATOR_HPP_