| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-License-Identifier: Apache-2.0 |
| // Copyright Network Geographics 2014 |
| /** @file |
| IP address and network related classes. |
| */ |
| |
| #pragma once |
| #include <limits.h> |
| #include <netinet/in.h> |
| #include <string_view> |
| #include <variant> |
| |
| #include "swoc/swoc_version.h" |
| #include "swoc/TextView.h" |
| #include "swoc/DiscreteRange.h" |
| #include "swoc/RBTree.h" |
| |
| namespace swoc { inline namespace SWOC_VERSION_NS { |
| |
| class IP4Addr; |
| |
| class IP6Addr; |
| |
| class IPAddr; |
| |
| class IPMask; |
| |
| class IP4Range; |
| |
| class IP6Range; |
| |
| class IPRange; |
| |
| class IP4Net; |
| |
| class IP6Net; |
| |
| class IPNet; |
| |
| using ::std::string_view; |
| extern void * const pseudo_nullptr ; |
| |
| /** A union to hold @c sockaddr compliant IP address structures. |
| |
| This class contains a number of static methods to perform operations on external @c sockaddr |
| instances. These are all duplicates of methods that operate on the internal @c sockaddr and |
| are provided primarily for backwards compatibility during the shift to using this class. |
| |
| We use the term "endpoint" because these contain more than just the raw address, all of the data |
| for an IP endpoint is present. |
| */ |
| union IPEndpoint { |
| using self_type = IPEndpoint; ///< Self reference type. |
| |
| struct sockaddr sa; ///< Generic address. |
| struct sockaddr_in sa4; ///< IPv4 |
| struct sockaddr_in6 sa6; ///< IPv6 |
| |
| /// Default construct invalid instance. |
| IPEndpoint(); |
| |
| IPEndpoint(self_type const& that); |
| |
| /// Construct from the @a text representation of an address. |
| IPEndpoint(string_view const& text); |
| |
| // Construct from @a IPAddr |
| explicit IPEndpoint(IPAddr const& addr); |
| |
| // Construct from @c sockaddr |
| IPEndpoint(sockaddr const *sa); |
| |
| /** Break a string in to IP address relevant tokens. |
| * |
| * @param src Source text. [in] |
| * @param host The host / address. [out] |
| * @param port The port. [out] |
| * @param rest Any text past the end of the IP address. [out] |
| * @return @c true if an IP address was found, @c false otherwise. |
| * |
| * 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); |
| |
| /** Parse a string for an IP address. |
| |
| The address resulting from the parse is copied to this object if the conversion is successful, |
| otherwise this object is invalidated. |
| |
| @return @c true on success, @c false otherwise. |
| */ |
| bool parse(string_view const& str); |
| |
| /// Invalidate a @c sockaddr. |
| static void invalidate(sockaddr *addr); |
| |
| /// Invalidate this endpoint. |
| self_type& invalidate(); |
| |
| /// Copy constructor. |
| self_type& operator=(self_type const& that); |
| |
| /** Copy (assign) the contents of @a src to @a dst. |
| * |
| * The caller must ensure @a dst is large enough to hold the contents of @a src, the size of which |
| * can vary depending on the type of address in @a dst. |
| * |
| * @param dst Destination. |
| * @param src Source. |
| * @return @c true if @a dst is a valid IP address, @c false otherwise. |
| */ |
| static bool assign(sockaddr *dst, sockaddr const *src); |
| |
| /** Assign from a socket address. |
| The entire address (all parts) are copied if the @a ip is valid. |
| */ |
| 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); |
| |
| /// Copy to @a sa. |
| 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; |
| |
| /** Effectively size of the address. |
| * |
| * @return The size of the structure appropriate for the address family of the stored address. |
| */ |
| socklen_t size() const; |
| |
| /// @return The IP address family. |
| sa_family_t family() const; |
| |
| /// 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); |
| |
| /// @return @c true if this is the ANY address, @c false if not. |
| bool is_any() const; |
| |
| /// 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); |
| |
| /// @return @c true if this is a loopback address, @c false if not. |
| bool is_loopback() const; |
| |
| /// Port in network order. |
| in_port_t& network_order_port(); |
| |
| /// Port in network order. |
| in_port_t network_order_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); |
| |
| /// 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; } |
| |
| /// The string name of the address family. |
| static string_view family_name(sa_family_t family); |
| }; |
| |
| /** Storage for an IPv4 address. |
| Stored in host order. |
| */ |
| 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 constexpr size_t WIDTH = std::numeric_limits<unsigned char>::digits * SIZE; ///< # of bits in an address. |
| static const self_type MIN; ///< Minimum value. |
| static const self_type MAX; ///< Maximum value. |
| static constexpr sa_family_t AF_value = AF_INET; ///< Address family type. |
| |
| 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); |
| |
| /// Construct from generic address @a addr. |
| explicit IP4Addr(IPAddr const& addr); |
| |
| /// Assign from IPv4 raw address. |
| self_type& operator=(in_addr_t ip); |
| |
| /// Set to the address in @a addr. |
| self_type& operator=(sockaddr_in const *sa); |
| |
| /// Increment address. |
| self_type& operator++(); |
| |
| /// Decrement address. |
| self_type& operator--(); |
| |
| /** Byte access. |
| * |
| * @param idx Byte index. |
| * @return The byte at @a idx in the address. |
| */ |
| 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); |
| |
| /// Apply @a mask to address, creating the broadcast address. |
| 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; |
| |
| /** Parse @a text as IPv4 address. |
| The address resulting from the parse is copied to this object if the conversion is successful, |
| otherwise this object is invalidated. |
| |
| @return @c true on success, @c false otherwise. |
| */ |
| bool load(string_view const& text); |
| |
| /// Standard ternary compare. |
| int cmp(self_type const& that) const { |
| return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0; |
| } |
| |
| /// Get the IP address family. |
| /// @return @c AF_INET |
| /// @note Useful primarily for template classes. |
| constexpr sa_family_t family(); |
| |
| /// Test for ANY address. |
| bool is_any() const { return _addr == INADDR_ANY; } |
| |
| /// Test for multicast |
| bool is_multicast() const { return IN_MULTICAST(_addr); } |
| |
| /// Test for loopback |
| bool is_loopback() const { return (*this)[3] == 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. |
| * @return @a src with the byte reversed. |
| * |
| * This performs the same computation as @c ntohl and @c htonl but is @c constexpr to be usable |
| * in situations those two functions are not. |
| */ |
| constexpr static in_addr_t reorder(in_addr_t src); |
| |
| 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&); |
| |
| in_addr_t _addr = INADDR_ANY; ///< Address in host order. |
| }; |
| |
| /** Storage for an IPv6 address. |
| This should be presumed to be in network order. |
| */ |
| class IP6Addr { |
| using self_type = IP6Addr; ///< Self reference type. |
| friend class IP6Range; |
| |
| friend class IPMask; |
| |
| public: |
| static constexpr size_t WIDTH = 128; ///< Number of bits in the address. |
| static constexpr size_t SIZE = WIDTH / std::numeric_limits<unsigned char>::digits; ///< Size of address in bytes. |
| static constexpr sa_family_t AF_value = AF_INET6; ///< Address family type. |
| |
| 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_store_type = std::array<quad_type, N_QUADS>; |
| |
| /// Number of bits per quad. |
| static constexpr size_t QUAD_WIDTH = std::numeric_limits<unsigned char>::digits * 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 = std::numeric_limits<unsigned char>::digits * 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; |
| /// Maximum value of an address. |
| 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); |
| |
| /// 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); |
| |
| /// Construct from generic @a addr. |
| explicit 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++(); |
| |
| /// Decrement address. |
| self_type& operator--(); |
| |
| /// Assign from IPv6 raw address. |
| self_type& operator=(in6_addr const& ip); |
| |
| /// Set to the address in @a 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; |
| |
| /// Return the address in network order. |
| in6_addr network_order() const; |
| |
| /** Parse a string for an IP address. |
| |
| The address resuling from the parse is copied to this object if the conversion is successful, |
| otherwise this object is invalidated. |
| |
| @return @c true on success, @c false otherwise. |
| */ |
| bool load(string_view const& str); |
| |
| /// Generic compare. |
| int cmp(self_type const& that) const; |
| |
| /// Get the address family. |
| /// @return The address family. |
| /// @note Useful primarily for templates. |
| constexpr sa_family_t family(); |
| |
| /// Test for ANY address. |
| bool is_any() const { return _addr._store[0] == 0 && _addr._store[1] == 0; } |
| |
| /// Test for loopback |
| bool is_loopback() const { return _addr._store[0] == 0 && _addr._store[1] == 1; } |
| |
| /// Test for multicast |
| bool is_multicast() const { return _addr._raw[7] == 0xFF; } |
| |
| self_type& clear() { |
| _addr._store[0] = _addr._store[1] = 0; |
| return *this; |
| } |
| |
| 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&); |
| |
| /// Type for digging around inside the address, with the various forms of access. |
| /// These are in sort of host order - @a _store elements are host order, but the |
| /// MSW and LSW are swapped (big-endian). This makes various bits of the implementation |
| /// easier. Conversion to and from network order is via the @c reorder method. |
| union { |
| 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 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(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. |
| IPAddr(self_type const& that) = default; ///< Copy constructor. |
| |
| /// Construct using IPv4 @a addr. |
| explicit IPAddr(in_addr_t addr); |
| |
| /// Construct using an IPv4 @a addr |
| IPAddr(IP4Addr const& addr) : _addr{addr}, _family(IP4Addr::AF_value) {} |
| |
| /// Construct using IPv6 @a addr. |
| explicit IPAddr(in6_addr const& addr); |
| |
| /// construct using an IPv6 @a addr |
| IPAddr(IP6Addr const& addr) : _addr{addr}, _family(IP6Addr::AF_value) {} |
| |
| /// Construct from @c sockaddr. |
| explicit IPAddr(sockaddr const *addr); |
| |
| /// Construct from @c IPEndpoint. |
| 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); |
| |
| /// Set to the address in @a addr. |
| self_type& assign(sockaddr_in const *addr); |
| |
| /// Set to the address in @a addr. |
| self_type& assign(sockaddr_in6 const *addr); |
| |
| /// Set to the address in @a addr. |
| self_type& assign(in_addr_t addr); |
| |
| /// Set to address in @a addr. |
| self_type& assign(in6_addr const& addr); |
| |
| /// Assign from end point. |
| self_type& operator=(IPEndpoint const& ip); |
| |
| /// Assign from IPv4 raw address. |
| self_type& operator=(in_addr_t ip); |
| |
| /// Assign from IPv6 raw address. |
| self_type& operator=(in6_addr const& ip); |
| |
| bool operator==(self_type const& that) const; |
| |
| bool operator!=(self_type const& that) const; |
| |
| bool operator<(self_type const& that) const; |
| |
| bool operator>(self_type const& that) const; |
| |
| bool operator<=(self_type const& that) const; |
| |
| bool operator>=(self_type const& that) const; |
| |
| /// Assign from @c sockaddr |
| self_type& operator=(sockaddr const *addr); |
| |
| self_type& operator&=(IPMask const& mask); |
| |
| self_type& operator|=(IPMask const& mask); |
| |
| |
| /** 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); |
| |
| /// Generic compare. |
| 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); |
| |
| /// 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; |
| |
| 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(); |
| |
| /// Test for multicast |
| bool is_multicast() const; |
| |
| /// Test for loopback |
| bool is_loopback() const; |
| |
| ///< Pre-constructed invalid instance. |
| static self_type const INVALID; |
| |
| protected: |
| friend IP4Addr; |
| friend IP6Addr; |
| |
| /// Address data. |
| union raw_addr_type { |
| IP4Addr _ip4; ///< IPv4 address (host) |
| IP6Addr _ip6; ///< IPv6 address (host) |
| uint8_t _octet[IP6Addr::SIZE]; ///< IPv4 octets. |
| 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; |
| |
| sa_family_t _family{AF_UNSPEC}; ///< Protocol family. |
| }; |
| |
| /** An IP address mask. |
| * |
| * This is essentially a width for a bit mask. |
| */ |
| class IPMask { |
| using self_type = IPMask; ///< Self reference type. |
| friend class IP4Addr; |
| |
| friend class IP6Addr; |
| |
| public: |
| using raw_type = uint8_t; ///< Storage for mask width. |
| |
| IPMask() = default; |
| |
| 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); |
| |
| /** 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. |
| */ |
| 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; |
| |
| self_type& operator<<=(raw_type n) { |
| _cidr -= n; |
| return *this; |
| } |
| |
| self_type& operator>>=(raw_type n) { |
| _cidr += n; |
| return *this; |
| } |
| |
| /** 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. |
| */ |
| class IP4Range : public DiscreteRange<IP4Addr> { |
| using self_type = IP4Range; |
| using super_type = DiscreteRange<IP4Addr>; |
| using metric_type = IP4Addr; |
| |
| public: |
| /// Default constructor, invalid range. |
| IP4Range() = default; |
| |
| /// Construct from an network expressed as @a addr and @a mask. |
| IP4Range(IP4Addr const& addr, IPMask const& mask); |
| |
| /// Construct from super type. |
| /// @internal Why do I have to do this, even though the super type constructors are inherited? |
| IP4Range(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. |
| */ |
| 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); |
| |
| using super_type::assign; ///< Import assign methods. |
| |
| /** Assign to this range from text. |
| * |
| * @param text Range text. |
| * |
| * The text must be in one of three formats. |
| * - A dashed range, "addr1-addr2" |
| * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. |
| * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. |
| */ |
| bool load(string_view text); |
| |
| /** Compute the mask for @a this as a network. |
| * |
| * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. |
| * |
| * @see IPMask::is_valid |
| */ |
| IPMask network_mask() const; |
| |
| 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 @c true if there are valid networks, @c false if not. |
| bool empty() const; |
| |
| /// @return The current network. |
| IP4Net operator*() const; |
| |
| /// Access @a this as if it were an @c IP4Net. |
| self_type *operator->(); |
| |
| /// Iterator support. |
| /// @return 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> { |
| using self_type = IP6Range; |
| using super_type = DiscreteRange<IP6Addr>; |
| |
| public: |
| /// 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); |
| |
| using super_type::assign; ///< Import assign methods. |
| |
| /** Assign to this range from text. |
| * |
| * @param text Range text. |
| * |
| * The text must be in one of three formats. |
| * - A dashed range, "addr1-addr2" |
| * - A singleton, "addr". This is treated as if it were "addr-addr", a range of size 1. |
| * - CIDR notation, "addr/cidr" where "cidr" is a number from 0 to the number of bits in the address. |
| */ |
| bool load(string_view text); |
| |
| /** Compute the mask for @a this as a network. |
| * |
| * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. |
| * |
| * @see IPMask::is_valid |
| */ |
| IPMask network_mask() const; |
| |
| 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 @c true if there are valid networks, @c false if not. |
| bool empty() const; |
| |
| /// @return The current network. |
| IP6Net operator*() const; |
| |
| /// Access @a this as if it were an @c IP6Net. |
| self_type *operator->(); |
| |
| /// Iterator support. |
| /// @return 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 { |
| using self_type = 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. |
| * |
| * The string can be a single address, two addresses separated by a dash '-' or a CIDR network. |
| */ |
| IPRange(string_view const& text); |
| |
| /// Equality |
| bool operator == (self_type const& that) const { |
| if (_family != that._family) { |
| return false; |
| } |
| if (this->is_ip4()) { |
| return _range._ip4 == that._range._ip4; |
| } |
| if (this->is_ip6()) { |
| return _range._ip6 == that._range._ip6; |
| } |
| return true; |
| } |
| |
| /// @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; |
| |
| /** Load the range from @a text. |
| * |
| * @param text Range specifier in text format. |
| * @return @c true if @a text was successfully parsed, @c false if not. |
| * |
| * A successful parse means @a this was loaded with the specified range. If not the range is |
| * marked as invalid. |
| */ |
| bool load(std::string_view const& text); |
| |
| /// @return The minimum address in the range. |
| IPAddr min() const; |
| |
| /// @return The maximum address in the range. |
| IPAddr max() const; |
| |
| bool empty() const; |
| |
| IP4Range const& ip4() const { return _range._ip4; } |
| |
| IP6Range const& ip6() const { return _range._ip6; } |
| |
| /** Compute the mask for @a this as a network. |
| * |
| * @return If @a this is a network, the mask for that network. Otherwise an invalid mask. |
| * |
| * @see IPMask::is_valid |
| */ |
| IPMask network_mask() const; |
| |
| 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. |
| * |
| * @internal |
| * |
| * This was a @c std::variant at one point, but the complexity got in the way because |
| * - These objects have no state, need no destruction. |
| * - Construction was problematic because @c variant requires construction, then access, |
| * whereas this needs access to construct (e.g. via the @c load method). |
| */ |
| union { |
| std::monostate _nil; ///< Make constructor easier to implement. |
| IP4Range _ip4; ///< IPv4 range. |
| IP6Range _ip6; ///< IPv6 range. |
| } _range{std::monostate{}}; |
| /// Family of @a _range. |
| 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. |
| /// @return 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. |
| public: |
| 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); |
| |
| IPNet(TextView 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. |
| IPAddr lower_bound() const; |
| |
| /// @return The largest address in the network. |
| IPAddr upper_bound() const; |
| |
| IPMask::raw_type width() const; |
| |
| /// @return The mask for the network. |
| IPMask const& mask() const; |
| |
| /// @return A range that exactly covers the network. |
| IPRange as_range() const; |
| |
| bool is_ip4() const { return _addr.is_ip4(); } |
| |
| bool is_ip6() const { return _addr.is_ip6(); } |
| |
| sa_family_t family() const { return _addr.family(); } |
| |
| IP4Net ip4() const { return IP4Net{_addr.ip4(), _mask}; } |
| |
| IP6Net ip6() const { return IP6Net{_addr.ip6(), _mask}; } |
| |
| /** 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; |
| |
| /// Inequality |
| bool operator!=(self_type const& that) const; |
| |
| protected: |
| IPAddr _addr; ///< Address and family. |
| IPMask _mask; ///< Network mask. |
| }; |
| |
| // -------------------------------------------------------------------------- |
| /** Coloring of IP address space. |
| * |
| * @tparam PAYLOAD The color class. |
| * |
| * This is a class to do fast coloring and lookup of the IP address space. It is range oriented and |
| * performs well for ranges, much less well for singletons. Conceptually every IP address is a key |
| * in the space and can have a color / payload of type @c PAYLOAD. |
| * |
| * @c PAYLOAD must have the properties |
| * |
| * - Cheap to copy. |
| * - Comparable via the equality and inequality operators. |
| */ |
| template<typename PAYLOAD> class IPSpace { |
| using self_type = IPSpace; |
| using IP4Space = DiscreteSpace<IP4Addr, PAYLOAD>; |
| using IP6Space = DiscreteSpace<IP6Addr, PAYLOAD>; |
| |
| public: |
| using payload_t = PAYLOAD; ///< Export payload type. |
| using value_type = std::tuple<IPRange const, PAYLOAD&>; |
| |
| /// Construct an empty space. |
| IPSpace() = default; |
| |
| /** Mark the range @a r with @a payload. |
| * |
| * @param range Range to mark. |
| * @param payload Payload to assign. |
| * @return @a this |
| * |
| * All addresses in @a r are set to have the @a payload. |
| */ |
| self_type& mark(IPRange const& range, PAYLOAD const& payload); |
| |
| /** Fill the @a range with @a payload. |
| * |
| * @param range Destination range. |
| * @param payload Payload for range. |
| * @return this |
| * |
| * 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); |
| |
| /** Erase addresses in @a range. |
| * |
| * @param range Address range. |
| * @return @a this |
| */ |
| self_type& erase(IPRange const& range); |
| |
| /** Blend @a color in to the @a range. |
| * |
| * @tparam F Blending functor type (deduced). |
| * @tparam U Data to blend in to payloads. |
| * @param range Target range. |
| * @param color Data to blend in to existing payloads in @a range. |
| * @param blender Blending functor. |
| * @return @a this |
| * |
| * @a blender is required to have the signature <tt>void(PAYLOAD& lhs , U CONST&rhs)</tt>. It must |
| * act as a compound assignment operator, blending @a rhs into @a lhs. That is, if the result of |
| * blending @a rhs in to @a lhs is defined as "lhs @ rhs" for the binary operator "@", then @a |
| * blender computes "lhs @= rhs". |
| * |
| * Every address in @a range is assigned a payload. If the address does not already have a color, |
| * it is assigned the default constructed @c PAYLOAD blended with @a color. If the address has a |
| * @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(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) { |
| _ip6.blend(range, color, blender); |
| return *this; |
| } |
| |
| /// @return The number of distinct ranges. |
| size_t count() const { return _ip4.count() + _ip6.count(); } |
| |
| size_t count_ip4() const { return _ip4.count(); } |
| size_t count_ip6() const { return _ip6.count(); } |
| |
| size_t count(sa_family_t f) const; |
| |
| /// Remove all ranges. |
| void clear(); |
| |
| /** Constant iterator. |
| * THe value type is a tuple of the IP address range and the @a PAYLOAD. Both are constant. |
| * |
| * @internal The non-const iterator is a subclass of this, in order to share implementation. This |
| * also makes it easy to convert from iterator to const iterator, which is desirable. |
| * |
| * @internal The return type is quite tricky because the value type of the nested containers is |
| * not the same as the value type for this container. It's not even a composite - @c IPRange is |
| * not an alias for either of the family specific range types. Therefore the iterator itself must |
| * contain a synthesized instance of the value type, which creates scoping and update problems. |
| * The approach here is to update the synthetic value when the iterator is modified and returning |
| * it by value for the dereference operator because a return by reference means code like |
| * @code |
| * auto && [ r , p ] = *(space.find(addr)); |
| * @endcode |
| * can fail due to the iterator going out of scope after the statement is finished making @a r |
| * and @a p dangling references. If the return is by value the compiler takes care of it. |
| */ |
| 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 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++(); |
| |
| /// Pre-decrement. |
| /// Move to the previous element in the list. |
| /// @return The iterator. |
| self_type& operator--(); |
| |
| /// Post-increment. |
| /// Move to the next element in the list. |
| /// @return The iterator value before the increment. |
| self_type operator++(int); |
| |
| /// Post-decrement. |
| /// Move to the previous element in the list. |
| /// @return The iterator value before the decrement. |
| self_type operator--(int); |
| |
| /// Dereference. |
| /// @return A reference to the referent. |
| value_type operator*() const; |
| |
| /// Dereference. |
| /// @return A pointer to the referent. |
| value_type const *operator->() const; |
| |
| /// Equality |
| bool operator==(self_type const& that) const; |
| |
| /// Inequality |
| bool operator!=(self_type const& that) const; |
| |
| protected: |
| // These are stored non-const to make implementing @c iterator easier. This class provides the |
| // required @c const protection. This is basic a tuple of iterators - for forward iteration if |
| // the primary (ipv4) iterator is at the end, then use the secondary (ipv6) iterator. The reverse |
| // is done for reverse iteration. This depends on the extra support @c IntrusiveDList iterators |
| // provide. |
| typename IP4Space::iterator _iter_4; ///< IPv4 sub-space iterator. |
| typename IP6Space::iterator _iter_6; ///< IPv6 sub-space iterator. |
| /// Current value. |
| value_type _value{IPRange{}, *static_cast<PAYLOAD*>(pseudo_nullptr)}; |
| |
| /// Dummy payload. |
| /// @internal Used to initialize @c value_type for invalid iterators. |
| // static constexpr PAYLOAD * const null_payload = nullptr; |
| |
| /** Internal constructor. |
| * |
| * @param iter4 Starting place for IPv4 subspace. |
| * @param iter6 Starting place for IPv6 subspace. |
| * |
| * 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); |
| }; |
| |
| /** Iterator. |
| * The value type is a tuple of the IP address range and the @a PAYLOAD. The range is constant |
| * and the @a PAYLOAD is a reference. This can be used to update the @a PAYLOAD for this range. |
| * |
| * @note Range merges are not trigged by modifications of the @a PAYLOAD via an iterator. |
| */ |
| 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&; |
| |
| /// 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++(); |
| |
| /// Pre-decrement. |
| /// Move to the previous element in the list. |
| /// @return The iterator. |
| 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; |
| } |
| |
| /// 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; |
| } |
| |
| /// Dereference. |
| /// @return A reference to the referent. |
| value_type operator*() const; |
| |
| /// Dereference. |
| /// @return A pointer to the referent. |
| value_type const *operator->() const; |
| |
| protected: |
| using super_type::super_type; /// Inherit supertype constructors. |
| }; |
| |
| /** Find the payload for an @a addr. |
| * |
| * @param addr Address to find. |
| * @return Iterator for the range containing @a addr. |
| */ |
| iterator find(IPAddr const& addr) { |
| if (addr.is_ip4()) { |
| return this->find(addr.ip4()); |
| } else if (addr.is_ip6()) { |
| return this->find(addr.ip6()); |
| } |
| return this->end(); |
| } |
| |
| /** Find the payload for an @a addr. |
| * |
| * @param addr Address to find. |
| * @return The payload if any, @c nullptr if the address is not in the space. |
| */ |
| iterator find(IP4Addr const& addr) { |
| auto spot = _ip4.find(addr); |
| return spot == _ip4.end() ? this->end() : iterator{_ip4.find(addr), _ip6.begin()}; |
| } |
| |
| /** Find the payload for an @a addr. |
| * |
| * @param addr Address to find. |
| * @return The payload if any, @c nullptr if the address is not in the space. |
| */ |
| iterator find(IP6Addr const& addr) { |
| return {_ip4.end(), _ip6.find(addr)}; |
| } |
| |
| /// @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(); |
| |
| iterator end(); |
| |
| /// Iterator to the first IPv4 address. |
| const_iterator begin_ip4() const; |
| /// Iterator past the last IPv4 address. |
| const_iterator end_ip4() const; |
| |
| const_iterator begin_ip6() const; |
| const_iterator end_ip6() const; |
| |
| const_iterator begin(sa_family_t family) const { |
| if (AF_INET == family) { |
| return this->begin_ip4(); |
| } else if (AF_INET6 == family) { |
| return this->begin_ip6(); |
| } |
| return this->end(); |
| } |
| |
| const_iterator end(sa_family_t family) const { |
| if (AF_INET == family) { |
| return this->end_ip4(); |
| } else if (AF_INET6 == family) { |
| return this->end_ip6(); |
| } |
| return this->end(); |
| } |
| |
| protected: |
| IP4Space _ip4; ///< Sub-space containing IPv4 ranges. |
| IP6Space _ip6; ///< sub-space containing IPv6 ranges. |
| }; |
| |
| 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) { |
| 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& { |
| bool incr_p = false; |
| if (_iter_4.has_next()) { |
| ++_iter_4; |
| incr_p = true; |
| if (_iter_4.has_next()) { |
| new(&_value) value_type{_iter_4->range(), _iter_4->payload()}; |
| return *this; |
| } |
| } |
| |
| if (_iter_6.has_next()) { |
| if (incr_p || (++_iter_6).has_next()) { |
| new(&_value) value_type{_iter_6->range(), _iter_6->payload()}; |
| return *this; |
| } |
| } |
| new(&_value) value_type{IPRange{}, *static_cast<PAYLOAD*>(pseudo_nullptr)}; |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::const_iterator::operator++(int) -> self_type { |
| self_type zret(*this); |
| ++*this; |
| return zret; |
| } |
| |
| template<typename PAYLOAD> |
| 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()}; |
| return *this; |
| } |
| if (_iter_4.has_prev()) { |
| --_iter_4; |
| new(&_value) value_type{_iter_4->range(), _iter_4->payload()}; |
| return *this; |
| } |
| new(&_value) value_type{IPRange{}, *static_cast<PAYLOAD*>(pseudo_nullptr)}; |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::const_iterator::operator--(int) -> self_type { |
| self_type zret(*this); |
| --*this; |
| return zret; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::const_iterator::operator*() const -> value_type { return _value; } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::const_iterator::operator->() const -> value_type const * { return &_value; } |
| |
| /* Bit of subtlety with equality - although it seems that if @a _iter_4 is valid, it doesn't matter |
| * where @a _iter6 is (because it is really the iterator location that's being checked), it's |
| * neccesary to do the @a _iter_4 validity on both iterators to avoid the case of a false positive |
| * where different internal iterators are valid. However, in practice the other (non-active) |
| * iterator won't have an arbitrary value, it will be either @c begin or @c end in step with the |
| * active iterator therefore it's effective and cheaper to just check both values. |
| */ |
| |
| template<typename PAYLOAD> |
| bool |
| IPSpace<PAYLOAD>::const_iterator::operator==(self_type const& that) const { |
| return _iter_4 == that._iter_4 && _iter_6 == that._iter_6; |
| } |
| |
| template<typename PAYLOAD> |
| bool |
| IPSpace<PAYLOAD>::const_iterator::operator!=(self_type const& that) const { |
| return _iter_4 != that._iter_4 || _iter_6 != that._iter_6; |
| } |
| |
| template<typename PAYLOAD> |
| 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> |
| auto IPSpace<PAYLOAD>::iterator::operator*() const -> value_type { |
| return reinterpret_cast<value_type const&>(super_type::_value); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::iterator::operator++() -> self_type& { |
| this->super_type::operator++(); |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::iterator::operator--() -> self_type& { |
| this->super_type::operator--(); |
| return *this; |
| } |
| |
| // -------------------------------------------------------------------------- |
| // -- IP4Addr -- |
| inline constexpr sa_family_t IP4Addr::family() { return AF_value; } |
| |
| // -- IP6Addr -- |
| inline constexpr sa_family_t IP6Addr::family() { return AF_value; } |
| |
| // -- IPAddr -- |
| // @c constexpr constructor is required to initialize _something_, it can't be completely uninitializing. |
| inline constexpr IPAddr::raw_addr_type::raw_addr_type() : _ip4(INADDR_ANY) {} |
| |
| inline IPAddr::IPAddr(in_addr_t addr) : _addr(addr), _family(IP4Addr::AF_value) {} |
| |
| inline IPAddr::IPAddr(in6_addr const& addr) : _addr(addr), _family(IP6Addr::AF_value) {} |
| |
| inline IPAddr::IPAddr(sockaddr const *addr) { |
| this->assign(addr); |
| } |
| |
| inline IPAddr::IPAddr(IPEndpoint const& addr) { |
| this->assign(&addr.sa); |
| } |
| |
| inline IPAddr::IPAddr(string_view const& text) { |
| this->load(text); |
| } |
| |
| inline IPAddr& |
| IPAddr::operator=(in_addr_t addr) { |
| _family = AF_INET; |
| _addr._ip4 = addr; |
| return *this; |
| } |
| |
| inline IPAddr& |
| IPAddr::operator=(in6_addr const& addr) { |
| _family = AF_INET6; |
| _addr._ip6 = addr; |
| return *this; |
| } |
| |
| inline IPAddr& |
| IPAddr::operator=(IPEndpoint const& addr) { |
| return this->assign(&addr.sa); |
| } |
| |
| inline IPAddr& |
| IPAddr::operator=(sockaddr const *addr) { |
| return this->assign(addr); |
| } |
| |
| inline sa_family_t |
| IPAddr::family() const { |
| return _family; |
| } |
| |
| inline bool |
| IPAddr::is_ip4() const { |
| return AF_INET == _family; |
| } |
| |
| inline bool |
| IPAddr::is_ip6() const { |
| return AF_INET6 == _family; |
| } |
| |
| inline bool |
| 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 && _addr._ip6.is_loopback()); |
| } |
| |
| inline IPAddr& |
| IPAddr::assign(in_addr_t addr) { |
| _family = AF_INET; |
| _addr._ip4 = addr; |
| return *this; |
| } |
| |
| inline IPAddr& |
| IPAddr::assign(in6_addr const& addr) { |
| _family = AF_INET6; |
| _addr._ip6 = addr; |
| return *this; |
| } |
| |
| inline IPAddr& |
| IPAddr::assign(sockaddr_in const *addr) { |
| if (addr) { |
| _family = AF_INET; |
| _addr._ip4 = addr; |
| } else { |
| _family = AF_UNSPEC; |
| } |
| return *this; |
| } |
| |
| inline IPAddr& |
| IPAddr::assign(sockaddr_in6 const *addr) { |
| if (addr) { |
| _family = AF_INET6; |
| _addr._ip6 = addr->sin6_addr; |
| } else { |
| _family = AF_UNSPEC; |
| } |
| return *this; |
| } |
| |
| inline bool |
| IPAddr::is_valid() const { |
| return _family == AF_INET || _family == AF_INET6; |
| } |
| |
| inline IPAddr& |
| IPAddr::invalidate() { |
| _family = AF_UNSPEC; |
| return *this; |
| } |
| |
| // Associated operators. |
| bool operator==(IPAddr const& lhs, sockaddr const *rhs); |
| |
| inline bool |
| operator==(sockaddr const *lhs, IPAddr const& rhs) { |
| return rhs == lhs; |
| } |
| |
| inline bool |
| operator!=(IPAddr const& lhs, sockaddr const *rhs) { |
| return !(lhs == rhs); |
| } |
| |
| inline bool |
| operator!=(sockaddr const *lhs, IPAddr const& rhs) { |
| return !(rhs == lhs); |
| } |
| |
| inline bool |
| operator==(IPAddr const& lhs, IPEndpoint const& rhs) { |
| return lhs == &rhs.sa; |
| } |
| |
| inline bool |
| operator==(IPEndpoint const& lhs, IPAddr const& rhs) { |
| return &lhs.sa == rhs; |
| } |
| |
| inline bool |
| operator!=(IPAddr const& lhs, IPEndpoint const& rhs) { |
| return !(lhs == &rhs.sa); |
| } |
| |
| inline bool |
| operator!=(IPEndpoint const& lhs, IPAddr const& rhs) { |
| return !(rhs == &lhs.sa); |
| } |
| |
| inline IP4Addr const& IPAddr::ip4() const { return _addr._ip4; } |
| |
| inline IP6Addr const& IPAddr::ip6() const { return _addr._ip6; } |
| |
| /// ------------------------------------------------------------------------------------ |
| |
| inline IPEndpoint::IPEndpoint() { |
| sa.sa_family = AF_UNSPEC; |
| } |
| |
| inline IPEndpoint::IPEndpoint(IPAddr const& addr) { |
| this->assign(addr); |
| } |
| |
| inline IPEndpoint::IPEndpoint(sockaddr const *sa) { |
| this->assign(sa); |
| } |
| |
| inline IPEndpoint::IPEndpoint(IPEndpoint::self_type const& that) : self_type(&that.sa) { |
| } |
| |
| inline IPEndpoint& |
| IPEndpoint::invalidate() { |
| sa.sa_family = AF_UNSPEC; |
| return *this; |
| } |
| |
| inline void |
| IPEndpoint::invalidate(sockaddr *addr) { |
| addr->sa_family = AF_UNSPEC; |
| } |
| |
| inline bool |
| IPEndpoint::is_valid() const { |
| return sa.sa_family == AF_INET || sa.sa_family == AF_INET6; |
| } |
| |
| inline IPEndpoint& |
| IPEndpoint::operator=(self_type const& that) { |
| self_type::assign(&sa, &that.sa); |
| return *this; |
| } |
| |
| inline IPEndpoint& |
| IPEndpoint::assign(sockaddr const *src) { |
| self_type::assign(&sa, src); |
| return *this; |
| } |
| |
| inline IPEndpoint const& |
| IPEndpoint::fill(sockaddr *addr) const { |
| self_type::assign(addr, &sa); |
| return *this; |
| } |
| |
| inline bool |
| IPEndpoint::is_ip4() const { |
| return AF_INET == sa.sa_family; |
| } |
| |
| inline bool |
| IPEndpoint::is_ip6() const { |
| return AF_INET6 == sa.sa_family; |
| } |
| |
| inline sa_family_t |
| IPEndpoint::family() const { |
| return sa.sa_family; |
| } |
| |
| inline in_port_t& |
| IPEndpoint::network_order_port() { |
| return self_type::port(&sa); |
| } |
| |
| inline in_port_t |
| IPEndpoint::network_order_port() const { |
| return self_type::port(&sa); |
| } |
| |
| inline in_port_t |
| IPEndpoint::host_order_port() const { |
| return ntohs(this->network_order_port()); |
| } |
| |
| 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; |
| } |
| // Force a failure upstream by returning a null reference. |
| throw std::domain_error("sockaddr is not a valid IP address"); |
| } |
| |
| inline in_port_t |
| IPEndpoint::port(sockaddr const *addr) { |
| return self_type::port(const_cast<sockaddr *>(addr)); |
| } |
| |
| inline in_port_t |
| IPEndpoint::host_order_port(sockaddr const *sa) { |
| return ntohs(self_type::port(sa)); |
| } |
| |
| // --- IPAddr variants --- |
| |
| inline constexpr IP4Addr::IP4Addr(in_addr_t addr) : _addr(addr) {} |
| |
| inline IP4Addr::IP4Addr(std::string_view const& 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::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& |
| IP4Addr::operator++() { |
| ++_addr; |
| return *this; |
| } |
| |
| inline IP4Addr& |
| IP4Addr::operator--() { |
| --_addr; |
| return *this; |
| } |
| |
| inline in_addr_t IP4Addr::network_order() const { |
| return htonl(_addr); |
| } |
| |
| inline in_addr_t IP4Addr::host_order() const { |
| return _addr; |
| } |
| |
| inline auto |
| IP4Addr::operator=(in_addr_t ip) -> self_type& { |
| _addr = ntohl(ip); |
| return *this; |
| } |
| |
| inline bool operator==(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return lhs._addr == rhs._addr; |
| } |
| |
| inline bool operator!=(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return lhs._addr != rhs._addr; |
| } |
| |
| inline bool operator<(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return lhs._addr < rhs._addr; |
| } |
| |
| inline bool operator<=(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return lhs._addr <= rhs._addr; |
| } |
| |
| inline bool operator>(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return rhs < lhs; |
| } |
| |
| inline bool operator>=(IP4Addr const& lhs, IP4Addr const& rhs) { |
| return rhs <= lhs; |
| } |
| |
| inline IP4Addr& IP4Addr::operator&=(IPMask const& mask) { |
| _addr &= mask.as_ip4()._addr; |
| return *this; |
| } |
| |
| 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); |
| } |
| |
| // --- |
| |
| inline IP6Addr::IP6Addr(in6_addr const& addr) { |
| *this = addr; |
| } |
| |
| inline IP6Addr::IP6Addr(std::string_view const& 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 { |
| self_type::reorder(addr, _addr._raw); |
| return addr; |
| } |
| |
| inline in6_addr IP6Addr::network_order() const { |
| in6_addr zret; |
| return this->copy_to(zret); |
| } |
| |
| 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& { |
| if (addr) { |
| *this = addr->sin6_addr; |
| } else { |
| this->clear(); |
| } |
| return *this; |
| } |
| |
| inline IP6Addr& |
| IP6Addr::operator++() { |
| if (++(_addr._store[1]) == 0) { |
| ++(_addr._store[0]); |
| } |
| return *this; |
| } |
| |
| inline IP6Addr& |
| IP6Addr::operator--() { |
| 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) { |
| dst[idx] = src[WORD_SIZE - (idx + 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._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._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) { |
| return rhs < lhs; |
| } |
| |
| 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) { |
| 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; |
| } |
| |
| // --- // |
| |
| inline bool IPAddr::operator==(self_type const& that) const { |
| switch (_family) { |
| case AF_INET: return that._family == AF_INET && _addr._ip4 == that._addr._ip4; |
| case AF_INET6: return that._family == AF_INET6 && _addr._ip6 == that._addr._ip6; |
| default: return false; |
| } |
| } |
| |
| inline bool |
| IPAddr::operator!=(self_type const& that) const { |
| return !(*this == that); |
| } |
| |
| inline bool IPAddr::operator>(self_type const& that) const { |
| return that < *this; |
| } |
| |
| inline bool IPAddr::operator<=(self_type const& that) const { |
| return !(that < *this); |
| } |
| |
| inline bool IPAddr::operator>=(self_type const& that) const { |
| return !(*this < that); |
| } |
| |
| // Disambiguating comparisons. |
| |
| 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) { |
| 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) { |
| 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) { |
| return rhs.is_ip6() && lhs == rhs.ip6(); |
| } |
| |
| inline bool operator!=(IP6Addr const& lhs, IPAddr const& rhs) { |
| return !rhs.is_ip6() || lhs != rhs.ip6(); |
| } |
| |
| // +++ IPRange +++ |
| |
| 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; |
| } |
| |
| inline IPRange::IPRange(IP6Range const& range) : _family(AF_INET6) { |
| _range._ip6 = range; |
| } |
| |
| 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 +++ |
| |
| inline IPMask::IPMask(raw_type width) : _cidr(width) {} |
| |
| inline bool IPMask::is_valid() const { return _cidr < INVALID; } |
| |
| inline auto IPMask::width() const -> raw_type { |
| return _cidr; |
| } |
| |
| inline bool |
| operator==(IPMask const& lhs, IPMask const& rhs) { |
| return lhs.width() == rhs.width(); |
| } |
| |
| inline bool |
| operator!=(IPMask const& lhs, IPMask const& rhs) { |
| return lhs.width() != rhs.width(); |
| } |
| |
| inline bool |
| operator<(IPMask const& lhs, IPMask const& rhs) { |
| return lhs.width() < rhs.width(); |
| } |
| |
| 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 mask operators +++ |
| |
| inline IP4Addr operator&(IP4Addr const& addr, IPMask const& mask) { |
| return IP4Addr{addr} &= mask; |
| } |
| |
| inline IP4Addr operator|(IP4Addr const& addr, IPMask const& mask) { |
| return IP4Addr{addr} |= mask; |
| } |
| |
| inline IP6Addr operator&(IP6Addr const& addr, IPMask const& mask) { |
| return IP6Addr{addr} &= mask; |
| } |
| |
| 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 bool IP4Range::NetSource::empty() const { |
| return _range.empty(); |
| } |
| |
| 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 bool IP6Range::NetSource::empty() const { |
| return _range.empty(); |
| } |
| |
| 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 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& { |
| if (range.is(AF_INET)) { |
| _ip4.mark(range.ip4(), payload); |
| } else if (range.is(AF_INET6)) { |
| _ip6.mark(range.ip6(), payload); |
| } |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::fill(IPRange const& range, PAYLOAD const& payload) -> self_type& { |
| if (range.is(AF_INET6)) { |
| _ip6.fill(range.ip6(), payload); |
| } else if (range.is(AF_INET)) { |
| _ip4.fill(range.ip4(), payload); |
| } |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::erase(IPRange const& range) -> self_type& { |
| if (range.is(AF_INET)) { |
| _ip4.erase(range.ip4()); |
| } else if (range.is(AF_INET6)) { |
| _ip6.erase(range.ip6()); |
| } |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| 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.ip4(), color, blender); |
| } else if (range.is(AF_INET6)) { |
| _ip6.blend(range.ip6(), color, blender); |
| } |
| return *this; |
| } |
| |
| template<typename PAYLOAD> |
| void IPSpace<PAYLOAD>::clear() { |
| _ip4.clear(); |
| _ip6.clear(); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::begin() const -> const_iterator { |
| 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); |
| return const_iterator(nc_this->_ip4.end(), nc_this->_ip6.end()); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::begin_ip4() const -> const_iterator { |
| return this->begin(); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::end_ip4() const -> const_iterator { |
| auto nc_this = const_cast<self_type *>(this); |
| return iterator(nc_this->_ip4.end(), nc_this->_ip6.begin()); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::begin_ip6() const -> const_iterator { |
| auto nc_this = const_cast<self_type *>(this); |
| return iterator(nc_this->_ip4.end(), nc_this->_ip6.begin()); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::end_ip6() const -> const_iterator { |
| return this->end(); |
| } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::begin() -> iterator { return iterator{_ip4.begin(), _ip6.begin()}; } |
| |
| template<typename PAYLOAD> |
| auto IPSpace<PAYLOAD>::end() -> iterator { return iterator{_ip4.end(), _ip6.end()}; } |
| |
| template<typename PAYLOAD> |
| size_t IPSpace<PAYLOAD>::count(sa_family_t f) const { |
| return IP4Addr::AF_value == f ? _ip4.count() |
| : IP6Addr::AF_value == f ? _ip6.count() |
| : 0; |
| } |
| |
| }} // namespace swoc |
| |
| /// @cond NOT_DOCUMENTED |
| 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 |
| /// @endcond |
| |
| namespace swoc { inline namespace SWOC_VERSION_NS { |
| |
| 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 |