[ip6] add method for creating/parsing IPv4-mapped IPv6 address (#9799)

The IPv4-mapped IPv6 address is useful in certain platform/public
APIs where we need to support both IPv6 or IPv4 address. We can
use IPv4-mapped IPv6 address to represent an IPv4 address. This
commit adds simple helper functions to validate, create and
parse such addresses.
This commit is contained in:
Abtin Keshavarzian
2024-01-25 15:14:58 -08:00
committed by GitHub
parent 81106dff88
commit dacdc7d36f
8 changed files with 129 additions and 1 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (390)
#define OPENTHREAD_API_VERSION (391)
/**
* @addtogroup api-instance
+24
View File
@@ -416,6 +416,30 @@ bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond
*/
void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address);
/**
* Extracts the IPv4 address from a given IPv4-mapped IPv6 address.
*
* An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to ones, and the remaining,
* least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing `192.0.2.128`.
*
* @param[in] aIp6Address An IPv6 address to extract IPv4 from.
* @param[out] aIp4Address An IPv4 address to output the extracted address.
*
* @retval OT_ERROR_NONE Extracted the IPv4 address successfully. @p aIp4Address is updated.
* @retval OT_ERROR_PARSE The @p aIp6Address does not follow the IPv4-mapped IPv6 address format.
*
*/
otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address);
/**
* Converts a given IP4 address to an IPv6 address following the IPv4-mapped IPv6 address format.
*
* @param[in] aIp4Address An IPv4 address to convert.
* @param[out] aIp6Address An IPv6 address to set.
*
*/
void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address);
#define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL
/**
+10
View File
@@ -140,6 +140,16 @@ void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6A
AsCoreType(aIp4Address).ExtractFromIp6Address(aPrefixLength, AsCoreType(aIp6Address));
}
otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address)
{
return AsCoreType(aIp4Address).ExtractFromIp4MappedIp6Address(AsCoreType(aIp6Address));
}
void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address)
{
AsCoreType(aIp6Address).SetToIp4Mapped(AsCoreType(aIp4Address));
}
otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress)
{
AssertPointerIsNotNull(aString);
+11
View File
@@ -66,6 +66,17 @@ exit:
return error;
}
Error Address::ExtractFromIp4MappedIp6Address(const Ip6::Address &aIp6Address)
{
Error error = kErrorNone;
VerifyOrExit(aIp6Address.IsIp4Mapped(), error = kErrorParse);
SetBytes(&aIp6Address.GetBytes()[12]);
exit:
return error;
}
void Address::ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &aIp6Address)
{
// The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added
+11
View File
@@ -122,6 +122,17 @@ public:
*/
void SetBytes(const uint8_t *aBuffer) { memcpy(mFields.m8, aBuffer, kSize); }
/**
* Sets the IPv4 address from a given IPv4-mapped IPv6 address.
*
* @param[in] aIp6Address An IPv6 address.
*
* @retval kErrorNone Set the IPv4 address successfully.
* @retval kErrorPase The @p aIp6Address does not follow the IPv4-mapped IPv6 address format.
*
*/
Error ExtractFromIp4MappedIp6Address(const Ip6::Address &aIp6Address);
/**
* Sets the IPv4 address by performing NAT64 address translation from a given IPv6 address as specified
* in RFC 6052.
+12
View File
@@ -388,6 +388,18 @@ bool Address::IsRealmLocalAllMplForwarders(void) const { return (*this == GetRea
void Address::SetToRealmLocalAllMplForwarders(void) { *this = GetRealmLocalAllMplForwarders(); }
bool Address::IsIp4Mapped(void) const
{
return (mFields.m32[0] == 0) && (mFields.m32[1] == 0) && (mFields.m32[2] == BigEndian::HostSwap32(0xffff));
}
void Address::SetToIp4Mapped(const Ip4::Address &aIp4Address)
{
Clear();
mFields.m16[5] = 0xffff;
memcpy(&mFields.m8[12], aIp4Address.GetBytes(), sizeof(Ip4::Address));
}
bool Address::MatchesPrefix(const Prefix &aPrefix) const
{
return Prefix::MatchLength(mFields.m8, aPrefix.GetBytes(), aPrefix.GetBytesSize()) >= aPrefix.GetLength();
+21
View File
@@ -808,6 +808,27 @@ public:
SetToLocator(aNetworkPrefix, aAloc16);
}
/**
* Indicates whether or not the IPv6 address follows the IPv4-mapped format.
*
* An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to ones, and the
* remaining, least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing
* `192.0.2.128` IPv4 address.
*
* @retval TRUE If the IPv6 address follows the IPv4-mapped format.
* @retval FALSE If the IPv6 address does not follow the IPv4-mapped format.
*
*/
bool IsIp4Mapped(void) const;
/**
* Sets the IPv6 address to follow the IPv4-mapped IPv6 address for a given IPv4 address.
*
* @param[in] aIp4Address An IPv4 address.
*
*/
void SetToIp4Mapped(const Ip4::Address &aIp4Address);
/**
* Returns the Network Prefix of the IPv6 address (most significant 64 bits of the address).
*
+39
View File
@@ -758,6 +758,44 @@ void TestIp6PrefixTidy(void)
}
}
void TestIp4MappedIp6Address(void)
{
const uint8_t kIp4Address[] = {192, 0, 2, 33};
const char *kInvalidIp4MappedFormats[] = {
"8000::ffff:192.0.2.23", "0:400::ffff:192.0.2.23", "0:0:1::ffff:192.0.2.23", "0:0:0:4:0:ffff:192.0.2.23",
"0:0:0:0:1:ffff:192.0.2.23", "::fffe:192.0.2.23", "::efff:192.0.2.23",
};
Ip4::Address expectedIp4Address;
Ip4::Address ip4Address;
Ip6::Address expectedIp6Address;
Ip6::Address ip6Address;
printf("\nTestIp4MappedIp6Address()\n");
expectedIp4Address.SetBytes(kIp4Address);
SuccessOrQuit(expectedIp6Address.FromString("::ffff:192.0.2.33"));
ip6Address.SetToIp4Mapped(expectedIp4Address);
printf("IPv4-mapped IPv6 address: %s\n", ip6Address.ToString().AsCString());
VerifyOrQuit(ip6Address.IsIp4Mapped());
VerifyOrQuit(ip6Address == expectedIp6Address);
SuccessOrQuit(ip4Address.ExtractFromIp4MappedIp6Address(ip6Address));
VerifyOrQuit(ip4Address == expectedIp4Address);
for (const char *invalidIp4MappedAddr : kInvalidIp4MappedFormats)
{
SuccessOrQuit(ip6Address.FromString(invalidIp4MappedAddr));
printf("Invalid IPv4-mapped IPv6 address: %s -> %s\n", invalidIp4MappedAddr, ip6Address.ToString().AsCString());
VerifyOrQuit(!ip6Address.IsIp4Mapped());
VerifyOrQuit(ip4Address.ExtractFromIp4MappedIp6Address(ip6Address) != kErrorNone);
}
}
void TestIp4Ip6Translation(void)
{
struct TestCase
@@ -884,6 +922,7 @@ int main(void)
ot::TestIp6PrefixFromString();
ot::TestIp6Prefix();
ot::TestIp6PrefixTidy();
ot::TestIp4MappedIp6Address();
ot::TestIp4Ip6Translation();
ot::TestIp4Cidr();
ot::TestIp4CidrFromString();