From a6be70f2963efed58df16756ebb3f64e1d49994c Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 26 Mar 2021 15:56:31 -0700 Subject: [PATCH] [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. --- Android.mk | 1 + src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 2 + src/core/net/ip4_address.cpp | 95 +++++++++++++++ src/core/net/ip4_address.hpp | 115 ++++++++++++++++++ src/core/net/ip6_address.cpp | 39 +----- src/core/net/ip6_address.hpp | 8 +- tests/unit/CMakeLists.txt | 14 +-- tests/unit/Makefile.am | 6 +- ...st_ip6_address.cpp => test_ip_address.cpp} | 55 +++++++-- 11 files changed, 279 insertions(+), 59 deletions(-) create mode 100644 src/core/net/ip4_address.cpp create mode 100644 src/core/net/ip4_address.hpp rename tests/unit/{test_ip6_address.cpp => test_ip_address.cpp} (87%) diff --git a/Android.mk b/Android.mk index 3c303af51..4ae6cd2c7 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 42cf45f02..8e5ffb1b5 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -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", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e1dafa0c1..157814d96 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 01c5f3359..0d243b8aa 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -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 \ diff --git a/src/core/net/ip4_address.cpp b/src/core/net/ip4_address.cpp new file mode 100644 index 000000000..f291113dd --- /dev/null +++ b/src/core/net/ip4_address.cpp @@ -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((value * 10) + static_cast(digitChar - '0')); + VerifyOrExit(value <= NumericLimits::Max()); + hasFirstDigit = true; + } + + VerifyOrExit(hasFirstDigit); + + mBytes[index] = static_cast(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 diff --git a/src/core/net/ip4_address.hpp b/src/core/net/ip4_address.hpp new file mode 100644 index 000000000..85de302dc --- /dev/null +++ b/src/core/net/ip4_address.hpp @@ -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
, public Clearable
+{ +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 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_ diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 808d531ae..653aa6894 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -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(mFields.m8 + 12); - endp = reinterpret_cast(mFields.m8 + 15); - - for (;;) - { - ch = *colonc++; - - if (ch == '.' || ch == '\0' || ch == ' ') - { - VerifyOrExit(dst <= endp, error = kErrorParse); - - *dst++ = static_cast(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: diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index d7aa68966..98359a6fc 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -38,6 +38,8 @@ #include +#include + #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). }; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index cbc56b646..e4330852c 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -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 diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 6ab946117..f9bdf8283 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -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 diff --git a/tests/unit/test_ip6_address.cpp b/tests/unit/test_ip_address.cpp similarity index 87% rename from tests/unit/test_ip6_address.cpp rename to tests/unit/test_ip_address.cpp index 0e0b1ec1d..21dd9011d 100644 --- a/tests/unit/test_ip6_address.cpp +++ b/tests/unit/test_ip_address.cpp @@ -29,6 +29,7 @@ #include #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 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 static void checkAddressFromString(TestVector *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 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 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();