From 3b349471ea02a0a1da47caab3a8fba5569c782c9 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sat, 6 Jul 2013 17:48:26 -0300 Subject: [PATCH] Added AddressRange class template. --- Makefile.am | 3 +- Makefile.in | 3 +- include/address_range.h | 299 ++++++++++++++++++++++++++++++++++++ include/internals.h | 7 + include/ip_address.h | 10 ++ include/ipv6_address.h | 13 ++ include/tins.h | 1 + src/internals.cpp | 42 +++++ src/ip_address.cpp | 10 ++ src/ipv6_address.cpp | 16 +- tests/depends.d | 63 +++++++- tests/src/address_range.cpp | 137 +++++++++++++++++ 12 files changed, 598 insertions(+), 6 deletions(-) create mode 100644 include/address_range.h create mode 100644 tests/src/address_range.cpp diff --git a/Makefile.am b/Makefile.am index debf49b..09f7c20 100644 --- a/Makefile.am +++ b/Makefile.am @@ -109,7 +109,8 @@ libtins_HEADERS = include/internals.h \ include/cxxstd.h \ include/stp.h \ include/exceptions.h \ - include/config.h + include/config.h \ + include/address_range.h libtins_dot11_HEADERS = include/dot11/dot11_base.h \ include/dot11/dot11_beacon.h \ diff --git a/Makefile.in b/Makefile.in index c779c3a..0ce37f9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -396,7 +396,8 @@ libtins_HEADERS = include/internals.h \ include/cxxstd.h \ include/stp.h \ include/exceptions.h \ - include/config.h + include/config.h \ + include/address_range.h libtins_dot11_HEADERS = include/dot11/dot11_base.h \ include/dot11/dot11_beacon.h \ diff --git a/include/address_range.h b/include/address_range.h new file mode 100644 index 0000000..d7f7039 --- /dev/null +++ b/include/address_range.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2012, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * 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 + * OWNER 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. + * + */ + +#ifndef TINS_ADDRESS_RANGE +#define TINS_ADDRESS_RANGE + +#include +#include +#include "ip_address.h" +#include "ipv6_address.h" +#include "endianness.h" +#include "internals.h" + +namespace Tins { +/** + * \brief AddressRange iterator class. + */ +template +class AddressRangeIterator : public std::iterator { +public: + typedef typename std::iterator::value_type value_type; + + struct end_iterator { + + }; + + /** + * Constructs an iterator. + * + * \param first The address held by this iterator. + */ + AddressRangeIterator(const value_type &addr) + : addr(addr), reached_end(false) + { + + } + + /** + * Constructs an iterator. + * + * \param first The address held by this iterator. + */ + AddressRangeIterator(const value_type &address, end_iterator) + : addr(address) + { + reached_end = Internals::increment(addr); + } + + /** + * Retrieves the current address pointed by this iterator. + */ + const value_type& operator*() const { + return addr; + } + + /** + * Retrieves a pointer to the current address pointed by this iterator. + */ + const value_type* operator->() const { + return &addr; + } + + /** + * Compares two iterators for equality. + * + * \param rhs The iterator with which to compare. + */ + bool operator==(const AddressRangeIterator &rhs) const { + return reached_end == rhs.reached_end && addr == rhs.addr; + } + + /** + * Compares two iterators for inequality. + * + * \param rhs The iterator with which to compare. + */ + bool operator!=(const AddressRangeIterator &rhs) const { + return !(*this == rhs); + } + + /** + * Increments this iterator. + */ + AddressRangeIterator& operator++() { + reached_end = Internals::increment(addr); + return *this; + } + + /** + * Increments this iterator. + */ + AddressRangeIterator operator++(int) { + AddressRangeIterator copy(*this); + (*this)++; + return copy; + } +private: + Address addr; + bool reached_end; +}; + +/** + * \brief Represents a range of addresses. + * + * This class provides a begin()/end() interface which allows + * iterating through every address stored in it. + * + * Note that when iterating a range that was created using + * operator/(IPv4Address, int) and the analog for IPv6, the + * network and broadcast addresses are discarded: + * + * \code + * auto range = IPv4Address("192.168.5.0") / 24; + * for(const auto &addr : range) { + * // process 192.168.5.1-254, .0 and .255 are discarded + * process(addr); + * } + * + * // That's only valid for iteration, not for AddressRange<>::contains + * + * assert(range.contains("192.168.5.0")); // works + * assert(range.contains("192.168.5.255")); // works + * \endcode + * + * Ranges created using AddressRange(address_type, address_type) + * will allow the iteration over the entire range: + * + * \code + * AddressRange range("192.168.5.0", "192.168.5.255"); + * for(const auto &addr : range) { + * // process 192.168.5.0-255, no addresses are discarded + * process(addr); + * } + * + * assert(range.contains("192.168.5.0")); // still valid + * assert(range.contains("192.168.5.255")); // still valid + * \endcode + * + */ +template +class AddressRange { +public: + /** + * The type of addresses stored in the range. + */ + typedef Address address_type; + + /** + * The iterator type. + */ + typedef AddressRangeIterator const_iterator; + + /** + * \brief The iterator type. + * + * This is the same type as const_iterator, since the + * addresses stored in this range are read only. + */ + typedef const_iterator iterator; + + /** + * \brief Constructs an address range from two addresses. + * + * The range will consist of the addresses [first, last]. + * + * If only_hosts is true, then the network and broadcast addresses + * will not be available when iterating the range. + * + * If last < first, an std::runtime_error exception is thrown. + * + * \param first The first address in the range. + * \param last The last address(inclusive) in the range. + * \param only_hosts Indicates whether only host addresses + * should be accessed when using iterators. + */ + AddressRange(const address_type &first, const address_type &last, bool only_hosts = false) + : first(first), last(last), only_hosts(only_hosts) + { + if(last < first) + throw std::runtime_error("Invalid address range"); + } + + /** + * \brief Creates an address range from a base address + * and a network mask. + * + * \param first The base address. + * \param mask The network mask to be used. + */ + static AddressRange from_mask(const address_type &first, const address_type &mask) { + return AddressRange(first, last_address_from_mask(first, mask), true); + } + + /** + * \brief Indicates whether an address is included in this range. + * \param addr The address to test. + * \return a bool indicating whether the address is in the range. + */ + bool contains(const address_type &addr) const { + return (first < addr && addr < last) || addr == first || addr == last; + } + + /** + * \brief Returns an interator to the beginning of this range. + * \brief const_iterator pointing to the beginning of this range. + */ + const_iterator begin() const { + address_type addr = first; + if(only_hosts) + Internals::increment(addr); + return const_iterator(addr); + } + + /** + * \brief Returns an interator to the end of this range. + * \brief const_iterator pointing to the end of this range. + */ + const_iterator end() const { + address_type addr = last; + if(only_hosts) + Internals::decrement(addr); + return const_iterator(addr, typename const_iterator::end_iterator()); + } + + /** + * \brief Indicates whether this range is iterable. + * + * Iterable ranges are those for which there is at least one + * address that could represent a host. For IPv4 ranges, a /31 or + * /32 ranges does not contain any, therefore it's not iterable. + * The same is true for /127 and /128 IPv6 ranges. + * + * If is_iterable returns false for a range, then iterating it + * through the iterators returned by begin() and end() is + * undefined. + * + * \return bool indicating whether this range is iterable. + */ + bool is_iterable() const { + // Since first < last, it's iterable + if(!only_hosts) + return true; + // We need that distance(first, last) >= 4 + address_type addr(first); + for(int i = 0; i < 3; ++i) { + // If there's overflow before the last iteration, we're done + if(Internals::increment(addr) && i != 2) + return false; + } + // If addr <= last, it's OK. + return addr < last || addr == last; + } +private: + static address_type last_address_from_mask(IPv4Address addr, IPv4Address mask) { + uint32_t addr_int = Endian::be_to_host(addr), + mask_int = Endian::be_to_host(mask); + return address_type(Endian::host_to_be(addr_int | ~mask_int)); + } + + static address_type last_address_from_mask(IPv6Address addr, const IPv6Address &mask) { + IPv6Address::iterator addr_iter = addr.begin(); + for(IPv6Address::const_iterator it = mask.begin(); it != mask.end(); ++it, ++addr_iter) { + *addr_iter = *addr_iter | ~*it; + } + return addr; + } + + address_type first, last; + bool only_hosts; +}; +} // namespace Tins + +#endif // TINS_ADDRESS_RANGE \ No newline at end of file diff --git a/include/internals.h b/include/internals.h index e8e2b3b..c89c9b7 100644 --- a/include/internals.h +++ b/include/internals.h @@ -40,6 +40,8 @@ * \cond */ namespace Tins { +class IPv4Address; +class IPv6Address; namespace Internals { template class byte_array { @@ -111,6 +113,11 @@ PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer, PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size); Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag); + +bool increment(IPv4Address &addr); +bool increment(IPv6Address &addr); +bool decrement(IPv4Address &addr); +bool decrement(IPv6Address &addr); } // namespace Internals } // namespace Tins /** diff --git a/include/ip_address.h b/include/ip_address.h index 0a2ad56..0c71189 100644 --- a/include/ip_address.h +++ b/include/ip_address.h @@ -36,6 +36,9 @@ #include "cxxstd.h" namespace Tins { +template +class AddressRange; + /** * \class IPv4Address * \brief Abstraction of an IPv4 address. @@ -137,6 +140,13 @@ private: uint32_t ip_addr; }; + +/** + * \brief Constructs an AddressRange from a base address and a mask. + * \param addr The range's first address. + * \param mask The bit-length of the prefix. + */ +AddressRange operator/(const IPv4Address &addr, int mask); } //namespace Tins #if TINS_IS_CXX11 diff --git a/include/ipv6_address.h b/include/ipv6_address.h index 8d4499a..fbb4de8 100644 --- a/include/ipv6_address.h +++ b/include/ipv6_address.h @@ -36,6 +36,12 @@ #include "cxxstd.h" namespace Tins { +template +class AddressRange; + +/** + * Represents an IPv6 address. + */ class IPv6Address { public: /** @@ -193,6 +199,13 @@ private: uint8_t address[address_size]; }; + +/** + * \brief Constructs an AddressRange from a base address and a mask. + * \param addr The range's first address. + * \param mask The bit-length of the prefix. + */ +AddressRange operator/(const IPv6Address &addr, int mask); } //namespace Tins #if TINS_IS_CXX11 diff --git a/include/tins.h b/include/tins.h index b410a30..1d07e92 100644 --- a/include/tins.h +++ b/include/tins.h @@ -68,5 +68,6 @@ #include "pppoe.h" #include "stp.h" #include "handshake_capturer.h" +#include "address_range.h" #endif // TINS_TINS_H diff --git a/src/internals.cpp b/src/internals.cpp index 8229a2e..8878ed8 100644 --- a/src/internals.cpp +++ b/src/internals.cpp @@ -39,6 +39,8 @@ #include "rawpdu.h" #include "dot1q.h" #include "pppoe.h" +#include "ip_address.h" +#include "ipv6_address.h" using std::string; @@ -149,5 +151,45 @@ Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag) { return Constants::Ethernet::UNKNOWN; } } + +bool increment(IPv4Address &addr) { + uint32_t addr_int = Endian::be_to_host(addr); + bool reached_end = ++addr_int == 0xffffffff; + addr = IPv4Address(Endian::be_to_host(addr_int)); + return reached_end; } + +bool increment(IPv6Address &addr) { + IPv6Address::iterator it = addr.end() - 1; + while(it >= addr.begin() && *it == 0xff) { + *it = 0; + --it; + } + // reached end + if(it < addr.begin()) + return true; + (*it)++; + return false; } + +bool decrement(IPv4Address &addr) { + uint32_t addr_int = Endian::be_to_host(addr); + bool reached_end = --addr_int == 0; + addr = IPv4Address(Endian::be_to_host(addr_int)); + return reached_end; +} + +bool decrement(IPv6Address &addr) { + IPv6Address::iterator it = addr.end() - 1; + while(it >= addr.begin() && *it == 0) { + *it = 0xff; + --it; + } + // reached end + if(it < addr.begin()) + return true; + (*it)--; + return false; +} +} // namespace Internals +} // namespace Tins diff --git a/src/ip_address.cpp b/src/ip_address.cpp index 6eea9d7..b41ea7a 100644 --- a/src/ip_address.cpp +++ b/src/ip_address.cpp @@ -31,6 +31,7 @@ #include #include "ip_address.h" #include "endianness.h" +#include "address_range.h" using std::string; @@ -94,4 +95,13 @@ std::ostream &operator<<(std::ostream &output, const IPv4Address &addr) { } return output;; } + +AddressRange operator/(const IPv4Address &addr, int mask) { + if(mask > 32) + throw std::logic_error("Prefix length cannot exceed 32"); + return AddressRange::from_mask( + addr, + IPv4Address(Endian::host_to_be(0xffffffff << (32 - mask))) + ); +} } diff --git a/src/ipv6_address.cpp b/src/ipv6_address.cpp index 3d9ae87..2bcd3ec 100644 --- a/src/ipv6_address.cpp +++ b/src/ipv6_address.cpp @@ -41,6 +41,7 @@ #include #include #include "ipv6_address.h" +#include "address_range.h" namespace Tins { IPv6Address::IPv6Address() { @@ -96,6 +97,19 @@ namespace Tins { #endif return buffer; } - + + AddressRange operator/(const IPv6Address &addr, int mask) { + if(mask > 128) + throw std::logic_error("Prefix length cannot exceed 128"); + IPv6Address last_addr; + IPv6Address::iterator it = last_addr.begin(); + while(mask > 8) { + *it = 0xff; + ++it; + mask -= 8; + } + *it = 0xff << (8 - mask); + return AddressRange::from_mask(addr, last_addr); + } } diff --git a/tests/depends.d b/tests/depends.d index c84345c..744a706 100644 --- a/tests/depends.d +++ b/tests/depends.d @@ -874,7 +874,7 @@ ../include/dot11/../cxxstd.h ../include/dot11/../macros.h \ ../include/ipv6.h ../include/ipv6_address.h ../include/arp.h \ ../include/eapol.h ../include/rawpdu.h ../include/dot1q.h \ - ../include/pppoe.h + ../include/pppoe.h ../include/ip_address.h ../include/ipv6_address.h ../include/internals.h: @@ -937,6 +937,10 @@ ../include/dot1q.h: ../include/pppoe.h: + +../include/ip_address.h: + +../include/ipv6_address.h: ../src/ip.o: ../src/ip.cpp ../include/ip.h ../include/pdu.h \ ../include/macros.h ../include/cxxstd.h ../include/exceptions.h \ ../include/small_uint.h ../include/endianness.h ../include/ip_address.h \ @@ -995,7 +999,10 @@ ../include/exceptions.h: ../src/ip_address.o: ../src/ip_address.cpp ../include/ip_address.h \ - ../include/cxxstd.h ../include/endianness.h ../include/macros.h + ../include/cxxstd.h ../include/endianness.h ../include/macros.h \ + ../include/address_range.h ../include/ip_address.h \ + ../include/ipv6_address.h ../include/endianness.h ../include/internals.h \ + ../include/constants.h ../include/pdu.h ../include/exceptions.h ../include/ip_address.h: @@ -1004,6 +1011,22 @@ ../include/endianness.h: ../include/macros.h: + +../include/address_range.h: + +../include/ip_address.h: + +../include/ipv6_address.h: + +../include/endianness.h: + +../include/internals.h: + +../include/constants.h: + +../include/pdu.h: + +../include/exceptions.h: ../src/ipv6.o: ../src/ipv6.cpp ../include/ipv6.h ../include/macros.h \ ../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \ ../include/endianness.h ../include/small_uint.h ../include/pdu_option.h \ @@ -1749,6 +1772,35 @@ ../include/network_interface.h: ../include/cxxstd.h: +src/address_range.o: src/address_range.cpp ../include/address_range.h \ + ../include/ip_address.h ../include/cxxstd.h ../include/ipv6_address.h \ + ../include/endianness.h ../include/macros.h ../include/internals.h \ + ../include/constants.h ../include/pdu.h ../include/exceptions.h \ + ../include/ip_address.h ../include/ipv6_address.h + +../include/address_range.h: + +../include/ip_address.h: + +../include/cxxstd.h: + +../include/ipv6_address.h: + +../include/endianness.h: + +../include/macros.h: + +../include/internals.h: + +../include/constants.h: + +../include/pdu.h: + +../include/exceptions.h: + +../include/ip_address.h: + +../include/ipv6_address.h: src/arp.o: src/arp.cpp ../include/arp.h ../include/macros.h \ ../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \ ../include/endianness.h ../include/hw_address.h ../include/ip_address.h \ @@ -3599,7 +3651,8 @@ src/wpa2_decrypt.o: src/wpa2_decrypt.cpp ../include/crypto.h \ ../include/exceptions.h ../include/snap.h ../include/endianness.h \ ../include/small_uint.h ../include/rawpdu.h \ ../include/handshake_capturer.h ../include/eapol.h ../include/config.h \ - ../include/radiotap.h ../include/udp.h + ../include/radiotap.h ../include/udp.h ../include/tcp.h \ + ../include/pdu_option.h ../include/crypto.h: @@ -3640,3 +3693,7 @@ src/wpa2_decrypt.o: src/wpa2_decrypt.cpp ../include/crypto.h \ ../include/radiotap.h: ../include/udp.h: + +../include/tcp.h: + +../include/pdu_option.h: diff --git a/tests/src/address_range.cpp b/tests/src/address_range.cpp new file mode 100644 index 0000000..0331bb2 --- /dev/null +++ b/tests/src/address_range.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include "address_range.h" +#include "ip_address.h" +#include "ipv6_address.h" + +using namespace std; +using namespace Tins; + +class AddressRangeTest : public testing::Test { +public: + void contain_tests24(const AddressRange &range); + void contain_tests24(const AddressRange &range); + void contain_tests26(const AddressRange &range); +}; + +void AddressRangeTest::contain_tests24(const AddressRange &range) { + EXPECT_TRUE(range.contains("192.168.0.0")); + EXPECT_TRUE(range.contains("192.168.0.1")); + EXPECT_TRUE(range.contains("192.168.0.254")); + EXPECT_TRUE(range.contains("192.168.0.255")); + EXPECT_TRUE(range.contains("192.168.0.123")); + EXPECT_FALSE(range.contains("192.168.1.1")); +} + +void AddressRangeTest::contain_tests26(const AddressRange &range) { + EXPECT_TRUE(range.contains("192.168.254.192")); + EXPECT_TRUE(range.contains("192.168.254.255")); + EXPECT_FALSE(range.contains("192.168.254.0")); + EXPECT_FALSE(range.contains("192.168.254.191")); +} + +void AddressRangeTest::contain_tests24(const AddressRange &range) { + EXPECT_TRUE(range.contains("dead::1")); + EXPECT_TRUE(range.contains("dead::1fee")); + EXPECT_TRUE(range.contains("dead::ffee")); + EXPECT_FALSE(range.contains("dead::1:1")); + EXPECT_FALSE(range.contains("dead::2:0")); +} + +TEST_F(AddressRangeTest, Contains) { + contain_tests24(AddressRange("192.168.0.0", "192.168.0.255")); + contain_tests24(AddressRange::from_mask("192.168.0.0", "255.255.255.0")); + contain_tests26(AddressRange("192.168.254.192", "192.168.254.255")); + contain_tests26(AddressRange::from_mask("192.168.254.192", "255.255.255.192")); + + contain_tests24(AddressRange("dead::0", "dead::ffff")); + contain_tests24(AddressRange::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0")); +} + +TEST_F(AddressRangeTest, Iterators) { + // v4 + { + AddressRange addr = AddressRange::from_mask("192.168.0.0", "255.255.255.252"); + std::vector addresses; + addresses.push_back("192.168.0.1"); + addresses.push_back("192.168.0.2"); + EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin())); + EXPECT_TRUE(addr.is_iterable()); + } + { + AddressRange addr = AddressRange::from_mask("255.255.255.252", "255.255.255.252"); + std::vector addresses; + addresses.push_back("255.255.255.253"); + addresses.push_back("255.255.255.254"); + EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin())); + EXPECT_TRUE(addr.is_iterable()); + } + + // v6 + { + AddressRange addr = AddressRange::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"); + std::vector addresses; + addresses.push_back("dead::1"); + addresses.push_back("dead::2"); + EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin())); + EXPECT_TRUE(addr.is_iterable()); + } + + { + AddressRange addr = + AddressRange::from_mask( + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc" + ); + std::vector addresses; + addresses.push_back("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd"); + addresses.push_back("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"); + EXPECT_TRUE(std::equal(addr.begin(), addr.end(), addresses.begin())); + EXPECT_TRUE(addr.is_iterable()); + } +} + +TEST_F(AddressRangeTest, Slash) { + // v4 + { + AddressRange range1 = AddressRange::from_mask("192.168.0.0", "255.255.255.252"); + AddressRange range2 = IPv4Address("192.168.0.0") / 30; + EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin())); + EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin())); + EXPECT_TRUE(range1.is_iterable()); + EXPECT_TRUE(range2.is_iterable()); + } + { + AddressRange range1 = AddressRange::from_mask("255.255.255.252", "255.255.255.252"); + AddressRange range2 = IPv4Address("255.255.255.252") / 30; + EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin())); + EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin())); + EXPECT_TRUE(range1.is_iterable()); + EXPECT_TRUE(range2.is_iterable()); + } + + // v6 + { + AddressRange range1 = AddressRange::from_mask("dead::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc"); + AddressRange range2 = IPv6Address("dead::0") / 126; + EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin())); + EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin())); + EXPECT_TRUE(range1.is_iterable()); + EXPECT_TRUE(range2.is_iterable()); + } + { + AddressRange range1 = + AddressRange::from_mask( + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc" + ); + AddressRange range2 = IPv6Address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc") / 126; + EXPECT_TRUE(std::equal(range1.begin(), range1.end(), range2.begin())); + EXPECT_TRUE(std::equal(range2.begin(), range2.end(), range1.begin())); + EXPECT_TRUE(range1.is_iterable()); + EXPECT_TRUE(range2.is_iterable()); + } +}