From 0ee7daa5f9ed12f8205769c96a955afff9a58a61 Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 00:59:39 +0200 Subject: [PATCH 01/10] AK: Add IPAddressCidr datatype --- AK/IpAddressCidr.h | 118 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 AK/IpAddressCidr.h diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h new file mode 100644 index 00000000000000..b1ab0700bd3695 --- /dev/null +++ b/AK/IpAddressCidr.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024, famfo + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace AK { + +class IPv4AddressCidr; +class IPv6AddressCidr; + +namespace Details { + +template +class AddressTraits; + +template AddressFamily> +class IPAddressCidr { +public: + enum class IPAddressCidrError { + CidrTooLong, + StringParsingFailed, + }; + + using IPAddress = Details::AddressTraits::IPAddress; + + static constexpr ErrorOr create(IPAddress address, u8 length) + { + if (length > AddressFamily::MAX_LENGTH) + return IPAddressCidrError::CidrTooLong; + + return AddressFamily(address, length); + } + + constexpr IPAddress const& ip_address() const& { return m_address; } + constexpr u32 length() const { return m_length; } + + constexpr void set_ip_address(IPAddress address) { m_address = address; } + constexpr ErrorOr set_length(u32 length) + { + if (length > AddressFamily::MAX_LENGTH) + return IPAddressCidrError::CidrTooLong; + + m_length = length; + return {}; + } + + constexpr static ErrorOr from_string(StringView string) + { + Vector const parts = string.split_view('/'); + + if (parts.size() != 2) + return IPAddressCidrError::StringParsingFailed; + + auto ip_address = IPAddress::from_string(parts[0]); + if (!ip_address.has_value()) + return IPAddressCidrError::StringParsingFailed; + + Optional length = parts[1].to_number(); + if (!length.has_value()) + return IPAddressCidrError::StringParsingFailed; + + return IPAddressCidr::create(ip_address.value(), length.release_value()); + } + +#ifdef KERNEL + ErrorOr> to_string() const +#else + ErrorOr to_string() const +#endif + { + StringBuilder builder; + + auto address_string = TRY(m_address.to_string()); + +#ifdef KERNEL + TRY(builder.try_append(address_string->view())); +#else + TRY(builder.try_append(address_string)); +#endif + + TRY(builder.try_append('/')); + TRY(builder.try_appendff("{}", m_length)); + +#ifdef KERNEL + return Kernel::KString::try_create(builder.string_view()); +#else + return builder.to_string(); +#endif + } + + constexpr bool operator==(IPAddressCidr const& other) const = default; + constexpr bool operator!=(IPAddressCidr const& other) const = default; + +protected: + constexpr IPAddressCidr(IPAddress address, u8 length) + : m_address(address) + , m_length(length) + { + } + +private: + IPAddress m_address; + u8 m_length; +}; + +} + +} + +#if USING_AK_GLOBALLY +#endif From 1103339693b67c9edf104e760edc14ea4c305dde Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 01:00:23 +0200 Subject: [PATCH 02/10] AK/IpAddressCidr: Add IPv4AddressCidr --- AK/IpAddressCidr.h | 80 ++++++++++++++++++ Tests/AK/CMakeLists.txt | 1 + Tests/AK/TestIPv4AddressCidr.cpp | 134 +++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 Tests/AK/TestIPv4AddressCidr.cpp diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index b1ab0700bd3695..d6576a7d74a8ca 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -110,9 +110,89 @@ class IPAddressCidr { u8 m_length; }; +template<> +class AddressTraits { +public: + using IPAddress = IPv4Address; +}; + } +class IPv4AddressCidr : public Details::IPAddressCidr { +public: + constexpr IPv4AddressCidr(IPv4Address address, u8 length) + : IPAddressCidr(address, length) + { + } + + constexpr IPv4Address netmask() const + { + IPv4Address netmask; + u8 free_bits = MAX_LENGTH - length(); + + if (free_bits == 32) { + netmask = IPv4Address(0, 0, 0, 0); + } else { + NetworkOrdered mask = NumericLimits::max() << free_bits; + + auto address = bit_cast>(mask); + netmask = IPv4Address(address.data()); + } + + return netmask; + } + + constexpr IPv4Address first_address_of_subnet() const + { + u32 mask = netmask().to_u32(); + return IPv4Address(ip_address().to_u32() & mask); + } + + constexpr IPv4Address last_address_of_subnet() const + { + u32 mask = netmask().to_u32(); + u32 first = ip_address().to_u32() & mask; + return IPv4Address(first | ~mask); + } + + bool contains(IPv4Address other) const + { + IPv4AddressCidr other_cidr = MUST(IPv4AddressCidr::create(other, length())); + return first_address_of_subnet() == other_cidr.first_address_of_subnet(); + } + + static u8 const MAX_LENGTH = 32; +}; + +template<> +struct Traits : public DefaultTraits { + static unsigned hash(IPv4AddressCidr const& address) + { + IPv4Address ip_address = address.ip_address(); + return sip_hash_bytes<4, 8>({ &ip_address, address.length() }); + } +}; + +#ifdef KERNEL +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, IPv4AddressCidr value) + { + return Formatter::format(builder, TRY(value.to_string())->view()); + } +}; +#else +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, IPv4AddressCidr value) + { + return Formatter::format(builder, TRY(value.to_string())); + } +}; +#endif + } #if USING_AK_GLOBALLY +using AK::IPv4AddressCidr; #endif diff --git a/Tests/AK/CMakeLists.txt b/Tests/AK/CMakeLists.txt index b4d3dd30ec9f20..db512e9b1daf18 100644 --- a/Tests/AK/CMakeLists.txt +++ b/Tests/AK/CMakeLists.txt @@ -42,6 +42,7 @@ set(AK_TEST_SOURCES TestHashTable.cpp TestHex.cpp TestIPv4Address.cpp + TestIPv4AddressCidr.cpp TestIPv6Address.cpp TestIndexSequence.cpp TestInsertionSort.cpp diff --git a/Tests/AK/TestIPv4AddressCidr.cpp b/Tests/AK/TestIPv4AddressCidr.cpp new file mode 100644 index 00000000000000..a3a311002a5017 --- /dev/null +++ b/Tests/AK/TestIPv4AddressCidr.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024, famfo + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +TEST_CASE(sanity_check) +{ + auto address_result = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 32); + + EXPECT(!address_result.is_error()); + + IPv4AddressCidr address = address_result.release_value(); + + EXPECT_EQ(address.length(), (u32)32); + EXPECT_EQ(address.ip_address(), IPv4Address(192, 0, 2, 1)); + EXPECT_EQ(address.first_address_of_subnet(), IPv4Address(192, 0, 2, 1)); + EXPECT_EQ(address.last_address_of_subnet(), IPv4Address(192, 0, 2, 1)); + EXPECT_EQ(address.netmask(), IPv4Address(255, 255, 255, 255)); + EXPECT(address.contains(IPv4Address(192, 0, 2, 1))); +} + +TEST_CASE(should_fail_on_invalid_length) +{ + auto address_result = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 33); + EXPECT(address_result.is_error()); + EXPECT_EQ(address_result.error(), IPv4AddressCidr::IPAddressCidrError::CidrTooLong); +} + +TEST_CASE(should_find_first_in_subnet) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value(); + EXPECT_EQ(address.first_address_of_subnet(), IPv4Address(192, 0, 2, 0)); +} + +TEST_CASE(should_find_last_in_subnet) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value(); + EXPECT_EQ(address.last_address_of_subnet(), IPv4Address(192, 0, 2, 255)); +} + +TEST_CASE(should_return_matching_netask) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value(); + EXPECT_EQ(address.netmask(), IPv4Address(255, 255, 255, 0)); +} + +TEST_CASE(should_contain_other) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value(); + EXPECT(address.contains(IPv4Address(192, 0, 2, 100))); +} + +TEST_CASE(should_set_address) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 8).release_value(); + EXPECT_EQ(address.ip_address(), IPv4Address(192, 0, 2, 1)); + + address.set_ip_address(IPv4Address(198, 51, 100, 1)); + EXPECT_EQ(address.ip_address(), IPv4Address(198, 51, 100, 1)); +} + +TEST_CASE(should_set_length) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 32).release_value(); + EXPECT_EQ(address.length(), (u32)32); + + EXPECT(!address.set_length(24).is_error()); + EXPECT_EQ(address.length(), (u32)24); +} + +TEST_CASE(should_not_set_invalid_length) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 32).release_value(); + EXPECT(address.set_length(33).is_error()); +} + +TEST_CASE(should_not_contain_other) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value(); + EXPECT(!address.contains(IPv4Address(198, 51, 100, 1))); +} + +TEST_CASE(should_contain_this) +{ + IPv4AddressCidr address = IPv4AddressCidr::create(IPv4Address(0, 0, 0, 0), 0).release_value(); + EXPECT(address.contains(IPv4Address(192, 0, 2, 1))); +} + +TEST_CASE(should_parse_cidr_string) +{ + auto address = IPv4AddressCidr::from_string("192.0.2.1/24"sv); + EXPECT(!address.is_error()); + EXPECT_EQ(address.release_value(), IPv4AddressCidr::create(IPv4Address(192, 0, 2, 1), 24).release_value()); +} + +TEST_CASE(should_not_parse_invalid_address) +{ + auto address = IPv4AddressCidr::from_string("256.0.0.1/24"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv4AddressCidr::IPAddressCidrError::StringParsingFailed); +} + +TEST_CASE(should_not_parse_invalid_length) +{ + auto address = IPv4AddressCidr::from_string("192.0.2.1/33"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv4AddressCidr::IPAddressCidrError::CidrTooLong); +} + +TEST_CASE(should_not_parse_invalid_cidr_format) +{ + auto address = IPv4AddressCidr::from_string("192.0.2.1"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv4AddressCidr::IPAddressCidrError::StringParsingFailed); +} + +TEST_CASE(should_format_cidr) +{ + auto address = IPv4AddressCidr(IPv4Address(192, 0, 2, 1), 24).to_string().release_value(); + EXPECT_EQ(address.bytes_as_string_view(), "192.0.2.1/24"sv); +} + +TEST_CASE(unaligned_mask) +{ + auto address = IPv4AddressCidr::create(IPv4Address(192, 0, 0, 42), 27).release_value(); + EXPECT_EQ(address.first_address_of_subnet(), IPv4Address(192, 0, 0, 32)); + EXPECT_EQ(address.last_address_of_subnet(), IPv4Address(192, 0, 0, 63)); +} From 695a66a0b33c87e13d5434805851bf19c013d1cc Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 00:48:49 +0200 Subject: [PATCH 03/10] AK/IpAddressCidr: Add IPv6AddressCidr --- AK/IpAddressCidr.h | 95 +++++++++++++++++++++++ Tests/AK/CMakeLists.txt | 1 + Tests/AK/TestIPv6AddressCidr.cpp | 126 +++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 Tests/AK/TestIPv6AddressCidr.cpp diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index d6576a7d74a8ca..2775dcaeca820e 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace AK { @@ -116,6 +117,12 @@ class AddressTraits { using IPAddress = IPv4Address; }; +template<> +class AddressTraits { +public: + using IPAddress = IPv6Address; +}; + } class IPv4AddressCidr : public Details::IPAddressCidr { @@ -191,8 +198,96 @@ struct Formatter : Formatter { }; #endif +class IPv6AddressCidr : public Details::IPAddressCidr { +public: + constexpr IPv6AddressCidr(IPv6Address address, u8 length) + : IPAddressCidr(address, length) + { + } + + constexpr IPv6Address first_address_of_subnet() const + { + u8 address[16] = { 0 }; + u8 free_bits = MAX_LENGTH - length(); + + if (free_bits != 128) { + NetworkOrdered mask = NumericLimits::max() << free_bits; + auto address_mask = bit_cast>(mask); + + auto const* original_address = ip_address().to_in6_addr_t(); + for (int i = 0; i < 16; i++) { + address[i] = original_address[i] & address_mask[i]; + } + } + + return address; + } + + constexpr IPv6Address last_address_of_subnet() const + { + u8 address[16] = { 0 }; + u8 free_bits = MAX_LENGTH - length(); + + NetworkOrdered inverse_mask = NumericLimits::max() >> (128 - free_bits); + + if (free_bits != 128) { + NetworkOrdered mask = NumericLimits::max() << free_bits; + auto address_mask = bit_cast>(mask); + + auto const* original_address = ip_address().to_in6_addr_t(); + for (int i = 0; i < 16; i++) { + address[i] = original_address[i] & address_mask[i]; + } + } + + auto inverse_address_mask = bit_cast>(inverse_mask); + + for (int i = 0; i < 16; i++) { + address[i] = address[i] | inverse_address_mask[i]; + } + + return address; + } + + bool contains(IPv6Address other) const + { + IPv6AddressCidr other_cidr = IPv6AddressCidr::create(other, length()).value(); + return first_address_of_subnet() == other_cidr.first_address_of_subnet(); + } + + static u8 const MAX_LENGTH = 128; +}; + +template<> +struct Traits : public DefaultTraits { + static unsigned hash(IPv6AddressCidr const& address) + { + IPv6Address ip_address = address.ip_address(); + return sip_hash_bytes<4, 8>({ &ip_address, address.length() }); + } +}; + +#ifdef KERNEL +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, IPv6AddressCidr value) + { + return Formatter::format(builder, TRY(value.to_string())->view()); + } +}; +#else +template<> +struct Formatter : Formatter { + ErrorOr format(FormatBuilder& builder, IPv6AddressCidr value) + { + return Formatter::format(builder, TRY(value.to_string())); + } +}; +#endif + } #if USING_AK_GLOBALLY using AK::IPv4AddressCidr; +using AK::IPv6AddressCidr; #endif diff --git a/Tests/AK/CMakeLists.txt b/Tests/AK/CMakeLists.txt index db512e9b1daf18..280749e02419b6 100644 --- a/Tests/AK/CMakeLists.txt +++ b/Tests/AK/CMakeLists.txt @@ -44,6 +44,7 @@ set(AK_TEST_SOURCES TestIPv4Address.cpp TestIPv4AddressCidr.cpp TestIPv6Address.cpp + TestIPv6AddressCidr.cpp TestIndexSequence.cpp TestInsertionSort.cpp TestIntegerMath.cpp diff --git a/Tests/AK/TestIPv6AddressCidr.cpp b/Tests/AK/TestIPv6AddressCidr.cpp new file mode 100644 index 00000000000000..50da23cc4a7d54 --- /dev/null +++ b/Tests/AK/TestIPv6AddressCidr.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024, famfo + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +TEST_CASE(sanity_check) +{ + auto address_result = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 128); + + EXPECT(!address_result.is_error()); + + IPv6AddressCidr address = address_result.release_value(); + + EXPECT_EQ(address.length(), (u32)128); + EXPECT_EQ(address.ip_address(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 })); + EXPECT_EQ(address.first_address_of_subnet(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 })); + EXPECT_EQ(address.last_address_of_subnet(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 })); + EXPECT(address.contains(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }))); +} +TEST_CASE(should_fail_on_invalid_length) +{ + auto address_result = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 129); + EXPECT(address_result.is_error()); + EXPECT_EQ(address_result.error(), IPv6AddressCidr::IPAddressCidrError::CidrTooLong); +} + +TEST_CASE(should_find_first_in_subnet) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT_EQ(address.first_address_of_subnet(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })); +} + +TEST_CASE(should_find_last_in_subnet) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT_EQ(address.last_address_of_subnet(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff })); +} + +TEST_CASE(should_contain_other) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT(address.contains(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 1 }))); +} + +TEST_CASE(should_set_address) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT_EQ(address.ip_address(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 })); + + address.set_ip_address(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 1 })); + EXPECT_EQ(address.ip_address(), IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 1 })); +} + +TEST_CASE(should_set_length) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT_EQ(address.length(), (u32)48); + + EXPECT(!address.set_length(64).is_error()); + EXPECT_EQ(address.length(), (u32)64); +} + +TEST_CASE(should_not_set_invalid_length) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT(address.set_length(129).is_error()); +} + +TEST_CASE(should_not_contain_other) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value(); + EXPECT(!address.contains(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }))); +} + +TEST_CASE(should_contain_this) +{ + IPv6AddressCidr address = IPv6AddressCidr::create(IPv6Address({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }), 0).release_value(); + EXPECT(address.contains(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }))); +} + +TEST_CASE(should_parse_cidr_string) +{ + auto address = IPv6AddressCidr::from_string("2001:db8::1/48"sv); + EXPECT(!address.is_error()); + EXPECT_EQ(address.release_value(), IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value()); +} + +TEST_CASE(should_not_parse_invalid_address) +{ + auto address = IPv6AddressCidr::from_string("200f:db8:::1/48"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv6AddressCidr::IPAddressCidrError::StringParsingFailed); +} + +TEST_CASE(should_not_parse_invalid_length) +{ + auto address = IPv6AddressCidr::from_string("2001:db8::1/129"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv6AddressCidr::IPAddressCidrError::CidrTooLong); +} + +TEST_CASE(should_not_parse_invalid_cidr_format) +{ + auto address = IPv6AddressCidr::from_string("2001:db8::1"sv); + EXPECT(address.is_error()); + EXPECT_EQ(address.error(), IPv6AddressCidr::IPAddressCidrError::StringParsingFailed); +} + +TEST_CASE(should_format_cidr) +{ + auto address = IPv6AddressCidr::create(IPv6Address({ 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 48).release_value().to_string().release_value(); + EXPECT_EQ(address.bytes_as_string_view(), "2001:db8::1/48"sv); +} + +TEST_CASE(unaligned_mask) +{ + auto address = IPv6AddressCidr::from_string("2001:db8:0:80::1/57"sv).release_value(); + EXPECT_EQ(address.first_address_of_subnet(), IPv6Address::from_string("2001:db8:0:80::"sv).release_value()); + EXPECT_EQ(address.last_address_of_subnet(), IPv6Address::from_string("2001:db8:0:ff:ffff:ffff:ffff:ffff"sv).release_value()); +} From 76bfe748f268b5c3d4026e261ed37bd112d04075 Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 15:43:01 +0200 Subject: [PATCH 04/10] fixup! Base: use more auto, change fromatting --- AK/IpAddressCidr.h | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index 2775dcaeca820e..ef2d17c2323ac0 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -63,7 +63,7 @@ class IPAddressCidr { if (!ip_address.has_value()) return IPAddressCidrError::StringParsingFailed; - Optional length = parts[1].to_number(); + auto length = parts[1].to_number(); if (!length.has_value()) return IPAddressCidrError::StringParsingFailed; @@ -72,29 +72,15 @@ class IPAddressCidr { #ifdef KERNEL ErrorOr> to_string() const + { + return Kernel::KString::formatted("{}/{}", m_address, m_length); + } #else ErrorOr to_string() const -#endif { - StringBuilder builder; - - auto address_string = TRY(m_address.to_string()); - -#ifdef KERNEL - TRY(builder.try_append(address_string->view())); -#else - TRY(builder.try_append(address_string)); -#endif - - TRY(builder.try_append('/')); - TRY(builder.try_appendff("{}", m_length)); - -#ifdef KERNEL - return Kernel::KString::try_create(builder.string_view()); -#else - return builder.to_string(); -#endif + return String::formatted("{}/{}", m_address, m_length); } +#endif constexpr bool operator==(IPAddressCidr const& other) const = default; constexpr bool operator!=(IPAddressCidr const& other) const = default; From bcf16e1959762f4f24544998c6261c0f04d5eea7 Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 15:43:33 +0200 Subject: [PATCH 05/10] fixup! IPv4: move MAX_LENGTH to the top --- AK/IpAddressCidr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index ef2d17c2323ac0..ee9cc5f938f617 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -113,6 +113,8 @@ class AddressTraits { class IPv4AddressCidr : public Details::IPAddressCidr { public: + constexpr static u8 MAX_LENGTH = 32; + constexpr IPv4AddressCidr(IPv4Address address, u8 length) : IPAddressCidr(address, length) { @@ -153,8 +155,6 @@ class IPv4AddressCidr : public Details::IPAddressCidr { IPv4AddressCidr other_cidr = MUST(IPv4AddressCidr::create(other, length())); return first_address_of_subnet() == other_cidr.first_address_of_subnet(); } - - static u8 const MAX_LENGTH = 32; }; template<> From 203e4bf27eb496be8611a1c05c8d64ad89dc1cf9 Mon Sep 17 00:00:00 2001 From: famfo Date: Tue, 10 Sep 2024 15:44:09 +0200 Subject: [PATCH 06/10] fixup! IPv6: move MAX_LENGTH to top, return IPv6Address explicitly --- AK/IpAddressCidr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index ee9cc5f938f617..aa859f7c0a680f 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -186,6 +186,8 @@ struct Formatter : Formatter { class IPv6AddressCidr : public Details::IPAddressCidr { public: + constexpr static u8 MAX_LENGTH = 128; + constexpr IPv6AddressCidr(IPv6Address address, u8 length) : IPAddressCidr(address, length) { @@ -206,7 +208,7 @@ class IPv6AddressCidr : public Details::IPAddressCidr { } } - return address; + return IPv6Address(address); } constexpr IPv6Address last_address_of_subnet() const @@ -232,7 +234,7 @@ class IPv6AddressCidr : public Details::IPAddressCidr { address[i] = address[i] | inverse_address_mask[i]; } - return address; + return IPv6Address(address); } bool contains(IPv6Address other) const @@ -240,8 +242,6 @@ class IPv6AddressCidr : public Details::IPAddressCidr { IPv6AddressCidr other_cidr = IPv6AddressCidr::create(other, length()).value(); return first_address_of_subnet() == other_cidr.first_address_of_subnet(); } - - static u8 const MAX_LENGTH = 128; }; template<> From b754b80f31c172db97f79c4aab845295067cbdb5 Mon Sep 17 00:00:00 2001 From: famfo Date: Wed, 11 Sep 2024 01:17:39 +0200 Subject: [PATCH 07/10] fixup: Base: use release_value --- AK/IpAddressCidr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index aa859f7c0a680f..3091d041ad6590 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -67,7 +67,7 @@ class IPAddressCidr { if (!length.has_value()) return IPAddressCidrError::StringParsingFailed; - return IPAddressCidr::create(ip_address.value(), length.release_value()); + return IPAddressCidr::create(ip_address.release_value(), length.release_value()); } #ifdef KERNEL From 5b792e98661a1b2fd85e51b84b53fc4edbc28523 Mon Sep 17 00:00:00 2001 From: famfo Date: Wed, 11 Sep 2024 01:17:50 +0200 Subject: [PATCH 08/10] fixup: IPv6: use release_value --- AK/IpAddressCidr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index 3091d041ad6590..098b7635266ee9 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -239,7 +239,7 @@ class IPv6AddressCidr : public Details::IPAddressCidr { bool contains(IPv6Address other) const { - IPv6AddressCidr other_cidr = IPv6AddressCidr::create(other, length()).value(); + IPv6AddressCidr other_cidr = IPv6AddressCidr::create(other, length()).release_value(); return first_address_of_subnet() == other_cidr.first_address_of_subnet(); } }; From 074cf8a0c509397dfdf69234f15edc9050688e88 Mon Sep 17 00:00:00 2001 From: famfo Date: Fri, 13 Sep 2024 03:29:51 +0200 Subject: [PATCH 09/10] AK/IPv6Address: Add to and from u128 functions --- AK/IPv6Address.h | 12 ++++++++++++ Tests/AK/TestIPv6Address.cpp | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/AK/IPv6Address.h b/AK/IPv6Address.h index 9282e663b62ecd..93e16a861577db 100644 --- a/AK/IPv6Address.h +++ b/AK/IPv6Address.h @@ -36,6 +36,13 @@ class [[gnu::packed]] IPv6Address { m_data[i] = data[i]; } + constexpr IPv6Address(NetworkOrdered const& data) + { + auto array = bit_cast>(data); + for (size_t i = 0; i < 16; i++) + m_data[i] = array[i]; + } + constexpr IPv6Address(IPv4Address const& ipv4_address) { // IPv4 mapped IPv6 address @@ -235,6 +242,11 @@ class [[gnu::packed]] IPv6Address { return true; } + constexpr NetworkOrdered to_u128() const + { + return bit_cast>(m_data); + } + constexpr bool is_ipv4_mapped() const { if (m_data[0] || m_data[1] || m_data[2] || m_data[3] || m_data[4] || m_data[5] || m_data[6] || m_data[7] || m_data[8] || m_data[9]) diff --git a/Tests/AK/TestIPv6Address.cpp b/Tests/AK/TestIPv6Address.cpp index 602982be74a06f..b9566c7f0b9fae 100644 --- a/Tests/AK/TestIPv6Address.cpp +++ b/Tests/AK/TestIPv6Address.cpp @@ -167,3 +167,14 @@ TEST_CASE(subnets) EXPECT(broadcast.is_in_subnet(all_routers.network(12), 12)); EXPECT(!documentation.is_in_subnet(lla, 4)); } + +TEST_CASE(should_convert_to_u128) +{ + constexpr u8 array_address[] = { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + constexpr IPv6Address addr(array_address); + auto addr_u128 = addr.to_u128(); + + auto expected = bit_cast>(array_address); + + EXPECT_EQ((u128)addr_u128, (u128)expected); +} From 06fea380b3c5e100bb2e74e3d6cd5747c0f6be26 Mon Sep 17 00:00:00 2001 From: famfo Date: Fri, 13 Sep 2024 03:30:36 +0200 Subject: [PATCH 10/10] fixup: IPv6: use u128 datatypes --- AK/IpAddressCidr.h | 50 +++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/AK/IpAddressCidr.h b/AK/IpAddressCidr.h index 098b7635266ee9..203936e80d31b3 100644 --- a/AK/IpAddressCidr.h +++ b/AK/IpAddressCidr.h @@ -195,44 +195,21 @@ class IPv6AddressCidr : public Details::IPAddressCidr { constexpr IPv6Address first_address_of_subnet() const { - u8 address[16] = { 0 }; - u8 free_bits = MAX_LENGTH - length(); + auto address = ip_address().to_u128(); + auto mask = address_mask(); - if (free_bits != 128) { - NetworkOrdered mask = NumericLimits::max() << free_bits; - auto address_mask = bit_cast>(mask); - - auto const* original_address = ip_address().to_in6_addr_t(); - for (int i = 0; i < 16; i++) { - address[i] = original_address[i] & address_mask[i]; - } - } + address = bit_cast>(bit_cast(address) & bit_cast(mask)); return IPv6Address(address); } constexpr IPv6Address last_address_of_subnet() const { - u8 address[16] = { 0 }; - u8 free_bits = MAX_LENGTH - length(); - - NetworkOrdered inverse_mask = NumericLimits::max() >> (128 - free_bits); - - if (free_bits != 128) { - NetworkOrdered mask = NumericLimits::max() << free_bits; - auto address_mask = bit_cast>(mask); - - auto const* original_address = ip_address().to_in6_addr_t(); - for (int i = 0; i < 16; i++) { - address[i] = original_address[i] & address_mask[i]; - } - } + auto address = ip_address().to_u128(); + auto mask = address_mask(); - auto inverse_address_mask = bit_cast>(inverse_mask); - - for (int i = 0; i < 16; i++) { - address[i] = address[i] | inverse_address_mask[i]; - } + address = bit_cast>(bit_cast(address) & bit_cast(mask)); + address = bit_cast>(bit_cast(address) | ~bit_cast(mask)); return IPv6Address(address); } @@ -242,6 +219,19 @@ class IPv6AddressCidr : public Details::IPAddressCidr { IPv6AddressCidr other_cidr = IPv6AddressCidr::create(other, length()).release_value(); return first_address_of_subnet() == other_cidr.first_address_of_subnet(); } + +private: + constexpr NetworkOrdered address_mask() const + { + u8 const free_bits = MAX_LENGTH - length(); + NetworkOrdered mask = (u128)0; + + if (free_bits != 128) { + mask = NumericLimits::max() << free_bits; + } + + return mask; + } }; template<>