/*
 *  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
 * @brief
 *  This file defines the OpenThread API for NAT64 on a border router.
 */

#ifndef OPENTHREAD_NAT64_H_
#define OPENTHREAD_NAT64_H_

#include <stdbool.h>
#include <stdint.h>

#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/message.h>
#include <openthread/platform/toolchain.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @addtogroup api-nat64
 *
 * @brief This module includes functions and structs for the NAT64 function on the border router. These functions are
 * only available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.
 *
 * @{
 */

#define OT_IP4_ADDRESS_SIZE 4 ///< Size of an IPv4 address (bytes)

/**
 * @struct otIp4Address
 *
 * Represents an IPv4 address.
 */
OT_TOOL_PACKED_BEGIN
struct otIp4Address
{
    union OT_TOOL_PACKED_FIELD
    {
        uint8_t  m8[OT_IP4_ADDRESS_SIZE]; ///< 8-bit fields
        uint32_t m32;                     ///< 32-bit representation
    } mFields;
} OT_TOOL_PACKED_END;

/**
 * Represents an IPv4 address.
 */
typedef struct otIp4Address otIp4Address;

/**
 * @struct otIp4Cidr
 *
 * Represents an IPv4 CIDR block.
 */
typedef struct otIp4Cidr
{
    otIp4Address mAddress;
    uint8_t      mLength;
} otIp4Cidr;

/**
 * Represents the counters for NAT64.
 */
typedef struct otNat64Counters
{
    uint64_t m4To6Packets; ///< Number of packets translated from IPv4 to IPv6.
    uint64_t m4To6Bytes;   ///< Sum of size of packets translated from IPv4 to IPv6.
    uint64_t m6To4Packets; ///< Number of packets translated from IPv6 to IPv4.
    uint64_t m6To4Bytes;   ///< Sum of size of packets translated from IPv6 to IPv4.
} otNat64Counters;

/**
 * Represents the counters for the protocols supported by NAT64.
 */
typedef struct otNat64ProtocolCounters
{
    otNat64Counters mTotal; ///< Counters for sum of all protocols.
    otNat64Counters mIcmp;  ///< Counters for ICMP and ICMPv6.
    otNat64Counters mUdp;   ///< Counters for UDP.
    otNat64Counters mTcp;   ///< Counters for TCP.
} otNat64ProtocolCounters;

/**
 * Packet drop reasons.
 */
typedef enum otNat64DropReason
{
    OT_NAT64_DROP_REASON_UNKNOWN = 0,       ///< Packet drop for unknown reasons.
    OT_NAT64_DROP_REASON_ILLEGAL_PACKET,    ///< Packet drop due to failed to parse the datagram.
    OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, ///< Packet drop due to unsupported IP protocol.
    OT_NAT64_DROP_REASON_NO_MAPPING,        ///< Packet drop due to no mappings found or mapping pool exhausted.
    //---
    OT_NAT64_DROP_REASON_COUNT,
} otNat64DropReason;

/**
 * Represents the counters of dropped packets due to errors when handling NAT64 packets.
 */
typedef struct otNat64ErrorCounters
{
    uint64_t mCount4To6[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv4 packets.
    uint64_t mCount6To4[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv6 packets.
} otNat64ErrorCounters;

/**
 * Gets NAT64 translator counters.
 *
 * The counter is counted since the instance initialized.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * @param[in]  aInstance A pointer to an OpenThread instance.
 * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.
 */
void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters);

/**
 * Gets the NAT64 translator error counters.
 *
 * The counters are initialized to zero when the OpenThread instance is initialized.
 *
 * @param[in]  aInstance A pointer to an OpenThread instance.
 * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed.
 */
void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters);

/**
 * Represents an address mapping record for NAT64.
 *
 * @note The counters will be reset for each mapping session even for the same address pair. Applications can use `mId`
 * to identify different sessions to calculate the packets correctly.
 */
typedef struct otNat64AddressMapping
{
    uint64_t     mId;  ///< The unique id for a mapping session.
    otIp4Address mIp4; ///< The IPv4 address of the mapping.
    otIp6Address mIp6; ///< The IPv6 address of the mapping.

    /** The source port or ICMP ID of the mapping. Used when
     *  OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE is true.
     */
    uint16_t mSrcPortOrId;

    /** The translated port or ICMP ID of the mapping. Used when
     *  OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE is true.
     */
    uint16_t mTranslatedPortOrId;

    /**
     * Remaining time in milliseconds before the entry expires.
     *
     * The remaining time is relative to the initialization of the `otNat64AddressMappingIterator`, i.e., when
     * `otNat64InitAddressMappingIterator()` was called.
     */
    uint32_t mRemainingTimeMs;

    otNat64ProtocolCounters mCounters; ///< Counters.
} otNat64AddressMapping;

/**
 * Used to iterate through NAT64 address mappings.
 *
 * The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be
 * accessed or used by caller.
 *
 * Before using an iterator, it MUST be initialized using `otNat64InitAddressMappingIterator()`.
 *
 * The member fields in this struct are for internal OpenThread stack use and should not be accessed directly.
 */
typedef struct otNat64AddressMappingIterator
{
    const void *mPtr;
    uint32_t    mData32;
} otNat64AddressMappingIterator;

/**
 * Initializes an `otNat64AddressMappingIterator`.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * An iterator MUST be initialized before it is used. An iterator can be initialized again to restart from the
 * beginning of the mapping info list.
 *
 * The iterator initialization time is used to report the `mRemainingTimeMs` in the `otNat64AddressMapping` retrieved
 * when calling `otNat64GetNextAddressMapping()` to iterate over the list. This ensures that all entry
 * `mRemainingTimeMs` values are consistent and are from the same time origin, regardless of how or when
 * `otNat64GetNextAddressMapping()` is called.
 *
 * @param[in]  aInstance  A pointer to the OpenThread instance.
 * @param[out] aIterator  A pointer to the iterator to initialize.
 */
void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator);

