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