0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00

IP Refactor

Signed-off-by: Leonard Ossa <leonard.ossa@openvpn.com>
This commit is contained in:
Leonard Ossa 2024-07-10 19:20:03 +02:00 committed by Jenkins-dev
parent 0faf0f89de
commit b550d6bf7f

View File

@ -22,12 +22,13 @@
#ifndef OPENVPN_ADDR_IP_H
#define OPENVPN_ADDR_IP_H
#include <algorithm>
#include <functional>
#include <string>
#include <cstring> // for std::memset
#include <openvpn/io/io.hpp>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/ostream.hpp>
#include <openvpn/common/hash.hpp>
@ -141,7 +142,7 @@ class Addr
const TITLE &title,
const Version required_version)
{
Addr a = from_string(ipstr, title, required_version);
const Addr a = from_string(ipstr, title, required_version);
return a.to_string();
}
@ -241,15 +242,9 @@ class Addr
static bool is_valid(const std::string &ipstr)
{
// fast path -- rule out validity if invalid chars
for (size_t i = 0; i < ipstr.length(); ++i)
{
const char c = ipstr[i];
if (!((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F')
|| (c == '.' || c == ':' || c == '%')))
return false;
}
if (std::any_of(ipstr.begin(), ipstr.end(), [](auto c)
{ return !(std::isxdigit(c) || c == '.' || c == ':' || c == '%'); }))
return false;
// slow path
{
@ -261,12 +256,15 @@ class Addr
static Addr from_hex(Version v, const std::string &s)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_hex(s));
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_hex(s));
else
default:
OPENVPN_IP_THROW("from_hex: address unspecified");
}
}
static Addr from_ipv4(IPv4::Addr addr)
@ -289,36 +287,40 @@ class Addr
{
if (ver == V4)
return u.v4;
else
OPENVPN_IP_THROW("to_ipv4: address is not IPv4");
OPENVPN_IP_THROW("to_ipv4: address is not IPv4");
}
IPv4::Addr to_ipv4_zero() const
{
if (ver == V4)
switch (ver)
{
case V4:
return u.v4;
else if (ver == UNSPEC)
case UNSPEC:
return IPv4::Addr::from_zero();
else
default:
OPENVPN_IP_THROW("to_ipv4_zero: address is not IPv4");
}
}
const IPv6::Addr &to_ipv6() const
{
if (ver == V6)
return u.v6;
else
OPENVPN_IP_THROW("to_ipv6: address is not IPv6");
OPENVPN_IP_THROW("to_ipv6: address is not IPv6");
}
IPv6::Addr to_ipv6_zero() const
{
if (ver == V6)
switch (ver)
{
case V6:
return u.v6;
else if (ver == UNSPEC)
case UNSPEC:
return IPv6::Addr::from_zero();
else
default:
OPENVPN_IP_THROW("to_ipv6_zero: address is not IPv6");
}
}
const IPv4::Addr &to_ipv4_nocheck() const
@ -335,10 +337,9 @@ class Addr
{
if (sa->sa_family == AF_INET)
return from_ipv4(IPv4::Addr::from_sockaddr(reinterpret_cast<const struct sockaddr_in *>(sa)));
else if (sa->sa_family == AF_INET6)
if (sa->sa_family == AF_INET6)
return from_ipv6(IPv6::Addr::from_sockaddr(reinterpret_cast<const struct sockaddr_in6 *>(sa)));
else
return Addr();
return Addr();
}
static bool sockaddr_defined(const struct sockaddr *sa)
@ -348,44 +349,56 @@ class Addr
static Addr from_ulong(Version v, unsigned long ul)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_ulong(ul));
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_ulong(ul));
else
default:
OPENVPN_IP_THROW("from_ulong: address unspecified");
}
}
// return *this as a ulong, will raise exception on overflow
unsigned long to_ulong() const
{
if (ver == V4)
switch (ver)
{
case V4:
return u.v4.to_ulong();
else if (ver == V6)
case V6:
return u.v6.to_ulong();
else
default:
OPENVPN_IP_THROW("to_ulong: address unspecified");
}
}
static Addr from_long(Version v, long ul)
static Addr from_long(Version v, const long ul)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_long(ul));
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_long(ul));
else
default:
OPENVPN_IP_THROW("from_long: address unspecified");
}
}
// return *this as a long, will raise exception on overflow
long to_long() const
{
if (ver == V4)
switch (ver)
{
case V4:
return u.v4.to_long();
else if (ver == V6)
case V6:
return u.v6.to_long();
else
default:
OPENVPN_IP_THROW("to_long: address unspecified");
}
}
// return Addr from 16 byte binary string
@ -408,102 +421,119 @@ class Addr
// convert Addr to 16 byte binary string
void to_byte_string(unsigned char *bytestr) const
{
if (ver == V4)
switch (ver)
{
case V4:
IPv6::Addr::v4_to_byte_string(bytestr, u.v4.to_uint32_net());
else if (ver == V6)
break;
case V6:
u.v6.to_byte_string(bytestr);
else
break;
default:
std::memset(bytestr, 0, 16);
break;
}
}
// convert Addr to variable length byte string
void to_byte_string_variable(unsigned char *bytestr) const
{
if (ver == V4)
switch (ver)
{
case V4:
u.v4.to_byte_string(bytestr);
else if (ver == V6)
break;
case V6:
u.v6.to_byte_string(bytestr);
else
break;
default:
OPENVPN_IP_THROW("to_byte_string_variable: address unspecified");
}
}
std::uint32_t to_uint32_net() const // return value in net byte order
{
if (ver == V4)
return u.v4.to_uint32_net();
else
return 0;
return (ver == V4) ? u.v4.to_uint32_net() : 0;
}
// construct an address where all bits are zero
static Addr from_zero(const Version v)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_zero());
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_zero());
else
default:
OPENVPN_IP_THROW("from_zero: IP version unspecified");
}
}
// construct the "one" address
static Addr from_one(const Version v)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_one());
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_one());
else
default:
OPENVPN_IP_THROW("from_one: IP version unspecified");
}
}
// construct an address where all bits are one
static Addr from_zero_complement(const Version v)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::from_zero_complement());
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::from_zero_complement());
else
default:
OPENVPN_IP_THROW("from_zero_complement: IP version unspecified");
}
}
// validate the prefix length for the IP version
static bool validate_prefix_len(Version v, const unsigned int prefix_len)
{
if (v == V4)
{
if (prefix_len <= V4_SIZE)
return true;
}
else if (v == V6)
{
if (prefix_len <= V6_SIZE)
return true;
}
if (v == V4 && prefix_len <= V4_SIZE)
return true;
if (v == V6 && prefix_len <= V6_SIZE)
return true;
return false;
}
// build a netmask using given prefix_len
static Addr netmask_from_prefix_len(Version v, const unsigned int prefix_len)
{
if (v == V4)
switch (v)
{
case V4:
return from_ipv4(IPv4::Addr::netmask_from_prefix_len(prefix_len));
else if (v == V6)
case V6:
return from_ipv6(IPv6::Addr::netmask_from_prefix_len(prefix_len));
else
default:
OPENVPN_IP_THROW("netmask_from_prefix_len: address unspecified");
}
}
// build a netmask using *this as extent
Addr netmask_from_extent() const
{
if (ver == V4)
switch (ver)
{
case V4:
return from_ipv4(u.v4.netmask_from_extent());
else if (ver == V6)
case V6:
return from_ipv6(u.v6.netmask_from_extent());
else
default:
OPENVPN_IP_THROW("netmask_from_extent: address unspecified");
}
}
std::string to_string() const
@ -514,8 +544,7 @@ class Addr
std::string ret = a.to_string();
return ret;
}
else
return "UNSPEC";
return "UNSPEC";
}
std::string to_string_bracket_ipv6() const
@ -531,42 +560,46 @@ class Addr
std::string to_hex() const
{
if (ver == V4)
switch (ver)
{
case V4:
return u.v4.to_hex();
else if (ver == V6)
case V6:
return u.v6.to_hex();
else
default:
OPENVPN_IP_THROW("to_hex: address unspecified");
}
}
std::string arpa() const
{
if (ver == V4)
switch (ver)
{
case V4:
return u.v4.arpa();
else if (ver == V6)
case V6:
return u.v6.arpa();
else
default:
OPENVPN_IP_THROW("arpa: address unspecified");
}
}
static Addr from_asio(const openvpn_io::ip::address &addr)
{
Addr ret;
if (addr.is_v4())
{
Addr a;
a.ver = V4;
a.u.v4 = IPv4::Addr::from_asio(addr.to_v4());
return a;
ret.ver = V4;
ret.u.v4 = IPv4::Addr::from_asio(addr.to_v4());
}
else if (addr.is_v6())
{
Addr a;
a.ver = V6;
a.u.v6 = IPv6::Addr::from_asio(addr.to_v6());
return a;
ret.ver = V6;
ret.u.v6 = IPv6::Addr::from_asio(addr.to_v6());
}
else
else if (ret.ver == UNSPEC)
OPENVPN_IP_THROW("from_asio: address unspecified");
return ret;
}
openvpn_io::ip::address to_asio() const
@ -610,42 +643,6 @@ class Addr
return operator+(-delta);
}
#define OPENVPN_IP_OPERATOR_BINOP(OP) \
Addr operator OP(const Addr &other) const \
{ \
if (ver != other.ver) \
OPENVPN_IP_THROW("binop: version inconsistency"); \
switch (ver) \
{ \
case V4: \
{ \
Addr ret; \
ret.ver = V4; \
ret.u.v4 = u.v4 OP other.u.v4; \
return ret; \
} \
case V6: \
{ \
Addr ret; \
ret.ver = V6; \
ret.u.v6 = u.v6 OP other.u.v6; \
return ret; \
} \
default: \
OPENVPN_IP_THROW("binop: address unspecified"); \
} \
}
OPENVPN_IP_OPERATOR_BINOP(+)
OPENVPN_IP_OPERATOR_BINOP(-)
OPENVPN_IP_OPERATOR_BINOP(*)
OPENVPN_IP_OPERATOR_BINOP(/)
OPENVPN_IP_OPERATOR_BINOP(%)
OPENVPN_IP_OPERATOR_BINOP(&)
OPENVPN_IP_OPERATOR_BINOP(|)
#undef OPENVPN_IP_OPERATOR_BINOP
Addr operator<<(const unsigned int shift) const
{
switch (ver)
@ -761,33 +758,61 @@ class Addr
return !operator==(other);
}
#define OPENVPN_IP_OPERATOR_REL(OP) \
bool operator OP(const Addr &other) const \
{ \
if (ver == other.ver) \
{ \
switch (ver) \
{ \
case V4: \
return u.v4 OP other.u.v4; \
case V6: \
return u.v6 OP other.u.v6; \
default: \
return false; \
} \
} \
else if (ver OP other.ver) \
return true; \
else \
return false; \
bool operator<(const Addr &other) const
{
return compare(other, std::less<>{});
}
OPENVPN_IP_OPERATOR_REL(<)
OPENVPN_IP_OPERATOR_REL(>)
OPENVPN_IP_OPERATOR_REL(<=)
OPENVPN_IP_OPERATOR_REL(>=)
bool operator>(const Addr &other) const
{
return compare(other, std::greater<>{});
}
#undef OPENVPN_IP_OPERATOR_REL
bool operator<=(const Addr &other) const
{
return compare(other, std::less_equal<>{});
}
bool operator>=(const Addr &other) const
{
return compare(other, std::greater_equal<>{});
}
// Operator overloads for binary operations
Addr operator+(const Addr &other) const
{
return binary_op(other, std::plus<>{});
}
Addr operator-(const Addr &other) const
{
return binary_op(other, std::minus<>{});
}
Addr operator*(const Addr &other) const
{
return binary_op(other, std::multiplies<>{});
}
Addr operator/(const Addr &other) const
{
return binary_op(other, std::divides<>{});
}
Addr operator%(const Addr &other) const
{
return binary_op(other, std::modulus<>{});
}
Addr operator&(const Addr &other) const
{
return binary_op(other, std::bit_and<>{});
}
Addr operator|(const Addr &other) const
{
return binary_op(other, std::bit_or<>{});
}
bool unspecified() const
{
@ -944,7 +969,7 @@ class Addr
}
// throw exception if address is not a valid netmask
void validate_netmask()
void validate_netmask() const
{
prefix_len();
}
@ -1013,12 +1038,15 @@ class Addr
// address size in bits of particular IP version
static unsigned int version_size(Version v)
{
if (v == V4)
switch (v)
{
case V4:
return IPv4::Addr::SIZE;
else if (v == V6)
case V6:
return IPv6::Addr::SIZE;
else
default:
return 0;
}
}
template <typename HASH>
@ -1128,6 +1156,50 @@ class Addr
} u{};
Version ver;
template <typename Comparator>
bool compare(const Addr &other, Comparator comp) const
{
if (ver == other.ver)
{
switch (ver)
{
case V4:
return comp(u.v4, other.u.v4);
case V6:
return comp(u.v6, other.u.v6);
default:
return false;
}
}
return comp(ver, other.ver);
}
template <typename BinaryOp>
Addr binary_op(const Addr &other, BinaryOp op) const
{
if (ver != other.ver)
{
OPENVPN_IP_THROW("binop: version inconsistency");
}
Addr ret;
ret.ver = ver;
switch (ver)
{
case V4:
ret.u.v4 = op(u.v4, other.u.v4);
break;
case V6:
ret.u.v6 = op(u.v6, other.u.v6);
break;
default:
OPENVPN_IP_THROW("binop: address unspecified");
}
return ret;
}
};
OPENVPN_OSTREAM(Addr, to_string)