/**
 * Gets the next AddressMapping info (using an iterator).
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * @param[in]      aInstance      A pointer to an OpenThread instance.
 * @param[in,out]  aIterator      A pointer to the iterator. On success the iterator will be updated to point to next
 *                                NAT64 address mapping record.
 * @param[out]     aMapping       A pointer to an `otNat64AddressMapping` where information of next NAT64 address
 *                                mapping record is placed (on success).
 *
 * @retval OT_ERROR_NONE       Successfully found the next NAT64 address mapping info (@p aMapping was successfully
 *                             updated).
 * @retval OT_ERROR_NOT_FOUND  No subsequent NAT64 address mapping info was found.
 */
otError otNat64GetNextAddressMapping(otInstance                    *aInstance,
                                     otNat64AddressMappingIterator *aIterator,
                                     otNat64AddressMapping         *aMapping);

/**
 * States of NAT64.
 */
typedef enum
{
    OT_NAT64_STATE_DISABLED = 0, ///< NAT64 is disabled.
    OT_NAT64_STATE_NOT_RUNNING,  ///< NAT64 is enabled, but one or more dependencies of NAT64 are not running.
    OT_NAT64_STATE_IDLE,         ///< NAT64 is enabled, but this BR is not an active NAT64 BR.
    OT_NAT64_STATE_ACTIVE,       ///< The BR is publishing a NAT64 prefix and/or translating packets.
} otNat64State;

/**
 * Gets the state of NAT64 translator.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * @param[in]  aInstance          A pointer to an OpenThread instance.
 *
 * @retval OT_NAT64_STATE_DISABLED    NAT64 translator is disabled.
 * @retval OT_NAT64_STATE_NOT_RUNNING NAT64 translator is enabled, but the translator is not configured with a valid
 *                                    NAT64 prefix and a CIDR.
 * @retval OT_NAT64_STATE_ACTIVE      NAT64 translator is enabled, and is translating packets.
 */
otNat64State otNat64GetTranslatorState(otInstance *aInstance);

/**
 * Gets the state of NAT64 prefix manager.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.
 *
 * @param[in]  aInstance          A pointer to an OpenThread instance.
 *
 * @retval OT_NAT64_STATE_DISABLED    NAT64 prefix manager is disabled.
 * @retval OT_NAT64_STATE_NOT_RUNNING NAT64 prefix manager is enabled, but is not running (because the routing manager
 *                                    is not running).
 * @retval OT_NAT64_STATE_IDLE        NAT64 prefix manager is enabled, but is not publishing a NAT64 prefix. Usually
 *                                    when there is another border router publishing a NAT64 prefix with higher
 *                                    priority.
 * @retval OT_NAT64_STATE_ACTIVE      NAT64 prefix manager is enabled, and is publishing NAT64 prefix to the Thread
 *                                    network.
 */
otNat64State otNat64GetPrefixManagerState(otInstance *aInstance);

/**
 * Enable or disable NAT64 functions.
 *
 * Note: This includes the NAT64 Translator (when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled) and the NAT64
 * Prefix Manager (when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled).
 *
 * When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, setting disabled to true resets the
 * mapping table in the translator.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is
 * enabled.
 *
 * @param[in]  aInstance  A pointer to an OpenThread instance.
 * @param[in]  aEnabled   A boolean to enable/disable the NAT64 functions
 *
 * @sa otNat64GetTranslatorState
 * @sa otNat64GetPrefixManagerState
 */
