code/include/swoc/IPAddr.h (809 lines of code) (raw):

// SPDX-License-Identifier: Apache-2.0 // Copyright Network Geographics 2014 /** @file IP address and network related classes. */ #pragma once #include <cstddef> #include "swoc/swoc_version.h" #include "swoc/swoc_meta.h" #include "swoc/MemSpan.h" #include "swoc/swoc_ip_util.h" namespace swoc { inline namespace SWOC_VERSION_NS { using std::string_view; union IPEndpoint; class IPAddr; class IPMask; /** 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 - ANY address. /// Copy constructor. IP4Addr(self_type const &that) = default; /// 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 *s); /// Construct from text representation. /// If the @a text is invalid the result is @c INADDR_ANY IP4Addr(string_view const &text); /// Self assignment. self_type &operator=(self_type const &that) = default; /// 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 (network order). * * For convenience, this returns in "text order" of the octets. */ uint8_t operator[](unsigned idx) const; /// 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); /** Update socket address with this address. * * @param sa Socket address. * @return @sa * * @a sa is assumed to be large enough to hold an IPv4 address. */ sockaddr *copy_to(sockaddr *sa) const; /** Update socket address with this address. * * @param sin IPv4 socket address. * @return @sin */ sockaddr_in *copy_to(sockaddr_in *sin) 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 IPv4 address. * * @param text Text to parse. * * @return @c true if @a text is a valid IPv4 address, @c false otherwise. * * Whitespace is trimmed from @text before parsing. If the parse fails @a this is set to @c INADDR_ANY. */ bool load(string_view const &text); /// Standard ternary compare. int cmp(self_type const &that) const; /// Get the IP address family. /// @return @c AF_INET /// @note Useful primarily for template classes. constexpr sa_family_t family() const; /// @return @c true if this is the "any" address, @c false if not. bool is_any() const; /// @return @c true if this is a multicast address, @c false if not. bool is_multicast() const; /// @return @c true if this is a loopback address, @c false if not. bool is_loopback() const; /// @return @c true if the address is in the link local network. bool is_link_local() const; /// @return @c true if the address is private. bool is_private() const; /** 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. Internal storage is not necessarily network ordered. @see network_order @see copy_to */ 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<uint8_t>::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. /// Number of bits per quad. static constexpr size_t QUAD_WIDTH = std::numeric_limits<uint8_t>::digits * sizeof(quad_type); /// 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<uint8_t, SIZE>; /// Minimum value of an address. static const self_type MIN; /// Maximum value of an address. static const self_type MAX; IP6Addr() = default; ///< Default constructor - ANY 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 any address. /// @see load IP6Addr(string_view const &text); /** Construct mapped IPv4 address. * * @param addr IPv4 address */ explicit IP6Addr(IP4Addr addr); /// Self assignment. self_type &operator=(self_type const &that) = default; /** 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 &addr); /// Set to the address in @a addr. self_type &operator=(sockaddr_in6 const *addr); /** Access a byte in the address. * * @param idx Byte index. * @return The "text order" byte. */ constexpr uint8_t operator[](int idx) const; /** Update socket address with this address. * * @param sin IPv6 socket address. * @return @sin */ sockaddr_in6 *copy_to(sockaddr_in6 *sin6) const; /** Update socket address with this address. * * @param sa Socket address. * @return @sin * * @a sa is assumed to be large enough to hold an IPv6 address. */ sockaddr *copy_to(sockaddr *sa) const; /// Return the address in host order. in6_addr host_order() const; /** Copy the address in host order. * * @param dst Destination for host order address. * @return @a dst */ in6_addr &host_order(in6_addr &dst) const; /// Return the address in network order. in6_addr network_order() const; /** Copy the address in network order. * * @param dst Destination for network order address. * @return @a dst */ in6_addr &network_order(in6_addr &dst) 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 three value compare. int cmp(self_type const &that) const; /// @return The address family. constexpr sa_family_t family() const; /// @return @c true if this is the "any" address, @c false if not. bool is_any() const; /// @return @c true if this is a loopback address, @c false if not. bool is_loopback() const; /// @return @c true if this is a multicast address, @c false if not. bool is_multicast() const; /// @return @c true if this is a link local address, @c false if not. bool is_link_local() const; /// @return @c true if the address is private. bool is_private() const; /// @return @c true if this is an IPv4 addressed mapped to IPv6, @c false if not. bool is_mapped_ip4() const; /** Reset to default constructed state. * * @return @a this */ self_type &clear(); /** Bitwise AND. * * @param that Source mask. * @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&=(IPMask const &that); /** Bitwise OR. * * @param that Source mask. * @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|=(IPMask const &that); /** Convert between network and host ordering. * * @param dst Destination for re-ordered address. * @param src Original address. */ static void reorder(in6_addr &dst, raw_type const &src); /** Convert between network and host ordering. * * @param dst Destination for re-ordered address. * @param src Original address. */ static void reorder(raw_type &dst, in6_addr const &src); template <typename T> auto as_span() -> std::enable_if_t<swoc::meta::is_any_of_v<T, std::byte, uint8_t, uint16_t, uint32_t, uint64_t>, swoc::MemSpan<T>> { return swoc::MemSpan(_addr._store).template rebind<T>(); } template <typename T> auto as_span() const -> std::enable_if_t<swoc::meta::is_any_of_v<typename std::remove_const_t<T>, std::byte, uint8_t, uint16_t, uint32_t, uint64_t>, swoc::MemSpan<T const>> { return swoc::MemSpan<uint64_t const>(_addr._store).template rebind<T const>(); } 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 &); /// 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>; /// 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<uint8_t>::digits * WORD_SIZE; /// Mask the size of a word. static constexpr word_type WORD_MASK = ~word_type(0); /// Number of words used for basic address storage. static constexpr size_t N_STORE = SIZE / WORD_SIZE; /// Type used to store the address. using word_store_type = std::array<word_type, N_STORE>; /// 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 Addr { word_store_type _store = {0}; ///< 0 is MSW, 1 is LSW. quad_store_type _quad; ///< By quad. raw_type _raw; ///< By byte. in6_addr _in6; ///< By networking type (but in host order!) } _addr; static_assert(sizeof(in6_addr) == sizeof(raw_type)); 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}; /// Index of bytes in @a _addr._raw /// This converts MSB (0) to LSB (15) indices to the bytes in the binary format. static constexpr std::array<unsigned, SIZE> RAW_IDX = {7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}; /// Convert between network and host order. /// The conversion is symmetric. /// @param dst Output where reordered value is placed. /// @param src Input value to resorder. 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); }; /** An IPv4 or IPv6 address. * * The family type is stored. For comparisons, invalid < IPv4 < IPv6. All invalid instances are equal. */ 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. self_type &operator=(self_type const &that) = default; ///< Copy assignment. /// 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 IPv4 @a addr. self_type &assign(in_addr_t addr); /// Set to IPv6 @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 &addr); 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); /** Copy the address to a socket address. * * @param sa Destination. * @return @a sa */ sockaddr *copy_to(sockaddr *sa) const; /** 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 is_same_family(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; /// @return As IPv4 address - results are undefined if it is not actually IPv4. IP4Addr const &ip4() const; /// @return As IPv4 address - results are undefined if it is not actually IPv4. explicit operator IP4Addr() const; /// @return As IPv6 address - results are undefined if it is not actually IPv6. IP6Addr const &ip6() const; /// @return As IPv6 address - results are undefined if it is not actually IPv6. explicit operator IP6Addr() const; /// Test for validity. bool is_valid() const; /// Make invalid. self_type &invalidate(); /// Test for loopback bool is_loopback() const; /// Test for multicast bool is_multicast() const; /// @return @c true if this is a link local address, @c false if not. bool is_link_local() const; /// @return @c true if this is a private address, @c false if not. bool is_private() 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) 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; ///< Default construct to invalid mask. /** Construct a mask of @a width. * * @param width Number of bits in the mask. * * @note Because this is a network mask, it is always left justified. */ explicit IPMask(raw_type width); /// @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 A mask with 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); /// Change to default constructed state (invalid). self_type &clear(); /// The width of the mask. raw_type width() const; /** Extend the mask (cover more addresses). * * @param n Number of bits to extend. * @return @a this * * Effectively shifts the mask left, bringing in 0 bits on the right. */ self_type &operator<<=(raw_type n); /** Narrow the mask (cover fewer addresses). * * @param n Number of bits to narrow. * @return @a this * * Effectively shift the mask right, bringing in 1 bits on the left. */ self_type &operator>>=(raw_type n); /** 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); }; // --- Implementation inline constexpr IP4Addr::IP4Addr(in_addr_t addr) : _addr(addr) {} inline IP4Addr::IP4Addr(string_view const &text) { if (!this->load(text)) { _addr = INADDR_ANY; } } inline constexpr sa_family_t IP4Addr::family() const { return AF_value; } inline auto IP4Addr::operator<<=(unsigned n) -> self_type & { _addr <<= n; return *this; } inline auto IP4Addr::operator>>=(unsigned n) -> self_type & { _addr >>= n; return *this; } inline auto IP4Addr::operator&=(self_type const &that) -> self_type & { _addr &= that._addr; return *this; } inline auto IP4Addr::operator|=(self_type const &that) -> self_type & { _addr |= that._addr; return *this; } inline auto IP4Addr::operator++() -> self_type & { ++_addr; return *this; } inline auto IP4Addr::operator--() -> self_type & { --_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 sockaddr * IP4Addr::copy_to(sockaddr *sa) const { this->copy_to(reinterpret_cast<sockaddr_in *>(sa)); return sa; } /// Equality. inline bool operator==(IP4Addr const &lhs, IP4Addr const &rhs) { return lhs._addr == rhs._addr; } /// @return @c true if @a lhs is equal to @a rhs. inline bool operator!=(IP4Addr const &lhs, IP4Addr const &rhs) { return lhs._addr != rhs._addr; } /// @return @c true if @a lhs is less than @a rhs (host order). inline bool operator<(IP4Addr const &lhs, IP4Addr const &rhs) { return lhs._addr < rhs._addr; } /// @return @c true if @a lhs is less than or equal to@a rhs (host order). inline bool operator<=(IP4Addr const &lhs, IP4Addr const &rhs) { return lhs._addr <= rhs._addr; } /// @return @c true if @a lhs is greater than @a rhs (host order). inline bool operator>(IP4Addr const &lhs, IP4Addr const &rhs) { return rhs < lhs; } /// @return @c true if @a lhs is greater than or equal to @a rhs (host order). 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; } inline bool IP4Addr::is_any() const { return _addr == INADDR_ANY; } inline bool IP4Addr::is_loopback() const { return (*this)[0] == IN_LOOPBACKNET; } inline bool IP4Addr::is_multicast() const { return IN_MULTICAST(_addr); } inline bool IP4Addr::is_link_local() const { return ip::is_link_local_host_order(_addr); } inline bool IP4Addr::is_private() const { return ip::is_private_host_order(_addr); } inline uint8_t IP4Addr::operator[](unsigned int idx) const { return reinterpret_cast<bytes const &>(_addr)[3 - idx]; } inline int IP4Addr::cmp(IP4Addr::self_type const &that) const { return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0; } 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); } // +++ IP6Addr +++ inline constexpr sa_family_t IP6Addr::family() const { return AF_value; } inline IP6Addr::IP6Addr(in6_addr const &addr) { *this = addr; } inline IP6Addr::IP6Addr(string_view const &text) { if (!this->load(text)) { this->clear(); } } inline IP6Addr::IP6Addr(IP4Addr addr) { _addr._store[MSW] = 0; _addr._quad[QUAD_IDX[4]] = 0; _addr._quad[QUAD_IDX[5]] = 0xffff; _addr._quad[QUAD_IDX[6]] = addr.host_order() >> QUAD_WIDTH; _addr._quad[QUAD_IDX[7]] = addr.host_order(); } inline bool IP6Addr::is_loopback() const { return _addr._store[MSW] == 0 && _addr._store[LSW] == 1; } inline bool IP6Addr::is_multicast() const { return _addr._raw[RAW_IDX[0]] == 0xFF; } inline bool IP6Addr::is_any() const { return _addr._store[MSW] == 0 && _addr._store[LSW] == 0; } inline bool IP6Addr::is_mapped_ip4() const { return 0 == _addr._store[MSW] && (_addr._quad[QUAD_IDX[4]] == 0 && _addr._quad[QUAD_IDX[5]] == 0xFFFF); } inline bool IP6Addr::is_link_local() const { return _addr._raw[RAW_IDX[0]] == 0xFE && (_addr._raw[RAW_IDX[1]] & 0xC0) == 0x80; // fe80::/10 } inline bool IP6Addr::is_private() const { return (_addr._raw[RAW_IDX[0]] & 0xFE) == 0xFC; // fc00::/7 } inline in6_addr IP6Addr::host_order() const { Addr zret{ {_addr._store[LSW], _addr._store[MSW]} }; return zret._in6; } inline in6_addr & IP6Addr::host_order(in6_addr &dst) const { Addr *addr = reinterpret_cast<Addr *>(&dst); addr->_store[0] = _addr._store[LSW]; addr->_store[1] = _addr._store[MSW]; return dst; } inline in6_addr IP6Addr::network_order() const { in6_addr zret; return this->network_order(zret); } inline in6_addr & IP6Addr::network_order(in6_addr &dst) const { self_type::reorder(dst, _addr._raw); return dst; } inline auto IP6Addr::clear() -> self_type & { _addr._store[MSW] = _addr._store[LSW] = 0; return *this; } 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 auto IP6Addr::operator++() -> self_type & { if (++(_addr._store[LSW]) == 0) { ++(_addr._store[MSW]); } return *this; } inline auto IP6Addr::operator--() -> self_type & { if (--(_addr._store[LSW]) == ~static_cast<uint64_t>(0)) { --(_addr._store[MSW]); } 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)]; } } /// @return @c true if @a lhs is equal to @a rhs. inline bool operator==(IP6Addr const &lhs, IP6Addr const &rhs) { return lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] == rhs._addr._store[IP6Addr::LSW]; } /// @return @c true if @a lhs is not equal to @a rhs. inline bool operator!=(IP6Addr const &lhs, IP6Addr const &rhs) { return lhs._addr._store[IP6Addr::MSW] != rhs._addr._store[IP6Addr::MSW] || lhs._addr._store[IP6Addr::LSW] != rhs._addr._store[IP6Addr::LSW]; } /// @return @c true if @a lhs is less than @a rhs. inline bool operator<(IP6Addr const &lhs, IP6Addr const &rhs) { return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] || (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] < rhs._addr._store[IP6Addr::LSW]); } /// @return @c true if @a lhs is greater than @a rhs. inline bool operator>(IP6Addr const &lhs, IP6Addr const &rhs) { return rhs < lhs; } /// @return @c true if @a lhs is less than or equal to @a rhs. inline bool operator<=(IP6Addr const &lhs, IP6Addr const &rhs) { return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] || (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && lhs._addr._store[IP6Addr::LSW] <= rhs._addr._store[IP6Addr::LSW]); } /// @return @c true if @a lhs is greater than or equal to @a rhs. inline bool operator>=(IP6Addr const &lhs, IP6Addr const &rhs) { return rhs <= lhs; } inline sockaddr * IP6Addr::copy_to(sockaddr *sa) const { this->copy_to(reinterpret_cast<sockaddr_in6 *>(sa)); return sa; } inline auto IP6Addr::operator&=(IPMask const &mask) -> self_type & { if (mask._cidr <= WORD_WIDTH) { _addr._store[LSW] = 0; if (0 == mask._cidr) { _addr._store[MSW] = 0; } else if (mask._cidr < WORD_WIDTH) { _addr._store[MSW] &= (WORD_MASK << (WORD_WIDTH - mask._cidr)); } } else if (mask._cidr < WIDTH) { _addr._store[LSW] &= (WORD_MASK << (2 * WORD_WIDTH - mask._cidr)); } return *this; } inline auto IP6Addr::operator|=(IPMask const &mask) -> self_type & { if (mask._cidr > WORD_WIDTH) { if (mask._cidr < WIDTH) { _addr._store[LSW] |= (WORD_MASK >> (mask._cidr - WORD_WIDTH)); } } else { _addr._store[LSW] = WORD_MASK; if (0 == mask._cidr) { _addr._store[MSW] = WORD_MASK; } else if (mask._cidr < WORD_WIDTH) { _addr._store[MSW] |= (WORD_MASK >> mask._cidr); } } return *this; } // +++ 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 (0 == _cidr) { addr = in_addr_t(0); } else if (_cidr < IP4Addr::WIDTH) { addr <<= IP4Addr::WIDTH - _cidr; } return IP4Addr{addr}; } inline auto IPMask::clear() -> self_type & { _cidr = INVALID; return *this; } inline auto IPMask::operator<<=(raw_type n) -> self_type & { _cidr -= n; return *this; } inline auto IPMask::operator>>=(IPMask::raw_type n) -> self_type & { _cidr += n; return *this; } // +++ 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; } constexpr uint8_t IP6Addr::operator[](int idx) const { return _addr._raw[RAW_IDX[idx]]; } 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; } // @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(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=(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::is_same_family(self_type const &that) { return this->is_valid() && _family == that._family; } inline bool IPAddr::is_loopback() const { return (AF_INET == _family && _addr._ip4.is_loopback()) || (AF_INET6 == _family && _addr._ip6.is_loopback()); } inline bool IPAddr::is_link_local() const { return this->is_ip4() ? this->ip4().is_link_local() : this->is_ip6() ? this->ip6().is_link_local() : false; } inline bool IPAddr::is_private() const { return this->is_ip4() ? this->ip4().is_private() : this->is_ip6() ? this->ip6().is_private() : false; } 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. /// Equality. bool operator==(IPAddr const &lhs, sockaddr const *rhs); /// Equality. inline bool operator==(sockaddr const *lhs, IPAddr const &rhs) { return rhs == lhs; } /// Inequality. inline bool operator!=(IPAddr const &lhs, sockaddr const *rhs) { return !(lhs == rhs); } /// Inequality. inline bool operator!=(sockaddr const *lhs, IPAddr const &rhs) { return !(rhs == lhs); } inline IP4Addr const & IPAddr::ip4() const { return _addr._ip4; } inline IPAddr::operator IP4Addr() const { return _addr._ip4; } inline IP6Addr const & IPAddr::ip6() const { return _addr._ip6; } inline IPAddr::operator IP6Addr() const { return _addr._ip6; } inline bool IPAddr::operator==(self_type const &that) const { return _family == that.family() && ( ( this->is_ip4() && _addr._ip4 == that._addr._ip4 ) || ( this->is_ip6() && _addr._ip6 == that._addr._ip6 ) || _family == AF_UNSPEC ); } 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 between comparisons and implicit conversions. inline bool operator==(IPAddr const &lhs, IP4Addr const &rhs) { return lhs.is_ip4() && lhs.ip4() == rhs; } inline bool operator!=(IPAddr const &lhs, IP4Addr const &rhs) { return !lhs.is_ip4() || lhs.ip4() != rhs; } inline bool operator==(IP4Addr const &lhs, IPAddr const &rhs) { return rhs.is_ip4() && lhs == rhs.ip4(); } inline bool operator!=(IP4Addr const &lhs, IPAddr const &rhs) { return !rhs.is_ip4() || lhs != rhs.ip4(); } inline bool operator==(IPAddr const &lhs, IP6Addr const &rhs) { return lhs.is_ip6() && lhs.ip6() == rhs; } inline bool operator!=(IPAddr const &lhs, IP6Addr const &rhs) { return !lhs.is_ip6() || lhs.ip6() != 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(); } }} // namespace swoc::SWOC_VERSION_NS namespace std { /// Standard hash support for @a IP4Addr. template <> struct hash<swoc::IP4Addr> { size_t operator()(swoc::IP4Addr const &addr) const { return addr.network_order(); } }; /// Standard hash support for @a IP6Addr. template <> struct hash<swoc::IP6Addr> { size_t operator()(swoc::IP6Addr const &addr) const { // XOR the 64 chunks auto words = addr.as_span<uint64_t>(); return words[0] ^ words[1]; } }; /// Standard hash support for @a IPAddr. template <> struct hash<swoc::IPAddr> { size_t operator()(swoc::IPAddr const &addr) const { return addr.is_ip4() ? hash<swoc::IP4Addr>()(addr.ip4()) : addr.is_ip6() ? hash<swoc::IP6Addr>()(addr.ip6()) : 0; } }; } // namespace std