From 2e614338252897bfbfc9697953b94754e6c8daf5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 29 Mar 2021 16:20:42 -0700 Subject: [PATCH] [ip6-address] add `SetFromTranslatedIp4Address()` for NAT64 (#6353) This commit adds `Ip6::Address::SetFromTranslatedIp4Address()` method performing NAT64 address translation from an IPv4 address and a NAT64 prefix to construct a corresponding IPv6 address as specified in RFC 6052. This commit also updates the unit test `test_ip6_address.cpp` to cover the behavior of the newly added method. --- src/core/net/ip6_address.cpp | 49 ++++++++++++++++++++++++++++++++ src/core/net/ip6_address.hpp | 14 ++++++++++ tests/unit/test_ip_address.cpp | 51 ++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 9a15db546..f7063eca4 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -453,6 +453,55 @@ bool Address::MatchesFilter(TypeFilter aFilter) const return matches; } +void Address::SetFromTranslatedIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address) +{ + // The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added + // after the prefix, skipping over the bits 64 to 71 (byte at `kSkipIndex`) + // which must be set to zero. The suffix is set to zero (per RFC 6502). + // + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------| + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |32| prefix |v4(32) | u | suffix | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |40| prefix |v4(24) | u |(8)| suffix | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |48| prefix |v4(16) | u | (16) | suffix | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |56| prefix |(8)| u | v4(24) | suffix | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |64| prefix | u | v4(32) | suffix | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |96| prefix | v4(32) | + // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + enum : uint8_t + { + kSkipIndex = 8, + }; + + uint8_t prefixLen = aPrefix.GetLength(); + uint8_t ip6Index; + + OT_ASSERT((prefixLen == 32) || (prefixLen == 40) || (prefixLen == 48) || (prefixLen == 56) || (prefixLen == 64) || + (prefixLen == 96)); + + Clear(); + SetPrefix(aPrefix); + + ip6Index = prefixLen / CHAR_BIT; + + for (uint8_t i = 0; i < Ip4::Address::kSize; i++) + { + if (ip6Index == kSkipIndex) + { + ip6Index++; + } + + mFields.m8[ip6Index++] = aIp4Address.GetBytes()[i]; + } +} + Error Address::FromString(const char *aString) { enum : uint8_t diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 69835fb42..c775b17f4 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -45,6 +45,7 @@ #include "common/equatable.hpp" #include "common/string.hpp" #include "mac/mac_types.hpp" +#include "net/ip4_address.hpp" using ot::Encoding::BigEndian::HostSwap16; @@ -880,6 +881,19 @@ public: */ bool MatchesFilter(TypeFilter aFilter) const; + /** + * This method sets the IPv6 address by performing NAT64 address translation from a given IPv4 address as specified + * in RFC 6052. + * + * The NAT64 @p aPrefix MUST have one of the following lengths: 32, 40, 48, 56, 64, or 96, otherwise the behavior + * of this method is undefined. + * + * @param[in] aPrefix The prefix to use for IPv4/IPv6 translation. + * @param[in] aIp4Address The IPv4 address to translate to IPv6. + * + */ + void SetFromTranslatedIp4Address(const Prefix &aPrefix, const Ip4::Address &aIp4Address); + /** * This method converts an IPv6 address string to binary. * diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp index 8f38349c4..b680a0feb 100644 --- a/tests/unit/test_ip_address.cpp +++ b/tests/unit/test_ip_address.cpp @@ -359,6 +359,56 @@ void TestIp6Prefix(void) } } +void TestIp4Ip6Translation(void) +{ + struct TestCase + { + const char *mPrefix; // NAT64 prefix + uint8_t mLength; // Prefix length in bits + const char *mIp6Address; // Expected IPv6 address (with embedded IPv4 "192.0.2.33"). + }; + + // The test cases are from RFC 6502 - section 2.4 + + const TestCase kTestCases[] = { + {"2001:db8::", 32, "2001:db8:c000:221::"}, + {"2001:db8:100::", 40, "2001:db8:1c0:2:21::"}, + {"2001:db8:122::", 48, "2001:db8:122:c000:2:2100::"}, + {"2001:db8:122:300::", 56, "2001:db8:122:3c0:0:221::"}, + {"2001:db8:122:344::", 64, "2001:db8:122:344:c0:2:2100::"}, + {"2001:db8:122:344::", 96, "2001:db8:122:344::192.0.2.33"}, + {"64:ff9b::", 96, "64:ff9b::192.0.2.33"}, + }; + + const uint8_t kIp4Address[] = {192, 0, 2, 33}; + + ot::Ip4::Address ip4Address; + + printf("\nTestIp4Ip6Translation()\n"); + + ip4Address.SetBytes(kIp4Address); + + for (const TestCase &testCase : kTestCases) + { + ot::Ip6::Prefix prefix; + ot::Ip6::Address address; + ot::Ip6::Address expectedAddress; + + SuccessOrQuit(address.FromString(testCase.mPrefix), "Ip6::FromString() failed"); + prefix.Set(address.GetBytes(), testCase.mLength); + + SuccessOrQuit(expectedAddress.FromString(testCase.mIp6Address), "Ip6::FromString() failed"); + + address.SetFromTranslatedIp4Address(prefix, ip4Address); + + printf("Prefix: %-26s IPv4Addr: %-12s Ipv6Address: %-36s Expected: %s (%s)\n", prefix.ToString().AsCString(), + ip4Address.ToString().AsCString(), address.ToString().AsCString(), testCase.mIp6Address, + expectedAddress.ToString().AsCString()); + + VerifyOrQuit(address == expectedAddress, "Ip6::SetFromTranslatedIp4Address() failed"); + } +} + void TestIp6Header(void) { ot::Ip6::Header header; @@ -420,6 +470,7 @@ int main(void) TestIp4AddressFromString(); TestIp6AddressFromString(); TestIp6Prefix(); + TestIp4Ip6Translation(); TestIp6Header(); printf("All tests passed\n"); return 0;