void otNat64SetEnabled(otInstance *aInstance, bool aEnabled);

/**
 * Allocate a new message buffer for sending an IPv4 message to the NAT64 translator.
 *
 * Message buffers allocated by this function will have 20 bytes (difference between the size of IPv6 headers
 * and IPv4 header sizes) reserved.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * @note If @p aSettings is `NULL`, the link layer security is enabled and the message priority is set to
 * OT_MESSAGE_PRIORITY_NORMAL by default.
 *
 * @param[in]  aInstance  A pointer to an OpenThread instance.
 * @param[in]  aSettings  A pointer to the message settings or NULL to set default settings.
 *
 * @returns A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid.
 *
 * @sa otNat64Send
 */
otMessage *otIp4NewMessage(otInstance *aInstance, const otMessageSettings *aSettings);

/**
 * Sets the CIDR used when setting the source address of the outgoing translated IPv4 packets.
 *
 * Is available only when OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE is enabled.
 *
 * @note A valid CIDR must have a non-zero prefix length. The actual addresses pool is limited by the size of the
 * mapping pool and the number of addresses available in the CIDR block.
 *
 * @note This function can be called at any time, but the NAT64 translator will be reset and all existing sessions will
 * be expired when updating the configured CIDR.
 *
 * @param[in] aInstance  A pointer to an OpenThread instance.
 * @param[in] aCidr      A pointer to an otIp4Cidr for the IPv4 CIDR block for NAT64.
 *
 * @retval  OT_ERROR_INVALID_ARGS   The given CIDR is not a valid IPv4 CIDR for NAT64.
 * @retval  OT_ERROR_NONE           Successfully set the CIDR for NAT64.
 *
 * @sa otNat64Send
 * @sa otNat64SetReceiveIp4Callback
 */
otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr);

/**
 * Clears the CIDR used when setting the source address of the outgoing translated IPv4 packets.
 *
 * Is available only when OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE is enabled.
 *
 * @note This function can be called at any time, but the NAT64 translator will be reset and all existing sessions
 * will be expired when clearing the configured CIDR.
 *
 * @param[in] aInstance  A pointer to an OpenThread instance.
 *
 * @sa otNat64SetIp4Cidr
 */
void otNat64ClearIp4Cidr(otInstance *aInstance);

/**
 * Translates an IPv4 datagram to an IPv6 datagram and sends via the Thread interface.
 *
 * The caller transfers ownership of @p aMessage when making this call. OpenThread will free @p aMessage when
 * processing is complete, including when a value other than `OT_ERROR_NONE` is returned.
 *
 * @param[in]  aInstance A pointer to an OpenThread instance.
 * @param[in]  aMessage  A pointer to the message buffer containing the IPv4 datagram.
 *
 * @retval OT_ERROR_NONE                    Successfully processed the message.
 * @retval OT_ERROR_DROP                    Message was well-formed but not fully processed due to packet processing
 *                                          rules.
 * @retval OT_ERROR_NO_BUFS                 Could not allocate necessary message buffers when processing the datagram.
 * @retval OT_ERROR_NO_ROUTE                No route to host.
 * @retval OT_ERROR_INVALID_SOURCE_ADDRESS  Source address is invalid, e.g. an anycast address or a multicast address.
 * @retval OT_ERROR_PARSE                   Encountered a malformed header when processing the message.
 */
otError otNat64Send(otInstance *aInstance, otMessage *aMessage);

/**
 * Pointer is called when an IPv4 datagram (translated by NAT64 translator) is received.
 *
 * @param[in]  aMessage  A pointer to the message buffer containing the received IPv6 datagram. This function transfers
 *                       the ownership of the @p aMessage to the receiver of the callback. The message should be
 *                       freed by the receiver of the callback after it is processed.
 * @param[in]  aContext  A pointer to application-specific context.
 */
typedef void (*otNat64ReceiveIp4Callback)(otMessage *aMessage, void *aContext);

/**
 * Registers a callback to provide received IPv4 datagrams.
 *
 * @param[in]  aInstance         A pointer to an OpenThread instance.
 * @param[in]  aCallback         A pointer to a function that is called when an IPv4 datagram is received or
 *                               NULL to disable the callback.
 * @param[in]  aContext          A pointer to application-specific context.
 */
void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext);

/**
 * Gets the IPv4 CIDR configured in the NAT64 translator.
 *
 * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
 *
 * @param[in]  aInstance         A pointer to an OpenThread instance.
 * @param[out] aCidr             A pointer to an otIp4Cidr. Where the CIDR will be filled.
 */
otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr);

