mirror of
https://github.com/espressif/openthread.git
synced 2026-09-21 00:17:37 +00:00
[posix] install route for the NAT64 prefix on the Thread interface (#13553)
The posix platform installs an IPv4 route for the NAT64 CIDR into the Thread network interface so that translated traffic finds its way back to the translator. The IPv6 side has no equivalent: the NAT64 prefix is published as an external route in the network data, but routes published by the device itself are deliberately not installed in the kernel. As a result the border router host cannot use the NAT64 service it provides itself. Traffic to NAT64-synthesized addresses leaks out the default route, or fails with ENETUNREACH when there is no IPv6 default route. This affects the OpenThread features whose sockets are bound to the unspecified network interface and therefore routed by the host: the DNS client (default `OPENTHREAD_CONFIG_DNS_CLIENT_BIND_UDP_TO_THREAD_NETIF` of 0) and the SNTP client, as well as applications running on the host. It makes the built-in NAT64 diagnostics fail in a way that suggests NAT64 itself is broken: `dns resolve4 <name> <ipv4-server>` times out while `ping` to the same synthesized address succeeds, because the ping sender runs inside the OpenThread stack where network data routing reaches the translator directly (see discussion #9782). Traffic originated by Thread devices is translated in the stack as well and was never affected. Install a route for the translator's NAT64 prefix into the Thread network interface while the translator is active, mirroring the existing NAT64 IPv4 CIDR route handling, and track which routes are installed so that repeated state change notifications and host netif link transitions do not add or delete a route twice. `Nat64::Translator::SetNat64Prefix()` now signals `kEventNat64TranslatorStateChanged` (like `SetIp4Cidr()` does), so a prefix change while the translator is already active is reported to the platform. A new public API `otNat64GetIp6Prefix()` is added to retrieve the IPv6 prefix configured in the NAT64 translator, mirroring `otNat64GetCidr()`. Also fix the guards of `AddIp4Route()`/`DeleteIp4Route()` and of the `isIp4` local in `processTransmit()`, which required `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` while their users only require `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE`, breaking the build of a NAT64 translator without border routing.
This commit is contained in:
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (619)
|
||||
#define OPENTHREAD_API_VERSION (620)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -410,6 +410,19 @@ void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callba
|
||||
*/
|
||||
otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr);
|
||||
|
||||
/**
|
||||
* Gets the IPv6 prefix 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] aPrefix A pointer to an `otIp6Prefix` to output the prefix.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The prefix was successfully filled.
|
||||
* @retval OT_ERROR_NOT_FOUND The translator is not configured with an IPv6 prefix.
|
||||
*/
|
||||
otError otNat64GetIp6Prefix(otInstance *aInstance, otIp6Prefix *aPrefix);
|
||||
|
||||
/**
|
||||
* Test if two IPv4 addresses are the same.
|
||||
*
|
||||
|
||||
@@ -100,6 +100,13 @@ otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr)
|
||||
return AsCoreType(aInstance).Get<Nat64::Translator>().GetIp4Cidr(AsCoreType(aCidr));
|
||||
}
|
||||
|
||||
otError otNat64GetIp6Prefix(otInstance *aInstance, otIp6Prefix *aPrefix)
|
||||
{
|
||||
AssertPointerIsNotNull(aPrefix);
|
||||
|
||||
return AsCoreType(aInstance).Get<Nat64::Translator>().GetNat64Prefix(AsCoreType(aPrefix));
|
||||
}
|
||||
|
||||
otNat64State otNat64GetTranslatorState(otInstance *aInstance)
|
||||
{
|
||||
return MapEnum(AsCoreType(aInstance).Get<Nat64::Translator>().GetState());
|
||||
|
||||
@@ -760,6 +760,11 @@ void Translator::SetNat64Prefix(const Ip6::Prefix &aNat64Prefix)
|
||||
mNat64Prefix = aNat64Prefix;
|
||||
UpdateState();
|
||||
|
||||
// `UpdateState()` only signals when the state itself changes. Signal here
|
||||
// as well (similar to `SetIp4Cidr()`), so that a prefix change while the
|
||||
// translator is already active gets reported too.
|
||||
Get<Notifier>().Signal(kEventNat64TranslatorStateChanged);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
+126
-36
@@ -903,7 +903,8 @@ exit:
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
|
||||
#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE || \
|
||||
OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
|
||||
{
|
||||
return AddRoute(aPrefix.mPrefix.mFields.m8, aPrefix.mLength, aPriority);
|
||||
@@ -913,7 +914,7 @@ static otError DeleteRoute(const otIp6Prefix &aPrefix)
|
||||
{
|
||||
return DeleteRoute(aPrefix.mPrefix.mFields.m8, aPrefix.mLength);
|
||||
}
|
||||
#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
|
||||
static bool HasAddedOmrRoute(const otIp6Prefix &aOmrPrefix)
|
||||
@@ -1091,7 +1092,7 @@ exit:
|
||||
}
|
||||
#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
static otError AddIp4Route(const otIp4Cidr &aIp4Cidr, uint32_t aPriority)
|
||||
{
|
||||
return AddRoute(aIp4Cidr.mAddress.mFields.m8, aIp4Cidr.mLength, aPriority);
|
||||
@@ -1118,7 +1119,10 @@ static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsA
|
||||
|
||||
#if defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
static otIp4Cidr sActiveNat64Cidr;
|
||||
static otIp4Cidr sActiveNat64Cidr;
|
||||
static otIp6Prefix sActiveNat64Ip6Prefix;
|
||||
static bool sIsNat64Ip4RouteAdded = false;
|
||||
static bool sIsNat64Ip6RouteAdded = false;
|
||||
|
||||
static constexpr uint32_t kNat64RoutePriority = 100; // Priority for route to NAT64 CIDR, 100 means a high priority.
|
||||
|
||||
@@ -1127,6 +1131,7 @@ static bool isSameIp4Cidr(const otIp4Cidr &aCidr1, const otIp4Cidr &aCidr2)
|
||||
bool res = true;
|
||||
|
||||
VerifyOrExit(aCidr1.mLength == aCidr2.mLength, res = false);
|
||||
VerifyOrExit(aCidr1.mLength > 0);
|
||||
|
||||
// The higher (32 - length) bits must be the same, host bits are ignored.
|
||||
VerifyOrExit(((ntohl(aCidr1.mAddress.mFields.m32) ^ ntohl(aCidr2.mAddress.mFields.m32)) >> (32 - aCidr1.mLength)) ==
|
||||
@@ -1137,50 +1142,127 @@ exit:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void addNat64Routes(void)
|
||||
{
|
||||
otError error;
|
||||
|
||||
if (!sIsNat64Ip4RouteAdded && sActiveNat64Cidr.mLength > 0)
|
||||
{
|
||||
if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to add route for NAT64 CIDR: %s", otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
sIsNat64Ip4RouteAdded = true;
|
||||
LogInfo("Added route for NAT64 CIDR");
|
||||
}
|
||||
}
|
||||
|
||||
// Route the NAT64 prefix into the Thread network interface, so that traffic
|
||||
// originated by the host to NAT64-synthesized addresses reaches the translator.
|
||||
if (!sIsNat64Ip6RouteAdded && sActiveNat64Ip6Prefix.mLength > 0)
|
||||
{
|
||||
if ((error = AddRoute(sActiveNat64Ip6Prefix, kNat64RoutePriority)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to add route for NAT64 prefix: %s", otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
sIsNat64Ip6RouteAdded = true;
|
||||
LogInfo("Added route for NAT64 prefix");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteNat64Ip4Route(void)
|
||||
{
|
||||
otError error;
|
||||
|
||||
VerifyOrExit(sIsNat64Ip4RouteAdded);
|
||||
|
||||
if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to delete route for NAT64 CIDR: %s", otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogInfo("Deleted route for NAT64 CIDR");
|
||||
}
|
||||
|
||||
sIsNat64Ip4RouteAdded = false;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
static void deleteNat64Ip6Route(void)
|
||||
{
|
||||
otError error;
|
||||
|
||||
VerifyOrExit(sIsNat64Ip6RouteAdded);
|
||||
|
||||
if ((error = DeleteRoute(sActiveNat64Ip6Prefix)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to delete route for NAT64 prefix: %s", otThreadErrorToString(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogInfo("Deleted route for NAT64 prefix");
|
||||
}
|
||||
|
||||
sIsNat64Ip6RouteAdded = false;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
static void processNat64StateChange(void)
|
||||
{
|
||||
otIp4Cidr translatorCidr;
|
||||
otError error = OT_ERROR_NONE;
|
||||
otIp4Cidr translatorCidr;
|
||||
otIp6Prefix translatorIp6Prefix;
|
||||
|
||||
// Skip if NAT64 translator has not been configured with a CIDR.
|
||||
SuccessOrExit(otNat64GetCidr(gInstance, &translatorCidr));
|
||||
if (otNat64GetCidr(gInstance, &translatorCidr) != OT_ERROR_NONE)
|
||||
{
|
||||
memset(&translatorCidr, 0, sizeof(translatorCidr));
|
||||
}
|
||||
|
||||
if (!isSameIp4Cidr(translatorCidr, sActiveNat64Cidr)) // Someone sets a new CIDR for NAT64.
|
||||
{
|
||||
char cidrString[OT_IP4_CIDR_STRING_SIZE];
|
||||
|
||||
if (sActiveNat64Cidr.mLength != 0)
|
||||
{
|
||||
if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
|
||||
}
|
||||
}
|
||||
deleteNat64Ip4Route(); // Delete the route of the previous CIDR, if any.
|
||||
sActiveNat64Cidr = translatorCidr;
|
||||
|
||||
otIp4CidrToString(&translatorCidr, cidrString, sizeof(cidrString));
|
||||
otIp4CidrToString(&sActiveNat64Cidr, cidrString, sizeof(cidrString));
|
||||
LogInfo("NAT64 CIDR updated to %s.", cidrString);
|
||||
}
|
||||
|
||||
if (otNat64GetIp6Prefix(gInstance, &translatorIp6Prefix) != OT_ERROR_NONE)
|
||||
{
|
||||
memset(&translatorIp6Prefix, 0, sizeof(translatorIp6Prefix));
|
||||
}
|
||||
|
||||
if (!otIp6ArePrefixesEqual(&translatorIp6Prefix, &sActiveNat64Ip6Prefix)) // The NAT64 prefix changed.
|
||||
{
|
||||
char prefixString[OT_IP6_PREFIX_STRING_SIZE];
|
||||
|
||||
deleteNat64Ip6Route(); // Delete the route of the previous prefix, if any.
|
||||
sActiveNat64Ip6Prefix = translatorIp6Prefix;
|
||||
|
||||
otIp6PrefixToString(&sActiveNat64Ip6Prefix, prefixString, sizeof(prefixString));
|
||||
LogInfo("NAT64 prefix updated to %s.", prefixString);
|
||||
}
|
||||
|
||||
if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
|
||||
{
|
||||
if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
|
||||
}
|
||||
LogInfo("Adding route for NAT64");
|
||||
addNat64Routes();
|
||||
}
|
||||
else if (sActiveNat64Cidr.mLength > 0) // Translator is not active.
|
||||
else // Translator is not active.
|
||||
{
|
||||
if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
|
||||
{
|
||||
LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
|
||||
}
|
||||
LogInfo("Deleting route for NAT64");
|
||||
deleteNat64Ip4Route();
|
||||
deleteNat64Ip6Route();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
#endif // defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
|
||||
@@ -1373,7 +1455,7 @@ 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
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
bool isIp4 = false;
|
||||
#endif
|
||||
|
||||
@@ -1606,15 +1688,23 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
|
||||
LogInfo("Succeeded to sync netif state with host");
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
if (isUp && otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
|
||||
#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
|
||||
if (isUp)
|
||||
{
|
||||
// Recover NAT64 route.
|
||||
if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
|
||||
if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
|
||||
{
|
||||
LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
|
||||
// Recover the NAT64 routes. This is a no-op for routes that are
|
||||
// already installed.
|
||||
addNat64Routes();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The kernel removes the routes of an interface that goes down, so
|
||||
// track them as no longer installed without issuing netlink requests.
|
||||
sIsNat64Ip4RouteAdded = false;
|
||||
sIsNat64Ip6RouteAdded = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
|
||||
Reference in New Issue
Block a user