blob: 9434221cd560e0b8a2803feb3d47746b4af2692f [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// Copyright Network Geographics 2014
/** @file
IP address support.
*/
#include "swoc/swoc_ip.h"
#include "swoc/swoc_meta.h"
using swoc::TextView;
using swoc::svtoi;
using swoc::svtou;
using namespace swoc::literals;
namespace {
// Handle the @c sin_len member, the presence of which varies across compilation environments.
template <typename T>
auto
Set_Sockaddr_Len_Case(T *, swoc::meta::CaseTag<0>) -> decltype(swoc::meta::TypeFunc<void>()) {}
template <typename T>
auto
Set_Sockaddr_Len_Case(T *addr, swoc::meta::CaseTag<1>) -> decltype(T::sin_len, swoc::meta::TypeFunc<void>()) {
addr->sin_len = sizeof(T);
}
template <typename T>
void
Set_Sockaddr_Len(T *addr) {
Set_Sockaddr_Len_Case(addr, swoc::meta::CaseArg);
}
} // namespace
namespace swoc { inline namespace SWOC_VERSION_NS {
IPAddr const IPAddr::INVALID;
IP4Addr const IP4Addr::MIN{INADDR_ANY};
IP4Addr const IP4Addr::MAX{INADDR_BROADCAST};
IP6Addr const IP6Addr::MIN{0, 0};
IP6Addr const IP6Addr::MAX{std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max()};
// The Intel compiler won't let me directly use @c nullptr to construct a reference, so I need
// to fake it out by using this psuedo-nullptr.
void *const detail::pseudo_nullptr = nullptr;
bool
IPEndpoint::assign(sockaddr *dst, sockaddr const *src) {
size_t n = 0;
if (dst != src) {
self_type::invalidate(dst);
switch (src->sa_family) {
case AF_INET:
n = sizeof(sockaddr_in);
break;
case AF_INET6:
n = sizeof(sockaddr_in6);
break;
}
if (n) {
memcpy(dst, src, n);
}
}
return n != 0;
}
IPEndpoint &
IPEndpoint::assign(IPAddr const &src, in_port_t port) {
switch (src.family()) {
case AF_INET: {
memset(&sa4, 0, sizeof sa4);
sa4.sin_family = AF_INET;
sa4.sin_addr.s_addr = src.ip4().network_order();
sa4.sin_port = port;
Set_Sockaddr_Len(&sa4);
} break;
case AF_INET6: {
memset(&sa6, 0, sizeof sa6);
sa6.sin6_family = AF_INET6;
sa6.sin6_addr = src.ip6().network_order();
sa6.sin6_port = port;
Set_Sockaddr_Len(&sa6);
} break;
}
return *this;
}
bool
IPEndpoint::tokenize(std::string_view str, std::string_view *addr, std::string_view *port, std::string_view *rest) {
TextView src(str); /// Easier to work with for parsing.
// In case the incoming arguments are null, set them here and only check for null once.
// it doesn't matter if it's all the same, the results will be thrown away.
std::string_view local;
if (!addr) {
addr = &local;
}
if (!port) {
port = &local;
}
if (!rest) {
rest = &local;
}
*addr = std::string_view{};
*port = std::string_view{};
*rest = std::string_view{};
// Let's see if we can find out what's in the address string.
if (src) {
bool colon_p = false;
src.ltrim_if(&isspace);
// Check for brackets.
if ('[' == *src) {
++src; // skip bracket.
*addr = src.take_prefix_at(']');
if (':' == *src) {
colon_p = true;
++src;
}
} else {
TextView::size_type last = src.rfind(':');
if (last != TextView::npos && last == src.find(':')) {
// Exactly one colon - leave post colon stuff in @a src.
*addr = src.take_prefix(last);
colon_p = true;
} else { // presume no port, use everything.
*addr = src;
src.clear();
}
}
if (colon_p) {
TextView tmp{src};
src.ltrim_if(&isdigit);
if (tmp.data() == src.data()) { // no digits at all
src.assign(tmp.data() - 1, tmp.size() + 1); // back up to include colon
} else {
*port = std::string_view(tmp.data(), src.data() - tmp.data());
}
}
*rest = src;
}
return !addr->empty(); // true if we found an address.
}
bool
IPEndpoint::parse(std::string_view const &str) {
TextView addr_str, port_str, rest;
TextView src{TextView{str}.trim_if(&isspace)};
if (this->tokenize(src, &addr_str, &port_str, &rest)) {
if (rest.empty()) {
if (IPAddr addr; addr.load(addr_str)) {
in_port_t port = 0;
if (!port_str.empty()) {
uintmax_t n{swoc::svto_radix<10>(port_str)};
if (!port_str.empty() || n > std::numeric_limits<in_port_t>::max()) {
return false;
}
port = n;
}
this->assign(addr, htons(port));
return true;
}
}
}
return false;
}
socklen_t
IPEndpoint::size() const {
switch (sa.sa_family) {
case AF_INET:
return sizeof(sockaddr_in);
case AF_INET6:
return sizeof(sockaddr_in6);
default:
return sizeof(sockaddr);
}
}
std::string_view
IPEndpoint::family_name(sa_family_t family) {
switch (family) {
case AF_INET:
return "ipv4"_sv;
case AF_INET6:
return "ipv6"_sv;
case AF_UNIX:
return "unix"_sv;
case AF_UNSPEC:
return "unspec"_sv;
}
return "unknown"_sv;
}
IPEndpoint &
IPEndpoint::set_to_any(int family) {
memset(this, 0, sizeof(*this));
if (AF_INET == family) {
sa4.sin_family = family;
sa4.sin_addr.s_addr = INADDR_ANY;
Set_Sockaddr_Len(&sa4);
} else if (AF_INET6 == family) {
sa6.sin6_family = family;
sa6.sin6_addr = in6addr_any;
Set_Sockaddr_Len(&sa6);
}
return *this;
}
bool
IPEndpoint::is_any() const {
bool zret = false;
switch (this->family()) {
case AF_INET:
zret = sa4.sin_addr.s_addr == INADDR_ANY;
break;
case AF_INET6:
zret = IN6_IS_ADDR_UNSPECIFIED(&sa6.sin6_addr);
break;
}
return zret;
}
IPEndpoint &
IPEndpoint::set_to_loopback(int family) {
memset(this, 0, sizeof(*this));
if (AF_INET == family) {
sa.sa_family = family;
sa4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
Set_Sockaddr_Len(&sa4);
} else if (AF_INET6 == family) {
sa.sa_family = family;
sa6.sin6_addr = in6addr_loopback;
Set_Sockaddr_Len(&sa6);
}
return *this;
}
bool
IPEndpoint::is_loopback() const {
bool zret = false;
switch (this->family()) {
case AF_INET:
zret = ((ntohl(sa4.sin_addr.s_addr) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET;
break;
case AF_INET6:
zret = IN6_IS_ADDR_LOOPBACK(&sa6.sin6_addr);
break;
}
return zret;
}
bool
IP4Addr::load(std::string_view const &text) {
TextView src{text};
int n = SIZE; /// # of octets
_addr = INADDR_ANY; // clear to zero.
// empty or trailing dot or empty brackets or unmatched brackets.
if (src.empty() || src.back() == '.' || ('[' == *src && ((++src).empty() || src.back() != ']'))) {
return false;
}
in_addr_t max = std::numeric_limits<in_addr_t>::max();
while (n > 0) {
TextView parsed;
auto token = src.take_prefix_at('.');
auto v = svtou(token, &parsed);
if (parsed.size() != token.size()) {
break;
}
if (src.empty()) {
if (v <= max) {
_addr += v;
n = 0; // signal complete.
}
break;
} else if (v <= std::numeric_limits<uint8_t>::max()){
_addr += v << ( --n * 8);
} else {
break; // invalid.
}
max >>= 8; // reduce by one octet.
}
// If there's text left, or not all the octets were filled, fail.
if (! src.empty() || n != 0) {
_addr = INADDR_ANY;
return false;
}
return true;
}
IP4Addr::IP4Addr(sockaddr_in const *sa) : _addr(reorder(sa->sin_addr.s_addr)) {}
auto
IP4Addr::operator=(sockaddr_in const *sa) -> self_type & {
_addr = reorder(sa->sin_addr.s_addr);
return *this;
}
sockaddr_in *
IP4Addr::copy_to(sockaddr_in *sa, in_port_t port) const {
sa->sin_addr.s_addr = this->network_order();
sa->sin_port = port;
return sa;
}
// --- IPv6
sockaddr *
IP6Addr::copy_to(sockaddr *sa, in_port_t port) const {
IPEndpoint addr(sa);
self_type::reorder(addr.sa6.sin6_addr, _addr._raw);
addr.network_order_port() = port;
return sa;
}
int
IP6Addr::cmp(const self_type &that) const {
return *this < that ? -1 : *this > that ? 1 : 0;
}
IP6Addr &
IP6Addr::operator<<=(unsigned int n) {
static constexpr auto MASK = ~word_type{0};
if (n < WORD_WIDTH) {
_addr._store[MSW] <<= n;
_addr._store[MSW] |= (_addr._store[LSW] >> (WORD_WIDTH - n)) & ~(MASK << n);
_addr._store[LSW] <<= n;
} else {
n -= WORD_WIDTH;
_addr._store[MSW] = _addr._store[LSW] << n;
_addr._store[LSW] = 0;
}
return *this;
}
IP6Addr &
IP6Addr::operator>>=(unsigned int n) {
static constexpr auto MASK = ~word_type{0};
if (n < WORD_WIDTH) {
_addr._store[LSW] >>= n;
_addr._store[LSW] |= (_addr._store[MSW] & ~(MASK << n)) << (WORD_WIDTH - n);
_addr._store[MSW] >>= n;
} else {
n -= WORD_WIDTH;
_addr._store[LSW] = _addr._store[MSW] >> n;
_addr._store[MSW] = 0;
}
return *this;
}
IP6Addr &
IP6Addr::operator&=(self_type const &that) {
_addr._store[MSW] &= that._addr._store[MSW];
_addr._store[LSW] &= that._addr._store[LSW];
return *this;
}
IP6Addr &
IP6Addr::operator|=(self_type const &that) {
_addr._store[MSW] |= that._addr._store[MSW];
_addr._store[LSW] |= that._addr._store[LSW];
return *this;
}
bool
IP6Addr::load(std::string_view const &str) {
TextView src{str};
int n = 0;
int empty_idx = -1;
auto quad = _addr._quad.data();
if (src && '[' == *src) {
++src;
if (src.empty() || src.back() != ']') {
return false;
}
src.remove_suffix(1);
}
if (src.size() < 2) {
return false;
}
// If the first character is ':' then it is an error for it not to be followed by another ':'.
// Special case the empty address and loopback, otherwise just make a note of the leading '::'.
if (src[0] == ':') {
if (src[1] == ':') {
if (src.size() == 2) {
this->clear();
return true;
} else if (src.size() == 3 && src[2] == '1') {
_addr._store[0] = 0;
_addr._store[1] = 1;
return true;
} else {
empty_idx = n;
src.remove_prefix(2);
}
} else {
return false;
}
}
// Sadly the empty quads can't be done in line because it's not possible to know the correct index
// of the next present quad until the entire address has been parsed.
while (n < static_cast<int>(N_QUADS) && !src.empty()) {
TextView token{src.take_prefix_at(':')};
if (token.empty()) {
if (empty_idx >= 0) { // two instances of "::", fail.
return false;
}
empty_idx = n;
} else {
TextView r;
auto x = svtoi(token, &r, 16);
if (r.size() == token.size()) {
quad[QUAD_IDX[n++]] = x;
} else {
break;
}
}
}
// Handle empty quads - invalid if empty and still had a full set of quads
if (empty_idx >= 0) {
if (n < static_cast<int>(N_QUADS)) {
int nil_idx = N_QUADS - (n - empty_idx);
int delta = N_QUADS - n;
for (int k = N_QUADS - 1; k >= empty_idx; --k) {
quad[QUAD_IDX[k]] = (k >= nil_idx ? quad[QUAD_IDX[k - delta]] : 0);
}
n = N_QUADS; // Mark success.
} else {
return false; // too many quads - full set plus empty.
}
}
if (n == N_QUADS && src.empty()) {
return true;
}
this->clear();
return false;
}
// These are Intel correct, at some point will need to be architecture dependent.
void
IP6Addr::reorder(in6_addr &dst, raw_type const &src) {
self_type::reorder(dst.s6_addr, src.data());
self_type::reorder(dst.s6_addr + WORD_SIZE, src.data() + WORD_SIZE);
}
void
IP6Addr::reorder(raw_type &dst, in6_addr const &src) {
self_type::reorder(dst.data(), src.s6_addr);
self_type::reorder(dst.data() + WORD_SIZE, src.s6_addr + WORD_SIZE);
}
IPAddr::IPAddr(IPEndpoint const &addr) {
this->assign(&addr.sa);
}
IPAddr &
IPAddr::operator=(IPEndpoint const &addr) {
return this->assign(&addr.sa);
}
bool
IPAddr::load(const std::string_view &text) {
TextView src{text};
src.ltrim_if(&isspace);
if (TextView::npos != src.prefix(5).find_first_of('.')) {
_family = AF_INET;
} else if (TextView::npos != src.prefix(6).find_first_of(':')) {
_family = AF_INET6;
} else {
_family = AF_UNSPEC;
}
// Do the real parse now
switch (_family) {
case AF_INET:
if (!_addr._ip4.load(src)) {
_family = AF_UNSPEC;
}
break;
case AF_INET6:
if (!_addr._ip6.load(src)) {
_family = AF_UNSPEC;
}
break;
}
return this->is_valid();
}
IPAddr &
IPAddr::assign(sockaddr const *addr) {
if (addr) {
switch (addr->sa_family) {
case AF_INET:
return this->assign(reinterpret_cast<sockaddr_in const *>(addr));
case AF_INET6:
return this->assign(reinterpret_cast<sockaddr_in6 const *>(addr));
default:
break;
}
}
_family = AF_UNSPEC;
return *this;
}
bool
IPAddr::operator<(self_type const &that) const {
if (AF_INET == _family) {
switch (that._family) {
case AF_INET:
return _addr._ip4 < that._addr._ip4;
case AF_INET6:
return true;
default:
return false;
}
} else if (AF_INET6 == _family) {
switch (that._family) {
case AF_INET:
return true;
case AF_INET6:
return _addr._ip6 < that._addr._ip6;
default:
return false;
}
}
return that.is_valid();
}
int
IPAddr::cmp(self_type const &that) const {
if (AF_INET == _family) {
switch (that._family) {
case AF_INET:
return _addr._ip4.cmp(that._addr._ip4);
case AF_INET6:
return -1;
default:
return 1;
}
} else if (AF_INET6 == _family) {
switch (that._family) {
case AF_INET:
return 1;
case AF_INET6:
return _addr._ip6.cmp(that._addr._ip6);
default:
return 1;
}
}
return that.is_valid() ? -1 : 0;
}
IPAddr::self_type &
IPAddr::operator&=(IPMask const &mask) {
if (_family == AF_INET) {
_addr._ip4 &= mask;
} else if (_family == AF_INET6) {
_addr._ip6 &= mask;
}
return *this;
}
IPAddr::self_type &
IPAddr::operator|=(IPMask const &mask) {
if (_family == AF_INET) {
_addr._ip4 |= mask;
} else if (_family == AF_INET6) {
_addr._ip6 |= mask;
}
return *this;
}
bool
IPAddr::is_multicast() const {
return (AF_INET == _family && _addr._ip4.is_multicast()) || (AF_INET6 == _family && _addr._ip6.is_multicast());
}
IPEndpoint::IPEndpoint(string_view const &text) {
this->invalidate();
this->parse(text);
}
bool
IPMask::load(string_view const &text) {
TextView parsed;
_cidr = swoc::svtou(text, &parsed);
if (parsed.size() != text.size()) {
_cidr = 0;
return false;
}
return true;
}
IPMask
IPMask::mask_for(IPAddr const &addr) {
if (addr.is_ip4()) {
return self_type::mask_for(addr.ip4());
} else if (addr.is_ip6()) {
return self_type::mask_for(addr.ip6());
}
return {};
}
auto
IPMask::mask_for_quad(IP6Addr::quad_type q) -> raw_type {
raw_type cidr = IP6Addr::QUAD_WIDTH;
if (q != 0) {
auto mask = IP6Addr::QUAD_MASK;
do {
mask <<= 1;
--cidr;
} while ((q | ~mask) == q);
++cidr; // loop goes exactly 1 too far.
}
return cidr;
}
IPMask
IPMask::mask_for(IP4Addr const &addr) {
auto n = addr.host_order();
raw_type cidr = 0;
if (auto q = (n & IP6Addr::QUAD_MASK); q != 0) {
cidr = IP6Addr::QUAD_WIDTH + self_type::mask_for_quad(q);
} else if (q = ((n >> IP6Addr::QUAD_WIDTH) & IP6Addr::QUAD_MASK); q != 0) {
cidr = self_type::mask_for_quad(q);
}
return self_type(cidr);
}
IPMask
IPMask::mask_for(IP6Addr const &addr) {
auto cidr = IP6Addr::WIDTH;
for (unsigned idx = IP6Addr::N_QUADS; idx > 0;) {
auto q = addr._addr._quad[IP6Addr::QUAD_IDX[--idx]];
cidr -= IP6Addr::QUAD_WIDTH;
if (q != 0) {
cidr += self_type::mask_for_quad(q);
break;
}
}
return self_type(cidr);
}
IP6Addr
IPMask::as_ip6() const {
static constexpr auto MASK = ~IP6Addr::word_type{0};
if (_cidr <= IP6Addr::WORD_WIDTH) {
return {MASK << (IP6Addr::WORD_WIDTH - _cidr), 0};
} else if (_cidr < 2 * IP6Addr::WORD_WIDTH) {
return {MASK, MASK << (2 * IP6Addr::WORD_WIDTH - _cidr)};
}
return {MASK, MASK};
}
// --- SRV ---
IP4Srv::IP4Srv(swoc::TextView text) {
this->load(text);
}
bool
IP4Srv::load(swoc::TextView text) {
TextView addr_text, port_text, rest;
if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
if (rest.empty()) {
uintmax_t n = 0;
if (!port_text.empty()) {
n = swoc::svtou(port_text, &rest);
if (rest.size() != port_text.size() || n > std::numeric_limits<in_port_t>::max()) {
return false; // bad port.
}
}
IP4Addr addr;
if (addr.load(addr_text)) {
this->assign(addr, n);
return true;
}
}
}
return false;
}
IP6Srv::IP6Srv(swoc::TextView text) {
this->load(text);
}
bool
IP6Srv::load(swoc::TextView text) {
TextView addr_text, port_text, rest;
if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
if (rest.empty()) {
uintmax_t n = 0;
if (!port_text.empty()) {
n = swoc::svtou(port_text, &rest);
if (rest.size() != port_text.size() || n > std::numeric_limits<in_port_t>::max()) {
return false; // bad port.
}
}
IP6Addr addr;
if (addr.load(addr_text)) {
this->assign(addr, n);
return true;
}
}
}
return false;
}
IPSrv::IPSrv(swoc::TextView text) {
this->load(text);
}
bool
IPSrv::load(swoc::TextView text) {
TextView addr_text, port_text, rest;
if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
if (rest.empty()) {
uintmax_t n = 0;
if (!port_text.empty()) {
n = swoc::svtou(port_text, &rest);
if (rest.size() != port_text.size() || n > std::numeric_limits<in_port_t>::max()) {
return false; // bad port.
}
}
IPAddr addr;
if (addr.load(addr_text)) {
this->assign(addr, n);
return true;
}
}
}
return false;
}
IPSrv::IPSrv(IPAddr addr, in_port_t port) {
_family = addr.family();
if (addr.is_ip4()) {
_srv._ip4.assign(addr.ip4(), port);
} else if (addr.is_ip6()) {
_srv._ip6.assign(addr.ip6(), port);
} else {
_family = AF_UNSPEC;
}
}
IPSrv::IPSrv(IPEndpoint const& ep) {
if (ep.is_ip4()) {
_family = _srv._ip4.family();
_srv._ip4.assign(&ep.sa4);
} else if (ep.is_ip6()) {
_family = _srv._ip6.family();
_srv._ip6.assign(&ep.sa6);
}
}
auto IPSrv::assign(const sockaddr *sa) -> self_type & {
if (AF_INET == sa->sa_family) {
_family = AF_INET;
_srv._ip4.assign(reinterpret_cast<sockaddr_in const *>(sa));
} else if (AF_INET6 == sa->sa_family) {
_family = AF_INET6;
_srv._ip6.assign(reinterpret_cast<sockaddr_in6 const *>(sa));
}
return *this;
}
// ++ IPNet ++
bool
IP4Net::load(TextView text) {
auto idx = text.find('/');
if (idx != text.npos) {
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
IP4Addr addr;
if (addr.load(text.substr(0, idx))) { // load the address
IPMask mask;
text.remove_prefix(idx + 1); // drop address and separator.
if (mask.load(text)) {
this->assign(addr, mask);
return true;
}
}
}
}
this->clear();
return false;
}
bool
IP6Net::load(TextView text) {
auto idx = text.find('/');
if (idx != text.npos) {
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
IP6Addr addr;
if (addr.load(text.substr(0, idx))) { // load the address
IPMask mask;
text.remove_prefix(idx + 1); // drop address and separator.
if (mask.load(text)) {
this->assign(addr, mask);
return true;
}
}
}
}
this->clear();
return false;
}
bool
IPNet::load(TextView text) {
if ( auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) {
IPMask mask;
if (mask.load(mask_text)) {
if (IP6Addr a6; a6.load(text)) { // load the address
this->assign(a6, mask);
return true;
} else if (IP4Addr a4; a4.load(text)) {
this->assign(a4, mask);
return true;
}
}
}
this->clear();
return false;
}
// +++ IP4Range +++
IP4Range::IP4Range(swoc::IP4Addr const &addr, swoc::IPMask const &mask) {
this->assign(addr, mask);
}
IP4Range &
IP4Range::assign(swoc::IP4Addr const &addr, swoc::IPMask const &mask) {
// Special case the cidr sizes for 0, maximum
if (0 == mask.width()) {
_min = metric_type::MIN;
_max = metric_type::MAX;
} else {
_min = _max = addr;
if (mask.width() < 32) {
in_addr_t bits = INADDR_BROADCAST << (32 - mask.width());
_min._addr &= bits;
_max._addr |= ~bits;
}
}
return *this;
}
bool
IP4Range::load(string_view text) {
static const string_view SEPARATORS("/-");
auto idx = text.find_first_of(SEPARATORS);
if (idx != text.npos) {
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
if ('/' == text[idx]) {
metric_type addr;
if (addr.load(text.substr(0, idx))) { // load the address
IPMask mask;
text.remove_prefix(idx + 1); // drop address and separator.
if (mask.load(text)) {
this->assign(addr, mask);
return true;
}
}
} else if (_min.load(text.substr(0, idx)) && _max.load(text.substr(idx + 1))) {
return true;
}
}
} else if (_min.load(text)) {
_max = _min;
return true;
}
this->clear();
return false;
}
IPMask
IP4Range::network_mask() const {
NetSource nets{*this};
if (!nets.empty() && (*nets).as_range() == *this) {
return nets->mask();
}
return {}; // default constructed (invalid) mask.
}
IP4Range::NetSource::NetSource(IP4Range::NetSource::range_type const &range) : _range(range) {
if (!_range.empty()) {
this->search_wider();
}
}
auto
IP4Range::NetSource::operator++() -> self_type & {
auto upper(IP4Addr{_range._min._addr | ~_mask._addr});
if (upper >= _range.max()) {
_range.clear();
} else {
_range.assign_min(++upper);
// @a _range is not empty, because there's at least one address still not covered.
if (this->is_valid(_mask)) {
this->search_wider();
} else {
this->search_narrower();
}
}
return *this;
}
auto IP4Range::NetSource::operator++(int) -> self_type {
auto zret { *this };
++*this;
return zret;
}
void
IP4Range::NetSource::search_wider() {
while (_cidr > 0) {
auto m = _mask;
m <<= 1;
if (this->is_valid(m)) {
_mask = m;
--_cidr;
} else {
break;
}
}
}
void
IP4Range::NetSource::search_narrower() {
while (!this->is_valid(_mask)) {
_mask._addr >>= 1;
_mask._addr |= 1U << (IP4Addr::WIDTH - 1); // put top bit back.
++_cidr;
}
}
// +++ IP6Range +++
IP6Range &
IP6Range::assign(IP6Addr const &addr, IPMask const &mask) {
static constexpr auto FULL_MASK{std::numeric_limits<uint64_t>::max()};
auto cidr = mask.width();
if (cidr == 0) {
_min = metric_type::MIN;
_max = metric_type::MAX;
} else if (cidr < 64) { // only upper bytes affected, lower bytes are forced.
auto bits = FULL_MASK << (64 - cidr);
_min._addr._store[0] = addr._addr._store[0] & bits;
_min._addr._store[1] = 0;
_max._addr._store[0] = addr._addr._store[0] | ~bits;
_max._addr._store[1] = FULL_MASK;
} else if (cidr == 64) {
_min._addr._store[0] = _max._addr._store[0] = addr._addr._store[0];
_min._addr._store[1] = 0;
_max._addr._store[1] = FULL_MASK;
} else if (cidr <= 128) { // _min bytes changed, _max bytes unaffected.
_min = _max = addr;
if (cidr < 128) {
auto bits = FULL_MASK << (128 - cidr);
_min._addr._store[1] &= bits;
_max._addr._store[1] |= ~bits;
}
}
return *this;
}
bool
IP6Range::load(std::string_view text) {
static const string_view SEPARATORS("/-");
auto idx = text.find_first_of(SEPARATORS);
if (idx != text.npos) {
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
if ('/' == text[idx]) {
metric_type addr;
if (addr.load(text.substr(0, idx))) { // load the address
IPMask mask;
text.remove_prefix(idx + 1); // drop address and separator.
if (mask.load(text)) {
this->assign(addr, mask);
return true;
}
}
} else if (_min.load(text.substr(0, idx)) && _max.load(text.substr(idx + 1))) {
return true;
}
}
} else if (_min.load(text)) {
_max = _min;
return true;
}
this->clear();
return false;
}
IPRange::IPRange(IPAddr const &min, IPAddr const &max) {
if (min.is_ip4() && max.is_ip4()) {
_range._ip4.assign(min.ip4(), max.ip4());
_family = AF_INET;
} else if (min.is_ip6() && max.is_ip6()) {
_range._ip6.assign(min.ip6(), max.ip6());
_family = AF_INET6;
}
}
bool
IPRange::load(std::string_view const &text) {
if ( auto idx = text.find_first_of(':') ; idx != text.npos ) {
if (_range._ip6.load(text)) {
_family = AF_INET6;
return true;
}
} else if (_range._ip4.load(text)) {
_family = AF_INET;
return true;
}
return false;
}
IPAddr
IPRange::min() const {
switch (_family) {
case AF_INET:
return _range._ip4.min();
case AF_INET6:
return _range._ip6.min();
default:
break;
}
return {};
}
IPAddr
IPRange::max() const {
switch (_family) {
case AF_INET:
return _range._ip4.max();
case AF_INET6:
return _range._ip6.max();
default:
break;
}
return {};
}
bool
IPRange::empty() const {
switch (_family) {
case AF_INET:
return _range._ip4.empty();
case AF_INET6:
return _range._ip6.empty();
default:
break;
}
return true;
}
IPMask
IPRange::network_mask() const {
switch (_family) {
case AF_INET:
return _range._ip4.network_mask();
case AF_INET6:
return _range._ip6.network_mask();
default:
break;
}
return {};
}
bool
IPRange::operator==(self_type const &that) const {
if (_family != that._family) {
return false;
}
if (this->is_ip4()) {
return _range._ip4 == that._range._ip4;
}
if (this->is_ip6()) {
return _range._ip6 == that._range._ip6;
}
return true;
}
IPMask
IP6Range::network_mask() const {
NetSource nets{*this};
if (!nets.empty() && (*nets).as_range() == *this) {
return nets->mask();
}
return {}; // default constructed (invalid) mask.
}
IP6Range::NetSource::NetSource(IP6Range::NetSource::range_type const &range) : _range(range) {
if (!_range.empty()) {
this->search_wider();
}
}
auto
IP6Range::NetSource::operator++() -> self_type & {
auto upper = _range.min() | _mask;
if (upper >= _range.max()) {
_range.clear();
} else {
_range.assign_min(++upper);
// @a _range is not empty, because there's at least one address still not covered.
if (this->is_valid(_mask)) {
this->search_wider();
} else {
this->search_narrower();
}
}
return *this;
}
void
IP6Range::NetSource::search_wider() {
while (_mask.width() > 0) {
auto m = _mask;
m <<= 1;
if (this->is_valid(m)) {
_mask = m;
} else {
break;
}
}
}
void
IP6Range::NetSource::search_narrower() {
while (!this->is_valid(_mask)) {
_mask >>= 1;
}
}
}} // namespace swoc::SWOC_VERSION_NS