/**
 * Test if two IPv4 addresses are the same.
 *
 * @param[in]  aFirst   A pointer to the first IPv4 address to compare.
 * @param[in]  aSecond  A pointer to the second IPv4 address to compare.
 *
 * @retval TRUE   The two IPv4 addresses are the same.
 * @retval FALSE  The two IPv4 addresses are not the same.
 */
bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond);

/**
 * Set @p aIp4Address by performing NAT64 address translation from @p aIp6Address as specified
 * in RFC 6052.
 *
 * The NAT64 @p aPrefixLength MUST be one of the following values: 32, 40, 48, 56, 64, or 96, otherwise the behavior
 * of this method is undefined.
 *
 * @param[in]  aPrefixLength  The prefix length to use for IPv4/IPv6 translation.
 * @param[in]  aIp6Address    A pointer to an IPv6 address.
 * @param[out] aIp4Address    A pointer to output the IPv4 address.
 */
void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address);

/**
 * Extracts the IPv4 address from a given IPv4-mapped IPv6 address.
 *
 * An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to ones, and the remaining,
 * least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing `192.0.2.128`.
 *
 * @param[in]  aIp6Address  An IPv6 address to extract IPv4 from.
 * @param[out] aIp4Address  An IPv4 address to output the extracted address.
 *
 * @retval OT_ERROR_NONE   Extracted the IPv4 address successfully. @p aIp4Address is updated.
 * @retval OT_ERROR_PARSE  The @p aIp6Address does not follow the IPv4-mapped IPv6 address format.
 */
otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address);

/**
 * Converts a given IP4 address to an IPv6 address following the IPv4-mapped IPv6 address format.
 *
 * @param[in]  aIp4Address  An IPv4 address to convert.
 * @param[out] aIp6Address  An IPv6 address to set.
 */
void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address);

#define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL

/**
 * Converts the address to a string.
 *
 * The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
 *
 * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
 * truncated but the outputted string is always null-terminated.
 *
 * @param[in]  aAddress  A pointer to an IPv4 address (MUST NOT be NULL).
 * @param[out] aBuffer   A pointer to a char array to output the string (MUST NOT be NULL).
 * @param[in]  aSize     The size of @p aBuffer (in bytes).
 */
void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize);

#define OT_IP4_CIDR_STRING_SIZE 20 ///< Length of 000.000.000.000/00 plus a suffix NUL

/**
 * Converts a human-readable IPv4 CIDR string into a binary representation.
 *
 * @param[in]   aString   A pointer to a NULL-terminated string.
 * @param[out]  aCidr     A pointer to an IPv4 CIDR.
 *
 * @retval OT_ERROR_NONE          Successfully parsed the string.
 * @retval OT_ERROR_INVALID_ARGS  Failed to parse the string.
 */
otError otIp4CidrFromString(const char *aString, otIp4Cidr *aCidr);

/**
 * Converts the IPv4 CIDR to a string.
 *
 * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g.,
 * "127.0.0.1/32").
 *
 * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be
 * truncated but the outputted string is always null-terminated.
 *
 * @param[in]  aCidr     A pointer to an IPv4 CIDR (MUST NOT be NULL).
 * @param[out] aBuffer   A pointer to a char array to output the string (MUST NOT be NULL).
 * @param[in]  aSize     The size of @p aBuffer (in bytes).
 */
void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize);

/**
 * Converts a human-readable IPv4 address string into a binary representation.
 *
 * @param[in]   aString   A pointer to a NULL-terminated string.
 * @param[out]  aAddress  A pointer to an IPv4 address.
 *
 * @retval OT_ERROR_NONE          Successfully parsed the string.
 * @retval OT_ERROR_INVALID_ARGS  Failed to parse the string.
 */
otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress);

/**
 * Sets the IPv6 address by performing NAT64 address translation from the preferred NAT64 prefix and the given IPv4
 * address as specified in RFC 6052.
 *
 * @param[in]   aInstance    A pointer to an OpenThread instance.
 * @param[in]   aIp4Address  A pointer to the IPv4 address to translate to IPv6.
 * @param[out]  aIp6Address  A pointer to the synthesized IPv6 address.
 *
 * @returns  OT_ERROR_NONE           Successfully synthesized the IPv6 address from NAT64 prefix and IPv4 address.
 * @returns  OT_ERROR_INVALID_STATE  No valid NAT64 prefix in the network data.
 */
otError otNat64SynthesizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address);

/**
 * Converts a given `otNat64State` to a human-readable string.
 *
 * @param[in] aState  The NAT64 state.
 *
 * @returns The string representation of @p aState.
 */
const char *otNat64StateToString(otNat64State aState);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif // OPENTHREAD_NAT64_H_
