| // 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 |