blob: 862237baac55864ebba0225213301bc9b8c16cd0 [file] [log] [blame]
#include <stdplus/net/addr/ip.hpp>
#include <stdplus/numeric/endian.hpp>
#include <limits>
#include <type_traits>
namespace stdplus
{
namespace detail
{
// AddressSan doesn't understand our masking of shift UB
__attribute__((no_sanitize("undefined"))) constexpr uint32_t
addr32Mask(std::ptrdiff_t pfx) noexcept
{
// Positive prefix check + mask to handle UB when the left shift becomes
// more than 31 bits
return hton(static_cast<uint32_t>(-int32_t{pfx > 0}) & ~uint32_t{0}
<< (32 - pfx));
}
constexpr In4Addr addrToSubnet(In4Addr a, std::size_t pfx) noexcept
{
return In4Addr{in_addr{a.s4_addr32 & addr32Mask(pfx)}};
}
constexpr In6Addr addrToSubnet(In6Addr a, std::size_t pfx, std::size_t i = 0,
std::size_t s = 0, In6Addr ret = {}) noexcept
{
if (s + 32 < pfx)
{
ret.s6_addr32[i] = a.s6_addr32[i];
return addrToSubnet(a, pfx, i + 1, s + 32, ret);
}
ret.s6_addr32[i] = a.s6_addr32[i] & addr32Mask(pfx - s);
return ret;
}
constexpr InAnyAddr addrToSubnet(InAnyAddr a, std::size_t pfx) noexcept
{
return std::visit([&](auto av) { return InAnyAddr{addrToSubnet(av, pfx)}; },
a);
}
constexpr bool subnetContains(auto, auto, std::size_t) noexcept
{
return false;
}
template <typename T>
constexpr bool subnetContains(T l, T r, std::size_t pfx) noexcept
{
return addrToSubnet(l, pfx) == addrToSubnet(r, pfx);
}
constexpr bool subnetContains(InAnyAddr l, auto r, std::size_t pfx) noexcept
{
return std::visit([&](auto v) { return detail::subnetContains(v, r, pfx); },
l);
}
constexpr std::size_t addrBits(auto a) noexcept
{
return sizeof(a) << 3;
}
void invalidSubnetPfx(std::size_t pfx);
template <typename Addr, typename Pfx>
class Subnet46
{
private:
static constexpr inline std::size_t maxPfx = sizeof(Addr) * 8;
static_assert(std::is_unsigned_v<Pfx> && std::is_integral_v<Pfx>);
static_assert(std::numeric_limits<Pfx>::max() >= maxPfx);
Addr addr;
Pfx pfx;
public:
constexpr Subnet46(Addr addr, Pfx pfx) : addr(addr), pfx(pfx)
{
if (addrBits(addr) < pfx)
{
invalidSubnetPfx(pfx);
}
}
constexpr auto getAddr() const noexcept
{
return addr;
}
constexpr auto getPfx() const noexcept
{
return pfx;
}
constexpr bool operator==(Subnet46 rhs) const noexcept
{
return addr == rhs.addr && pfx == rhs.pfx;
}
constexpr Addr network() const noexcept
{
return addrToSubnet(addr, pfx);
}
constexpr bool contains(Addr addr) const noexcept
{
return addrToSubnet(this->addr, pfx) == addrToSubnet(addr, pfx);
}
};
} // namespace detail
using Subnet4 = detail::Subnet46<In4Addr, uint8_t>;
using Subnet6 = detail::Subnet46<In6Addr, uint8_t>;
class SubnetAny
{
private:
InAnyAddr addr;
uint8_t pfx;
public:
constexpr SubnetAny(auto addr, uint8_t pfx) : addr(addr), pfx(pfx)
{
if (detail::addrBits(addr) < pfx)
{
detail::invalidSubnetPfx(pfx);
}
}
constexpr SubnetAny(InAnyAddr addr, uint8_t pfx) : addr(addr), pfx(pfx)
{
if (std::visit([](auto v) { return detail::addrBits(v); }, addr) < pfx)
{
detail::invalidSubnetPfx(pfx);
}
}
template <typename T, typename S>
constexpr SubnetAny(detail::Subnet46<T, S> o) noexcept :
addr(o.getAddr()), pfx(o.getPfx())
{}
constexpr auto getAddr() const noexcept
{
return addr;
}
constexpr auto getPfx() const noexcept
{
return pfx;
}
template <typename T, typename S>
constexpr bool operator==(detail::Subnet46<T, S> rhs) const noexcept
{
return addr == rhs.getAddr() && pfx == rhs.getPfx();
}
constexpr bool operator==(SubnetAny rhs) const noexcept
{
return addr == rhs.addr && pfx == rhs.pfx;
}
constexpr InAnyAddr network() const noexcept
{
return detail::addrToSubnet(addr, pfx);
}
constexpr bool contains(In4Addr addr) const noexcept
{
return detail::subnetContains(this->addr, addr, pfx);
}
constexpr bool contains(in_addr addr) const noexcept
{
return contains(In4Addr{addr});
}
constexpr bool contains(In6Addr addr) const noexcept
{
return detail::subnetContains(this->addr, addr, pfx);
}
constexpr bool contains(in6_addr addr) const noexcept
{
return contains(In6Addr{addr});
}
constexpr bool contains(InAnyAddr addr) const noexcept
{
return std::visit([&](auto v) { return contains(v); }, addr);
}
};
} // namespace stdplus