mirror of
https://github.com/espressif/openthread.git
synced 2026-08-08 19:57:46 +00:00
[nat64] implement nat64 translator (ot::Nat64::Translator) (#7836)
This commit: - implements the core logic for translating packets for NAT64, including the public APIs exposed to platform daemons. - includes changes for POSIX platform, use `OT_POSIX_NAT64_CIDR`, `OPENTHREAD_POSIX_CONFIG_NAT64_CIDR` for setting the CIDR for NAT64 during build time. - exposes `otNat64Send(otInstance *aInstance, otMessage *aMessage)` and `void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext)`.
This commit is contained in:
@@ -306,6 +306,7 @@ LOCAL_SRC_FILES := \
|
||||
src/core/net/ip6_filter.cpp \
|
||||
src/core/net/ip6_headers.cpp \
|
||||
src/core/net/ip6_mpl.cpp \
|
||||
src/core/net/nat64_translator.cpp \
|
||||
src/core/net/nd6.cpp \
|
||||
src/core/net/nd_agent.cpp \
|
||||
src/core/net/netif.cpp \
|
||||
|
||||
@@ -115,6 +115,7 @@ ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long
|
||||
ot_option(OT_MLR OPENTHREAD_CONFIG_MLR_ENABLE "Multicast Listener Registration (MLR)")
|
||||
ot_option(OT_MTD_NETDIAG OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE "TMF network diagnostics on MTDs")
|
||||
ot_option(OT_MULTIPLE_INSTANCE OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE "multiple instances")
|
||||
ot_option(OT_NAT64_TRANSLATOR OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE "NAT64 translator support")
|
||||
ot_option(OT_NEIGHBOR_DISCOVERY_AGENT OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE "neighbor discovery agent")
|
||||
ot_option(OT_NETDATA_PUBLISHER OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE "Network Data publisher")
|
||||
ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS")
|
||||
|
||||
@@ -13,6 +13,7 @@ This page lists the available common switches with description. Unless stated ot
|
||||
| BORDER_ROUTER | OT_BORDER_ROUTER | Enables support for Border Router. This switch is usually combined with the BORDER_AGENT and UDP_FORWARD (or PLATFORM_UDP in case of RCP design) switches to build Border Router device. |
|
||||
| BORDER_ROUTING | OT_BORDER_ROUTING | Enables bi-directional border routing between Thread and Infrastructure networks for Border Router. |
|
||||
| BORDER_ROUTING_NAT64 | OT_BORDER_ROUTING_NAT64 | Enables NAT64 border routing support for Border Router. |
|
||||
| NAT64_TRANSLATOR | OT_NAT64_TRANSLATOR | Enables NAT64 translator for Border Router. |
|
||||
| BUILTIN_MBEDTLS_MANAGEMENT | OT_BUILTIN_MBEDTLS_MANAGEMENT | Enables the built-in mbedTLS management. Enable this switch if the external mbedTLS is used, but mbedTLS memory allocation and debug config should be managed internally by OpenThread. |
|
||||
| CHANNEL_MANAGER | OT_CHANNEL_MANAGER | Enables support for channel manager. Enable this switch on devices that are supposed to request a Thread network channel change. This switch should be used only with an FTD build. |
|
||||
| CHANNEL_MONITOR | OT_CHANNEL_MONITOR | Enables support for channel monitor. Enable this switch on devices that are supposed to determine the cleaner channels. |
|
||||
|
||||
@@ -73,6 +73,7 @@ MLE_LONG_ROUTES ?= 0
|
||||
MLR ?= 0
|
||||
MTD_NETDIAG ?= 0
|
||||
MULTIPLE_INSTANCE ?= 0
|
||||
NAT64_TRANSLATOR ?= 0
|
||||
NEIGHBOR_DISCOVERY_AGENT ?= 0
|
||||
NETDATA_PUBLISHER ?= 0
|
||||
OTNS ?= 0
|
||||
@@ -121,6 +122,10 @@ ifeq ($(BORDER_ROUTING_NAT64),1)
|
||||
COMMONCFLAGS += -DOPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE=1
|
||||
endif
|
||||
|
||||
ifeq ($(NAT64_TRANSLATOR),1)
|
||||
COMMONCFLAGS += -DOPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE=1
|
||||
endif
|
||||
|
||||
ifeq ($(COAP),1)
|
||||
COMMONCFLAGS += -DOPENTHREAD_CONFIG_COAP_API_ENABLE=1
|
||||
endif
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (235)
|
||||
#define OPENTHREAD_API_VERSION (236)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -88,6 +88,92 @@ typedef struct otIp4Cidr
|
||||
uint8_t mLength;
|
||||
} otIp4Cidr;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This function is available only 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.
|
||||
*
|
||||
* This function 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 otBorderRouterSend
|
||||
* @sa otBorderRouterSetReceiveCallback
|
||||
*
|
||||
*/
|
||||
otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* This function 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] aCallbackContext A pointer to application-specific context.
|
||||
*
|
||||
*/
|
||||
void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Test if two IPv4 addresses are the same.
|
||||
*
|
||||
|
||||
@@ -38,6 +38,7 @@ readonly OT_BUILD_OPTIONS=(
|
||||
"-DOT_BORDER_ROUTER=ON"
|
||||
"-DOT_BORDER_ROUTING=ON"
|
||||
"-DOT_BORDER_ROUTING_NAT64=ON"
|
||||
"-DOT_NAT64_TRANSLATOR=ON"
|
||||
"-DOT_COAP=ON"
|
||||
"-DOT_COAP_BLOCK=ON"
|
||||
"-DOT_COAP_OBSERVE=ON"
|
||||
|
||||
@@ -95,6 +95,8 @@ build_all_features()
|
||||
|
||||
local options_1_3=(
|
||||
"-DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1"
|
||||
"-DOPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE=1"
|
||||
"-DOPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE=1"
|
||||
"-DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1"
|
||||
"-DOPENTHREAD_CONFIG_DUA_ENABLE=1"
|
||||
"-DOPENTHREAD_CONFIG_MLR_ENABLE=1"
|
||||
|
||||
@@ -57,6 +57,8 @@ build_all_features()
|
||||
local options=(
|
||||
"-DOT_BACKBONE_ROUTER=ON"
|
||||
"-DOT_BORDER_ROUTING=ON"
|
||||
"-DOT_BORDER_ROUTING_NAT64=ON"
|
||||
"-DOT_NAT64_TRANSLATOR=ON"
|
||||
"-DOT_CSL_RECEIVER=ON"
|
||||
"-DOT_MLR=ON"
|
||||
"-DOT_OTNS=ON"
|
||||
|
||||
@@ -84,6 +84,7 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=(
|
||||
'-DOT_BORDER_ROUTER=ON'
|
||||
'-DOT_BORDER_ROUTING=ON'
|
||||
'-DOT_BORDER_ROUTING_NAT64=ON'
|
||||
'-DOT_NAT64_TRANSLATOR=ON'
|
||||
'-DOT_CHANNEL_MANAGER=ON'
|
||||
'-DOT_CHANNEL_MONITOR=ON'
|
||||
'-DOT_CHILD_SUPERVISION=ON'
|
||||
|
||||
@@ -565,6 +565,8 @@ openthread_core_files = [
|
||||
"net/ip6_mpl.cpp",
|
||||
"net/ip6_mpl.hpp",
|
||||
"net/ip6_types.hpp",
|
||||
"net/nat64_translator.cpp",
|
||||
"net/nat64_translator.hpp",
|
||||
"net/nd6.cpp",
|
||||
"net/nd6.hpp",
|
||||
"net/nd_agent.cpp",
|
||||
|
||||
@@ -172,6 +172,7 @@ set(COMMON_SOURCES
|
||||
net/ip6_filter.cpp
|
||||
net/ip6_headers.cpp
|
||||
net/ip6_mpl.cpp
|
||||
net/nat64_translator.cpp
|
||||
net/nd6.cpp
|
||||
net/nd_agent.cpp
|
||||
net/netif.cpp
|
||||
|
||||
@@ -262,6 +262,7 @@ SOURCES_COMMON = \
|
||||
net/ip6_filter.cpp \
|
||||
net/ip6_headers.cpp \
|
||||
net/ip6_mpl.cpp \
|
||||
net/nat64_translator.cpp \
|
||||
net/nd6.cpp \
|
||||
net/nd_agent.cpp \
|
||||
net/netif.cpp \
|
||||
@@ -572,6 +573,7 @@ HEADERS_COMMON = \
|
||||
net/ip6_headers.hpp \
|
||||
net/ip6_mpl.hpp \
|
||||
net/ip6_types.hpp \
|
||||
net/nat64_translator.hpp \
|
||||
net/nd6.hpp \
|
||||
net/nd_agent.hpp \
|
||||
net/netif.hpp \
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#include "common/as_core_type.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "net/ip4_types.hpp"
|
||||
#include "net/ip6_headers.hpp"
|
||||
#include "utils/slaac_address.hpp"
|
||||
|
||||
using namespace ot;
|
||||
|
||||
@@ -31,12 +31,43 @@
|
||||
* This file implements the OpenThread APIs for handling IPv4 (NAT64) messages
|
||||
*/
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include <openthread/border_router.h>
|
||||
#include <openthread/ip6.h>
|
||||
#include <openthread/nat64.h>
|
||||
|
||||
#include "border_router/routing_manager.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "net/ip4_types.hpp"
|
||||
#include "net/ip6_headers.hpp"
|
||||
#include "net/nat64_translator.hpp"
|
||||
|
||||
using namespace ot;
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Nat64::Translator>().SetIp4Cidr(AsCoreType(aCidr));
|
||||
}
|
||||
|
||||
otMessage *otIp4NewMessage(otInstance *aInstance, const otMessageSettings *aSettings)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Nat64::Translator>().NewIp4Message(Message::Settings::From(aSettings));
|
||||
}
|
||||
|
||||
otError otNat64Send(otInstance *aInstance, otMessage *aMessage)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Nat64::Translator>().SendMessage(AsCoreType(aMessage));
|
||||
}
|
||||
|
||||
void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext)
|
||||
{
|
||||
AsCoreType(aInstance).Get<Ip6::Ip6>().SetNat64ReceiveIp4DatagramCallback(aCallback, aContext);
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond)
|
||||
{
|
||||
return AsCoreType(aFirst) == AsCoreType(aSecond);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <openthread/border_router.h>
|
||||
#include <openthread/platform/infra_if.h>
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
@@ -50,6 +51,7 @@
|
||||
#include "common/settings.hpp"
|
||||
#include "meshcop/extended_panid.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
#include "net/nat64_translator.hpp"
|
||||
#include "thread/network_data_leader.hpp"
|
||||
#include "thread/network_data_local.hpp"
|
||||
#include "thread/network_data_notifier.hpp"
|
||||
@@ -600,6 +602,9 @@ void RoutingManager::EvaluateNat64Prefix(void)
|
||||
{
|
||||
mAdvertisedNat64Prefix = nat64Prefix;
|
||||
}
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
Get<Nat64::Translator>().SetNat64Prefix(mAdvertisedNat64Prefix);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include "common/string.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
#include "net/nat64_translator.hpp"
|
||||
#include "net/nd6.hpp"
|
||||
#include "thread/network_data.hpp"
|
||||
|
||||
|
||||
@@ -223,6 +223,9 @@ Instance::Instance(void)
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
, mRoutingManager(*this)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
, mNat64Translator(*this)
|
||||
#endif
|
||||
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
|
||||
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
|
||||
, mLinkRaw(*this)
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
#include "net/dnssd_server.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
#include "net/ip6_filter.hpp"
|
||||
#include "net/nat64_translator.hpp"
|
||||
#include "net/nd_agent.hpp"
|
||||
#include "net/netif.hpp"
|
||||
#include "net/sntp_client.hpp"
|
||||
@@ -596,6 +597,10 @@ private:
|
||||
BorderRouter::RoutingManager mRoutingManager;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
Nat64::Translator mNat64Translator;
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
|
||||
|
||||
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
|
||||
@@ -1173,6 +1178,13 @@ template <> inline BorderRouter::InfraIf &Instance::Get(void)
|
||||
{
|
||||
return mRoutingManager.mInfraIf;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
template <> inline Nat64::Translator &Instance::Get(void)
|
||||
{
|
||||
return mNat64Translator;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/nat64.h>
|
||||
#include <openthread/platform/messagepool.h>
|
||||
|
||||
#include "common/as_core_type.hpp"
|
||||
@@ -280,7 +281,8 @@ public:
|
||||
kType6lowpan = 1, ///< A 6lowpan frame
|
||||
kTypeSupervision = 2, ///< A child supervision frame.
|
||||
kTypeMacEmptyData = 3, ///< An empty MAC data frame.
|
||||
kTypeOther = 4, ///< Other (data) message.
|
||||
kTypeIp4 = 4, ///< A full uncompressed IPv4 packet, for NAT64.
|
||||
kTypeOther = 5, ///< Other (data) message.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,4 +71,34 @@
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
*
|
||||
* Define to 1 to enable the internal NAT64 translator.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
#define OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS
|
||||
*
|
||||
* Specifies maximum number of active mappings for NAT64.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS
|
||||
#define OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS 254
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS
|
||||
*
|
||||
* Specifies timeout in seconds before removing an inactive address mapping.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS
|
||||
#define OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS 7200
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_BORDER_ROUTER_H_
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "net/icmp6.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
#include "net/ip6_filter.hpp"
|
||||
#include "net/nat64_translator.hpp"
|
||||
#include "net/netif.hpp"
|
||||
#include "net/udp6.hpp"
|
||||
#include "openthread/ip6.h"
|
||||
@@ -70,6 +71,10 @@ Ip6::Ip6(Instance &aInstance)
|
||||
, mIsReceiveIp6FilterEnabled(false)
|
||||
, mReceiveIp6DatagramCallback(nullptr)
|
||||
, mReceiveIp6DatagramCallbackContext(nullptr)
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
, mReceiveIp4DatagramCallback(nullptr)
|
||||
, mReceiveIp4DatagramCallbackContext(nullptr)
|
||||
#endif
|
||||
, mSendQueueTask(aInstance, Ip6::HandleSendQueue)
|
||||
, mIcmp(aInstance)
|
||||
, mUdp(aInstance)
|
||||
@@ -192,6 +197,14 @@ void Ip6::SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCall
|
||||
mReceiveIp6DatagramCallbackContext = aCallbackContext;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
void Ip6::SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback, void *aCallbackContext)
|
||||
{
|
||||
mReceiveIp4DatagramCallback = aCallback;
|
||||
mReceiveIp4DatagramCallbackContext = aCallbackContext;
|
||||
}
|
||||
#endif
|
||||
|
||||
Error Ip6::AddMplOption(Message &aMessage, Header &aHeader)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
@@ -1081,6 +1094,21 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage,
|
||||
}
|
||||
|
||||
IgnoreError(RemoveMplOption(*message));
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
switch (Get<Nat64::Translator>().TranslateFromIp6(aMessage))
|
||||
{
|
||||
case Nat64::Translator::kNotTranslated:
|
||||
break;
|
||||
case Nat64::Translator::kDrop:
|
||||
ExitNow(error = kErrorDrop);
|
||||
case Nat64::Translator::kForward:
|
||||
VerifyOrExit(mReceiveIp4DatagramCallback != nullptr, error = kErrorNoRoute);
|
||||
mReceiveIp4DatagramCallback(message, mReceiveIp4DatagramCallbackContext);
|
||||
ExitNow();
|
||||
}
|
||||
#endif
|
||||
|
||||
mReceiveIp6DatagramCallback(message, mReceiveIp6DatagramCallbackContext);
|
||||
|
||||
exit:
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <openthread/ip6.h>
|
||||
#include <openthread/nat64.h>
|
||||
#include <openthread/udp.h>
|
||||
|
||||
#include "common/encoding.hpp"
|
||||
@@ -231,6 +232,20 @@ public:
|
||||
*/
|
||||
void SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext);
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
/**
|
||||
* This method registers a callback to provide received translated IPv4 datagrams.
|
||||
*
|
||||
* @param[in] aCallback A pointer to a function that is called when a translated IPv4 datagram is received
|
||||
* or `nullptr` to disable the callback.
|
||||
* @param[in] aCallbackContext A pointer to application-specific context.
|
||||
*
|
||||
* @sa SetReceiveDatagramCallback
|
||||
*
|
||||
*/
|
||||
void SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback, void *aCallbackContext);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This method indicates whether or not Thread control traffic is filtered out when delivering IPv6 datagrams
|
||||
* via the callback specified in SetReceiveIp6DatagramCallback().
|
||||
@@ -361,6 +376,11 @@ private:
|
||||
otIp6ReceiveCallback mReceiveIp6DatagramCallback;
|
||||
void * mReceiveIp6DatagramCallbackContext;
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
otNat64ReceiveIp4Callback mReceiveIp4DatagramCallback;
|
||||
void * mReceiveIp4DatagramCallbackContext;
|
||||
#endif
|
||||
|
||||
PriorityQueue mSendQueue;
|
||||
Tasklet mSendQueueTask;
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* 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 implementation for the NAT64 translator.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nat64_translator.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "net/checksum.hpp"
|
||||
#include "net/ip4_types.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nat64 {
|
||||
|
||||
RegisterLogModule("Nat64");
|
||||
|
||||
Translator::Translator(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mMappingExpirer(aInstance, MappingExpirerHandler)
|
||||
{
|
||||
mNat64Prefix.Clear();
|
||||
mIp4Cidr.Clear();
|
||||
mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec);
|
||||
}
|
||||
|
||||
Message *Translator::NewIp4Message(const Message::Settings &aSettings)
|
||||
{
|
||||
Message *message = Get<Ip6::Ip6>().NewMessage(sizeof(Ip6::Header) - sizeof(Ip4::Header), aSettings);
|
||||
|
||||
if (message != nullptr)
|
||||
{
|
||||
message->SetType(Message::kTypeIp4);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
Error Translator::SendMessage(Message &aMessage)
|
||||
{
|
||||
bool freed = false;
|
||||
Error error = kErrorDrop;
|
||||
Result result = TranslateToIp6(aMessage);
|
||||
|
||||
VerifyOrExit(result == kForward);
|
||||
|
||||
error = Get<Ip6::Ip6>().SendRaw(aMessage, !OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS);
|
||||
freed = true;
|
||||
|
||||
exit:
|
||||
if (!freed)
|
||||
{
|
||||
aMessage.Free();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Translator::Result Translator::TranslateFromIp6(Message &aMessage)
|
||||
{
|
||||
Result res = kDrop;
|
||||
Ip6::Header ip6Header;
|
||||
Ip4::Header ip4Header;
|
||||
AddressMapping *mapping = nullptr;
|
||||
|
||||
if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64())
|
||||
{
|
||||
ExitNow(res = kNotTranslated);
|
||||
}
|
||||
|
||||
// ParseFrom will do basic checks for the message, including the message length and IP protocol version.
|
||||
if (ip6Header.ParseFrom(aMessage) != kErrorNone)
|
||||
{
|
||||
LogWarn("outgoing datagram is not a valid IPv6 datagram, drop");
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
if (!ip6Header.GetDestination().MatchesPrefix(mNat64Prefix))
|
||||
{
|
||||
ExitNow(res = kNotTranslated);
|
||||
}
|
||||
|
||||
mapping = FindOrAllocateMapping(ip6Header.GetSource());
|
||||
if (mapping == nullptr)
|
||||
{
|
||||
LogWarn("failed to get a mapping for %s (mapping pool full?)", ip6Header.GetSource().ToString().AsCString());
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
aMessage.RemoveHeader(sizeof(Ip6::Header));
|
||||
|
||||
ip4Header.Clear();
|
||||
ip4Header.InitVersionIhl();
|
||||
ip4Header.SetSource(mapping->mIp4);
|
||||
ip4Header.GetDestination().ExtractFromIp6Address(mNat64Prefix.mLength, ip6Header.GetDestination());
|
||||
ip4Header.SetTtl(ip6Header.GetHopLimit());
|
||||
ip4Header.SetIdentification(0);
|
||||
|
||||
switch (ip6Header.GetNextHeader())
|
||||
{
|
||||
case Ip6::kProtoUdp:
|
||||
ip4Header.SetProtocol(Ip4::kProtoUdp);
|
||||
res = kForward;
|
||||
break;
|
||||
case Ip6::kProtoTcp:
|
||||
ip4Header.SetProtocol(Ip4::kProtoTcp);
|
||||
res = kForward;
|
||||
break;
|
||||
case Ip6::kProtoIcmp6:
|
||||
ip4Header.SetProtocol(Ip4::kProtoIcmp);
|
||||
SuccessOrExit(TranslateIcmp6(aMessage));
|
||||
res = kForward;
|
||||
break;
|
||||
default:
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
// res 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.
|
||||
LogCrit("failed to prepend IPv4 head to translated message");
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
aMessage.SetType(Message::kTypeIp4);
|
||||
|
||||
exit:
|
||||
return res;
|
||||
}
|
||||
|
||||
Translator::Result Translator::TranslateToIp6(Message &aMessage)
|
||||
{
|
||||
Result res = Result::kDrop;
|
||||
Ip6::Header ip6Header;
|
||||
Ip4::Header ip4Header;
|
||||
AddressMapping *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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!mNat64Prefix.IsValidNat64())
|
||||
{
|
||||
LogWarn("incoming message is an IPv4 datagram but no NAT64 prefix configured, drop");
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
if (ip4Header.ParseFrom(aMessage) != kErrorNone)
|
||||
{
|
||||
LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop");
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
mapping = FindMapping(ip4Header.GetDestination());
|
||||
if (mapping == nullptr)
|
||||
{
|
||||
LogWarn("no mapping found for the IPv4 address");
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
aMessage.RemoveHeader(sizeof(Ip4::Header));
|
||||
|
||||
ip6Header.Clear();
|
||||
ip6Header.InitVersionTrafficClassFlow();
|
||||
ip6Header.GetSource().SynthesizeFromIp4Address(mNat64Prefix, ip4Header.GetSource());
|
||||
ip6Header.SetDestination(mapping->mIp6);
|
||||
ip6Header.SetFlow(0);
|
||||
ip6Header.SetHopLimit(ip4Header.GetTtl());
|
||||
|
||||
// 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 (ip4Header.GetProtocol())
|
||||
{
|
||||
case Ip4::kProtoUdp:
|
||||
ip6Header.SetNextHeader(Ip6::kProtoUdp);
|
||||
res = kForward;
|
||||
break;
|
||||
case Ip4::kProtoTcp:
|
||||
ip6Header.SetNextHeader(Ip6::kProtoTcp);
|
||||
res = kForward;
|
||||
break;
|
||||
case Ip4::kProtoIcmp:
|
||||
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
|
||||
SuccessOrExit(TranslateIcmp4(aMessage));
|
||||
res = kForward;
|
||||
break;
|
||||
default:
|
||||
ExitNow(res = kDrop);
|
||||
}
|
||||
|
||||
// res 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);
|
||||
}
|
||||
aMessage.SetType(Message::kTypeIp6);
|
||||
|
||||
exit:
|
||||
return res;
|
||||
}
|
||||
|
||||
Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void)
|
||||
{
|
||||
InfoString string;
|
||||
|
||||
string.Append("%s -> %s", mIp6.ToString().AsCString(), mIp4.ToString().AsCString());
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void Translator::ReleaseMapping(AddressMapping &aMapping)
|
||||
{
|
||||
IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4));
|
||||
mAddressMappingPool.Free(aMapping);
|
||||
LogInfo("mapping removed: %s", aMapping.ToString().AsCString());
|
||||
}
|
||||
|
||||
uint16_t Translator::ReleaseExpiredMappings(void)
|
||||
{
|
||||
uint16_t numRemoved = 0;
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
LinkedList<AddressMapping> idleMappings;
|
||||
|
||||
mActiveAddressMappings.RemoveAllMatching(now, idleMappings);
|
||||
|
||||
for (AddressMapping *idleMapping = idleMappings.Pop(); idleMapping != nullptr; idleMapping = idleMappings.Pop())
|
||||
{
|
||||
numRemoved++;
|
||||
ReleaseMapping(*idleMapping);
|
||||
}
|
||||
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Address &aIp6Addr)
|
||||
{
|
||||
AddressMapping *mapping = nullptr;
|
||||
|
||||
// The address pool will be no larger than the mapping pool, so checking the address pool is enough.
|
||||
if (mIp4AddressPool.IsEmpty())
|
||||
{
|
||||
// ReleaseExpiredMappings returns the number of mappings removed.
|
||||
VerifyOrExit(ReleaseExpiredMappings() > 0);
|
||||
}
|
||||
|
||||
mapping = mAddressMappingPool.Allocate();
|
||||
// We should get a valid item since address pool is no larger than the mapping pool, and the address pool is not
|
||||
// empty.
|
||||
VerifyOrExit(mapping != nullptr);
|
||||
|
||||
mActiveAddressMappings.Push(*mapping);
|
||||
mapping->mIp6 = aIp6Addr;
|
||||
// PopBack must return a valid address since it is not empty.
|
||||
mapping->mIp4 = *mIp4AddressPool.PopBack();
|
||||
mapping->Touch(TimerMilli::GetNow());
|
||||
LogInfo("mapping created: %s", mapping->ToString().AsCString());
|
||||
|
||||
exit:
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Translator::AddressMapping *Translator::FindOrAllocateMapping(const Ip6::Address &aIp6Addr)
|
||||
{
|
||||
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Addr);
|
||||
|
||||
// Exit if we found a valid mapping.
|
||||
VerifyOrExit(mapping == nullptr);
|
||||
|
||||
mapping = AllocateMapping(aIp6Addr);
|
||||
|
||||
exit:
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Translator::AddressMapping *Translator::FindMapping(const Ip4::Address &aIp4Addr)
|
||||
{
|
||||
AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Addr);
|
||||
|
||||
if (mapping != nullptr)
|
||||
{
|
||||
mapping->Touch(TimerMilli::GetNow());
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Error Translator::TranslateIcmp4(Message &aMessage)
|
||||
{
|
||||
Error err = 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));
|
||||
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 reinteprete it as
|
||||
// ICMP6 header and set the message type.
|
||||
SuccessOrExit(err = aMessage.Read(0, icmp6Header));
|
||||
icmp6Header.SetType(Ip6::Icmp::Header::Type::kTypeEchoReply);
|
||||
aMessage.Write(0, icmp6Header);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = kErrorInvalidArgs;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
Error Translator::TranslateIcmp6(Message &aMessage)
|
||||
{
|
||||
Error err = 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));
|
||||
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 reinteprete it as
|
||||
// ICMP6 header and set the message type.
|
||||
SuccessOrExit(err = aMessage.Read(0, icmp4Header));
|
||||
icmp4Header.SetType(Ip4::Icmp::Header::Type::kTypeEchoRequest);
|
||||
aMessage.Write(0, icmp4Header);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = kErrorInvalidArgs;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
|
||||
{
|
||||
Error err = kErrorNone;
|
||||
|
||||
uint32_t numberOfHosts;
|
||||
uint32_t hostIdBegin;
|
||||
|
||||
VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, err = 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?
|
||||
if (aCidr.mLength == 32)
|
||||
{
|
||||
hostIdBegin = 0;
|
||||
numberOfHosts = 1;
|
||||
}
|
||||
else if (aCidr.mLength == 31)
|
||||
{
|
||||
hostIdBegin = 0;
|
||||
numberOfHosts = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
hostIdBegin = 1;
|
||||
numberOfHosts = static_cast<uint32_t>((1 << (Ip4::Address::kSize * 8 - aCidr.mLength)) - 2);
|
||||
}
|
||||
numberOfHosts = OT_MIN(numberOfHosts, kAddressMappingPoolSize);
|
||||
|
||||
mAddressMappingPool.FreeAll();
|
||||
mActiveAddressMappings.Clear();
|
||||
mIp4AddressPool.Clear();
|
||||
|
||||
for (uint32_t i = 0; i < numberOfHosts; i++)
|
||||
{
|
||||
Ip4::Address addr;
|
||||
|
||||
addr.SynthesizeFromCidrAndHost(aCidr, i + hostIdBegin);
|
||||
IgnoreError(mIp4AddressPool.PushBack(addr));
|
||||
}
|
||||
|
||||
LogInfo("IPv4 CIDR for NAT64: %s (actual address pool: %s - %s, %u addresses)", aCidr.ToString().AsCString(),
|
||||
mIp4AddressPool.Front()->ToString().AsCString(), mIp4AddressPool.Back()->ToString().AsCString(),
|
||||
numberOfHosts);
|
||||
mIp4Cidr = aCidr;
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
void Translator::SetNat64Prefix(const Ip6::Prefix &aNat64Prefix)
|
||||
{
|
||||
if (mNat64Prefix != aNat64Prefix)
|
||||
{
|
||||
LogInfo("IPv6 Prefix for NAT64 updated to %s", aNat64Prefix.ToString().AsCString());
|
||||
mNat64Prefix = aNat64Prefix;
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::MappingExpirerHandler(Timer &aTimer)
|
||||
{
|
||||
LogInfo("Released %d expired mappings", aTimer.Get<Translator>().ReleaseExpiredMappings());
|
||||
aTimer.Get<Translator>().mMappingExpirer.Start(kAddressMappingIdleTimeoutMsec);
|
||||
}
|
||||
|
||||
} // namespace Nat64
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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 NAT64_TRANSLATOR_HPP_
|
||||
#define NAT64_TRANSLATOR_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
#include "common/array.hpp"
|
||||
#include "common/linked_list.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/pool.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/ip4_types.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nat64 {
|
||||
|
||||
/**
|
||||
* This class implements the NAT64 translator.
|
||||
*
|
||||
*/
|
||||
class Translator : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t kAddressMappingIdleTimeoutMsec =
|
||||
OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec;
|
||||
static constexpr uint32_t kAddressMappingPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
};
|
||||
|
||||
/**
|
||||
* This constructor initializes the NAT64 translator.
|
||||
*
|
||||
*/
|
||||
explicit Translator(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* This method translates an IPv4 datagram to an IPv6 datagram and sends it via 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 `kErrorNone` is returned.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
*
|
||||
* @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(Message &aMessage);
|
||||
|
||||
/**
|
||||
* 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 datagram to IPv6 datagram. Note the datagram and datagramLength might be adjusted.
|
||||
* Note the message can have 20 bytes reserved before the message to avoid potential copy operations. If the message
|
||||
* is already an IPv6 datagram, `Result::kNotTranslated` will be returned and @p aMessage won't be modified.
|
||||
*
|
||||
* @param[in,out] aMessage the message to be processed.
|
||||
*
|
||||
* @retval kNotTranslated The message is already an IPv6 datagram. @p aMessage is not updated.
|
||||
* @retval kForward The caller should contiue forwarding the datagram.
|
||||
* @retval kDrop The caller should drop the datagram silently.
|
||||
*
|
||||
*/
|
||||
Result TranslateToIp6(Message &message);
|
||||
|
||||
/**
|
||||
* Translates an IPv6 datagram to IPv4 datagram. Note the datagram and datagramLength might be adjusted.
|
||||
* If the message is not targeted to NAT64-mapped address, `Result::kNotTranslated` will be returned and @p aMessage
|
||||
* won't be modified.
|
||||
*
|
||||
* @param[in,out] aMessage the message to be processed.
|
||||
*
|
||||
* @retval kNotTranslated The datagram is not sending to the configured NAT64 prefix.
|
||||
* @retval kForward The caller should contiue forwarding the datagram.
|
||||
* @retval kDrop The caller should drop the datagram silently.
|
||||
*
|
||||
*/
|
||||
Result TranslateFromIp6(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);
|
||||
|
||||
/**
|
||||
* Sets the prefix of NAT64-mapped addresses in the thread network. The address mapping table will not be cleared.
|
||||
* If an empty NAT64 prefix is set, the translator will return kNotTranslated for all IPv6 datagrams and kDrop for
|
||||
* all IPv4 datagrams.
|
||||
*
|
||||
* @param[in] aNat64Prefix The prefix of the NAT64-mapped addresses.
|
||||
*
|
||||
*/
|
||||
void SetNat64Prefix(const Ip6::Prefix &aNat64Prefix);
|
||||
|
||||
private:
|
||||
class AddressMapping : public LinkedListEntry<AddressMapping>
|
||||
{
|
||||
public:
|
||||
friend class LinkedListEntry<AddressMapping>;
|
||||
friend class LinkedList<AddressMapping>;
|
||||
|
||||
typedef String<Ip6::Address::kInfoStringSize + Ip4::Address::kAddressStringSize + 4> InfoString;
|
||||
|
||||
void Touch(TimeMilli aNow) { mExpiry = aNow + kAddressMappingIdleTimeoutMsec; }
|
||||
InfoString ToString(void);
|
||||
|
||||
Ip4::Address mIp4;
|
||||
Ip6::Address mIp6;
|
||||
TimeMilli mExpiry; // The timestamp when this mapping expires, in milliseconds.
|
||||
|
||||
private:
|
||||
bool Matches(const Ip4::Address &aIp4) const { return mIp4 == aIp4; }
|
||||
bool Matches(const Ip6::Address &aIp6) const { return mIp6 == aIp6; }
|
||||
bool Matches(const TimeMilli aNow) const { return mExpiry < aNow; }
|
||||
|
||||
AddressMapping *mNext;
|
||||
};
|
||||
|
||||
Error TranslateIcmp4(Message &aMessage);
|
||||
Error TranslateIcmp6(Message &aMessage);
|
||||
|
||||
void ReleaseMapping(AddressMapping &aMapping);
|
||||
uint16_t ReleaseExpiredMappings(void);
|
||||
AddressMapping *AllocateMapping(const Ip6::Address &aIp6Addr);
|
||||
AddressMapping *FindOrAllocateMapping(const Ip6::Address &aIp6Addr);
|
||||
AddressMapping *FindMapping(const Ip4::Address &aIp4Addr);
|
||||
|
||||
static void MappingExpirerHandler(Timer &aTimer);
|
||||
|
||||
Array<Ip4::Address, kAddressMappingPoolSize> mIp4AddressPool;
|
||||
Pool<AddressMapping, kAddressMappingPoolSize> mAddressMappingPool;
|
||||
LinkedList<AddressMapping> mActiveAddressMappings;
|
||||
|
||||
Ip6::Prefix mNat64Prefix;
|
||||
Ip4::Cidr mIp4Cidr;
|
||||
|
||||
TimerMilli mMappingExpirer;
|
||||
};
|
||||
|
||||
} // namespace Nat64
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
#endif // NAT64_TRANSLATOR_HPP_
|
||||
@@ -76,6 +76,13 @@ if(OT_POSIX_CONFIG_RCP_BUS)
|
||||
)
|
||||
endif()
|
||||
|
||||
set(OT_POSIX_NAT64_CIDR "192.168.64.0/24" CACHE STRING "NAT64 CIDR for OpenThread NAT64")
|
||||
if(OT_POSIX_NAT64_CIDR)
|
||||
target_compile_definitions(ot-posix-config
|
||||
INTERFACE "OPENTHREAD_POSIX_CONFIG_NAT64_CIDR=\"${OT_POSIX_NAT64_CIDR}\""
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT OT_CONFIG)
|
||||
set(OT_CONFIG "openthread-core-posix-config.h" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
@@ -143,6 +143,7 @@ extern int
|
||||
#include <openthread/ip6.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/nat64.h>
|
||||
#include <openthread/netdata.h>
|
||||
#include <openthread/platform/misc.h>
|
||||
|
||||
@@ -152,6 +153,7 @@ extern int
|
||||
|
||||
unsigned int gNetifIndex = 0;
|
||||
char gNetifName[IFNAMSIZ];
|
||||
otIp4Cidr gNat64Cidr;
|
||||
|
||||
const char *otSysGetThreadNetifName(void)
|
||||
{
|
||||
@@ -567,7 +569,7 @@ exit:
|
||||
#if __linux__ && \
|
||||
(OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
|
||||
|
||||
static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
template <size_t N> static otError AddRoute(const uint8_t (&aAddress)[N], uint8_t aPrefixLen, uint32_t aPriority)
|
||||
{
|
||||
constexpr unsigned int kBufSize = 128;
|
||||
struct
|
||||
@@ -576,10 +578,10 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
struct rtmsg msg;
|
||||
char buf[kBufSize];
|
||||
} req{};
|
||||
unsigned char data[sizeof(in6_addr)];
|
||||
char addrBuf[OT_IP6_ADDRESS_STRING_SIZE];
|
||||
unsigned int netifIdx = otSysGetThreadNetifIndex();
|
||||
otError error = OT_ERROR_NONE;
|
||||
unsigned int netifIdx = otSysGetThreadNetifIndex();
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
static_assert(N == sizeof(in6_addr) || N == sizeof(in_addr), "aAddress should be 4 octets or 16 octets");
|
||||
|
||||
VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE);
|
||||
VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE);
|
||||
@@ -591,9 +593,9 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
req.header.nlmsg_pid = 0;
|
||||
req.header.nlmsg_seq = ++sNetlinkSequence;
|
||||
|
||||
req.msg.rtm_family = AF_INET6;
|
||||
req.msg.rtm_family = (N == sizeof(in6_addr) ? AF_INET6 : AF_INET);
|
||||
req.msg.rtm_src_len = 0;
|
||||
req.msg.rtm_dst_len = aPrefix.mLength;
|
||||
req.msg.rtm_dst_len = aPrefixLen;
|
||||
req.msg.rtm_tos = 0;
|
||||
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
req.msg.rtm_type = RTN_UNICAST;
|
||||
@@ -601,9 +603,7 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
req.msg.rtm_protocol = RTPROT_BOOT;
|
||||
req.msg.rtm_flags = 0;
|
||||
|
||||
otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE);
|
||||
inet_pton(AF_INET6, addrBuf, data);
|
||||
AddRtAttr(reinterpret_cast<nlmsghdr *>(&req), sizeof(req), RTA_DST, data, sizeof(data));
|
||||
AddRtAttr(reinterpret_cast<nlmsghdr *>(&req), sizeof(req), RTA_DST, aAddress, sizeof(aAddress));
|
||||
AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority);
|
||||
AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx);
|
||||
|
||||
@@ -665,6 +665,18 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
{
|
||||
return AddRoute(aPrefix.mPrefix.mFields.m8, aPrefix.mLength, aPriority);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
static otError AddIp4Route(const otIp4Cidr &aCidr, uint32_t aPriority)
|
||||
{
|
||||
return AddRoute(aCidr.mAddress.mFields.m8, aCidr.mLength, aPriority);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __linux__ && (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE ||
|
||||
// OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
|
||||
|
||||
@@ -935,21 +947,15 @@ static void processTransmit(otInstance *aInstance)
|
||||
char packet[kMaxIp6Size];
|
||||
otError error = OT_ERROR_NONE;
|
||||
size_t offset = 0;
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
bool isIp4 = false;
|
||||
#endif
|
||||
|
||||
assert(gInstance == aInstance);
|
||||
|
||||
rval = read(sTunFd, packet, sizeof(packet));
|
||||
VerifyOrExit(rval > 0, error = OT_ERROR_FAILED);
|
||||
|
||||
{
|
||||
otMessageSettings settings;
|
||||
|
||||
settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED);
|
||||
settings.mPriority = OT_MESSAGE_PRIORITY_LOW;
|
||||
message = otIp6NewMessage(aInstance, &settings);
|
||||
VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
// BSD tunnel drivers have (for legacy reasons), may have a 4-byte header on them
|
||||
if ((rval >= 4) && (packet[0] == 0) && (packet[1] == 0))
|
||||
@@ -959,6 +965,20 @@ static void processTransmit(otInstance *aInstance)
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
otMessageSettings settings;
|
||||
|
||||
settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED);
|
||||
settings.mPriority = OT_MESSAGE_PRIORITY_LOW;
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
isIp4 = (packet[offset] & 0xf0) == 0x40;
|
||||
message = isIp4 ? otIp4NewMessage(aInstance, &settings) : otIp6NewMessage(aInstance, &settings);
|
||||
#else
|
||||
message = otIp6NewMessage(aInstance, &settings);
|
||||
#endif
|
||||
VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_POSIX_LOG_TUN_PACKETS
|
||||
otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
|
||||
otDumpInfoPlat("", &packet[offset], static_cast<size_t>(rval));
|
||||
@@ -966,7 +986,11 @@ static void processTransmit(otInstance *aInstance)
|
||||
|
||||
SuccessOrExit(error = otMessageAppend(message, &packet[offset], static_cast<uint16_t>(rval)));
|
||||
|
||||
error = otIp6Send(aInstance, message);
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
error = isIp4 ? otNat64Send(aInstance, message) : otIp6Send(aInstance, message);
|
||||
#else
|
||||
error = otIp6Send(aInstance, message);
|
||||
#endif
|
||||
message = nullptr;
|
||||
|
||||
exit:
|
||||
@@ -1134,6 +1158,15 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
|
||||
otLogInfoPlat("[netif] Succeeded to sync netif state with host");
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
if (isUp && gNat64Cidr.mLength > 0)
|
||||
{
|
||||
SuccessOrExit(error = otNat64SetIp4Cidr(gInstance, &gNat64Cidr));
|
||||
AddIp4Route(gNat64Cidr, kExternalRoutePriority);
|
||||
otLogInfoPlat("[netif] Succeeded to enable NAT64");
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
@@ -1755,6 +1788,10 @@ void platformNetifSetUp(void)
|
||||
otIcmp6SetEchoMode(gInstance, OT_ICMP6_ECHO_HANDLER_DISABLED);
|
||||
#endif
|
||||
otIp6SetReceiveCallback(gInstance, processReceive, gInstance);
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
// We can use the same function for IPv6 and translated IPv4 messages.
|
||||
otNat64SetReceiveIp4Callback(gInstance, processReceive, gInstance);
|
||||
#endif
|
||||
otIp6SetAddressCallback(gInstance, processAddressChange, gInstance);
|
||||
#if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED
|
||||
otIp6SetMulticastPromiscuousEnabled(aInstance, true);
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/ip6.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/nat64.h>
|
||||
#include <openthread/openthread-system.h>
|
||||
#include <openthread/platform/time.h>
|
||||
|
||||
@@ -447,6 +448,11 @@ extern char gNetifName[IFNAMSIZ];
|
||||
*/
|
||||
extern unsigned int gNetifIndex;
|
||||
|
||||
/**
|
||||
* The CIDR for NAT64
|
||||
*/
|
||||
extern otIp4Cidr gNat64Cidr;
|
||||
|
||||
/**
|
||||
* This function initializes platform Backbone network.
|
||||
*
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "platform-posix.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <openthread-core-config.h>
|
||||
#include <openthread/border_router.h>
|
||||
@@ -143,6 +144,15 @@ void platformInit(otPlatformConfig *aPlatformConfig)
|
||||
|
||||
gNetifName[0] = '\0';
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
if ((sscanf(OPENTHREAD_POSIX_CONFIG_NAT64_CIDR, "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8 "/%" SCNu8,
|
||||
&gNat64Cidr.mAddress.mFields.m8[0], &gNat64Cidr.mAddress.mFields.m8[1],
|
||||
&gNat64Cidr.mAddress.mFields.m8[2], &gNat64Cidr.mAddress.mFields.m8[3], &gNat64Cidr.mLength)) != 5)
|
||||
{
|
||||
gNat64Cidr.mLength = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
||||
platformNetifInit(aPlatformConfig->mInterfaceName);
|
||||
#endif
|
||||
|
||||
@@ -683,6 +683,27 @@ target_link_libraries(ot-test-multicast-listeners-table
|
||||
|
||||
add_test(NAME ot-test-multicast-listeners-table COMMAND ot-test-multicast-listeners-table)
|
||||
|
||||
add_test(NAME ot-test-nat64 COMMAND ot-test-nat64)
|
||||
|
||||
add_executable(ot-test-nat64
|
||||
test_nat64.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ot-test-nat64
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(ot-test-nat64
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(ot-test-nat64
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_executable(ot-test-ndproxy-table
|
||||
test_ndproxy_table.cpp
|
||||
)
|
||||
|
||||
@@ -141,6 +141,7 @@ check_PROGRAMS += \
|
||||
ot-test-message \
|
||||
ot-test-message-queue \
|
||||
ot-test-multicast-listeners-table \
|
||||
ot-test-nat64 \
|
||||
ot-test-ndproxy-table \
|
||||
ot-test-netif \
|
||||
ot-test-network-data \
|
||||
@@ -313,6 +314,10 @@ ot_test_multicast_listeners_table_LDADD = $(COMMON_LDADD)
|
||||
ot_test_multicast_listeners_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
|
||||
ot_test_multicast_listeners_table_SOURCES = $(COMMON_SOURCES) test_multicast_listeners_table.cpp
|
||||
|
||||
ot_test_nat64_LDADD = $(COMMON_LDADD)
|
||||
ot_test_nat64_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
|
||||
ot_test_nat64_SOURCES = $(COMMON_SOURCES) test_nat64.cpp
|
||||
|
||||
ot_test_network_name_LDADD = $(COMMON_LDADD)
|
||||
ot_test_network_name_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
|
||||
ot_test_network_name_SOURCES = $(COMMON_SOURCES) test_network_name.cpp
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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 "net/nat64_translator.hpp"
|
||||
|
||||
#include "test_platform.h"
|
||||
#include "test_util.hpp"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/message.hpp"
|
||||
#include "net/ip6.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
namespace ot {
|
||||
namespace BorderRouter {
|
||||
|
||||
static ot::Instance *sInstance;
|
||||
|
||||
void DumpMessageInHex(const char *prefix, const uint8_t *aBuf, size_t aBufLen)
|
||||
{
|
||||
// This function dumps all packets the output of this function can be imported to packet analyser for debugging.
|
||||
printf("%s", prefix);
|
||||
for (uint16_t i = 0; i < aBufLen; i++)
|
||||
{
|
||||
printf("%02x", aBuf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool CheckMessage(const Message &aMessage, const uint8_t *aExpectedMessage, size_t aExpectedMessageLen)
|
||||
{
|
||||
uint8_t readMessage[OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH];
|
||||
uint16_t messageLength;
|
||||
bool success = true;
|
||||
|
||||
success = success && (aMessage.GetLength() == aExpectedMessageLen);
|
||||
messageLength = aMessage.ReadBytes(0, readMessage, aMessage.GetLength());
|
||||
success = success && (aExpectedMessageLen == messageLength);
|
||||
success = success && (memcmp(readMessage, aExpectedMessage, aExpectedMessageLen) == 0);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
printf("Expected Message\n");
|
||||
for (uint16_t i = 0; i < aExpectedMessageLen; i++)
|
||||
{
|
||||
printf("%02x%c", aExpectedMessage[i], " \n"[(i & 0xf) == 0xf]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("Actual Message\n");
|
||||
for (uint16_t i = 0; i < messageLength; i++)
|
||||
{
|
||||
printf("%02x%c", readMessage[i], " \n"[(i & 0xf) == 0xf]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void TestCase6To4(const char *aTestName,
|
||||
const uint8_t (&aIp6Message)[N],
|
||||
Nat64::Translator::Result aResult,
|
||||
const uint8_t * aOutMessage,
|
||||
size_t aOutMessageLen)
|
||||
{
|
||||
Message *msg = sInstance->Get<Ip6::Ip6>().NewMessage(0);
|
||||
|
||||
printf("Testing NAT64 6 to 4: %s\n", aTestName);
|
||||
|
||||
VerifyOrQuit(msg != nullptr);
|
||||
SuccessOrQuit(msg->AppendBytes(aIp6Message, N));
|
||||
|
||||
DumpMessageInHex("I ", aIp6Message, N);
|
||||
|
||||
VerifyOrQuit(sInstance->Get<Nat64::Translator>().TranslateFromIp6(*msg) == aResult);
|
||||
|
||||
if (aOutMessage != nullptr)
|
||||
{
|
||||
DumpMessageInHex("O ", aOutMessage, aOutMessageLen);
|
||||
VerifyOrQuit(CheckMessage(*msg, aOutMessage, aOutMessageLen));
|
||||
}
|
||||
|
||||
printf(" ... PASS\n");
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void TestCase4To6(const char *aTestName,
|
||||
const uint8_t (&aIp4Message)[N],
|
||||
Nat64::Translator::Result aResult,
|
||||
const uint8_t * aOutMessage,
|
||||
size_t aOutMessageLen)
|
||||
{
|
||||
Message *msg = sInstance->Get<Ip6::Ip6>().NewMessage(0);
|
||||
|
||||
printf("Testing NAT64 4 to 6: %s\n", aTestName);
|
||||
|
||||
VerifyOrQuit(msg != nullptr);
|
||||
SuccessOrQuit(msg->AppendBytes(aIp4Message, N));
|
||||
|
||||
DumpMessageInHex("I ", aIp4Message, N);
|
||||
|
||||
VerifyOrQuit(sInstance->Get<Nat64::Translator>().TranslateToIp6(*msg) == aResult);
|
||||
|
||||
if (aOutMessage != nullptr)
|
||||
{
|
||||
DumpMessageInHex("O ", aOutMessage, aOutMessageLen);
|
||||
VerifyOrQuit(CheckMessage(*msg, aOutMessage, aOutMessageLen));
|
||||
}
|
||||
|
||||
printf(" ... PASS\n");
|
||||
}
|
||||
|
||||
void TestNat64(void)
|
||||
{
|
||||
Ip6::Prefix nat64prefix;
|
||||
Ip4::Cidr nat64cidr;
|
||||
Ip6::Address ip6Source;
|
||||
Ip6::Address ip6Dest;
|
||||
|
||||
sInstance = testInitInstance();
|
||||
|
||||
{
|
||||
const uint8_t ip6Address[] = {0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const uint8_t ip4Address[] = {192, 168, 123, 1};
|
||||
|
||||
nat64cidr.Set(ip4Address, 32);
|
||||
nat64prefix.Set(ip6Address, 96);
|
||||
SuccessOrQuit(sInstance->Get<Nat64::Translator>().SetIp4Cidr(nat64cidr));
|
||||
sInstance->Get<Nat64::Translator>().SetNat64Prefix(nat64prefix);
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase6To4("good v6 udp datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet, sizeof(kIp4Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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,
|
||||
};
|
||||
|
||||
TestCase4To6("good v4 udp datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet, sizeof(kIp6Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase6To4("good v6 tcp datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet, sizeof(kIp4Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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,
|
||||
};
|
||||
|
||||
TestCase4To6("good v4 tcp datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet, sizeof(kIp6Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase6To4("good v6 icmp ping request datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet,
|
||||
sizeof(kIp4Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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,
|
||||
};
|
||||
|
||||
TestCase4To6("good v4 icmp ping response datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet,
|
||||
sizeof(kIp6Packet));
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase6To4("bad v6 datagram", kIp6Packet, Nat64::Translator::kDrop, nullptr, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase4To6("bad v4 datagram", kIp4Packet, Nat64::Translator::kDrop, nullptr, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// 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};
|
||||
|
||||
TestCase4To6("no v4 mapping", kIp4Packet, Nat64::Translator::kDrop, nullptr, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// fd02::2 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, 0x02, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
172, 16, 243, 197, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x30, 0x61, 0x62, 0x63, 0x64,
|
||||
};
|
||||
|
||||
TestCase6To4("mapping pool exhausted", kIp6Packet, Nat64::Translator::kDrop, nullptr, 0);
|
||||
}
|
||||
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
} // namespace BorderRouter
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
ot::BorderRouter::TestNat64();
|
||||
printf("All tests passed\n");
|
||||
#else // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
printf("NAT64 is not enabled\n");
|
||||
#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user