[net] add Ip4::Address class (#6334)

This commit adds IPv4 address definitions in OpenThread:
- Adds `Ip4::Address` class.
- Adds methods to convert IPv4 address to/from string.
- Updates the `Ip6::Address::FromString()` to use the IPv4
  `FromString()` for the case where the IPv6 address contains
  an embedded IPv4 address.
- Renames unit test to `test_ip_address` and updates it to
  cover the behavior of newly added IPv4 definitions in addition
  to IPv6 related tests.
This commit is contained in:
Abtin Keshavarzian
2021-03-26 15:56:31 -07:00
committed by GitHub
parent b0e1712d3b
commit a6be70f296
11 changed files with 279 additions and 59 deletions
+1
View File
@@ -269,6 +269,7 @@ LOCAL_SRC_FILES := \
src/core/net/dns_types.cpp \
src/core/net/dnssd_server.cpp \
src/core/net/icmp6.cpp \
src/core/net/ip4_address.cpp \
src/core/net/ip6.cpp \
src/core/net/ip6_address.cpp \
src/core/net/ip6_filter.cpp \
+2
View File
@@ -492,6 +492,8 @@ openthread_core_files = [
"net/dnssd_server.hpp",
"net/icmp6.cpp",
"net/icmp6.hpp",
"net/ip4_address.cpp",
"net/ip4_address.hpp",
"net/ip6.cpp",
"net/ip6.hpp",
"net/ip6_address.cpp",
+1
View File
@@ -145,6 +145,7 @@ set(COMMON_SOURCES
net/dns_types.cpp
net/dnssd_server.cpp
net/icmp6.cpp
net/ip4_address.cpp
net/ip6.cpp
net/ip6_address.cpp
net/ip6_filter.cpp
+2
View File
@@ -222,6 +222,7 @@ SOURCES_COMMON = \
net/dns_types.cpp \
net/dnssd_server.cpp \
net/icmp6.cpp \
net/ip4_address.cpp \
net/ip6.cpp \
net/ip6_address.cpp \
net/ip6_filter.cpp \
@@ -476,6 +477,7 @@ HEADERS_COMMON = \
net/dns_types.hpp \
net/dnssd_server.hpp \
net/icmp6.hpp \
net/ip4_address.hpp \
net/ip6.hpp \
net/ip6_address.hpp \
net/ip6_filter.hpp \
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2021, 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 implements IPv4 address related functionality.
*/
#include "ip4_address.hpp"
#include "common/code_utils.hpp"
#include "common/numeric_limits.hpp"
namespace ot {
namespace Ip4 {
Error Address::FromString(const char *aString)
{
enum : char
{
kSeperatorChar = '.',
kNullChar = '\0',
};
Error error = kErrorParse;
for (uint8_t index = 0;; index++)
{
uint16_t value = 0;
uint8_t hasFirstDigit = false;
for (char digitChar = *aString;; ++aString, digitChar = *aString)
{
if ((digitChar < '0') || (digitChar > '9'))
{
break;
}
value = static_cast<uint16_t>((value * 10) + static_cast<uint8_t>(digitChar - '0'));
VerifyOrExit(value <= NumericLimits<uint8_t>::Max());
hasFirstDigit = true;
}
VerifyOrExit(hasFirstDigit);
mBytes[index] = static_cast<uint8_t>(value);
if (index == sizeof(Address) - 1)
{
break;
}
VerifyOrExit(*aString == kSeperatorChar);
aString++;
}
VerifyOrExit(*aString == kNullChar);
error = kErrorNone;
exit:
return error;
}
Address::InfoString Address::ToString(void) const
{
return InfoString("%d.%d.%d.%d", mBytes[0], mBytes[1], mBytes[2], mBytes[3]);
}
} // namespace Ip4
} // namespace ot
+115
View File
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, 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 IPv4 addresses.
*/
#ifndef IP4_ADDRESS_HPP_
#define IP4_ADDRESS_HPP_
#include "openthread-core-config.h"
#include "common/clearable.hpp"
#include "common/equatable.hpp"
#include "common/error.hpp"
#include "common/string.hpp"
namespace ot {
namespace Ip4 {
/**
* This class represents an IPv4 address.
*
*/
OT_TOOL_PACKED_BEGIN
class Address : public Equatable<Address>, public Clearable<Address>
{
public:
enum : uint8_t
{
kSize = 4, ///< Size of an IPv4 Address (in bytes).
kAddressStringSize = 17, ///< String size used by `ToString()`.
};
/**
* This type defines the fixed-length `String` object returned from `ToString()`.
*
*/
typedef String<kAddressStringSize> InfoString;
/**
* This method gets the IPv4 address as a pointer to a byte array.
*
* @returns A pointer to a byte array containing the IPv4 address.
*
*/
const uint8_t *GetBytes(void) const { return mBytes; }
/**
* This method sets the IPv4 address from a given byte array.
*
* @param[in] aBuffer Pointer to an array containing the IPv4 address. `kSize` bytes from the buffer
* are copied to form the IPv4 address.
*
*/
void SetBytes(const uint8_t *aBuffer) { memcpy(mBytes, aBuffer, kSize); }
/**
* This method parses an IPv4 address string.
*
* The string MUST follow the quad-dotted notation of four decimal values (ranging from 0 to 255 each). For
* example, "127.0.0.1"
*
* @param[in] aString A pointer to the null-terminated string.
*
* @retval kErrorNone Successfully parsed the IPv4 address string.
* @retval kErrorParse Failed to parse the IPv4 address string.
*
*/
Error FromString(const char *aString);
/**
* This method converts the IPv4 address to a string.
*
* The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1").
*
* @returns An `InfoString` representing the IPv4 address.
*
*/
InfoString ToString(void) const;
private:
uint8_t mBytes[kSize];
} OT_TOOL_PACKED_END;
} // namespace Ip4
} // namespace ot
#endif // IP4_ADDRESS_HPP_
+5 -34
View File
@@ -39,6 +39,7 @@
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/random.hpp"
#include "net/ip4_address.hpp"
#include "net/netif.hpp"
using ot::Encoding::BigEndian::HostSwap32;
@@ -509,7 +510,7 @@ Error Address::FromString(const char *aBuf)
hasIp4 = true;
// Do not count bytes of the embedded IPv4 address.
endp -= kIp4AddressSize;
endp -= Ip4::Address::kSize;
VerifyOrExit(dst <= endp, error = kErrorParse);
@@ -539,40 +540,10 @@ Error Address::FromString(const char *aBuf)
if (hasIp4)
{
val = 0;
Ip4::Address ip4Addr;
// Reset the start and end pointers.
dst = reinterpret_cast<uint8_t *>(mFields.m8 + 12);
endp = reinterpret_cast<uint8_t *>(mFields.m8 + 15);
for (;;)
{
ch = *colonc++;
if (ch == '.' || ch == '\0' || ch == ' ')
{
VerifyOrExit(dst <= endp, error = kErrorParse);
*dst++ = static_cast<uint8_t>(val);
val = 0;
if (ch == '\0' || ch == ' ')
{
// Check if embedded IPv4 address had exactly four parts.
VerifyOrExit(dst == endp + 1, error = kErrorParse);
break;
}
}
else
{
VerifyOrExit('0' <= ch && ch <= '9', error = kErrorParse);
val = (10 * val) + (ch & 0xf);
// Single part of IPv4 address has to fit in one byte.
VerifyOrExit(val <= 0xff, error = kErrorParse);
}
}
SuccessOrExit(error = ip4Addr.FromString(colonc));
memcpy(mFields.m8 + 12, ip4Addr.GetBytes(), Ip4::Address::kSize);
}
exit:
+3 -5
View File
@@ -38,6 +38,8 @@
#include <stdint.h>
#include <openthread/ip6.h>
#include "common/clearable.hpp"
#include "common/encoding.hpp"
#include "common/equatable.hpp"
@@ -906,10 +908,7 @@ public:
* @retval false The IPv6 address is larger than or equal to @p aOther.
*
*/
bool operator<(const Ip6::Address &aOther) const
{
return memcmp(mFields.m8, aOther.mFields.m8, sizeof(Ip6::Address)) < 0;
}
bool operator<(const Address &aOther) const { return memcmp(mFields.m8, aOther.mFields.m8, sizeof(Address)) < 0; }
private:
void SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength);
@@ -923,7 +922,6 @@ private:
enum
{
kIp4AddressSize = 4, ///< Size of the IPv4 address.
kMulticastNetworkPrefixLengthOffset = 3, ///< Prefix-Based Multicast Address (RFC3306).
kMulticastNetworkPrefixOffset = 4, ///< Prefix-Based Multicast Address (RFC3306).
};
+7 -7
View File
@@ -306,26 +306,26 @@ target_link_libraries(test-hmac-sha256
add_test(NAME test-hmac-sha256 COMMAND test-hmac-sha256)
add_executable(test-ip6-address
test_ip6_address.cpp
add_executable(test-ip-address
test_ip_address.cpp
)
target_include_directories(test-ip6-address
target_include_directories(test-ip-address
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(test-ip6-address
target_compile_options(test-ip-address
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(test-ip6-address
target_link_libraries(test-ip-address
PRIVATE
${COMMON_LIBS}
)
add_test(NAME test-ip6-address COMMAND test-ip6-address)
add_test(NAME test-ip-address COMMAND test-ip-address)
add_executable(test-link-quality
test_link_quality.cpp
@@ -720,7 +720,7 @@ set_target_properties(
test-heap
test-hkdf-sha256
test-hmac-sha256
test-ip6-address
test-ip-address
test-link-quality
test-linked-list
test-lookup-table
+3 -3
View File
@@ -117,7 +117,7 @@ check_PROGRAMS += \
test-heap \
test-hkdf-sha256 \
test-hmac-sha256 \
test-ip6-address \
test-ip-address \
test-link-quality \
test-linked-list \
test-lookup-table \
@@ -212,8 +212,8 @@ test_hkdf_sha256_SOURCES = $(COMMON_SOURCES) test_hkdf_sha256.cpp
test_hmac_sha256_LDADD = $(COMMON_LDADD)
test_hmac_sha256_SOURCES = $(COMMON_SOURCES) test_hmac_sha256.cpp
test_ip6_address_LDADD = $(COMMON_LDADD)
test_ip6_address_SOURCES = $(COMMON_SOURCES) test_ip6_address.cpp
test_ip_address_LDADD = $(COMMON_LDADD)
test_ip_address_SOURCES = $(COMMON_SOURCES) test_ip_address.cpp
test_link_quality_LDADD = $(COMMON_LDADD)
test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp
@@ -29,6 +29,7 @@
#include <limits.h>
#include "common/encoding.hpp"
#include "net/ip4_address.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_headers.hpp"
@@ -36,32 +37,39 @@
using ot::Encoding::BigEndian::ReadUint16;
struct Ip6AddressStringTestVector
template <typename AddressType> struct TestVector
{
const char * mString;
const uint8_t mAddr[OT_IP6_ADDRESS_SIZE];
const uint8_t mAddr[sizeof(AddressType)];
ot::Error mError;
};
static void checkAddressFromString(Ip6AddressStringTestVector *aTestVector)
template <typename AddressType> static void checkAddressFromString(TestVector<AddressType> *aTestVector)
{
ot::Error error;
ot::Ip6::Address address;
ot::Error error;
AddressType address;
address.Clear();
error = address.FromString(aTestVector->mString);
VerifyOrQuit(error == aTestVector->mError, "Ip6::Address::FromString returned unexpected error code");
printf("%-42s -> %-42s\n", aTestVector->mString,
(error == ot::kErrorNone) ? address.ToString().AsCString() : "(parse error)");
VerifyOrQuit(error == aTestVector->mError, "Address::FromString returned unexpected error code");
if (error == ot::kErrorNone)
{
VerifyOrQuit(0 == memcmp(address.mFields.m8, aTestVector->mAddr, OT_IP6_ADDRESS_SIZE),
"Ip6::Address::FromString parsing failed");
VerifyOrQuit(0 == memcmp(address.GetBytes(), aTestVector->mAddr, sizeof(AddressType)),
"Address::FromString parsing failed");
}
}
void TestIp6AddressFromString(void)
{
Ip6AddressStringTestVector testVectors[] = {
typedef TestVector<ot::Ip6::Address> Ip6AddressTestVector;
Ip6AddressTestVector testVectors[] = {
// Valid full IPv6 address.
{"0102:0304:0506:0708:090a:0b0c:0d0e:0f00",
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00},
@@ -134,7 +142,33 @@ void TestIp6AddressFromString(void)
{":f:0:0:c:0:f:f:.", {0}, ot::kErrorParse},
};
for (Ip6AddressStringTestVector &testVector : testVectors)
for (Ip6AddressTestVector &testVector : testVectors)
{
checkAddressFromString(&testVector);
}
}
void TestIp4AddressFromString(void)
{
typedef TestVector<ot::Ip4::Address> Ip4AddressTestVector;
Ip4AddressTestVector testVectors[] = {
{"0.0.0.0", {0, 0, 0, 0}, ot::kErrorNone},
{"255.255.255.255", {255, 255, 255, 255}, ot::kErrorNone},
{"127.0.0.1", {127, 0, 0, 1}, ot::kErrorNone},
{"1.2.3.4", {1, 2, 3, 4}, ot::kErrorNone},
{"001.002.003.004", {1, 2, 3, 4}, ot::kErrorNone},
{"00000127.000.000.000001", {127, 0, 0, 1}, ot::kErrorNone},
{"123.231.0.256", {0}, ot::kErrorParse}, // Invalid byte value.
{"100123.231.0.256", {0}, ot::kErrorParse}, // Invalid byte value.
{"1.22.33", {0}, ot::kErrorParse}, // Too few bytes.
{"1.22.33.44.5", {0}, ot::kErrorParse}, // Too many bytes.
{"a.b.c.d", {0}, ot::kErrorParse}, // Wrong digit char.
{"123.23.45 .12", {0}, ot::kErrorParse}, // Extra space.
{".", {0}, ot::kErrorParse}, // Invalid.
};
for (Ip4AddressTestVector &testVector : testVectors)
{
checkAddressFromString(&testVector);
}
@@ -360,6 +394,7 @@ void TestIp6Header(void)
int main(void)
{
TestIp6AddressSetPrefix();
TestIp4AddressFromString();
TestIp6AddressFromString();
TestIp6Prefix();
TestIp6Header();