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

#include <stdio.h>

#include "test_platform.h"
#include "test_util.hpp"

#include "instance/instance.hpp"

#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE

namespace ot {
namespace Nat64 {

#define Log(...) printf(OT_FIRST_ARG(__VA_ARGS__) "\n" OT_REST_ARGS(__VA_ARGS__))

static ot::Instance *sInstance;

void DumpIp6Message(const char *aTextMessage, const Message &aMessage)
{
    Ip6::Headers ip6Headers;

    Log("%s", aTextMessage);

    if (ip6Headers.ParseFrom(aMessage) != kErrorNone)
    {
        Log("    Malformed IPv6 message");
        ExitNow();
    }

    Log("    IPv6 Headers");
    Log("       src      : %s", ip6Headers.GetSourceAddress().ToString().AsCString());
    Log("       dst      : %s", ip6Headers.GetDestinationAddress().ToString().AsCString());
    Log("       proto    : %s", otIp6ProtoToString(ip6Headers.GetIpProto()));

    if (ip6Headers.IsTcp() || ip6Headers.IsUdp())
    {
        Log("       src-port : %u", ip6Headers.GetSourcePort());
        Log("       dst-port : %u", ip6Headers.GetDestinationPort());
    }
    else if (ip6Headers.IsIcmp6())
    {
        Log("       icmp6-id : %u", ip6Headers.GetIcmpHeader().GetId());
    }

exit:
    return;
}

void DumpIp4Message(const char *aTextMessage, const Message &aMessage)
{
    Ip4::Headers ip4Headers;

    Log("%s", aTextMessage);

    if (ip4Headers.ParseFrom(aMessage) != kErrorNone)
    {
        Log("    Malformed IPv4 message");
        ExitNow();
    }

    Log("    IPv4 Headers");
    Log("       src      : %s", ip4Headers.GetSourceAddress().ToString().AsCString());
    Log("       dst      : %s", ip4Headers.GetDestinationAddress().ToString().AsCString());
    Log("       proto    : %s", ip4Headers.IsIcmp4() ? "ICMP4" : otIp6ProtoToString(ip4Headers.GetIpProto()));

    if (ip4Headers.IsTcp() || ip4Headers.IsUdp())
    {
        Log("       src-port : %u", ip4Headers.GetSourcePort());
        Log("       dst-port : %u", ip4Headers.GetDestinationPort());
    }
    else if (ip4Headers.IsIcmp4())
    {
        Log("       icmp4-id : %u", ip4Headers.GetIcmpHeader().GetId());
    }

exit:
    return;
}

void VerifyMessage(const Message &aMessage, const uint8_t *aExpectedContent, uint16_t aExpectedLength)
{
    VerifyOrQuit(aMessage.GetLength() == aExpectedLength);
    VerifyOrQuit(aMessage.CompareBytes(0, aExpectedContent, aExpectedLength));
}

void Verify6To4(const char    *aTestName,
                const uint8_t *aIp6Message,
                const uint16_t aIp6Length,
                const uint8_t *aIp4Message,
                uint16_t       aIp4Length,
                Error          aError)
{
    Message *message = sInstance->Get<Ip6::Ip6>().NewMessage();
    Error    error;

    Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
    Log("Translate IPv6 to IPv4: %s", aTestName);

    VerifyOrQuit(message != nullptr);
    SuccessOrQuit(message->AppendBytes(aIp6Message, aIp6Length));

    DumpIp6Message("IPv6 message", *message);

    error = sInstance->Get<Translator>().TranslateIp6ToIp4(*message);
    Log("Error: %s (expecting:%s)", ErrorToString(error), ErrorToString(aError));
    VerifyOrQuit(error == aError);

    if (aIp4Message != nullptr)
    {
        DumpIp4Message("Translated IPv4 message", *message);
        VerifyMessage(*message, aIp4Message, aIp4Length);
    }
}

template <uint16_t kIp6Length, uint16_t kIp4Length>
void Verify6To4(const char *aTestName,
                const uint8_t (&aIp6Message)[kIp6Length],
                const uint8_t (&aIp4Message)[kIp4Length],
                Error aError)
{
    Verify6To4(aTestName, aIp6Message, kIp6Length, aIp4Message, kIp4Length, aError);
}

template <uint16_t kIp6Length>
void Verify6To4(const char *aTestName, const uint8_t (&aIp6Message)[kIp6Length], Error aError)
{
    Verify6To4(aTestName, aIp6Message, kIp6Length, nullptr, 0, aError);
}

void Verify4To6(const char    *aTestName,
                const uint8_t *aIp4Message,
                uint16_t       aIp4Length,
                const uint8_t *aIp6Message,
                uint16_t       aIp6Length,
                Error          aError)
{
    Message *message = sInstance->Get<Ip6::Ip6>().NewMessage();
    Error    error;

    Log("- - - - - - - - - - - - - - - - - - - - - - - - - ");
    Log("Translate IPv4 to IPv6: %s", aTestName);

    VerifyOrQuit(message != nullptr);
    SuccessOrQuit(message->AppendBytes(aIp4Message, aIp4Length));

    DumpIp4Message("IPv4 message", *message);

    error = sInstance->Get<Translator>().TranslateIp4ToIp6(*message);
    Log("Error: %s (expecting:%s)", ErrorToString(error), ErrorToString(aError));
    VerifyOrQuit(error == aError);

    if (aIp6Message != nullptr)
    {
        DumpIp6Message("Translated IPv6 message", *message);
        VerifyMessage(*message, aIp6Message, aIp6Length);
    }
}

template <uint16_t kIp4Length, uint16_t kIp6Length>
void Verify4To6(const char *aTestName,
                const uint8_t (&aIp4Message)[kIp4Length],
                const uint8_t (&aIp6Message)[kIp6Length],
                Error aError)
{
    Verify4To6(aTestName, aIp4Message, kIp4Length, aIp6Message, kIp6Length, aError);
}

template <uint16_t kIp4Length>
void Verify4To6(const char *aTestName, const uint8_t (&aIp4Message)[kIp4Length], Error aError)
{
    Verify4To6(aTestName, aIp4Message, kIp4Length, nullptr, 0, aError);
}

//----------------------------------------------------------------------------------------------------------------------

void TestNat64Translation(void)
{
    Ip6::Prefix prefix;
    Ip4::Cidr   cidr;

    Log("--------------------------------------------------------------------------------------------");
    Log("TestNat64Translation");

    sInstance = testInitInstance();
    VerifyOrQuit(sInstance != nullptr);

    SuccessOrQuit(prefix.FromString("fd01::/96"));
    SuccessOrQuit(cidr.FromString("192.168.123.1/32"));

    SuccessOrQuit(sInstance->Get<Translator>().SetIp4Cidr(cidr));
    sInstance->Get<Translator>().SetNat64Prefix(prefix);
    sInstance->Get<Translator>().SetEnabled(true);

    {
        // fd02::1               fd01::ac10:f3c5       UDP      52     43981 → 4660 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            172,  16,   243,  197,  0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64,
        };
        // 192.168.123.1         172.16.243.197        UDP      32     43981 → 4660 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x9f,
                                      0x4d, 192,  168,  123,  1,    172,  16,   243,  197,  0xab, 0xcd,
                                      0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8d, 0x61, 0x62, 0x63, 0x64};

