| /* |
| * 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_ |