This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch dev-1-1-0 in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 416f1e9a26de49b02d9a882668572792f470ba69 Author: Alan M. Carroll <[email protected]> AuthorDate: Sun Mar 1 20:26:12 2020 -0600 Network extraction support. --- swoc++/include/swoc/DiscreteRange.h | 29 +- swoc++/include/swoc/Errata.h | 24 +- swoc++/include/swoc/MemSpan.h | 2 +- swoc++/include/swoc/TextView.h | 23 +- swoc++/include/swoc/swoc_ip.h | 1744 ++++++++++++++++++++++++++++------- swoc++/src/bw_ip_format.cc | 6 +- swoc++/src/swoc_ip.cc | 328 ++++++- swoc++/swoc++-headers.part | 6 + swoc++/swoc++-shared.part | 2 + swoc++/swoc++-static.part | 2 + swoc++/swoc++.part | 15 +- unit_tests/CMakeLists.txt | 2 +- unit_tests/ex_ipspace_properties.cc | 3 +- unit_tests/test_ip.cc | 339 ++++++- 14 files changed, 2091 insertions(+), 434 deletions(-) diff --git a/swoc++/include/swoc/DiscreteRange.h b/swoc++/include/swoc/DiscreteRange.h index 2886a4b..c76f235 100644 --- a/swoc++/include/swoc/DiscreteRange.h +++ b/swoc++/include/swoc/DiscreteRange.h @@ -107,14 +107,12 @@ protected: T _max; ///< the maximum value in the interval public: - using metric_type = T; - using Relation = DiscreteRangeRelation; - using EdgeRelation = DiscreteRangeEdgeRelation; - -// static constexpr self_type ALL{detail::minimum<metric_type>(), detail::maximum<metric_type>()}; + using metric_type = T; ///< Export metric type. + using Relation = DiscreteRangeRelation; ///< Import type for convenience. + using EdgeRelation = DiscreteRangeEdgeRelation; ///< Import type for convenience. /** Default constructor. - An empty range is constructed. + An invalid (empty) range is constructed. */ constexpr DiscreteRange() : _min(detail::maximum<T>()), _max(detail::minimum<T>()) {} @@ -134,6 +132,13 @@ public: ~DiscreteRange() = default; + /** Check if there are no values in the range. + * + * @return @c true if the range is empty (contains no values), @c false if it contains at least + * one value. + */ + bool empty() const; + self_type &assign(metric_type const &min, metric_type const &max); /// Set the interval to be a singleton. @@ -159,8 +164,13 @@ public: */ metric_type const &max() const; - bool contains(metric_type const& n) { - return _min <= n && n <= _max; + /** Check if a value is in @a this range. + * + * @param m Metric value to check. + * @return @c true if @a m is in the range, @c false if not. + */ + bool contains(metric_type const& m) { + return _min <= m && m <= _max; } /** Logical intersection test for two intervals. @@ -249,9 +259,6 @@ public: //! Check if the interval is exactly one element. bool is_singleton() const; - //! Check if the interval is empty. - bool empty() const; - /** Test for empty, operator form. @return @c true if the interval is empty, @c false otherwise. */ diff --git a/swoc++/include/swoc/Errata.h b/swoc++/include/swoc/Errata.h index 5cc3db5..f352e8b 100644 --- a/swoc++/include/swoc/Errata.h +++ b/swoc++/include/swoc/Errata.h @@ -905,31 +905,31 @@ namespace swoc template <size_t IDX, typename R> typename std::tuple_element<IDX, swoc::Rv<R>>::type & get(swoc::Rv<R> &&rv) { - if constexpr (IDX == 0) - { + if constexpr (IDX == 0) { return rv.result(); - } else if constexpr (IDX == 1) - { return rv.errata(); } + } else if constexpr (IDX == 1) { + return rv.errata(); + } } template <size_t IDX, typename R> typename std::tuple_element<IDX, swoc::Rv<R>>::type & get(swoc::Rv<R> &rv) { - if constexpr (IDX == 0) - { + if constexpr (IDX == 0) { return rv.result(); - } else if constexpr (IDX == 1) - { return rv.errata(); } + } else if constexpr (IDX == 1) { + return rv.errata(); + } } template <size_t IDX, typename R> typename std::tuple_element<IDX, swoc::Rv<R>>::type const & get(swoc::Rv<R> const &rv) { - if constexpr (IDX == 0) - { + if constexpr (IDX == 0) { return rv.result(); - } else if constexpr (IDX == 1) - { return rv.errata(); } + } else if constexpr (IDX == 1) { + return rv.errata(); + } } } // namespace swoc diff --git a/swoc++/include/swoc/MemSpan.h b/swoc++/include/swoc/MemSpan.h index 81862c5..f341311 100644 --- a/swoc++/include/swoc/MemSpan.h +++ b/swoc++/include/swoc/MemSpan.h @@ -749,7 +749,7 @@ MemSpan<T>::remove_suffix(size_t count) { template <typename T> constexpr MemSpan<T> MemSpan<T>::subspan(size_t offset, size_t count) const { - return offset <= _count ? self_type{this->data() + offset, std::min(count, _count - offset)} : self_type{}; + return offset < _count ? self_type{this->data() + offset, std::min(count, _count - offset)} : self_type{}; } template <typename T> diff --git a/swoc++/include/swoc/TextView.h b/swoc++/include/swoc/TextView.h index 49d0909..db20a21 100644 --- a/swoc++/include/swoc/TextView.h +++ b/swoc++/include/swoc/TextView.h @@ -174,11 +174,25 @@ public: /// Assign from a @c std::string. self_type &operator=(const std::string &s); + /** Assign a view of the @a c_str + * + * @param c_str Pointer to C string. + * @return @a this + * + * @note @c c_str must be a null terminated string. The null byte is not included in the view. + */ + self_type& assign(char const* c_str);; + /// Explicitly set the start @a ptr and size @a n of the view. self_type &assign(char const *ptr, size_t n); - /// Explicitly set the view to the half open range [ @a first , @a last ) - self_type &assign(char const *first, char const *lsat); + /** Assign the half open view [ @a b , @a e ) to @a this + * + * @param b First character in the view. + * @param e One character after the last character in the view. + * @return @a this + */ + self_type &assign(char const *b, char const *e); /// Explicitly set the view from a @c std::string self_type &assign(std::string const &s); @@ -900,6 +914,11 @@ TextView::operator=(const std::string &s) { return *this; } +inline TextView& +TextView::assign(char const *c_str) { + return this->assign(c_str, strlen(c_str)); +} + inline TextView & TextView::assign(const std::string &s) { *this = super_type(s); diff --git a/swoc++/include/swoc/swoc_ip.h b/swoc++/include/swoc/swoc_ip.h index 0ee3f9b..4d40e3f 100644 --- a/swoc++/include/swoc/swoc_ip.h +++ b/swoc++/include/swoc/swoc_ip.h @@ -1,4 +1,5 @@ #pragma once +#pragma once // SPDX-License-Identifier: Apache-2.0 /** @file IP address and network related classes. @@ -11,17 +12,29 @@ #include <swoc/TextView.h> #include <swoc/DiscreteRange.h> #include <swoc/RBTree.h> +#include <values.h> -namespace swoc -{ +namespace swoc { class IP4Addr; + class IP6Addr; + class IPAddr; + class IPMask; + class IP4Range; + class IP6Range; + class IPRange; +class IP4Net; + +class IP6Net; + +class IPNet; + using ::std::string_view; /** A union to hold @c sockaddr compliant IP address structures. @@ -42,10 +55,12 @@ union IPEndpoint { /// Default construct invalid instance. IPEndpoint(); + /// Construct from the @a text representation of an address. - IPEndpoint(string_view const &text); + IPEndpoint(string_view const& text); + // Construct from @a IPAddr - IPEndpoint(IPAddr const &addr); + IPEndpoint(IPAddr const& addr); /** Break a string in to IP address relevant tokens. * @@ -58,7 +73,8 @@ union IPEndpoint { * Any of the out parameters can be @c nullptr in which case they are not updated. * This parses and discards the IPv6 brackets. */ - static bool tokenize(string_view src, string_view *host = nullptr, string_view *port = nullptr, string_view *rest = nullptr); + static bool tokenize(string_view src, string_view *host = nullptr, string_view *port = nullptr + , string_view *rest = nullptr); /** Parse a string for an IP address. @@ -67,16 +83,16 @@ union IPEndpoint { @return @c true on success, @c false otherwise. */ - bool parse(string_view const &str); + bool parse(string_view const& str); /// Invalidate a @c sockaddr. static void invalidate(sockaddr *addr); /// Invalidate this endpoint. - self_type &invalidate(); + self_type& invalidate(); /// Copy constructor. - self_type &operator=(self_type const &that); + self_type& operator=(self_type const& that); /** Copy (assign) the contents of @a src to @a dst. * @@ -92,18 +108,20 @@ union IPEndpoint { /** Assign from a socket address. The entire address (all parts) are copied if the @a ip is valid. */ - self_type &assign(sockaddr const *addr); + self_type& assign(sockaddr const *addr); /// Assign from an @a addr and @a port. - self_type &assign(IPAddr const &addr, in_port_t port = 0); + self_type& assign(IPAddr const& addr, in_port_t port = 0); /// Copy to @a sa. - const self_type &fill(sockaddr *addr) const; + const self_type& fill(sockaddr *addr) const; /// Test for valid IP address. bool is_valid() const; + /// Test for IPv4. bool is_ip4() const; + /// Test for IPv6. bool is_ip6() const; @@ -119,28 +137,34 @@ union IPEndpoint { /// Set to be the ANY address for family @a family. /// @a family must be @c AF_INET or @c AF_INET6. /// @return This object. - self_type &set_to_any(int family); + self_type& set_to_any(int family); /// Set to be loopback address for family @a family. /// @a family must be @c AF_INET or @c AF_INET6. /// @return This object. - self_type &set_to_loopback(int family); + self_type& set_to_loopback(int family); /// Port in network order. - in_port_t &port(); + in_port_t& port(); + /// Port in network order. in_port_t port() const; + /// Port in host horder. in_port_t host_order_port() const; + /// Port in network order from @a sockaddr. - static in_port_t &port(sockaddr *sa); + static in_port_t& port(sockaddr *sa); + /// Port in network order from @a sockaddr. static in_port_t port(sockaddr const *sa); + /// Port in host order directly from a @c sockaddr static in_port_t host_order_port(sockaddr const *sa); /// Automatic conversion to @c sockaddr. operator sockaddr *() { return &sa; } + /// Automatic conversion to @c sockaddr. operator sockaddr const *() const { return &sa; } @@ -154,55 +178,63 @@ union IPEndpoint { class IP4Addr { using self_type = IP4Addr; ///< Self reference type. friend class IP4Range; + public: static constexpr size_t SIZE = sizeof(in_addr_t); ///< Size of IPv4 address in bytes. - static const self_type MIN; - static const self_type MAX; + static constexpr size_t WIDTH = BITSPERBYTE * SIZE; ///< # of bits in an address. + static const self_type MIN; ///< Minimum value. + static const self_type MAX; ///< Maximum value. - constexpr IP4Addr() = default; ///< Default constructor - invalid result. + constexpr IP4Addr() = default; ///< Default constructor - minimum address. /// Construct using IPv4 @a addr (in host order). /// @note Host order seems odd, but all of the standard network macro values such as @c INADDR_LOOPBACK /// are in host order. explicit constexpr IP4Addr(in_addr_t addr); + /// Construct from @c sockaddr_in. explicit IP4Addr(sockaddr_in const *sa); + /// Construct from text representation. /// If the @a text is invalid the result is an invalid instance. - IP4Addr(string_view const &text); + IP4Addr(string_view const& text); + /// Construct from generic address @a addr. explicit IP4Addr(IPAddr const& addr); /// Assign from IPv4 raw address. - self_type &operator=(in_addr_t ip); + self_type& operator=(in_addr_t ip); + /// Set to the address in @a addr. - self_type &operator=(sockaddr_in const *sa); + self_type& operator=(sockaddr_in const *sa); /// Increment address. - self_type &operator++(); + self_type& operator++(); /// Decrement address. - self_type &operator--(); + self_type& operator--(); /** Byte access. * * @param idx Byte index. * @return The byte at @a idx in the address. */ - uint8_t operator [] (unsigned idx) const { + uint8_t operator[](unsigned idx) const { return reinterpret_cast<bytes const&>(_addr)[idx]; } /// Apply @a mask to address, leaving the network portion. - self_type &operator&=(IPMask const& mask); + self_type& operator&=(IPMask const& mask); + /// Apply @a mask to address, creating the broadcast address. - self_type &operator|=(IPMask const& mask); + self_type& operator|=(IPMask const& mask); /// Write this adddress and @a port to the sockaddr @a sa. sockaddr_in *fill(sockaddr_in *sa, in_port_t port = 0) const; /// @return The address in network order. in_addr_t network_order() const; + /// @return The address in host order. in_addr_t host_order() const; @@ -212,10 +244,10 @@ public: @return @c true on success, @c false otherwise. */ - bool load(string_view const &text); + bool load(string_view const& text); /// Standard ternary compare. - int cmp(self_type const &that) const; + int cmp(self_type const& that) const; /// Get the IP address family. /// @return @c AF_INET @@ -227,6 +259,38 @@ public: /// Test for loopback bool is_loopback() const { return (*this)[0] == IN_LOOPBACKNET; } + /** Left shift. + * + * @param n Number of bits to shift left. + * @return @a this. + */ + self_type& operator<<=(unsigned n); + + /** Right shift. + * + * @param n Number of bits to shift right. + * @return @a this. + */ + self_type& operator>>=(unsigned n); + + /** Bitwise AND. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. + */ + self_type& operator&=(self_type const& that); + + /** Bitwise OR. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. + */ + self_type& operator|=(self_type const& that); + /** Convert between network and host order. * * @param src Input address. @@ -241,10 +305,13 @@ protected: /// Access by bytes. using bytes = std::array<uint8_t, 4>; - friend bool operator==(self_type const &, self_type const &); - friend bool operator!=(self_type const &, self_type const &); - friend bool operator<(self_type const &, self_type const &); - friend bool operator<=(self_type const &, self_type const &); + friend bool operator==(self_type const&, self_type const&); + + friend bool operator!=(self_type const&, self_type const&); + + friend bool operator<(self_type const&, self_type const&); + + friend bool operator<=(self_type const&, self_type const&); in_addr_t _addr = INADDR_ANY; ///< Address in host order. }; @@ -255,18 +322,43 @@ protected: class IP6Addr { using self_type = IP6Addr; ///< Self reference type. friend class IP6Range; + + friend class IPMask; + public: - using QUAD = uint16_t; ///< Size of one segment of an IPv6 address. - static constexpr size_t SIZE = 16; ///< Size of address in bytes. - static constexpr size_t WORD_SIZE = sizeof(uint64_t); ///< Size of words used to store address. - static constexpr size_t N_QUADS = SIZE / sizeof(QUAD); ///< # of quads in an IPv6 address. + static constexpr size_t WIDTH = 128; ///< Number of bits in the address. + static constexpr size_t SIZE = WIDTH / BITSPERBYTE; ///< Size of address in bytes. + + using quad_type = uint16_t; ///< Size of one segment of an IPv6 address. + static constexpr size_t N_QUADS = SIZE / sizeof(quad_type); ///< # of quads in an IPv6 address. /// Direct access type for the address. /// Equivalent to the data type for data member @c s6_addr in @c in6_addr. using raw_type = std::array<unsigned char, SIZE>; + /// Direct access type for the address by quads (16 bits). /// This corresponds to the elements of the text format of the address. - using quad_type = std::array<unsigned short, N_QUADS>; + using quad_store_type = std::array<quad_type, N_QUADS>; + + /// Number of bits per quad. + static constexpr size_t QUAD_WIDTH = BITSPERBYTE * sizeof(quad_type); + + /// A bit mask of all 1 bits the size of a quad. + static constexpr quad_type QUAD_MASK = ~quad_type{0}; + + /// Type used as a "word", the natural working unit of the address. + using word_type = uint64_t; + + static constexpr size_t WORD_SIZE = sizeof(word_type); + + /// Number of bits per word. + static constexpr size_t WORD_WIDTH = BITSPERBYTE * WORD_SIZE; + + /// Number of words used for basic address storage. + static constexpr size_t N_STORE = SIZE / sizeof(word_type); + + /// Type used to store the address. + using word_store_type = std::array<word_type, N_STORE>; /// Minimum value of an address. static const self_type MIN; @@ -274,13 +366,16 @@ public: static const self_type MAX; IP6Addr() = default; ///< Default constructor - 0 address. + IP6Addr(self_type const& that) = default; /// Construct using IPv6 @a addr. - explicit IP6Addr(in6_addr const & addr); + explicit IP6Addr(in6_addr const& addr); + /// Construct from @c sockaddr_in. explicit IP6Addr(sockaddr_in6 const *addr) { *this = addr; } + /// Construct from text representation. /// If the @a text is invalid the result is an invalid instance. IP6Addr(string_view const& text); @@ -288,22 +383,55 @@ public: /// Construct from generic @a addr. IP6Addr(IPAddr const& addr); + /** Left shift. + * + * @param n Number of bits to shift left. + * @return @a this. + */ + self_type& operator<<=(unsigned n); + + /** Right shift. + * + * @param n Number of bits to shift right. + * @return @a this. + */ + self_type& operator>>=(unsigned n); + + /** Bitwise AND. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise AND of the corresponding bits in @a this and @a that. + */ + self_type& operator&=(self_type const& that); + + /** Bitwise OR. + * + * @param that Source address. + * @return @a this. + * + * The bits in @a this are set to the bitwise OR of the corresponding bits in @a this and @a that. + */ + self_type& operator|=(self_type const& that); + /// Increment address. - self_type &operator++(); + self_type& operator++(); /// Decrement address. - self_type &operator--(); + self_type& operator--(); /// Assign from IPv6 raw address. - self_type &operator=(in6_addr const& ip); + self_type& operator=(in6_addr const& ip); + /// Set to the address in @a addr. - self_type &operator=(sockaddr_in6 const *addr); + self_type& operator=(sockaddr_in6 const *addr); /// Write to @c sockaddr using network order and @a port. sockaddr *copy_to(sockaddr *sa, in_port_t port = 0) const; /// Copy address to @a addr in network order. - in6_addr & copy_to(in6_addr & addr) const; + in6_addr& copy_to(in6_addr& addr) const; /// Return the address in network order. in6_addr network_order() const; @@ -315,10 +443,10 @@ public: @return @c true on success, @c false otherwise. */ - bool load(string_view const &str); + bool load(string_view const& str); /// Generic compare. - int cmp(self_type const &that) const; + int cmp(self_type const& that) const; /// Get the address family. /// @return The address family. @@ -330,44 +458,61 @@ public: /// Test for loopback bool is_multicast() const { return IN6_IS_ADDR_MULTICAST(_addr._raw.data()); } - self_type & clear() { - _addr._u64[0] = _addr._u64[1] = 0; + self_type& clear() { + _addr._store[0] = _addr._store[1] = 0; return *this; } - static void reorder(in6_addr & dst, raw_type const & src); - static void reorder(raw_type & dst, in6_addr const& src); - static void reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]); + self_type& operator&=(IPMask const& mask); + + self_type& operator|=(IPMask const& mask); + + static void reorder(in6_addr& dst, raw_type const& src); + + static void reorder(raw_type& dst, in6_addr const& src); protected: - friend bool operator==(self_type const &, self_type const &); - friend bool operator!=(self_type const &, self_type const &); - friend bool operator<(self_type const &, self_type const &); - friend bool operator<=(self_type const &, self_type const &); + friend bool operator==(self_type const&, self_type const&); + + friend bool operator!=(self_type const&, self_type const&); + + friend bool operator<(self_type const&, self_type const&); + + friend bool operator<=(self_type const&, self_type const&); /// Type for digging around inside the address, with the various forms of access. union { - uint64_t _u64[2] = {0, 0}; ///< 0 is MSW, 1 is LSW. - quad_type _quad; ///< By quad. + word_store_type _store = {0}; ///< 0 is MSW, 1 is LSW. + quad_store_type _quad; ///< By quad. raw_type _raw; ///< By byte. } _addr; + static constexpr unsigned LSW = 1; ///< Least significant word index. + static constexpr unsigned MSW = 0; ///< Most significant word index. + /// Index of quads in @a _addr._quad. /// This converts from the position in the text format to the quads in the binary format. - static constexpr std::array<unsigned, N_QUADS> QUAD_IDX = { 3,2,1,0,7,6,5,4 }; + static constexpr std::array<unsigned, N_QUADS> QUAD_IDX = {3, 2, 1, 0, 7, 6, 5, 4}; + + static void reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]); /** Construct from two 64 bit values. * * @param msw The most significant 64 bits, host order. * @param lsw The least significant 64 bits, host order. */ - IP6Addr(uint64_t msw, uint64_t lsw) : _addr{msw, lsw} {} + IP6Addr(word_store_type::value_type msw, word_store_type::value_type lsw) : _addr{msw, lsw} {} + + friend IP6Addr operator&(IP6Addr const& addr, IPMask const& mask); + + friend IP6Addr operator|(IP6Addr const& addr, IPMask const& mask); }; /** Storage for an IP address. */ class IPAddr { friend class IPRange; + using self_type = IPAddr; ///< Self reference type. public: IPAddr() = default; ///< Default constructor - invalid result. @@ -375,80 +520,99 @@ public: /// Construct using IPv4 @a addr. explicit IPAddr(in_addr_t addr); + /// Construct using an IPv4 @a addr - IPAddr(IP4Addr const &addr) : _family(AF_INET), _addr{addr} {} + IPAddr(IP4Addr const& addr) : _family(AF_INET), _addr{addr} {} + /// Construct using IPv6 @a addr. - explicit IPAddr(in6_addr const &addr); + explicit IPAddr(in6_addr const& addr); + /// construct using an IPv6 @a addr - IPAddr(IP6Addr const &addr) : _family(AF_INET6), _addr{addr} {} + IPAddr(IP6Addr const& addr) : _family(AF_INET6), _addr{addr} {} + /// Construct from @c sockaddr. explicit IPAddr(sockaddr const *addr); + /// Construct from @c IPEndpoint. - explicit IPAddr(IPEndpoint const &addr); + explicit IPAddr(IPEndpoint const& addr); + /// Construct from text representation. /// If the @a text is invalid the result is an invalid instance. explicit IPAddr(string_view const& text); /// Set to the address in @a addr. - self_type &assign(sockaddr const *addr); + self_type& assign(sockaddr const *addr); + /// Set to the address in @a addr. - self_type &assign(sockaddr_in const *addr); + self_type& assign(sockaddr_in const *addr); + /// Set to the address in @a addr. - self_type &assign(sockaddr_in6 const *addr); + self_type& assign(sockaddr_in6 const *addr); + /// Set to the address in @a addr. - self_type &assign(in_addr_t addr); + self_type& assign(in_addr_t addr); + /// Set to address in @a addr. - self_type &assign(in6_addr const &addr); + self_type& assign(in6_addr const& addr); /// Assign from end point. - self_type &operator=(IPEndpoint const &ip); + self_type& operator=(IPEndpoint const& ip); + /// Assign from IPv4 raw address. - self_type &operator=(in_addr_t ip); + self_type& operator=(in_addr_t ip); + /// Assign from IPv6 raw address. - self_type &operator=(in6_addr const &ip); + self_type& operator=(in6_addr const& ip); + /// Assign from @c sockaddr - self_type &operator=(sockaddr const *addr); + self_type& operator=(sockaddr const *addr); + + self_type& operator&=(IPMask const& mask); - self_type &operator&=(IPMask const& mask); - self_type &operator|=(IPMask const& mask); + self_type& operator|=(IPMask const& mask); - /// Write to @c sockaddr. - sockaddr *fill(sockaddr *sa, in_port_t port = 0) const; - /** Parse a string and load the result in @a this. +/** Parse a string and load the result in @a this. * * @param text Text to parse. * @return @c true on success, @c false otherwise. */ - bool load(string_view const &text); + bool load(string_view const& text); /// Generic compare. - int cmp(self_type const &that) const; + int cmp(self_type const& that) const; /// Test for same address family. /// @c return @c true if @a that is the same address family as @a this. - bool isCompatibleWith(self_type const &that); + bool isCompatibleWith(self_type const& that); /// Get the address family. /// @return The address family. sa_family_t family() const; + /// Test for IPv4. bool is_ip4() const; + /// Test for IPv6. bool is_ip6() const; - in_addr_t network_ip4() const; - in6_addr network_ip6() const; + IP4Addr const& ip4() const; + + IP6Addr const& ip6() const; explicit operator IP4Addr const&() const { return _addr._ip4; } + explicit operator IP4Addr&() { return _addr._ip4; } + explicit operator IP6Addr const&() const { return _addr._ip6; } + explicit operator IP6Addr&() { return _addr._ip6; } + /// Test for validity. bool is_valid() const; /// Make invalid. - self_type &invalidate(); + self_type& invalidate(); /// Test for multicast bool is_multicast() const; @@ -460,7 +624,8 @@ public: static self_type const INVALID; protected: - friend bool operator==(self_type const &, self_type const &); + friend bool operator==(self_type const&, self_type const&); + friend IP4Addr; friend IP6Addr; @@ -472,10 +637,13 @@ protected: uint64_t _u64[IP6Addr::SIZE / sizeof(uint64_t)]; ///< As 64 bit chunks. constexpr raw_addr_type(); + raw_addr_type(in_addr_t addr) : _ip4(addr) {} + raw_addr_type(in6_addr const& addr) : _ip6(addr) {} raw_addr_type(IP4Addr const& addr) : _ip4(addr) {} + raw_addr_type(IP6Addr const& addr) : _ip6(addr) {} } _addr; @@ -488,36 +656,89 @@ protected: */ class IPMask { using self_type = IPMask; ///< Self reference type. - using raw_type = uint8_t; ///< Storage for mask width. + friend class IP4Addr; + + friend class IP6Addr; public: + using raw_type = uint8_t; ///< Storage for mask width. + IPMask() = default; - IPMask(raw_type count, sa_family_t family = AF_INET); - IPMask(string_view text); + explicit IPMask(raw_type count); + + /// @return @c true if the mask is valid, @c false if not. + bool is_valid() const; + + /** Parse mask from @a text. + * + * @param text A number in string format. + * @return @a true if a valid CIDR value, @c false if not. + */ bool load(string_view const& text); - /** Get the CIDR mask wide enough to cover this address. - * @param addr Input address. - * @return Effectively the reverse index of the least significant bit set to 1. + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return The width of the largest network starting at @a addr. + */ + static self_type mask_for(IPAddr const& addr); + + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return A mask with the width of the largest network starting at @a addr. */ - int cidr_of(IPAddr addr); + static self_type mask_for(IP4Addr const& addr); + + /** Copmute a mask for the network at @a addr. + * @param addr Lower bound of network. + * @return A mask with the width of the largest network starting at @a addr. + */ + static self_type mask_for(IP6Addr const& addr); + + /// Force @a this to an invalid state. + self_type& clear() { + _cidr = INVALID; + return *this; + } /// The width of the mask. raw_type width() const; - /// Family type. - sa_family_t family() const { return _family; } + self_type& operator<<=(raw_type n) { + _cidr -= n; + return *this; + } - /// Write the mask as an address to @a addr. - /// @return The filled address. - IPAddr &fill(IPAddr &addr); + self_type& operator>>=(raw_type n) { + _cidr += n; + return *this; + } -private: - raw_type _mask{0}; - sa_family_t _family{AF_UNSPEC}; -}; + /** The mask as an IPv4 address. + * + * @return An IPv4 address that is the mask. + * + * If the mask is wider than an IPv4 address, the maximum mask is returned. + */ + IP4Addr as_ip4() const; + + /** The mask as an IPv6 address. + * + * @return An IPv6 address that is the mask. + * + * If the mask is wider than an IPv6 address, the maximum mask is returned. + */ + IP6Addr as_ip6() const; +protected: + /// Marker value for an invalid mask. + static constexpr auto INVALID = std::numeric_limits<raw_type>::max(); + + raw_type _cidr = INVALID; ///< Mask width in bits. + + /// Compute a partial IPv6 mask, sized for the basic storage type. + static raw_type mask_for_quad(IP6Addr::quad_type q); +}; /** An inclusive range of IPv4 addresses. */ @@ -527,8 +748,6 @@ class IP4Range : public DiscreteRange<IP4Addr> { using metric_type = IP4Addr; public: - using super_type::super_type; ///< Import super class constructors. - /// Default constructor, invalid range. IP4Range() = default; @@ -549,13 +768,17 @@ public: */ IP4Range(string_view const& text); + using super_type::super_type; ///< Import super class constructors. + /** Set @a this range. * * @param addr Minimum address. * @param mask CIDR mask to compute maximum adddress from @a addr. * @return @a this */ - self_type & assign(IP4Addr const& addr, IPMask const& mask); + self_type& assign(IP4Addr const& addr, IPMask const& mask); + + using super_type::assign; ///< Import assign methods. /** Assign to this range from text. * @@ -568,7 +791,86 @@ public: */ bool load(string_view text); + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IP4Range const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IP4Range::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IP4Range; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const& range); + + /// Copy constructor. + NetSource(self_type const& that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + iterator end() const; ///< Past last network. + + /// @return The current network. + IP4Net operator*() const; + + /// Access @a this as if it were an @c IP4Net. + self_type *operator->(); + + /// Iterator support. + /// @areturn The current network address. + IP4Addr const& addr() const; + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const; + + /// Move to next network. + self_type& operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const& that) const; + + /// Inequality. + bool operator!=(self_type const& that) const; + protected: + IP4Range _range; ///< Remaining range. + /// Mask for current network. + IP4Addr _mask{~static_cast<in_addr_t>(0)}; + IPMask::raw_type _cidr = IP4Addr::WIDTH; ///< Current CIDR value. + + void search_wider(); + + void search_narrower(); + + bool is_valid(IP4Addr mask); }; class IP6Range : public DiscreteRange<IP6Addr> { @@ -576,19 +878,31 @@ class IP6Range : public DiscreteRange<IP6Addr> { using super_type = DiscreteRange<IP6Addr>; public: - using super_type::super_type; ///< Import super class constructors. - /// Construct from super type. /// @internal Why do I have to do this, even though the super type constructors are inherited? IP6Range(super_type const& r) : super_type(r) {} + /** Construct range from @a text. + * + * @param text Range text. + * @see IP4Range::load + * + * This results in a zero address if @a text is not a valid string. If this should be checked, + * use @c load. + */ + IP6Range(string_view const& text); + + using super_type::super_type; ///< Import super class constructors. + /** Set @a this range. * * @param addr Minimum address. * @param mask CIDR mask to compute maximum adddress from @a addr. * @return @a this */ - self_type & assign(IP6Addr const& addr, IPMask const& mask); + self_type& assign(IP6Addr const& addr, IPMask const& mask); + + using super_type::assign; ///< Import assign methods. /** Assign to this range from text. * @@ -601,6 +915,84 @@ public: */ bool load(string_view text); + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IP6Range const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IP6Range::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IP6Range; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const& range); + + /// Copy constructor. + NetSource(self_type const& that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + iterator end() const; ///< Past last network. + + /// @return The current network. + IP6Net operator*() const; + + /// Access @a this as if it were an @c IP6Net. + self_type *operator->(); + + /// Iterator support. + /// @areturn The current network address. + IP6Addr const& addr() const { return _range.min(); } + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const { return _mask; } + + /// Move to next network. + self_type& operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const& that) const; + + /// Inequality. + bool operator!=(self_type const& that) const; + +protected: + IP6Range _range; ///< Remaining range. + IPMask _mask{IP6Addr::WIDTH}; ///< Current CIDR value. + + void search_wider(); + + void search_narrower(); + + bool is_valid(IPMask const& mask); }; class IPRange { @@ -608,10 +1000,15 @@ class IPRange { public: /// Default constructor - construct invalid range. IPRange() = default; + + IPRange(IPAddr const& min, IPAddr const& max); + /// Construct from an IPv4 @a range. IPRange(IP4Range const& range); + /// Construct from an IPv6 @a range. IPRange(IP6Range const& range); + /** Construct from a string format. * * @param text Text form of range. @@ -620,12 +1017,18 @@ public: */ IPRange(string_view const& text); + /// @return @c true if this is an IPv4 range, @c false if not. + bool is_ip4() const { return AF_INET == _family; } + + /// @return @c true if this is an IPv6 range, @c false if not. + bool is_ip6() const { return AF_INET6 == _family; } + /** Check if @a this range is the IP address @a family. * * @param family IP address family. * @return @c true if this is @a family, @c false if not. */ - bool is(sa_family_t family) const ; + bool is(sa_family_t family) const; /** Load the range from @a text. * @@ -639,15 +1042,34 @@ public: /// @return The minimum address in the range. IPAddr min() const; + /// @return The maximum address in the range. IPAddr max() const; bool empty() const; - operator IP4Range & () { return _range._ip4; } - operator IP6Range & () { return _range._ip6; } - operator IP4Range const & () const { return _range._ip4; } - operator IP6Range const & () const { return _range._ip6; } + IP4Range const& ip4() const { return _range._ip4; } + IP6Range const& ip6() const { return _range._ip6; } + + class NetSource; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * The returned object can be used as an iterator, or as a container to iterating over + * the unique minimal set of networks that cover @a this range. + * + * @code + * void (IPRange const& range) { + * for ( auto const& net : range ) { + * net.addr(); // network address. + * net.mask(); // network mask; + * } + * } + * @endcode + */ + NetSource networks() const; protected: /** Range container. @@ -663,53 +1085,278 @@ protected: std::monostate _nil; ///< Make constructor easier to implement. IP4Range _ip4; ///< IPv4 range. IP6Range _ip6; ///< IPv6 range. - } _range { std::monostate{} }; + } _range{std::monostate{}}; /// Family of @a _range. - sa_family_t _family { AF_UNSPEC }; + sa_family_t _family{AF_UNSPEC}; +}; + +/** Network generator class. + * This generates networks from a range and acts as both a forward iterator and a container. + */ +class IPRange::NetSource { + using self_type = NetSource; ///< Self reference type. +public: + using range_type = IPRange; ///< Import base range type. + + /// Construct from @a range. + explicit NetSource(range_type const& range); + + /// Copy constructor. + NetSource(self_type const& that) = default; + + /// This class acts as a container and an iterator. + using iterator = self_type; + /// All iteration is constant so no distinction between iterators. + using const_iterator = iterator; + + iterator begin() const; ///< First network. + iterator end() const; ///< Past last network. + + /// @return The current network. + IPNet operator*() const; + + /// Access @a this as if it were an @c IP6Net. + self_type *operator->(); + + /// Iterator support. + /// @areturn The current network address. + IPAddr addr() const; + + /// Iterator support. + /// @return The current network mask. + IPMask mask() const; + + /// Move to next network. + self_type& operator++(); + + /// Move to next network. + self_type operator++(int); + + /// Equality. + bool operator==(self_type const& that) const; + + /// Inequality. + bool operator!=(self_type const& that) const; + +protected: + union { + std::monostate _nil; + IP4Range::NetSource _ip4; + IP6Range::NetSource _ip6; + }; + sa_family_t _family = AF_UNSPEC; +}; + +/// An IPv4 network. +class IP4Net { + using self_type = IP4Net; ///< Self reference type. +public: + IP4Net() = default; ///< Construct invalid network. + IP4Net(self_type const& that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IP4Net(IP4Addr addr, IPMask mask); + + IP4Net(swoc::TextView text) { + this->load(text); + } + + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + + /// @return THh smallest address in the network. + IP4Addr lower_bound() const; + + /// @return The largest address in the network. + IP4Addr upper_bound() const; + + /// @return The mask for the network. + IPMask const& mask() const; + + /// @return A range that exactly covers the network. + IP4Range as_range() const; + + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type& assign(IP4Addr const& addr, IPMask const& mask); + + /// Reset network to invalid state. + self_type& clear() { + _mask.clear(); + return *this; + } + + /// Equality. + bool operator==(self_type const& that) const; + + /// Inequality + bool operator!=(self_type const& that) const; + +protected: + IP4Addr _addr; ///< Network address (also lower_bound). + IPMask _mask; ///< Network mask. +}; + +class IP6Net { + using self_type = IP6Net; ///< Self reference type. +public: + IP6Net() = default; ///< Construct invalid network. + IP6Net(self_type const& that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IP6Net(IP6Addr addr, IPMask mask); + + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); + + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + + /// @return THh smallest address in the network. + IP6Addr lower_bound() const; + + /// @return The largest address in the network. + IP6Addr upper_bound() const; + + /// @return The mask for the network. + IPMask const& mask() const; + + /// @return A range that exactly covers the network. + IP6Range as_range() const; + + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type& assign(IP6Addr const& addr, IPMask const& mask); + + /// Reset network to invalid state. + self_type& clear() { + _mask.clear(); + return *this; + } + + /// Equality. + bool operator==(self_type const& that) const; + + /// Inequality + bool operator!=(self_type const& that) const; + +protected: + IP6Addr _addr; ///< Network address (also lower_bound). + IPMask _mask; ///< Network mask. }; /** Representation of an IP address network. * */ -class IpNet { - using self_type = IpNet; ///< Self reference type. +class IPNet { + using self_type = IPNet; ///< Self reference type. public: - IpNet(); - IpNet(const IPAddr &addr, const IPMask &mask); + IPNet() = default; ///< Construct invalid network. + IPNet(self_type const& that) = default; ///< Copy constructor. + + /** Construct from @a addr and @a mask. + * + * @param addr An address in the network. + * @param mask The mask for the network. + * + * The network is based on the mask, and the resulting network address is chosen such that the + * network will contain @a addr. For a given @a addr and @a mask there is only one network + * that satisifies these criteria. + */ + IPNet(IPAddr const& addr, IPMask const& mask); - operator IPAddr const &() const; - operator IPMask const &() const; + IPNet(TextView text); - IPAddr const &addr() const; + /** Parse network as @a text. + * + * @param text String describing the network in CIDR format. + * @return @c true if a valid string, @c false if not. + */ + bool load(swoc::TextView text); - IPMask const &mask() const; + /// @return @c true if the network is valid, @c false if not. + bool is_valid() const; + /// @return THh smallest address in the network. IPAddr lower_bound() const; + + /// @return The largest address in the network. IPAddr upper_bound() const; - bool contains(IPAddr const &addr) const; + IPMask::raw_type width() const; + + /// @return The mask for the network. + IPMask const& mask() const; - // computes this is strict subset of other - bool is_subnet_of(self_type const &that); + /// @return A range that exactly covers the network. + IPRange as_range() const; - // Check if there are any addresses in both @a this and @a that. - bool intersects(self_type const &that); + bool is_ip4() const { return _addr.is_ip4(); } + bool is_ip6() const { return _addr.is_ip6(); } - self_type &assign(IPAddr const &addr, IPMask const &mask); + sa_family_t family() const { return _addr.family(); } - static char const SEPARATOR; // the character used between the address and mask + IP4Net ip4() const { return IP4Net{_addr.ip4(), _mask}; } + IP6Net ip6() const { return IP6Net{_addr.ip6(), _mask};} - operator ::std::string() const; // implicit - ::std::string ntoa() const; // explicit - // the address width is per octet, the mask width for the bit count - ::std::string ntoa(int addr_width, int mask_width) const; + /** Assign an @a addr and @a mask to @a this. + * + * @param addr Network addres. + * @param mask Network mask. + * @return @a this. + */ + self_type& assign(IPAddr const& addr, IPMask const& mask); + + /// Reset network to invalid state. + self_type& clear() { + _mask.clear(); + return *this; + } + + /// Equality. + bool operator==(self_type const& that) const; - static ::std::string ntoa(IpNet const &net); // DEPRECATED - static IpNet aton(::std::string const &str); // DEPRECATED - use ctor + /// Inequality + bool operator!=(self_type const& that) const; protected: - IPAddr _addr; - IPMask _mask; + IPAddr _addr; ///< Address and family. + IPMask _mask; ///< Network mask. }; // -------------------------------------------------------------------------- @@ -726,7 +1373,7 @@ protected: * - Cheap to copy. * - Comparable via the equality and inequality operators. */ -template <typename PAYLOAD> class IPSpace { +template<typename PAYLOAD> class IPSpace { using self_type = IPSpace; using IP4Space = DiscreteSpace<IP4Addr, PAYLOAD>; using IP6Space = DiscreteSpace<IP6Addr, PAYLOAD>; @@ -745,7 +1392,7 @@ public: * * All addresses in @a r are set to have the @a payload. */ - self_type & mark(IPRange const &range, PAYLOAD const &payload); + self_type& mark(IPRange const& range, PAYLOAD const& payload); /** Fill the @a range with @a payload. * @@ -755,7 +1402,7 @@ public: * * Addresses in @a range are set to have @a payload if the address does not already have a payload. */ - self_type & fill(IPRange const& range, PAYLOAD const& payload); + self_type& fill(IPRange const& range, PAYLOAD const& payload); /** Blend @a color in to the @a range. * @@ -776,17 +1423,17 @@ public: * @c PAYLOAD @a p, @a p is updated by invoking <tt>blender(p, color)</tt>, with the expectation * that @a p will be updated in place. */ - template < typename F , typename U = PAYLOAD > - self_type & blend(IPRange const& range, U const& color, F && blender); + template<typename F, typename U = PAYLOAD> + self_type& blend(IPRange const& range, U const& color, F&& blender); - template < typename F , typename U = PAYLOAD > - self_type & blend(IP4Range const& range, U const& color, F && blender) { + template<typename F, typename U = PAYLOAD> + self_type& blend(IP4Range const& range, U const& color, F&& blender) { _ip4.blend(range, color, blender); return *this; } - template < typename F , typename U = PAYLOAD > - self_type & blend(IP6Range const& range, U const& color, F && blender) { + template<typename F, typename U = PAYLOAD> + self_type& blend(IP6Range const& range, U const& color, F&& blender) { _ip6.blend(range, color, blender); return *this; } @@ -796,7 +1443,7 @@ public: * @param addr Address to find. * @return The payload if any, @c nullptr if the address is not in the space. */ - PAYLOAD *find(IP4Addr const &addr) { + PAYLOAD *find(IP4Addr const& addr) { return _ip4.find(addr); } @@ -805,7 +1452,7 @@ public: * @param addr Address to find. * @return The payload if any, @c nullptr if the address is not in the space. */ - PAYLOAD *find(IP6Addr const &addr) { + PAYLOAD *find(IP6Addr const& addr) { return _ip6.find(addr); } @@ -814,7 +1461,7 @@ public: * @param addr Address to find. * @return The payload if any, @c nullptr if the address is not in the space. */ - PAYLOAD *find(IPAddr const &addr) { + PAYLOAD *find(IPAddr const& addr) { if (addr.is_ip4()) { return _ip4.find(IP4Addr{addr}); } else if (addr.is_ip6()) { @@ -838,26 +1485,33 @@ public: class const_iterator { using self_type = const_iterator; ///< Self reference type. friend class IPSpace; + public: using value_type = std::tuple<IPRange const, PAYLOAD const&>; /// Import for API compliance. // STL algorithm compliance. using iterator_category = std::bidirectional_iterator_tag; using pointer = value_type *; - using reference = value_type &; + using reference = value_type&; using difference_type = int; /// Default constructor. const_iterator() = default; + /// Copy constructor. + const_iterator(self_type const& that); + + /// Assignment. + self_type & operator=(self_type const& that); + /// Pre-increment. /// Move to the next element in the list. /// @return The iterator. - self_type &operator++(); + self_type& operator++(); /// Pre-decrement. /// Move to the previous element in the list. /// @return The iterator. - self_type &operator--(); + self_type& operator--(); /// Post-increment. /// Move to the next element in the list. @@ -875,13 +1529,13 @@ public: /// Dereference. /// @return A pointer to the referent. - value_type const* operator->() const; + value_type const *operator->() const; /// Equality - bool operator==(self_type const &that) const; + bool operator==(self_type const& that) const; /// Inequality - bool operator!=(self_type const &that) const; + bool operator!=(self_type const& that) const; protected: // These are stored non-const to make implementing @c iterator easier. This class provides the @@ -892,11 +1546,11 @@ public: typename IP4Space::iterator _iter_4; ///< IPv4 sub-space iterator. typename IP6Space::iterator _iter_6; ///< IPv6 sub-space iterator. /// Current value. - value_type _value { IPRange{}, *null_payload }; + value_type _value{IPRange{}, *null_payload}; /// Dummy payload. /// @internal Used to initialize @c value_type for invalid iterators. - static constexpr PAYLOAD * null_payload = nullptr; + static constexpr PAYLOAD *null_payload = nullptr; /** Internal constructor. * @@ -905,7 +1559,8 @@ public: * * In practice, both iterators should be either the beginning or ending iterator for the subspace. */ - const_iterator(typename IP4Space::iterator const& iter4, typename IP6Space::iterator const& iter6); + const_iterator(typename IP4Space::iterator const& iter4 + , typename IP6Space::iterator const& iter6); }; /** Iterator. @@ -917,36 +1572,52 @@ public: class iterator : public const_iterator { using self_type = iterator; using super_type = const_iterator; + friend class IPSpace; + public: public: /// Value type of iteration. using value_type = std::tuple<IPRange const, PAYLOAD&>; using pointer = value_type *; - using reference = value_type &; + using reference = value_type&; /// Default constructor. iterator() = default; + /// Copy constructor. + iterator(self_type const& that); + + /// Assignment. + self_type & operator=(self_type const& that); + /// Pre-increment. /// Move to the next element in the list. /// @return The iterator. - self_type &operator++(); + self_type& operator++(); /// Pre-decrement. /// Move to the previous element in the list. /// @return The iterator. - self_type &operator--(); + self_type& operator--(); /// Post-increment. /// Move to the next element in the list. /// @return The iterator value before the increment. - self_type operator++(int) { self_type zret{*this}; ++*this; return zret; } + self_type operator++(int) { + self_type zret{*this}; + ++*this; + return zret; + } /// Post-decrement. /// Move to the previous element in the list. /// @return The iterator value before the decrement. - self_type operator--(int) { self_type zret{*this}; --*this; return zret; } + self_type operator--(int) { + self_type zret{*this}; + --*this; + return zret; + } /// Dereference. /// @return A reference to the referent. @@ -954,7 +1625,7 @@ public: /// Dereference. /// @return A pointer to the referent. - value_type const* operator->() const; + value_type const *operator->() const; protected: using super_type::super_type; /// Inherit supertype constructors. @@ -962,10 +1633,12 @@ public: /// @return A constant iterator to the first element. const_iterator begin() const; + /// @return A constent iterator past the last element. const_iterator end() const; iterator begin() { return iterator{_ip4.begin(), _ip6.begin()}; } + iterator end() { return iterator{_ip4.end(), _ip6.end()}; } protected: @@ -974,15 +1647,31 @@ protected: }; template<typename PAYLOAD> -IPSpace<PAYLOAD>::const_iterator::const_iterator(typename IP4Space::iterator const& iter4, typename IP6Space::iterator const& iter6) : _iter_4(iter4), _iter_6(iter6) { +IPSpace<PAYLOAD>::const_iterator::const_iterator(typename IP4Space::iterator const& iter4 + , typename IP6Space::iterator const& iter6) + : _iter_4(iter4), _iter_6(iter6) { if (_iter_4.has_next()) { new(&_value) value_type{_iter_4->range(), _iter_4->payload()}; } else if (_iter_6.has_next()) { new(&_value) value_type{_iter_6->range(), _iter_6->payload()}; } } + +template<typename PAYLOAD> +IPSpace<PAYLOAD>::const_iterator::const_iterator(self_type const& that) { + *this = that; +} + +template<typename PAYLOAD> +auto IPSpace<PAYLOAD>::const_iterator::operator=(self_type const& that) -> self_type &{ + _iter_4 = that._iter_4; + _iter_6 = that._iter_6; + new(&_value) value_type{that._value}; + return *this; +} + template<typename PAYLOAD> -auto IPSpace<PAYLOAD>::const_iterator::operator++() -> self_type & { +auto IPSpace<PAYLOAD>::const_iterator::operator++() -> self_type& { bool incr_p = false; if (_iter_4.has_next()) { ++_iter_4; @@ -999,7 +1688,7 @@ auto IPSpace<PAYLOAD>::const_iterator::operator++() -> self_type & { return *this; } } - new (&_value) value_type{IPRange{}, *null_payload}; + new(&_value) value_type{IPRange{}, *null_payload}; return *this; } @@ -1011,10 +1700,10 @@ auto IPSpace<PAYLOAD>::const_iterator::operator++(int) -> self_type { } template<typename PAYLOAD> -auto IPSpace<PAYLOAD>::const_iterator::operator--() -> self_type & { +auto IPSpace<PAYLOAD>::const_iterator::operator--() -> self_type& { if (_iter_6.has_prev()) { --_iter_6; - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + new(&_value) value_type{_iter_6->range(), _iter_6->payload()}; return *this; } if (_iter_4.has_prev()) { @@ -1022,7 +1711,7 @@ auto IPSpace<PAYLOAD>::const_iterator::operator--() -> self_type & { new(&_value) value_type{_iter_4->range(), _iter_4->payload()}; return *this; } - new (&_value) value_type{IPRange{}, *null_payload}; + new(&_value) value_type{IPRange{}, *null_payload}; return *this; } @@ -1060,8 +1749,19 @@ IPSpace<PAYLOAD>::const_iterator::operator!=(self_type const& that) const { } template<typename PAYLOAD> -auto IPSpace<PAYLOAD>::iterator::operator->() const -> value_type const* { - return static_cast<value_type*>(&super_type::_value); +IPSpace<PAYLOAD>::iterator::iterator(self_type const& that) { + *this = that; +} + +template<typename PAYLOAD> +auto IPSpace<PAYLOAD>::iterator::operator=(self_type const& that) -> self_type & { + this->super_type::operator=(that); + return *this; +} + +template<typename PAYLOAD> +auto IPSpace<PAYLOAD>::iterator::operator->() const -> value_type const * { + return static_cast<value_type *>(&super_type::_value); } template<typename PAYLOAD> @@ -1070,13 +1770,13 @@ auto IPSpace<PAYLOAD>::iterator::operator*() const -> value_type const& { } template<typename PAYLOAD> -auto IPSpace<PAYLOAD>::iterator::operator++() -> self_type & { +auto IPSpace<PAYLOAD>::iterator::operator++() -> self_type& { this->super_type::operator++(); return *this; } template<typename PAYLOAD> -auto IPSpace<PAYLOAD>::iterator::operator--() -> self_type & { +auto IPSpace<PAYLOAD>::iterator::operator--() -> self_type& { this->super_type::operator--(); return *this; } @@ -1088,13 +1788,13 @@ inline constexpr IPAddr::raw_addr_type::raw_addr_type() : _ip4(INADDR_ANY) {} inline IPAddr::IPAddr(in_addr_t addr) : _family(AF_INET), _addr(addr) {} -inline IPAddr::IPAddr(in6_addr const &addr) : _family(AF_INET6), _addr(addr) {} +inline IPAddr::IPAddr(in6_addr const& addr) : _family(AF_INET6), _addr(addr) {} inline IPAddr::IPAddr(sockaddr const *addr) { this->assign(addr); } -inline IPAddr::IPAddr(IPEndpoint const &addr) { +inline IPAddr::IPAddr(IPEndpoint const& addr) { this->assign(&addr.sa); } @@ -1102,26 +1802,26 @@ inline IPAddr::IPAddr(string_view const& text) { this->load(text); } -inline IPAddr & +inline IPAddr& IPAddr::operator=(in_addr_t addr) { - _family = AF_INET; + _family = AF_INET; _addr._ip4 = addr; return *this; } -inline IPAddr & -IPAddr::operator=(in6_addr const &addr) { - _family = AF_INET6; +inline IPAddr& +IPAddr::operator=(in6_addr const& addr) { + _family = AF_INET6; _addr._ip6 = addr; return *this; } -inline IPAddr & -IPAddr::operator=(IPEndpoint const &addr) { +inline IPAddr& +IPAddr::operator=(IPEndpoint const& addr) { return this->assign(&addr.sa); } -inline IPAddr & +inline IPAddr& IPAddr::operator=(sockaddr const *addr) { return this->assign(addr); } @@ -1142,56 +1842,53 @@ IPAddr::is_ip6() const { } inline bool -IPAddr::isCompatibleWith(self_type const &that) { +IPAddr::isCompatibleWith(self_type const& that) { return this->is_valid() && _family == that._family; } inline bool IPAddr::is_loopback() const { - return (AF_INET == _family && 0x7F == _addr._octet[0]) || (AF_INET6 == _family && IN6_IS_ADDR_LOOPBACK(&_addr._ip6)); + return (AF_INET == _family && 0x7F == _addr._octet[0]) || + (AF_INET6 == _family && IN6_IS_ADDR_LOOPBACK(&_addr._ip6)); } inline bool -operator==(IPAddr const &lhs, IPAddr const &rhs) { - if (lhs._family != rhs._family) +operator==(IPAddr const& lhs, IPAddr const& rhs) { + if (lhs._family != rhs._family) { return false; - switch (lhs._family) - { - case AF_INET: - return lhs._addr._ip4 == rhs._addr._ip4; - case AF_INET6: - return 0 == memcmp(&lhs._addr._ip6, &rhs._addr._ip6, IP6Addr::SIZE); - case AF_UNSPEC: - return true; - default: - break; + } + switch (lhs._family) { + case AF_INET:return lhs._addr._ip4 == rhs._addr._ip4; + case AF_INET6:return 0 == memcmp(&lhs._addr._ip6, &rhs._addr._ip6, IP6Addr::SIZE); + case AF_UNSPEC:return true; + default:break; } return false; } inline bool -operator!=(IPAddr const &lhs, IPAddr const &rhs) { +operator!=(IPAddr const& lhs, IPAddr const& rhs) { return !(lhs == rhs); } -inline IPAddr & +inline IPAddr& IPAddr::assign(in_addr_t addr) { - _family = AF_INET; + _family = AF_INET; _addr._ip4 = addr; return *this; } -inline IPAddr & -IPAddr::assign(in6_addr const &addr) { - _family = AF_INET6; +inline IPAddr& +IPAddr::assign(in6_addr const& addr) { + _family = AF_INET6; _addr._ip6 = addr; return *this; } -inline IPAddr & +inline IPAddr& IPAddr::assign(sockaddr_in const *addr) { if (addr) { - _family = AF_INET; + _family = AF_INET; _addr._ip4 = addr; } else { _family = AF_UNSPEC; @@ -1199,10 +1896,10 @@ IPAddr::assign(sockaddr_in const *addr) { return *this; } -inline IPAddr & +inline IPAddr& IPAddr::assign(sockaddr_in6 const *addr) { if (addr) { - _family = AF_INET6; + _family = AF_INET6; _addr._ip6 = addr->sin6_addr; } else { _family = AF_UNSPEC; @@ -1215,72 +1912,73 @@ IPAddr::is_valid() const { return _family == AF_INET || _family == AF_INET6; } -inline IPAddr & +inline IPAddr& IPAddr::invalidate() { _family = AF_UNSPEC; return *this; } // Associated operators. -bool operator==(IPAddr const &lhs, sockaddr const *rhs); +bool operator==(IPAddr const& lhs, sockaddr const *rhs); + inline bool -operator==(sockaddr const *lhs, IPAddr const &rhs) { +operator==(sockaddr const *lhs, IPAddr const& rhs) { return rhs == lhs; } + inline bool -operator!=(IPAddr const &lhs, sockaddr const *rhs) { +operator!=(IPAddr const& lhs, sockaddr const *rhs) { return !(lhs == rhs); } + inline bool -operator!=(sockaddr const *lhs, IPAddr const &rhs) { +operator!=(sockaddr const *lhs, IPAddr const& rhs) { return !(rhs == lhs); } + inline bool -operator==(IPAddr const &lhs, IPEndpoint const &rhs) { +operator==(IPAddr const& lhs, IPEndpoint const& rhs) { return lhs == &rhs.sa; } + inline bool -operator==(IPEndpoint const &lhs, IPAddr const &rhs) { +operator==(IPEndpoint const& lhs, IPAddr const& rhs) { return &lhs.sa == rhs; } + inline bool -operator!=(IPAddr const &lhs, IPEndpoint const &rhs) { +operator!=(IPAddr const& lhs, IPEndpoint const& rhs) { return !(lhs == &rhs.sa); } + inline bool -operator!=(IPEndpoint const &lhs, IPAddr const &rhs) { +operator!=(IPEndpoint const& lhs, IPAddr const& rhs) { return !(rhs == &lhs.sa); } inline bool -operator<(IPAddr const &lhs, IPAddr const &rhs) { +operator<(IPAddr const& lhs, IPAddr const& rhs) { return -1 == lhs.cmp(rhs); } inline bool -operator>=(IPAddr const &lhs, IPAddr const &rhs) { +operator>=(IPAddr const& lhs, IPAddr const& rhs) { return lhs.cmp(rhs) >= 0; } inline bool -operator>(IPAddr const &lhs, IPAddr const &rhs) { +operator>(IPAddr const& lhs, IPAddr const& rhs) { return 1 == lhs.cmp(rhs); } inline bool -operator<=(IPAddr const &lhs, IPAddr const &rhs) { +operator<=(IPAddr const& lhs, IPAddr const& rhs) { return lhs.cmp(rhs) <= 0; } -inline in_addr_t -IPAddr::network_ip4() const { - return _addr._ip4.network_order(); -} +inline IP4Addr const& IPAddr::ip4() const { return _addr._ip4; } -inline in6_addr -IPAddr::network_ip6() const { - return _addr._ip6.network_order(); -} +inline IP6Addr const& IPAddr::ip6() const { return _addr._ip6; } /// ------------------------------------------------------------------------------------ @@ -1288,11 +1986,11 @@ inline IPEndpoint::IPEndpoint() { sa.sa_family = AF_UNSPEC; } -inline IPEndpoint::IPEndpoint(IPAddr const &addr) { +inline IPEndpoint::IPEndpoint(IPAddr const& addr) { this->assign(addr); } -inline IPEndpoint & +inline IPEndpoint& IPEndpoint::invalidate() { sa.sa_family = AF_UNSPEC; return *this; @@ -1308,19 +2006,19 @@ IPEndpoint::is_valid() const { return sa.sa_family == AF_INET || sa.sa_family == AF_INET6; } -inline IPEndpoint & -IPEndpoint::operator=(self_type const &that) { +inline IPEndpoint& +IPEndpoint::operator=(self_type const& that) { self_type::assign(&sa, &that.sa); return *this; } -inline IPEndpoint & +inline IPEndpoint& IPEndpoint::assign(sockaddr const *src) { self_type::assign(&sa, src); return *this; } -inline IPEndpoint const & +inline IPEndpoint const& IPEndpoint::fill(sockaddr *addr) const { self_type::assign(addr, &sa); return *this; @@ -1341,7 +2039,7 @@ IPEndpoint::family() const { return sa.sa_family; } -inline in_port_t & +inline in_port_t& IPEndpoint::port() { return self_type::port(&sa); } @@ -1356,14 +2054,11 @@ IPEndpoint::host_order_port() const { return ntohs(this->port()); } -inline in_port_t & +inline in_port_t& IPEndpoint::port(sockaddr *sa) { - switch (sa->sa_family) - { - case AF_INET: - return reinterpret_cast<sockaddr_in *>(sa)->sin_port; - case AF_INET6: - return reinterpret_cast<sockaddr_in6 *>(sa)->sin6_port; + switch (sa->sa_family) { + case AF_INET:return reinterpret_cast<sockaddr_in *>(sa)->sin_port; + case AF_INET6:return reinterpret_cast<sockaddr_in6 *>(sa)->sin6_port; } // Force a failure upstream by returning a null reference. return *static_cast<in_port_t *>(nullptr); @@ -1384,20 +2079,41 @@ IPEndpoint::host_order_port(sockaddr const *sa) { inline constexpr IP4Addr::IP4Addr(in_addr_t addr) : _addr(addr) {} inline IP4Addr::IP4Addr(std::string_view const& text) { - if (! this->load(text)) { + if (!this->load(text)) { _addr = INADDR_ANY; } } -inline IP4Addr::IP4Addr(IPAddr const& addr) : _addr(addr._family == AF_INET ? addr._addr._ip4._addr : INADDR_ANY) {} +inline IP4Addr::IP4Addr(IPAddr const& addr) : _addr( + addr._family == AF_INET ? addr._addr._ip4._addr : INADDR_ANY) {} + +inline IP4Addr& IP4Addr::operator<<=(unsigned n) { + _addr <<= n; + return *this; +} + +inline IP4Addr& IP4Addr::operator>>=(unsigned n) { + _addr >>= n; + return *this; +} + +inline IP4Addr& IP4Addr::operator&=(self_type const& that) { + _addr &= that._addr; + return *this; +} + +inline IP4Addr& IP4Addr::operator|=(self_type const& that) { + _addr |= that._addr; + return *this; +} -inline IP4Addr & +inline IP4Addr& IP4Addr::operator++() { ++_addr; return *this; } -inline IP4Addr & +inline IP4Addr& IP4Addr::operator--() { --_addr; return *this; @@ -1412,51 +2128,48 @@ inline in_addr_t IP4Addr::host_order() const { } inline auto -IP4Addr::operator=(in_addr_t ip) -> self_type & { +IP4Addr::operator=(in_addr_t ip) -> self_type& { _addr = ntohl(ip); return *this; } -inline bool operator == (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator==(IP4Addr const& lhs, IP4Addr const& rhs) { return lhs._addr == rhs._addr; } -inline bool operator != (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator!=(IP4Addr const& lhs, IP4Addr const& rhs) { return lhs._addr != rhs._addr; } -inline bool operator < (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator<(IP4Addr const& lhs, IP4Addr const& rhs) { return lhs._addr < rhs._addr; } -inline bool operator <= (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator<=(IP4Addr const& lhs, IP4Addr const& rhs) { return lhs._addr <= rhs._addr; } -inline bool operator > (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator>(IP4Addr const& lhs, IP4Addr const& rhs) { return rhs < lhs; } -inline bool operator >= (IP4Addr const& lhs, IP4Addr const& rhs) { +inline bool operator>=(IP4Addr const& lhs, IP4Addr const& rhs) { return rhs <= lhs; } -inline IP4Addr & IP4Addr::operator&=(IPMask const& mask) { - auto n = std::min<unsigned>(32, mask.width()); - in_addr_t bits = htonl(INADDR_BROADCAST << (32 - n)); - _addr &= bits; +inline IP4Addr& IP4Addr::operator&=(IPMask const& mask) { + _addr &= mask.as_ip4()._addr; return *this; } -inline IP4Addr & IP4Addr::operator|=(IPMask const& mask) { - auto n = std::min<unsigned>(32, mask.width()); - in_addr_t bits = htonl(INADDR_BROADCAST << (32 - n)); - _addr |= bits; +inline IP4Addr& IP4Addr::operator|=(IPMask const& mask) { + _addr |= ~(mask.as_ip4()._addr); return *this; } constexpr in_addr_t IP4Addr::reorder(in_addr_t src) { - return ((src & 0xFF) << 24) | (((src >> 8) & 0xFF) << 16) | (((src >> 16) & 0xFF) << 8) | ((src >> 24) & 0xFF); + return ((src & 0xFF) << 24) | (((src >> 8) & 0xFF) << 16) | (((src >> 16) & 0xFF) << 8) | + ((src >> 24) & 0xFF); } // --- @@ -1466,14 +2179,14 @@ inline IP6Addr::IP6Addr(in6_addr const& addr) { } inline IP6Addr::IP6Addr(std::string_view const& text) { - if (! this->load(text)) { + if (!this->load(text)) { this->clear(); } } inline IP6Addr::IP6Addr(IPAddr const& addr) : _addr{addr._addr._ip6._addr} {} -inline in6_addr& IP6Addr::copy_to(in6_addr & addr) const { +inline in6_addr& IP6Addr::copy_to(in6_addr& addr) const { self_type::reorder(addr, _addr._raw); return addr; } @@ -1483,98 +2196,120 @@ inline in6_addr IP6Addr::network_order() const { return this->copy_to(zret); } -inline auto IP6Addr::operator = (in6_addr const& addr) -> self_type & { +inline auto IP6Addr::operator=(in6_addr const& addr) -> self_type& { self_type::reorder(_addr._raw, addr); return *this; } -inline auto IP6Addr::operator = (sockaddr_in6 const* addr) -> self_type & { +inline auto IP6Addr::operator=(sockaddr_in6 const *addr) -> self_type& { if (addr) { return *this = addr->sin6_addr; } this->clear(); } -inline IP6Addr & +inline IP6Addr& IP6Addr::operator++() { - if (++(_addr._u64[1]) == 0) { - ++(_addr._u64[0]); + if (++(_addr._store[1]) == 0) { + ++(_addr._store[0]); } return *this; } -inline IP6Addr & +inline IP6Addr& IP6Addr::operator--() { - if (--(_addr._u64[1]) == ~static_cast<uint64_t >(0)) { - --(_addr._u64[0]); + if (--(_addr._store[1]) == ~static_cast<uint64_t >(0)) { + --(_addr._store[0]); } return *this; } inline void IP6Addr::reorder(unsigned char dst[WORD_SIZE], unsigned char const src[WORD_SIZE]) { - for ( size_t idx = 0 ; idx < WORD_SIZE ; ++idx ) { + for (size_t idx = 0; idx < WORD_SIZE; ++idx) { dst[idx] = src[WORD_SIZE - (idx + 1)]; } } -inline bool operator == (IP6Addr const& lhs, IP6Addr const& rhs) { - return lhs._addr._u64[0] == rhs._addr._u64[0] && - lhs._addr._u64[1] == rhs._addr._u64[1]; +inline bool operator==(IP6Addr const& lhs, IP6Addr const& rhs) { + return lhs._addr._store[0] == rhs._addr._store[0] && + lhs._addr._store[1] == rhs._addr._store[1]; } -inline bool operator != (IP6Addr const& lhs, IP6Addr const& rhs) { - return lhs._addr._u64[0] != rhs._addr._u64[0] || - lhs._addr._u64[1] != rhs._addr._u64[1]; +inline bool operator!=(IP6Addr const& lhs, IP6Addr const& rhs) { + return lhs._addr._store[0] != rhs._addr._store[0] || + lhs._addr._store[1] != rhs._addr._store[1]; } -inline bool operator < (IP6Addr const& lhs, IP6Addr const& rhs) { - return lhs._addr._u64[0] < rhs._addr._u64[0] || (lhs._addr._u64[0] == rhs._addr._u64[0] && lhs._addr._u64[1] < rhs._addr._u64[1]); +inline bool operator<(IP6Addr const& lhs, IP6Addr const& rhs) { + return lhs._addr._store[0] < rhs._addr._store[0] || + (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] < rhs._addr._store[1]); } -inline bool operator > (IP6Addr const& lhs, IP6Addr const& rhs) { +inline bool operator>(IP6Addr const& lhs, IP6Addr const& rhs) { return rhs < lhs; } -inline bool operator <= (IP6Addr const& lhs, IP6Addr const& rhs) { - return lhs._addr._u64[0] < rhs._addr._u64[0] || (lhs._addr._u64[0] == rhs._addr._u64[0] && lhs._addr._u64[1] <= rhs._addr._u64[1]); +inline bool operator<=(IP6Addr const& lhs, IP6Addr const& rhs) { + return lhs._addr._store[0] < rhs._addr._store[0] || + (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] <= rhs._addr._store[1]); } -inline bool operator >= (IP6Addr const& lhs, IP6Addr const& rhs) { +inline bool operator>=(IP6Addr const& lhs, IP6Addr const& rhs) { return rhs <= lhs; } +inline IP6Addr& IP6Addr::operator&=(IPMask const& mask) { + if (mask._cidr < WORD_WIDTH) { + _addr._store[MSW] &= (~word_type{0} << (WORD_WIDTH - mask._cidr)); + _addr._store[LSW] = 0; + } else if (mask._cidr < WIDTH) { + _addr._store[LSW] &= (~word_type{0} << (2 * WORD_WIDTH - mask._cidr)); + } + return *this; +} + +inline IP6Addr& IP6Addr::operator|=(IPMask const& mask) { + if (mask._cidr < WORD_WIDTH) { + _addr._store[MSW] |= (~word_type{0} >> mask._cidr); + _addr._store[LSW] = ~word_type{0}; + } else if (mask._cidr < WIDTH) { + _addr._store[LSW] |= (~word_type{0} >> (mask._cidr - WORD_WIDTH)); + } + return *this; +} + // Disambiguating comparisons. -inline bool operator == (IPAddr const& lhs, IP4Addr const& rhs) { +inline bool operator==(IPAddr const& lhs, IP4Addr const& rhs) { return lhs.is_ip4() && static_cast<IP4Addr const&>(lhs) == rhs; } -inline bool operator != (IPAddr const& lhs, IP4Addr const& rhs) { - return ! lhs.is_ip4() || static_cast<IP4Addr const&>(lhs) != rhs; +inline bool operator!=(IPAddr const& lhs, IP4Addr const& rhs) { + return !lhs.is_ip4() || static_cast<IP4Addr const&>(lhs) != rhs; } -inline bool operator == (IP4Addr const& lhs, IPAddr const& rhs) { +inline bool operator==(IP4Addr const& lhs, IPAddr const& rhs) { return rhs.is_ip4() && lhs == static_cast<IP4Addr const&>(rhs); } -inline bool operator != (IP4Addr const& lhs, IPAddr const& rhs) { - return ! rhs.is_ip4() || lhs != static_cast<IP4Addr const&>(rhs); +inline bool operator!=(IP4Addr const& lhs, IPAddr const& rhs) { + return !rhs.is_ip4() || lhs != static_cast<IP4Addr const&>(rhs); } -inline bool operator == (IPAddr const& lhs, IP6Addr const& rhs) { +inline bool operator==(IPAddr const& lhs, IP6Addr const& rhs) { return lhs.is_ip6() && static_cast<IP6Addr const&>(lhs) == rhs; } -inline bool operator != (IPAddr const& lhs, IP6Addr const& rhs) { - return ! lhs.is_ip6() || static_cast<IP6Addr const&>(lhs) != rhs; +inline bool operator!=(IPAddr const& lhs, IP6Addr const& rhs) { + return !lhs.is_ip6() || static_cast<IP6Addr const&>(lhs) != rhs; } -inline bool operator == (IP6Addr const& lhs, IPAddr const& rhs) { +inline bool operator==(IP6Addr const& lhs, IPAddr const& rhs) { return rhs.is_ip6() && lhs == static_cast<IP6Addr const&>(rhs); } -inline bool operator != (IP6Addr const& lhs, IPAddr const& rhs) { - return ! rhs.is_ip6() || lhs != static_cast<IP6Addr const&>(rhs); +inline bool operator!=(IP6Addr const& lhs, IPAddr const& rhs) { + return !rhs.is_ip6() || lhs != static_cast<IP6Addr const&>(rhs); } // +++ IPRange +++ @@ -1583,6 +2318,18 @@ inline IP4Range::IP4Range(string_view const& text) { this->load(text); } +inline auto IP4Range::networks() const -> NetSource { + return {NetSource{*this}}; +} + +inline IP6Range::IP6Range(string_view const& text) { + this->load(text); +} + +inline auto IP6Range::networks() const -> NetSource { + return {NetSource{*this}}; +} + inline IPRange::IPRange(IP4Range const& range) : _family(AF_INET) { _range._ip4 = range; } @@ -1595,81 +2342,334 @@ inline IPRange::IPRange(string_view const& text) { this->load(text); } +inline auto IPRange::networks() const -> NetSource { + return {NetSource{*this}}; +} + inline bool IPRange::is(sa_family_t family) const { return family == _family; } -// +++ IpMask +++ +// +++ IPMask +++ + +inline IPMask::IPMask(raw_type width) : _cidr(width) {} -inline IPMask::IPMask(raw_type width, sa_family_t family) : _mask(width), _family(family) {} +inline bool IPMask::is_valid() const { return _cidr < INVALID; } -inline IPMask::raw_type -IPMask::width() const { - return _mask; +inline auto IPMask::width() const -> raw_type { + return _cidr; } inline bool -operator==(IPMask const &lhs, IPMask const &rhs) { +operator==(IPMask const& lhs, IPMask const& rhs) { return lhs.width() == rhs.width(); } inline bool -operator!=(IPMask const &lhs, IPMask const &rhs) { +operator!=(IPMask const& lhs, IPMask const& rhs) { return lhs.width() != rhs.width(); } inline bool -operator<(IPMask const &lhs, IPMask const &rhs) { +operator<(IPMask const& lhs, IPMask const& rhs) { return lhs.width() < rhs.width(); } -inline IpNet::IpNet() {} +inline IP4Addr IPMask::as_ip4() const { + static constexpr auto MASK = ~in_addr_t{0}; + in_addr_t addr = MASK; + if (_cidr < IP4Addr::WIDTH) { + addr <<= IP4Addr::WIDTH - _cidr; + } + return IP4Addr{addr}; +} + +// +++ mixed operators +++ -inline IpNet::IpNet(IPAddr const &addr, IPMask const &mask) : _addr(addr), _mask(mask) {} +inline IP4Addr operator&(IP4Addr const& addr, IPMask const& mask) { + return IP4Addr{addr} &= mask; +} -inline IpNet::operator IPAddr const &() const { - return _addr; +inline IP4Addr operator|(IP4Addr const& addr, IPMask const& mask) { + return IP4Addr{addr} |= mask; } -inline IpNet::operator IPMask const &() const { - return _mask; +inline IP6Addr operator&(IP6Addr const& addr, IPMask const& mask) { + return IP6Addr{addr} &= mask; } -inline IPAddr const & -IpNet::addr() const { - return _addr; +inline IP6Addr operator|(IP6Addr const& addr, IPMask const& mask) { + return IP6Addr{addr} |= mask; +} + +inline IPAddr operator&(IPAddr const& addr, IPMask const& mask) { + return IPAddr{addr} &= mask; +} + +inline IPAddr operator|(IPAddr const& addr, IPMask const& mask) { + return IPAddr{addr} |= mask; +} + +// +++ IPNet +++ + +inline IP4Net::IP4Net(swoc::IP4Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} + +inline IPMask const& IP4Net::mask() const { return _mask; } + +inline bool IP4Net::is_valid() const { return _mask.is_valid(); } + +inline IP4Addr IP4Net::lower_bound() const { return _addr; } + +inline IP4Addr IP4Net::upper_bound() const { return _addr | _mask; } + +inline IP4Range IP4Net::as_range() const { return {this->lower_bound(), this->upper_bound()}; } + +inline bool IP4Net::operator==(self_type const& that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool IP4Net::operator!=(self_type const& that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline IP4Net::self_type& IP4Net::assign(IP4Addr const& addr, IPMask const& mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline IP6Net::IP6Net(swoc::IP6Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} + +inline IPMask const& IP6Net::mask() const { return _mask; } + +inline bool IP6Net::is_valid() const { return _mask.is_valid(); } + +inline IP6Addr IP6Net::lower_bound() const { return _addr; } + +inline IP6Addr IP6Net::upper_bound() const { return _addr | _mask; } + +inline IP6Range IP6Net::as_range() const { return {this->lower_bound(), this->upper_bound()}; } + +inline bool IP6Net::operator==(self_type const& that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool IP6Net::operator!=(self_type const& that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline IP6Net::self_type& IP6Net::assign(IP6Addr const& addr, IPMask const& mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline IPNet::IPNet(IPAddr const& addr, IPMask const& mask) : _addr(addr & mask), _mask(mask) {} + +inline IPNet::IPNet(TextView text) { this->load(text); } + +inline bool IPNet::is_valid() const { return _mask.is_valid(); } + +inline IPAddr IPNet::lower_bound() const { return _addr; } + +inline IPAddr IPNet::upper_bound() const { return _addr | _mask; } + +inline IPMask::raw_type IPNet::width() const { return _mask.width(); } + +inline IPMask const& IPNet::mask() const { return _mask; } + +inline IPRange IPNet::as_range() const { return {this->lower_bound(), this->upper_bound()};} + +inline IPNet::self_type& IPNet::assign(IPAddr const& addr, IPMask const& mask) { + _addr = addr & mask; + _mask = mask; + return *this; +} + +inline bool IPNet::operator==(IPNet::self_type const& that) const { + return _mask == that._mask && _addr == that._addr; +} + +inline bool IPNet::operator!=(IPNet::self_type const& that) const { + return _mask != that._mask || _addr != that._addr; +} + +inline bool operator == (IPNet const& lhs, IP4Net const& rhs) { + return lhs.is_ip4() && lhs.ip4() == rhs; +} + +inline bool operator == (IP4Net const& lhs, IPNet const& rhs) { + return rhs.is_ip4() && rhs.ip4() == lhs; +} + +inline bool operator == (IPNet const& lhs, IP6Net const& rhs) { + return lhs.is_ip6() && lhs.ip6() == rhs; +} + +inline bool operator == (IP6Net const& lhs, IPNet const& rhs) { + return rhs.is_ip6() && rhs.ip6() == lhs; +} + +// +++ Range -> Network classes +++ + +inline bool IP4Range::NetSource::is_valid(swoc::IP4Addr mask) { + return ((mask._addr & _range._min._addr) == _range._min._addr) && + ((_range._min._addr | ~mask._addr) <= _range._max._addr); +} + +inline IP4Net IP4Range::NetSource::operator*() const { + return IP4Net{_range.min(), IPMask{_cidr}}; +} + +inline IP4Range::NetSource::iterator IP4Range::NetSource::begin() const { + return *this; +} + +inline IP4Range::NetSource::iterator IP4Range::NetSource::end() const { + return self_type{range_type{}}; +} + +inline IPMask IP4Range::NetSource::mask() const { return IPMask{_cidr}; } + +inline auto IP4Range::NetSource::operator->() -> self_type * { return this; } + +inline IP4Addr const& IP4Range::NetSource::addr() const { return _range.min(); } + +inline bool IP4Range::NetSource::operator==(IP4Range::NetSource::self_type const& that) const { + return ((_cidr == that._cidr) && (_range == that._range)) || + (_range.empty() && that._range.empty()); +} + +inline bool IP4Range::NetSource::operator!=(IP4Range::NetSource::self_type const& that) const { + return !(*this == that); +} + +inline auto IP6Range::NetSource::begin() const -> iterator { + return *this; +} + +inline auto IP6Range::NetSource::end() const -> iterator { + return self_type{range_type{}}; +} + +inline IP6Net IP6Range::NetSource::operator*() const { + return IP6Net{_range.min(), _mask}; +} + +inline auto IP6Range::NetSource::operator->() -> self_type * { return this; } + +inline bool IP6Range::NetSource::is_valid(IPMask const& mask) { + return ((_range.min() & mask) == _range.min()) && + ((_range.min() | mask) <= _range.max()); +} + +inline bool IP6Range::NetSource::operator==(IP6Range::NetSource::self_type const& that) const { + return ((_mask == that._mask) && (_range == that._range)) || + (_range.empty() && that._range.empty()); +} + +inline bool IP6Range::NetSource::operator!=(IP6Range::NetSource::self_type const& that) const { + return !(*this == that); +} + +inline IPRange::NetSource::NetSource(IPRange::NetSource::range_type const& range) { + if (range.is_ip4()) { + new (&_ip4) decltype(_ip4)(range.ip4()); + _family = AF_INET; + } else if (range.is_ip6()) { + new (&_ip6) decltype(_ip6)(range.ip6()); + _family = AF_INET6; + } +} + +inline auto IPRange::NetSource::begin() const -> iterator { + return *this; +} + +inline auto IPRange::NetSource::end() const -> iterator { + return AF_INET == _family ? self_type{IP4Range{}} + : AF_INET6 == _family ? self_type{IP6Range{}} + : self_type{IPRange{}}; +} + +inline IPAddr IPRange::NetSource::addr() const { + if (AF_INET == _family) { + return _ip4.addr(); + } else if (AF_INET6 == _family) { + return _ip6.addr(); + } + return {}; } -inline IPMask const & -IpNet::mask() const { - return _mask; +inline IPMask IPRange::NetSource::mask() const { + if (AF_INET == _family) { + return _ip4.mask(); + } else if (AF_INET6 == _family) { + return _ip6.mask(); + } + return {}; +} + +inline IPNet IPRange::NetSource::operator*() const { + return { this->addr(), this->mask() }; +} + +inline auto IPRange::NetSource::operator++() -> self_type & { + if (AF_INET == _family) { + ++_ip4; + } else if (AF_INET6 == _family) { + ++_ip6; + } + return *this; +} + +inline auto IPRange::NetSource::operator->() -> self_type * { return this; } + +inline bool IPRange::NetSource::operator==(self_type const& that) const { + if (_family != that._family) { + return false; + } + if (AF_INET == _family) { + return _ip4 == that._ip4; + } else if (AF_INET6 == _family) { + return _ip6 == that._ip6; + } else if (AF_UNSPEC == _family) { + return true; + } + return false; +} + +inline bool IPRange::NetSource::operator!=(self_type const& that) const { + return !(*this == that); } // --- IPSpace -template < typename PAYLOAD > auto IPSpace<PAYLOAD>::mark(IPRange const &range, PAYLOAD const &payload) -> self_type & { +template<typename PAYLOAD> +auto IPSpace<PAYLOAD>::mark(IPRange const& range, PAYLOAD const& payload) -> self_type& { if (range.is(AF_INET)) { - _ip4.mark(range, payload); + _ip4.mark(range.ip4(), payload); } else if (range.is(AF_INET6)) { - _ip6.mark(range, payload); + _ip6.mark(range.ip6(), payload); } return *this; } -template < typename PAYLOAD > auto IPSpace<PAYLOAD>::fill(IPRange const &range, PAYLOAD const &payload) -> self_type & { +template<typename PAYLOAD> +auto IPSpace<PAYLOAD>::fill(IPRange const& range, PAYLOAD const& payload) -> self_type& { if (range.is(AF_INET6)) { - _ip6.fill(range, payload); + _ip6.fill(range.ip6(), payload); } else if (range.is(AF_INET)) { - _ip4.fill(range, payload); + _ip4.fill(range.ip4(), payload); } return *this; } template<typename PAYLOAD> -template<typename F, typename U > -auto IPSpace<PAYLOAD>::blend(IPRange const&range, U const&color, F&&blender) -> self_type & { +template<typename F, typename U> +auto IPSpace<PAYLOAD>::blend(IPRange const& range, U const& color, F&& blender) -> self_type& { if (range.is(AF_INET)) { - _ip4.blend(range, color, blender); + _ip4.blend(range.ip4(), color, blender); } else if (range.is(AF_INET6)) { - _ip6.blend(range, color, blender); + _ip6.blend(range.ip6(), color, blender); } return *this; } @@ -1682,15 +2682,103 @@ void IPSpace<PAYLOAD>::clear() { template<typename PAYLOAD> auto IPSpace<PAYLOAD>::begin() const -> const_iterator { - auto nc_this = const_cast<self_type*>(this); + auto nc_this = const_cast<self_type *>(this); return const_iterator(nc_this->_ip4.begin(), nc_this->_ip6.begin()); } template<typename PAYLOAD> auto IPSpace<PAYLOAD>::end() const -> const_iterator { - auto nc_this = const_cast<self_type*>(this); + auto nc_this = const_cast<self_type *>(this); return const_iterator(nc_this->_ip4.end(), nc_this->_ip6.end()); } +} // namespace swoc + +namespace std { + +// -- Tuple support for IP4Net -- +template<> class tuple_size<swoc::IP4Net> : public std::integral_constant<size_t, 2> { +}; + +template<size_t IDX> class tuple_element<IDX, swoc::IP4Net> { + static_assert("swoc::IP4Net tuple index out of range"); +}; + +template<> class tuple_element<0, swoc::IP4Net> { +public: + using type = swoc::IP4Addr; +}; + +template<> class tuple_element<1, swoc::IP4Net> { +public: + using type = swoc::IPMask; +}; + +// -- Tuple support for IP6Net -- +template<> class tuple_size<swoc::IP6Net> : public std::integral_constant<size_t, 2> { +}; + +template<size_t IDX> class tuple_element<IDX, swoc::IP6Net> { + static_assert("swoc::IP6Net tuple index out of range"); +}; + +template<> class tuple_element<0, swoc::IP6Net> { +public: + using type = swoc::IP6Addr; +}; + +template<> class tuple_element<1, swoc::IP6Net> { +public: + using type = swoc::IPMask; +}; + +// -- Tuple support for IPNet -- +template<> class tuple_size<swoc::IPNet> : public std::integral_constant<size_t, 2> { +}; + +template<size_t IDX> class tuple_element<IDX, swoc::IPNet> { + static_assert("swoc::IPNet tuple index out of range"); +}; + +template<> class tuple_element<0, swoc::IPNet> { +public: + using type = swoc::IPAddr; +}; + +template<> class tuple_element<1, swoc::IPNet> { +public: + using type = swoc::IPMask; +}; + +} // namespace std + +namespace swoc { + +template<size_t IDX> typename std::tuple_element<IDX, IP4Net>::type +get(swoc::IP4Net const& net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} + +template<size_t IDX> typename std::tuple_element<IDX, IP6Net>::type +get(swoc::IP6Net const& net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} + +template<size_t IDX> typename std::tuple_element<IDX, IPNet>::type +get(swoc::IPNet const& net) { + if constexpr (IDX == 0) { + return net.lower_bound(); + } else if constexpr (IDX == 1) { + return net.mask(); + } +} } // namespace swoc diff --git a/swoc++/src/bw_ip_format.cc b/swoc++/src/bw_ip_format.cc index e0e8b58..9da2a77 100644 --- a/swoc++/src/bw_ip_format.cc +++ b/swoc++/src/bw_ip_format.cc @@ -280,7 +280,7 @@ bwformat(BufferWriter &w, Spec const &spec, IPAddr const &addr) if (addr.is_ip4()) { swoc::bwformat(w, spec, static_cast<IP4Addr const&>(addr)); } else if (addr.is_ip6()) { - swoc::bwformat(w, spec, addr.network_ip6()); + swoc::bwformat(w, spec, addr.ip6().network_order()); } else { w.print("*Not IP address [{}]*", addr.family()); } @@ -317,9 +317,9 @@ bwformat(BufferWriter & w, Spec const& spec, IP6Range const& range) { BufferWriter & bwformat(BufferWriter & w, Spec const& spec, IPRange const& range) { return range.is(AF_INET) - ? bwformat(w, spec, static_cast<IP4Range const&>(range)) + ? bwformat(w, spec, range.ip4()) : range.is(AF_INET6) - ? bwformat(w, spec, static_cast<IP6Range const&>(range)) + ? bwformat(w, spec, range.ip6()) : w.write("*-*"_tv) ; } diff --git a/swoc++/src/swoc_ip.cc b/swoc++/src/swoc_ip.cc index 7fd4264..59075a6 100644 --- a/swoc++/src/swoc_ip.cc +++ b/swoc++/src/swoc_ip.cc @@ -81,7 +81,7 @@ IPEndpoint::assign(IPAddr const&src, in_port_t port) { case AF_INET: { memset(&sa4, 0, sizeof sa4); sa4.sin_family = AF_INET; - sa4.sin_addr.s_addr = src.network_ip4(); + sa4.sin_addr.s_addr = src.ip4().network_order(); sa4.sin_port = port; Set_Sockaddr_Len(&sa4); } @@ -89,7 +89,7 @@ IPEndpoint::assign(IPAddr const&src, in_port_t port) { case AF_INET6: { memset(&sa6, 0, sizeof sa6); sa6.sin6_family = AF_INET6; - sa6.sin6_addr = src.network_ip6(); + sa6.sin6_addr = src.ip6().network_order(); sa6.sin6_port = port; Set_Sockaddr_Len(&sa6); } @@ -98,19 +98,6 @@ IPEndpoint::assign(IPAddr const&src, in_port_t port) { return *this; } -IPAddr& -IPAddr::assign(sockaddr const *addr) { - if (addr) { - switch (addr->sa_family) { - case AF_INET:return this->assign(reinterpret_cast<sockaddr_in const *>(addr)); - case AF_INET6:return this->assign(reinterpret_cast<sockaddr_in6 const *>(addr)); - default:break; - } - } - _family = AF_UNSPEC; - return *this; -} - bool IPEndpoint::tokenize(std::string_view str, std::string_view *addr, std::string_view *port , std::string_view *rest) { @@ -190,6 +177,7 @@ IPEndpoint::parse(std::string_view const&str) { return false; } +#if 0 sockaddr * IPAddr::fill(sockaddr *sa, in_port_t port) const { switch (sa->sa_family = _family) { @@ -211,6 +199,7 @@ IPAddr::fill(sockaddr *sa, in_port_t port) const { } return sa; } +#endif socklen_t IPEndpoint::size() const { @@ -302,6 +291,46 @@ sockaddr_in *IP4Addr::fill(sockaddr_in *sa, in_port_t port) const { return sa; } +IP6Addr& IP6Addr::operator<<=(unsigned int n) { + static constexpr auto MASK = ~word_type{0}; + if (n < WORD_WIDTH) { + _addr._store[MSW] <<= n; + _addr._store[MSW] |= (_addr._store[LSW] >> (WORD_WIDTH - n)) & ~(MASK << n); + _addr._store[LSW] <<= n; + } else { + n -= WORD_WIDTH; + _addr._store[MSW] = _addr._store[LSW] << n; + _addr._store[LSW] = 0; + } + return *this; +} + +IP6Addr& IP6Addr::operator>>=(unsigned int n) { + static constexpr auto MASK = ~word_type{0}; + if (n < WORD_WIDTH) { + _addr._store[LSW] >>= n; + _addr._store[LSW] |= (_addr._store[MSW] & ~(MASK << n)) << (WORD_WIDTH - n); + _addr._store[MSW] >>= n; + } else { + n -= WORD_WIDTH; + _addr._store[LSW] = _addr._store[MSW] >> n; + _addr._store[MSW] = 0; + } + return *this; +} + +IP6Addr& IP6Addr::operator&=(self_type const& that) { + _addr._store[MSW] &= that._addr._store[MSW]; + _addr._store[LSW] &= that._addr._store[LSW]; + return *this; +} + +IP6Addr& IP6Addr::operator|=(self_type const& that) { + _addr._store[MSW] |= that._addr._store[MSW]; + _addr._store[LSW] |= that._addr._store[LSW]; + return *this; +} + bool IP6Addr::load(std::string_view const&str) { TextView src{str}; @@ -329,8 +358,8 @@ IP6Addr::load(std::string_view const&str) { this->clear(); return true; } else if (src.size() == 3 && src[2] == '1') { - _addr._u64[0] = 0; - _addr._u64[1] = 1; + _addr._store[0] = 0; + _addr._store[1] = 1; return true; } else { empty_idx = n; @@ -424,6 +453,37 @@ IPAddr::load(const std::string_view&text) { return this->is_valid(); } +IPAddr& +IPAddr::assign(sockaddr const *addr) { + if (addr) { + switch (addr->sa_family) { + case AF_INET:return this->assign(reinterpret_cast<sockaddr_in const *>(addr)); + case AF_INET6:return this->assign(reinterpret_cast<sockaddr_in6 const *>(addr)); + default:break; + } + } + _family = AF_UNSPEC; + return *this; +} + +IPAddr::self_type& IPAddr::operator&=(IPMask const& mask) { + if (_family == AF_INET) { + _addr._ip4 &= mask; + } else if (_family == AF_INET6) { + _addr._ip6 &= mask; + } + return *this; +} + +IPAddr::self_type& IPAddr::operator|=(IPMask const& mask) { + if (_family == AF_INET) { + _addr._ip4 |= mask; + } else if (_family == AF_INET6) { + _addr._ip6 |= mask; + } + return *this; +} + #if 0 bool operator==(IPAddr const &lhs, sockaddr const *rhs) @@ -512,14 +572,132 @@ IPEndpoint::IPEndpoint(std::string_view const&text) { bool IPMask::load(string_view const&text) { TextView parsed; - _mask = swoc::svtou(text, &parsed); + _cidr = swoc::svtou(text, &parsed); if (parsed.size() != text.size()) { - _mask = 0; + _cidr = 0; return false; } return true; } +IPMask IPMask::mask_for(IPAddr const&addr) { + if (addr.is_ip4()) { + return self_type::mask_for(static_cast<IP4Addr const&>(addr)); + } else if (addr.is_ip6()) { + return self_type::mask_for(static_cast<IP6Addr const&>(addr)); + } + return {}; +} + +auto IPMask::mask_for_quad(IP6Addr::quad_type q) -> raw_type { + raw_type cidr = IP6Addr::QUAD_WIDTH; + if (q != 0) { + auto mask = IP6Addr::QUAD_MASK; + do { + mask <<= 1; + --cidr; + } while ((q | ~mask) == q); + ++cidr; // loop goes exactly 1 too far. + } + return cidr; +} + +IPMask IPMask::mask_for(IP4Addr const& addr) { + auto n = addr.host_order(); + raw_type cidr = 0; + if (auto q = (n & IP6Addr::QUAD_MASK) ; q != 0) { + cidr = IP6Addr::QUAD_WIDTH + self_type::mask_for_quad(q); + } else if (auto q = ((n >> IP6Addr::QUAD_WIDTH) & IP6Addr::QUAD_MASK) ; q != 0) { + cidr = self_type::mask_for_quad(q); + } + return self_type(cidr); +} + +IPMask IPMask::mask_for(IP6Addr const& addr) { + auto cidr = IP6Addr::WIDTH; + for ( unsigned idx = IP6Addr::N_QUADS ; idx > 0 ; ) { + auto q = addr._addr._quad[IP6Addr::QUAD_IDX[--idx]]; + cidr -= IP6Addr::QUAD_WIDTH; + if (q != 0) { + cidr += self_type::mask_for_quad(q); + break; + } + } + return self_type(cidr); +} + +IP6Addr IPMask::as_ip6() const { + static constexpr auto MASK = ~IP6Addr::word_type{0}; + if (_cidr <= IP6Addr::WORD_WIDTH) { + return { MASK << (IP6Addr::WORD_WIDTH - _cidr), 0}; + } else if (_cidr < 2 * IP6Addr::WORD_WIDTH) { + return {MASK, MASK << (2 * IP6Addr::WORD_WIDTH - _cidr)}; + } + return { MASK, MASK }; +} + +// ++ IPNet ++ + +bool IP4Net::load(TextView text) { + auto idx = text.find('/'); + if (idx != text.npos) { + if (idx + 1 < text.size()) { // must have something past the separator or it's bogus. + IP4Addr addr; + if (addr.load(text.substr(0, idx))) { // load the address + IPMask mask; + text.remove_prefix(idx + 1); // drop address and separator. + if (mask.load(text)) { + this->assign(addr, mask); + return true; + } + } + } + } + + this->clear(); + return false; +} + +bool IP6Net::load(TextView text) { + auto idx = text.find('/'); + if (idx != text.npos) { + if (idx + 1 < text.size()) { // must have something past the separator or it's bogus. + IP6Addr addr; + if (addr.load(text.substr(0, idx))) { // load the address + IPMask mask; + text.remove_prefix(idx + 1); // drop address and separator. + if (mask.load(text)) { + this->assign(addr, mask); + return true; + } + } + } + } + + this->clear(); + return false; +} + +bool IPNet::load(TextView text) { + auto mask_text = text.split_suffix_at('/'); + if (!mask_text.empty()) { + IPMask mask; + if (mask.load(mask_text)) { + if (IP6Addr addr; addr.load(text)) { // load the address + this->assign(addr, mask); + return true; + } else if (IP4Addr addr; addr.load(text)) { + this->assign(addr, mask); + return true; + } + } + } + this->clear(); + return false; +} + +// +++ IP4Range +++ + IP4Range::IP4Range(swoc::IP4Addr const&addr, swoc::IPMask const&mask) { this->assign(addr, mask); } @@ -568,6 +746,50 @@ bool IP4Range::load(string_view text) { return false; } +IP4Range::NetSource::NetSource(IP4Range::NetSource::range_type const&range) : _range(range) { + if (!_range.empty()) { + this->search_wider(); + } +} + +auto IP4Range::NetSource::operator++() -> self_type & { + auto upper (IP4Addr{_range._min._addr | ~_mask._addr} ); + if (upper >= _range.max()) { + _range.clear(); + } else { + _range.assign_min(++upper); + // @a _range is not empty, because there's at least one address still not covered. + if (this->is_valid(_mask)) { + this->search_wider(); + } else { + this->search_narrower(); + } + } + return *this; +} + +void IP4Range::NetSource::search_wider() { + while (_cidr > 0) { + auto m = _mask; + m <<= 1; + if (this->is_valid(m)) { + _mask = m; + --_cidr; + } else { + break; + } + } +} + +void IP4Range::NetSource::search_narrower() { + while (! this->is_valid(_mask)) { + _mask._addr = (_mask._addr >>= 1) | (1<<(IP4Addr::WIDTH-1)); + ++_cidr; + } +} + +// +++ IP6Range +++ + IP6Range&IP6Range::assign(IP6Addr const&addr, IPMask const&mask) { static constexpr auto FULL_MASK{std::numeric_limits<uint64_t>::max()}; auto cidr = mask.width(); @@ -576,20 +798,20 @@ IP6Range&IP6Range::assign(IP6Addr const&addr, IPMask const&mask) { _max = metric_type::MAX; } else if (cidr < 64) { // only upper bytes affected, lower bytes are forced. auto bits = FULL_MASK << (64 - cidr); - _min._addr._u64[0] = addr._addr._u64[0] & bits; - _min._addr._u64[1] = 0; - _max._addr._u64[0] = addr._addr._u64[0] | ~bits; - _max._addr._u64[1] = FULL_MASK; + _min._addr._store[0] = addr._addr._store[0] & bits; + _min._addr._store[1] = 0; + _max._addr._store[0] = addr._addr._store[0] | ~bits; + _max._addr._store[1] = FULL_MASK; } else if (cidr == 64) { - _min._addr._u64[0] = _max._addr._u64[0] = addr._addr._u64[0]; - _min._addr._u64[1] = 0; - _max._addr._u64[1] = FULL_MASK; + _min._addr._store[0] = _max._addr._store[0] = addr._addr._store[0]; + _min._addr._store[1] = 0; + _max._addr._store[1] = FULL_MASK; } else if (cidr <= 128) { // _min bytes changed, _max bytes unaffected. _min = _max = addr; if (cidr < 128) { auto bits = FULL_MASK << (128 - cidr); - _min._addr._u64[1] &= bits; - _max._addr._u64[1] |= ~bits; + _min._addr._store[1] &= bits; + _max._addr._store[1] |= ~bits; } } return *this; @@ -622,6 +844,16 @@ bool IP6Range::load(std::string_view text) { return false; } +IPRange::IPRange(IPAddr const& min, IPAddr const& max) { + if (min.is_ip4() && max.is_ip4()) { + _range._ip4.assign(min.ip4(), max.ip4()); + _family = AF_INET; + } else if (min.is_ip6() && max.is_ip6()) { + _range._ip6.assign(min.ip6(), max.ip6()); + _family = AF_INET6; + } +} + bool IPRange::load(std::string_view const&text) { static const string_view CHARS{".:"}; auto idx = text.find_first_of(CHARS); @@ -668,4 +900,44 @@ bool IPRange::empty() const { return true; } +IP6Range::NetSource::NetSource(IP6Range::NetSource::range_type const&range) : _range(range) { + if (!_range.empty()) { + this->search_wider(); + } +} + +auto IP6Range::NetSource::operator++() -> self_type & { + auto upper = _range.min() | _mask; + if (upper >= _range.max()) { + _range.clear(); + } else { + _range.assign_min(++upper); + // @a _range is not empty, because there's at least one address still not covered. + if (this->is_valid(_mask)) { + this->search_wider(); + } else { + this->search_narrower(); + } + } + return *this; +} + +void IP6Range::NetSource::search_wider() { + while (_mask.width() > 0) { + auto m = _mask; + m <<= 1; + if (this->is_valid(m)) { + _mask = m; + } else { + break; + } + } +} + +void IP6Range::NetSource::search_narrower() { + while (! this->is_valid(_mask)) { + _mask >>= 1; + } +} + } // namespace swoc diff --git a/swoc++/swoc++-headers.part b/swoc++/swoc++-headers.part new file mode 100644 index 0000000..4c61a89 --- /dev/null +++ b/swoc++/swoc++-headers.part @@ -0,0 +1,6 @@ +Import("*") +PartName("headers") + +# export the include directory +inc_files = Pattern(src_dir="include", includes=["swoc/*.h"]) +env.InstallInclude(inc_files) diff --git a/swoc++/swoc++-shared.part b/swoc++/swoc++-shared.part index df0b7d3..7d52392 100644 --- a/swoc++/swoc++-shared.part +++ b/swoc++/swoc++-shared.part @@ -1,6 +1,8 @@ Import("*") PartName("shared") +DependsOn([ Component("libswoc.headers") ]) + src_files = env.get("src_files") env.AppendUnique( diff --git a/swoc++/swoc++-static.part b/swoc++/swoc++-static.part index e7defe3..02b1759 100644 --- a/swoc++/swoc++-static.part +++ b/swoc++/swoc++-static.part @@ -1,6 +1,8 @@ Import("*") PartName("static") +DependsOn([ Component("libswoc.headers") ]) + src_files = env.get("src_files") env.AppendUnique( diff --git a/swoc++/swoc++.part b/swoc++/swoc++.part index 4c4df61..83c9d5d 100644 --- a/swoc++/swoc++.part +++ b/swoc++/swoc++.part @@ -14,15 +14,6 @@ src_files = [ "src/TextView.cc", ] -# export the include directory -inc_files = Pattern(src_dir="include/swoc", includes=["*.h"]) -#env.InstallInclude(env.get("inc_files")) -env.InstallInclude(inc_files) - -env.AppendUnique( - CCFLAGS=['-std=c++17' ], - CPPPATH=["include"], -) - -env.Part("swoc++-static.part", package_group="libswoc", src_files=src_files, inc_files=inc_files) -env.Part("swoc++-shared.part", package_group="libswoc", src_files=src_files, inc_files=inc_files) +env.Part("swoc++-static.part", package_group="libswoc", src_files=src_files) +env.Part("swoc++-shared.part", package_group="libswoc", src_files=src_files) +env.Part("swoc++-headers.part", package_group="libswoc") diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index fd17d9f..39de7a7 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -28,4 +28,4 @@ add_executable(test_libswoc target_link_libraries(test_libswoc PUBLIC swoc++) set_target_properties(test_libswoc PROPERTIES CLANG_FORMAT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -add_definitions(-DVERBOSE_EXAMPLE_OUTPUT=1) +#add_definitions(-DVERBOSE_EXAMPLE_OUTPUT=1) diff --git a/unit_tests/ex_ipspace_properties.cc b/unit_tests/ex_ipspace_properties.cc index e0f0e8e..3420e6a 100644 --- a/unit_tests/ex_ipspace_properties.cc +++ b/unit_tests/ex_ipspace_properties.cc @@ -348,7 +348,8 @@ TextView Table::localize(TextView const&src) { TextView Table::token(TextView & line) { TextView::size_type idx = 0; // Characters of interest. - TextView sep_list { {'"', SEP} , 2 }; + static char constexpr separators[2] = { '"', SEP }; + static TextView sep_list { separators, 2 }; bool in_quote_p = false; while (idx < line.size()) { // Next character of interest. diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc index 9d97cc2..a088e72 100644 --- a/unit_tests/test_ip.cc +++ b/unit_tests/test_ip.cc @@ -13,6 +13,7 @@ #include <swoc/TextView.h> #include <swoc/swoc_ip.h> #include <swoc/bwf_ip.h> +#include <swoc/bwf_std.h> #include <swoc/swoc_file.h> using namespace std::literals; @@ -28,6 +29,9 @@ using swoc::IP6Range; using swoc::IPAddr; using swoc::IPRange; + +using swoc::IPMask; + using W = swoc::LocalBufferWriter<256>; TEST_CASE("Basic IP", "[libswoc][ip]") { @@ -63,7 +67,7 @@ TEST_CASE("Basic IP", "[libswoc][ip]") { REQUIRE(s.rest == rest); } - IP4Addr alpha { "172.96.12.134"}; + IP4Addr alpha{"172.96.12.134"}; CHECK(alpha == IP4Addr{"172.96.12.134"}); CHECK(alpha == IP4Addr{IPAddr{"172.96.12.134"}}); CHECK(alpha == IPAddr{IPEndpoint{"172.96.12.134:80"}}); @@ -269,6 +273,257 @@ TEST_CASE("IP Formatting", "[libswoc][ip][bwformat]") { REQUIRE(w.view() == " 0: 0: 0: 0: 0: 0: 0: 1"); } +TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") { + swoc::IP4Range r_0; + swoc::IP4Range r_1{"1.1.1.0-1.1.1.9"}; + swoc::IP4Range r_2{"1.1.2.0-1.1.2.97"}; + swoc::IP4Range r_3{"1.1.0.0-1.2.0.0"}; + swoc::IP4Range r_4{"10.33.45.19-10.33.45.76"}; + swoc::IP6Range r_5{ + "2001:1f2d:c587:24c3:9128:3349:3cee:143-ffee:1f2d:c587:24c3:9128:3349:3cFF:FFFF"_tv}; + + CHECK(true == r_0.empty()); + CHECK(false == r_1.empty()); + + swoc::IPMask mask{127}; + CHECK(r_5.min() == (r_5.min() | swoc::IPMask(128))); + CHECK(r_5.min() == (r_5.min() | mask)); + CHECK(r_5.min() != (r_5.min() & mask)); + + swoc::IP6Addr a_1{"2001:1f2d:c587:24c4::"}; + CHECK(a_1 == (a_1 & swoc::IPMask{62})); + + std::array<swoc::IP4Net, 7> r_4_nets = + {{ + "10.33.45.19/32"_tv + , "10.33.45.20/30"_tv + , "10.33.45.24/29"_tv + , "10.33.45.32/27"_tv + , "10.33.45.64/29"_tv + , "10.33.45.72/30"_tv + , "10.33.45.76/32"_tv + }}; + auto r4_net = r_4_nets.begin(); + for (auto const &net : r_4.networks()) { + REQUIRE(r4_net != r_4_nets.end()); + CHECK(*r4_net == net); + ++r4_net; + } + + // Let's try that again, with @c IPRange instead. + r4_net = r_4_nets.begin(); + for (auto const& net : IPRange{r_4}.networks()) { + REQUIRE(r4_net != r_4_nets.end()); + CHECK(*r4_net == net); + ++r4_net; + } + + std::array<swoc::IP6Net, 130> r_5_nets = + {{ + {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:143"}, IPMask{ + 128}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:144"}, IPMask{126}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:148"}, IPMask{125}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:150"}, IPMask{124}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:160"}, IPMask{123}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:180"}, IPMask{121}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:200"}, IPMask{119}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:400"}, IPMask{118}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:800"}, IPMask{117}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:1000"}, IPMask{116}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:2000"}, IPMask{115}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:4000"}, IPMask{114}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cee:8000"}, IPMask{113}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cef:0"}, IPMask{112}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3cf0:0"}, IPMask{108}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3d00:0"}, IPMask{104}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:3e00:0"}, IPMask{103}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:4000:0"}, IPMask{98}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3349:8000:0"}, IPMask{97}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:334a::"}, IPMask{95}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:334c::"}, IPMask{94}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3350::"}, IPMask{92}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3360::"}, IPMask{91}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3380::"}, IPMask{89}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3400::"}, IPMask{86}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:3800::"}, IPMask{85}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:4000::"}, IPMask{82}} + , {IP6Addr{ + "2001:1f2d:c587:24c3:9128:8000::"}, IPMask{81}} + , {IP6Addr{"2001:1f2d:c587:24c3:9129::"}, IPMask{ + 80}} + , {IP6Addr{"2001:1f2d:c587:24c3:912a::"}, IPMask{ + 79}} + , {IP6Addr{"2001:1f2d:c587:24c3:912c::"}, IPMask{ + 78}} + , {IP6Addr{"2001:1f2d:c587:24c3:9130::"}, IPMask{ + 76}} + , {IP6Addr{"2001:1f2d:c587:24c3:9140::"}, IPMask{ + 74}} + , {IP6Addr{"2001:1f2d:c587:24c3:9180::"}, IPMask{ + 73}} + , {IP6Addr{"2001:1f2d:c587:24c3:9200::"}, IPMask{ + 71}} + , {IP6Addr{"2001:1f2d:c587:24c3:9400::"}, IPMask{ + 70}} + , {IP6Addr{"2001:1f2d:c587:24c3:9800::"}, IPMask{ + 69}} + , {IP6Addr{"2001:1f2d:c587:24c3:a000::"}, IPMask{ + 67}} + , {IP6Addr{"2001:1f2d:c587:24c3:c000::"}, IPMask{ + 66}} + , {IP6Addr{"2001:1f2d:c587:24c4::"}, IPMask{62}} + , {IP6Addr{"2001:1f2d:c587:24c8::"}, IPMask{61}} + , {IP6Addr{"2001:1f2d:c587:24d0::"}, IPMask{60}} + , {IP6Addr{"2001:1f2d:c587:24e0::"}, IPMask{59}} + , {IP6Addr{"2001:1f2d:c587:2500::"}, IPMask{56}} + , {IP6Addr{"2001:1f2d:c587:2600::"}, IPMask{55}} + , {IP6Addr{"2001:1f2d:c587:2800::"}, IPMask{53}} + , {IP6Addr{"2001:1f2d:c587:3000::"}, IPMask{52}} + , {IP6Addr{"2001:1f2d:c587:4000::"}, IPMask{50}} + , {IP6Addr{"2001:1f2d:c587:8000::"}, IPMask{49}} + , {IP6Addr{"2001:1f2d:c588::"}, IPMask{45}} + , {IP6Addr{"2001:1f2d:c590::"}, IPMask{44}} + , {IP6Addr{"2001:1f2d:c5a0::"}, IPMask{43}} + , {IP6Addr{"2001:1f2d:c5c0::"}, IPMask{42}} + , {IP6Addr{"2001:1f2d:c600::"}, IPMask{39}} + , {IP6Addr{"2001:1f2d:c800::"}, IPMask{37}} + , {IP6Addr{"2001:1f2d:d000::"}, IPMask{36}} + , {IP6Addr{"2001:1f2d:e000::"}, IPMask{35}} + , {IP6Addr{"2001:1f2e::"}, IPMask{31}} + , {IP6Addr{"2001:1f30::"}, IPMask{28}} + , {IP6Addr{"2001:1f40::"}, IPMask{26}} + , {IP6Addr{"2001:1f80::"}, IPMask{25}} + , {IP6Addr{"2001:2000::"}, IPMask{19}} + , {IP6Addr{"2001:4000::"}, IPMask{18}} + , {IP6Addr{"2001:8000::"}, IPMask{17}} + , {IP6Addr{"2002::"}, IPMask{15}} + , {IP6Addr{"2004::"}, IPMask{14}} + , {IP6Addr{"2008::"}, IPMask{13}} + , {IP6Addr{"2010::"}, IPMask{12}} + , {IP6Addr{"2020::"}, IPMask{11}} + , {IP6Addr{"2040::"}, IPMask{10}} + , {IP6Addr{"2080::"}, IPMask{9}} + , {IP6Addr{"2100::"}, IPMask{8}} + , {IP6Addr{"2200::"}, IPMask{7}} + , {IP6Addr{"2400::"}, IPMask{6}} + , {IP6Addr{"2800::"}, IPMask{5}} + , {IP6Addr{"3000::"}, IPMask{4}} + , {IP6Addr{"4000::"}, IPMask{2}} + , {IP6Addr{"8000::"}, IPMask{2}} + , {IP6Addr{"c000::"}, IPMask{3}} + , {IP6Addr{"e000::"}, IPMask{4}} + , {IP6Addr{"f000::"}, IPMask{5}} + , {IP6Addr{"f800::"}, IPMask{6}} + , {IP6Addr{"fc00::"}, IPMask{7}} + , {IP6Addr{"fe00::"}, IPMask{8}} + , {IP6Addr{"ff00::"}, IPMask{9}} + , {IP6Addr{"ff80::"}, IPMask{10}} + , {IP6Addr{"ffc0::"}, IPMask{11}} + , {IP6Addr{"ffe0::"}, IPMask{13}} + , {IP6Addr{"ffe8::"}, IPMask{14}} + , {IP6Addr{"ffec::"}, IPMask{15}} + , {IP6Addr{"ffee::"}, IPMask{20}} + , {IP6Addr{"ffee:1000::"}, IPMask{21}} + , {IP6Addr{"ffee:1800::"}, IPMask{22}} + , {IP6Addr{"ffee:1c00::"}, IPMask{23}} + , {IP6Addr{"ffee:1e00::"}, IPMask{24}} + , {IP6Addr{"ffee:1f00::"}, IPMask{27}} + , {IP6Addr{"ffee:1f20::"}, IPMask{29}} + , {IP6Addr{"ffee:1f28::"}, IPMask{30}} + , {IP6Addr{"ffee:1f2c::"}, IPMask{32}} + , {IP6Addr{"ffee:1f2d::"}, IPMask{33}} + , {IP6Addr{"ffee:1f2d:8000::"}, IPMask{34}} + , {IP6Addr{"ffee:1f2d:c000::"}, IPMask{38}} + , {IP6Addr{"ffee:1f2d:c400::"}, IPMask{40}} + , {IP6Addr{"ffee:1f2d:c500::"}, IPMask{41}} + , {IP6Addr{"ffee:1f2d:c580::"}, IPMask{46}} + , {IP6Addr{"ffee:1f2d:c584::"}, IPMask{47}} + , {IP6Addr{"ffee:1f2d:c586::"}, IPMask{48}} + , {IP6Addr{"ffee:1f2d:c587::"}, IPMask{51}} + , {IP6Addr{"ffee:1f2d:c587:2000::"}, IPMask{54}} + , {IP6Addr{"ffee:1f2d:c587:2400::"}, IPMask{57}} + , {IP6Addr{"ffee:1f2d:c587:2480::"}, IPMask{58}} + , {IP6Addr{"ffee:1f2d:c587:24c0::"}, IPMask{63}} + , {IP6Addr{"ffee:1f2d:c587:24c2::"}, IPMask{64}} + , {IP6Addr{"ffee:1f2d:c587:24c3::"}, IPMask{65}} + , {IP6Addr{"ffee:1f2d:c587:24c3:8000::"}, IPMask{ + 68}} + , {IP6Addr{"ffee:1f2d:c587:24c3:9000::"}, IPMask{ + 72}} + , {IP6Addr{"ffee:1f2d:c587:24c3:9100::"}, IPMask{ + 75}} + , {IP6Addr{"ffee:1f2d:c587:24c3:9120::"}, IPMask{ + 77}} + , {IP6Addr{"ffee:1f2d:c587:24c3:9128::"}, IPMask{ + 83}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:2000::"}, IPMask{84}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3000::"}, IPMask{87}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3200::"}, IPMask{88}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3300::"}, IPMask{90}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3340::"}, IPMask{93}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3348::"}, IPMask{96}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3349::"}, IPMask{99}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3349:2000:0"}, IPMask{100}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3349:3000:0"}, IPMask{101}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3349:3800:0"}, IPMask{102}} + , {IP6Addr{ + "ffee:1f2d:c587:24c3:9128:3349:3c00:0"}, IPMask{104}} + }}; + + auto r5_net = r_5_nets.begin(); + for (auto const&[addr, mask] : r_5.networks()) { + REQUIRE(r5_net != r_5_nets.end()); + CHECK(*r5_net == swoc::IP6Net{addr, mask}); + ++r5_net; + } + + // Try it again, using @c IPRange. + r5_net = r_5_nets.begin(); + for ( auto const&[addr, mask] : IPRange{r_5}.networks()) { + REQUIRE(r5_net != r_5_nets.end()); + CHECK(*r5_net == swoc::IPNet{addr, mask}); + ++r5_net; + } +} + TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { using int_space = swoc::IPSpace<unsigned>; int_space space; @@ -336,20 +591,20 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { REQUIRE(payload != nullptr); REQUIRE(*payload == 0x5); - space.blend({r_2.min(), r_3.max()}, 0x6, BF); + space.blend(IPRange{r_2.min(), r_3.max()}, 0x6, BF); REQUIRE(space.count() == 4); std::array<std::tuple<TextView, int>, 9> ranges = { { - { "100.0.0.0-100.0.0.255", 0 } - , { "100.0.1.0-100.0.1.255", 1 } - , { "100.0.2.0-100.0.2.255", 2 } - , { "100.0.3.0-100.0.3.255", 3 } - , { "100.0.4.0-100.0.4.255", 4 } - , { "100.0.5.0-100.0.5.255", 5 } - , { "100.0.6.0-100.0.6.255", 6 } - , { "100.0.0.0-100.0.0.255", 31 } - , { "100.0.1.0-100.0.1.255", 30 } + {"100.0.0.0-100.0.0.255", 0} + , {"100.0.1.0-100.0.1.255", 1} + , {"100.0.2.0-100.0.2.255", 2} + , {"100.0.3.0-100.0.3.255", 3} + , {"100.0.4.0-100.0.4.255", 4} + , {"100.0.5.0-100.0.5.255", 5} + , {"100.0.6.0-100.0.6.255", 6} + , {"100.0.0.0-100.0.0.255", 31} + , {"100.0.1.0-100.0.1.255", 30} }}; space.clear(); @@ -393,7 +648,7 @@ TEST_CASE("IPSpace bitset", "[libswoc][ipspace][bitset]") { TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") { using PAYLOAD = std::bitset<32>; using Space = swoc::IPSpace<PAYLOAD>; - auto blender = [](PAYLOAD& lhs, PAYLOAD const& rhs) -> bool { + auto blender = [](PAYLOAD&lhs, PAYLOAD const&rhs) -> bool { lhs |= rhs; return true; }; @@ -405,33 +660,34 @@ TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") { return bits; }; - std::array<std::tuple<TextView, std::initializer_list<unsigned>>, 9> ranges = { + std::array<std::tuple<TextView, PAYLOAD>, 9> ranges = { { - { "100.0.0.0-100.0.0.255", { 0 } } - , { "100.0.1.0-100.0.1.255", { 1 } } - , { "100.0.2.0-100.0.2.255", { 2 } } - , { "100.0.3.0-100.0.3.255", { 3 } } - , { "100.0.4.0-100.0.4.255", { 4 } } - , { "100.0.5.0-100.0.5.255", { 5 } } - , { "100.0.6.0-100.0.6.255", { 6 } } - , { "100.0.0.0-100.0.0.255", { 31 } } - , { "100.0.1.0-100.0.1.255", { 30 } } + {"100.0.0.0-100.0.0.255", make_bits({0})} + , {"100.0.1.0-100.0.1.255", make_bits({1})} + , {"100.0.2.0-100.0.2.255", make_bits({2})} + , {"100.0.3.0-100.0.3.255", make_bits({3})} + , {"100.0.4.0-100.0.4.255", make_bits({4})} + , {"100.0.5.0-100.0.5.255", make_bits({5})} + , {"100.0.6.0-100.0.6.255", make_bits({6})} + , {"100.0.0.0-100.0.0.255", make_bits({31})} + , {"100.0.1.0-100.0.1.255", make_bits({30})} }}; std::array<std::initializer_list<unsigned>, 7> results = {{ - { 0, 31 } - , { 1, 30 } - , { 2 } - , { 3 } - , { 4 } - , { 5 } - , { 6 } - }}; + {0, 31} + , {1, 30} + , {2} + , {3} + , {4} + , {5} + , {6} + }}; Space space; - for (auto &&[text, bit_list] : ranges) { - space.blend(IPRange{text}, make_bits(bit_list), blender); + for (auto && [text, bit_list] : ranges) { + std::cout << W().print("{} = {}\n", text, bit_list); + space.blend(IPRange{text}, bit_list, blender); } // Check iteration - verify forward and reverse iteration yield the correct number of ranges @@ -440,19 +696,32 @@ TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") { unsigned idx; idx = 0; - for ( auto const& [ range, bits ] : space) { + for (auto const&[range, bits] : space) { REQUIRE(idx < results.size()); CHECK(bits == make_bits(results[idx])); ++idx; } idx = results.size(); - for ( auto spot = space.end() ; spot != space.begin() ; ) { - auto const& [ range, bits ] { *--spot }; + for (auto spot = space.end(); spot != space.begin();) { + auto const&[range, bits]{*--spot}; REQUIRE(idx > 0); --idx; CHECK(bits == make_bits(results[idx])); } + + // Check iterator copying. + idx = 0; + Space::iterator iter; + IPRange range; + PAYLOAD bits; + for (auto spot = space.begin(); spot != space.end() ; ++spot) { + iter = spot; + std::tie(range, bits) = *iter; + REQUIRE(idx < results.size()); + CHECK(bits == make_bits(results[idx])); + ++idx; + } } #if 0