        Verify6To4("Valid v6 UDP", kIp6Packet, kIp4Packet, kErrorNone);
    }

    {
        // 172.16.243.197        192.168.123.1         UDP      32     43981 → 4660 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11, 0xa0,
                                      0x4d, 172,  16,   243,  197,  192,  168,  123,  1,    0xab, 0xcd,
                                      0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8d, 0x61, 0x62, 0x63, 0x64};
        // fd01::ac10:f3c5       fd02::1               UDP      52     43981 → 4660 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x3f, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 172,  16,   243,  197,  0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x01, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64,
        };

        Verify4To6("Valid v4 UDP", kIp4Packet, kIp6Packet, kErrorNone);
    }

    {
        // fd02::1               fd01::ac10:f3c5       TCP      64     43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x08, 0x6e, 0x38, 0x00, 0x18, 0x06, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 172,  16,   243,  197,  0xab, 0xcd, 0x12, 0x34, 0x87, 0x65, 0x43, 0x21,
            0x12, 0x34, 0x56, 0x78, 0x50, 0x10, 0x00, 0x01, 0x5f, 0xf8, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64,
        };
        // 192.168.123.1         172.16.243.197        TCP      44     43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x9f,
                                      0x4c, 192,  168,  123,  1,    172,  16,   243,  197,  0xab, 0xcd,
                                      0x12, 0x34, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x50,
                                      0x10, 0x00, 0x01, 0x1e, 0x54, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64};

        Verify6To4("Valid v6 TCP", kIp6Packet, kIp4Packet, kErrorNone);
    }

    {
        // 172.16.243.197        192.168.123.1         TCP      44     43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x9f,
                                      0x4c, 172,  16,   243,  197,  192,  168,  123,  1,    0xab, 0xcd,
                                      0x12, 0x34, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x50,
                                      0x10, 0x00, 0x01, 0x1e, 0x54, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64};
        // fd01::ac10:f3c5       fd02::1               TCP      64     43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x40, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 172,  16,   243,  197,  0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xab, 0xcd, 0x12, 0x34, 0x87, 0x65, 0x43, 0x21,
            0x12, 0x34, 0x56, 0x78, 0x50, 0x10, 0x00, 0x01, 0x5f, 0xf8, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64,
        };

        Verify4To6("Valid v4 TCP", kIp4Packet, kIp6Packet, kErrorNone);
    }

    {
        // fd02::1         fd01::ac10:f3c5     ICMPv6   52     Echo (ping) request id=0xaabb, seq=1, hop limit=64
        const uint8_t kIp6Packet[] = {
            0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x3a, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            172,  16,   243,  197,  0x80, 0x00, 0x76, 0x59, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64,
        };
        // 192.168.123.1   172.16.243.197      ICMP     32     Echo (ping) request  id=0xaabb, seq=1/256, ttl=63
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x9f,
                                      0x5d, 192,  168,  123,  1,    172,  16,   243,  197,  0x08, 0x00,
                                      0x88, 0x7c, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64};

        Verify6To4("Valid v6 ICMP ping", kIp6Packet, kIp4Packet, kErrorNone);
    }

    {
        // 172.16.243.197        192.168.123.1         ICMP     32     Echo (ping) reply    id=0xaabb, seq=1/256, ttl=63
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa0,
                                      0x5d, 172,  16,   243,  197,  192,  168,  123,  1,    0x00, 0x00,
                                      0x90, 0x7c, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64};
        // fd01::ac10:f3c5       fd02::1               ICMPv6   52     Echo (ping) reply id=0xaabb, seq=1, hop limit=62
        const uint8_t kIp6Packet[] = {
            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x3a, 0x3f, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 172,  16,   243,  197,  0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x01, 0x81, 0x00, 0x75, 0x59, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64,
        };

        Verify4To6("Valid v4 ICMP ping", kIp4Packet, kIp6Packet, kErrorNone);
    }

    {
        // fd02::1               N/A                   IPv6     39     Invalid IPv6 header
        const uint8_t kIp6Packet[] = {0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00,
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01,
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172,  16,   243};

        Verify6To4("Invalid v6", kIp6Packet, kErrorDrop);
    }

    {
        // 172.16.243.197        N/A                   IPv4     19     [Malformed Packet]
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11,
                                      0xa0, 0x4c, 172,  16,   243,  197,  192,  168,  123};

        Verify4To6("Invalid v4", kIp4Packet, kErrorDrop);
    }

    {
        // 172.16.243.197        192.168.123.2         UDP      32     43981 → 4660 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11, 0xa0,
                                      0x4c, 172,  16,   243,  197,  192,  168,  123,  2,    0xab, 0xcd,
                                      0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8c, 0x61, 0x62, 0x63, 0x64};

        Verify4To6("No v4 mapping", kIp4Packet, kErrorDrop);
    }

    Log("End of TestNat64Translation");

    testFreeInstance(sInstance);
}

void TestNat64Counters(void)
{
    Ip6::Prefix                        prefix;
    Ip4::Cidr                          cidr;
    Translator::AddressMappingIterator iter;
    Translator::AddressMapping         mapping;
    Translator::ProtocolCounters       expectedCounters;

    Log("--------------------------------------------------------------------------------------------");
    Log("TestNat64Counters");

    sInstance = testInitInstance();
    VerifyOrQuit(sInstance != nullptr);

    SuccessOrQuit(prefix.FromString("fd01::/96"));
    SuccessOrQuit(cidr.FromString("192.168.123.1/32"));

    SuccessOrQuit(sInstance->Get<Translator>().SetIp4Cidr(cidr));
    sInstance->Get<Translator>().SetNat64Prefix(prefix);
    sInstance->Get<Translator>().SetEnabled(true);

    // Step 1: Make the mapping table dirty.
    {
        // fd02::1               fd01::ac10:f3c5       UDP      52     43981 → 4660 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            172,  16,   243,  197,  0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64,
        };
        // 192.168.123.1         172.16.243.197        UDP      32     43981 → 4660 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x9f,
                                      0x4d, 192,  168,  123,  1,    172,  16,   243,  197,  0xab, 0xcd,
                                      0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8d, 0x61, 0x62, 0x63, 0x64};

        Verify6To4("First translation", kIp6Packet, kIp4Packet, kErrorNone);
    }

    iter.Init(*sInstance);

    SuccessOrQuit(iter.GetNext(mapping));

    expectedCounters.Clear();
    expectedCounters.mUdp.m6To4Packets   = 1;
    expectedCounters.mUdp.m6To4Bytes     = 12;
    expectedCounters.mTotal.m6To4Packets = 1;
    expectedCounters.mTotal.m6To4Bytes   = 12;
    VerifyOrQuit(AsCoreType(&mapping.mCounters) == expectedCounters);

    VerifyOrQuit(iter.GetNext(mapping) == kErrorNotFound);

    // Step 2: Release the mapping table item.
    {
        SuccessOrQuit(prefix.FromString("fd01::/96"));
        SuccessOrQuit(cidr.FromString("192.168.124.1/32"));

        SuccessOrQuit(sInstance->Get<Translator>().SetIp4Cidr(cidr));
        sInstance->Get<Translator>().SetNat64Prefix(prefix);
    }

    // Step 3: Reuse the same object for new mapping table item.
    // If the counters are not reset, the verification below will fail.
    {
        // fd02::1               fd01::ac10:f3c5       UDP      52     43981 → 4660 Len=4
        const uint8_t kIp6Packet[] = {
            0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            172,  16,   243,  197,  0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64,
        };
        // 192.168.124.1         172.16.243.197        UDP      32     43981 → 4660 Len=4
        const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x9e,
                                      0x4d, 192,  168,  124,  1,    172,  16,   243,  197,  0xab, 0xcd,
                                      0x12, 0x34, 0x00, 0x0c, 0xa0, 0x8d, 0x61, 0x62, 0x63, 0x64};

        Verify6To4("Translation with new mapping", kIp6Packet, kIp4Packet, kErrorNone);
    }

    iter.Init(*sInstance);

    SuccessOrQuit(iter.GetNext(mapping));

    expectedCounters.Clear();
    expectedCounters.mUdp.m6To4Packets   = 1;
    expectedCounters.mUdp.m6To4Bytes     = 12;
    expectedCounters.mTotal.m6To4Packets = 1;
    expectedCounters.mTotal.m6To4Bytes   = 12;
    VerifyOrQuit(AsCoreType(&mapping.mCounters) == expectedCounters);

    VerifyOrQuit(iter.GetNext(mapping) == kErrorNotFound);

    Log("End of TestNat64Counters");
}

} // namespace Nat64
} // namespace ot

#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE

int main(void)
{
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
    ot::Nat64::TestNat64Translation();
    ot::Nat64::TestNat64Counters();
    printf("All tests passed\n");
#else
    printf("NAT64 is not enabled\n");
#endif

    return 0;
}
