From 9cafba7c7ed1ac8e3237cb19d5b7a626ad830de3 Mon Sep 17 00:00:00 2001 From: Leonard Ossa Date: Fri, 16 Aug 2024 13:13:12 +0200 Subject: [PATCH] Refactor IPv4 IPv6 to modern C++ Signed-off-by: Leonard Ossa --- openvpn/addr/ip.hpp | 7 +- openvpn/addr/ipv4.hpp | 103 ++++++++++++---------------- openvpn/addr/ipv6.hpp | 152 +++++++++++++++++++++--------------------- 3 files changed, 121 insertions(+), 141 deletions(-) diff --git a/openvpn/addr/ip.hpp b/openvpn/addr/ip.hpp index 07c91f92a..364de4b06 100644 --- a/openvpn/addr/ip.hpp +++ b/openvpn/addr/ip.hpp @@ -522,15 +522,14 @@ class Addr } } - // build a netmask using *this as extent - Addr netmask_from_extent() const + Addr netmask_from_this_as_extent() const { switch (ver) { case V4: - return from_ipv4(u.v4.netmask_from_extent()); + return from_ipv4(u.v4.netmask_from_this_as_extent()); case V6: - return from_ipv6(u.v6.netmask_from_extent()); + return from_ipv6(u.v6.netmask_from_this_as_extent()); default: OPENVPN_IP_THROW("netmask_from_extent: address unspecified"); } diff --git a/openvpn/addr/ipv4.hpp b/openvpn/addr/ipv4.hpp index a6077012f..8fe1ac4b8 100644 --- a/openvpn/addr/ipv4.hpp +++ b/openvpn/addr/ipv4.hpp @@ -28,24 +28,20 @@ #include -#include #include #include #include -#include #include #include -#include #include -namespace openvpn { -namespace IP { +namespace openvpn::IP { class Addr; } // Fundamental classes for representing an IPv4 IP address. -namespace IPv4 { +namespace openvpn::IPv4 { OPENVPN_EXCEPTION(ipv4_exception); @@ -67,7 +63,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return 4; } - bool defined() const + static constexpr bool defined() { return true; } @@ -77,31 +73,30 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return addr; } - static Addr from_in_addr(const struct in_addr *in4) + static Addr from_in_addr(const in_addr *in4) { Addr ret; ret.u.addr = ntohl(in4->s_addr); return ret; } - struct in_addr to_in_addr() const + in_addr to_in_addr() const { - struct in_addr ret; + in_addr ret; ret.s_addr = htonl(u.addr); return ret; } - static Addr from_sockaddr(const struct sockaddr_in *sa) + static Addr from_sockaddr(const sockaddr_in *sa) { Addr ret; ret.u.addr = ntohl(sa->sin_addr.s_addr); return ret; } - struct sockaddr_in to_sockaddr(const unsigned short port = 0) const + sockaddr_in to_sockaddr(const unsigned short port = 0) const { - struct sockaddr_in ret; - std::memset(&ret, 0, sizeof(ret)); + sockaddr_in ret = {}; ret.sin_family = AF_INET; ret.sin_port = htons(port); ret.sin_addr.s_addr = htonl(u.addr); @@ -129,7 +124,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial void to_byte_string(unsigned char *bytestr) const { - *(base_type *)bytestr = ntohl(u.addr); + *reinterpret_cast(bytestr) = ntohl(u.addr); } std::uint32_t to_uint32_net() const // return value in net byte order @@ -140,27 +135,25 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial static Addr from_ulong(unsigned long ul) { Addr ret; - ret.u.addr = (base_type)ul; + ret.u.addr = static_cast(ul); return ret; } - // return *this as a unsigned long unsigned long to_ulong() const { - return (unsigned long)u.addr; + return u.addr; } static Addr from_long(long ul) { Addr ret; - ret.u.addr = (base_type)(signed_base_type)ul; + ret.u.addr = static_cast(ul); return ret; } - // return *this as a long long to_long() const { - return (long)(signed_base_type)u.addr; + return u.addr; } static Addr from_bytes(const unsigned char *bytes) // host byte order @@ -199,7 +192,6 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - // build a netmask using given prefix_len static Addr netmask_from_prefix_len(const unsigned int prefix_len) { Addr ret; @@ -207,8 +199,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - // build a netmask using given extent - Addr netmask_from_extent() const + Addr netmask_from_this_as_extent() const { const int lb = find_last_set(u.addr - 1); return netmask_from_prefix_len(SIZE - lb); @@ -267,7 +258,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial if (len < 1 || len > 8) throw ipv4_exception("parse hex error"); size_t di = (len - 1) >> 1; - for (int i = (len & 1) ? -1 : 0; i < int(len); i += 2) + for (int i = (len & 1) ? -1 : 0; i < static_cast(len); i += 2) { const size_t idx = base + i; const int bh = (i >= 0) ? parse_hex_char(s[idx]) : 0; @@ -302,17 +293,17 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial std::string arpa() const { std::ostringstream os; - os << int(u.bytes[Endian::e4(0)]) << '.' - << int(u.bytes[Endian::e4(1)]) << '.' - << int(u.bytes[Endian::e4(2)]) << '.' - << int(u.bytes[Endian::e4(3)]) << ".in-addr.arpa"; + os << static_cast(u.bytes[Endian::e4(0)]) << '.' + << static_cast(u.bytes[Endian::e4(1)]) << '.' + << static_cast(u.bytes[Endian::e4(2)]) << '.' + << static_cast(u.bytes[Endian::e4(3)]) << ".in-addr.arpa"; return os.str(); } static Addr from_asio(const openvpn_io::ip::address_v4 &asio_addr) { Addr ret; - ret.u.addr = (std::uint32_t)asio_addr.to_uint(); + ret.u.addr = asio_addr.to_uint(); return ret; } @@ -338,7 +329,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial Addr operator+(const long delta) const { Addr ret; - ret.u.addr = u.addr + (std::uint32_t)delta; + ret.u.addr = u.addr + static_cast(delta); return ret; } @@ -473,8 +464,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial const int ret = prefix_len_32(u.addr); if (ret >= 0) return ret; - else - throw ipv4_exception("malformed netmask"); + throw ipv4_exception("malformed netmask"); } int prefix_len_nothrow() const @@ -501,10 +491,9 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial const unsigned int hl = host_len(); if (hl < SIZE) return 1 << hl; - else if (hl == SIZE) + if (hl == SIZE) return 0; - else - throw ipv4_exception("extent overflow"); + throw ipv4_exception("extent overflow"); } // convert netmask in addr to prefix_len, will return -1 on error @@ -512,29 +501,26 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial { if (addr == ~std::uint32_t(0)) return 32; - else if (addr == 0) + if (addr == 0) return 0; - else + unsigned int high = 32; + unsigned int low = 1; + for (unsigned int i = 0; i < 5; ++i) { - unsigned int high = 32; - unsigned int low = 1; - for (unsigned int i = 0; i < 5; ++i) - { - const unsigned int mid = (high + low) / 2; - const IPv4::Addr::base_type test = prefix_len_to_netmask_unchecked(mid); - if (addr == test) - return mid; - else if (addr > test) - low = mid; - else - high = mid; - } - return -1; + const unsigned int mid = (high + low) / 2; + const IPv4::Addr::base_type test = prefix_len_to_netmask_unchecked(mid); + if (addr == test) + return mid; + if (addr > test) + low = mid; + else + high = mid; } + return -1; } // address size in bits - static unsigned int size() + static constexpr unsigned int size() { return SIZE; } @@ -596,20 +582,18 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial } private: - static base_type prefix_len_to_netmask_unchecked(const unsigned int prefix_len) + static base_type prefix_len_to_netmask_unchecked(const unsigned int prefix_len) noexcept { if (prefix_len) return ~((1u << (SIZE - prefix_len)) - 1); - else - return 0; + return 0; } static base_type prefix_len_to_netmask(const unsigned int prefix_len) { if (prefix_len <= SIZE) return prefix_len_to_netmask_unchecked(prefix_len); - else - throw ipv4_exception("bad prefix len"); + throw ipv4_exception("bad prefix len"); } union { @@ -619,8 +603,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial }; OPENVPN_OSTREAM(Addr, to_string) -} // namespace IPv4 -} // namespace openvpn +} // namespace openvpn::IPv4 #ifdef USE_OPENVPN_HASH OPENVPN_HASH_METHOD(openvpn::IPv4::Addr, hashval); diff --git a/openvpn/addr/ipv6.hpp b/openvpn/addr/ipv6.hpp index 3f5ec53e5..36b7c0f25 100644 --- a/openvpn/addr/ipv6.hpp +++ b/openvpn/addr/ipv6.hpp @@ -22,30 +22,26 @@ #ifndef OPENVPN_ADDR_IPV6_H #define OPENVPN_ADDR_IPV6_H -#include // for std::memcpy, std::memset -#include // for std::min -#include // for std::uint32_t +#include // for std::memcpy, std::memset +#include // for std::uint32_t #include -#include #include #include #include #include #include -#include #include #include -namespace openvpn { -namespace IP { +namespace openvpn::IP { class Addr; } // Fundamental classes for representing an IPv6 IP address. -namespace IPv6 { +namespace openvpn::IPv6 { OPENVPN_EXCEPTION(ipv6_exception); @@ -64,7 +60,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return 6; } - bool defined() const + static constexpr bool defined() { return true; } @@ -74,7 +70,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return addr; } - static Addr from_in6_addr(const struct in6_addr *in6) + static Addr from_in6_addr(const in6_addr *in6) { Addr ret; /* Alignment of in6_addr is only 4 while our ipv6 addr requires an @@ -86,28 +82,27 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - struct in6_addr to_in6_addr() const + in6_addr to_in6_addr() const { - struct in6_addr ret; - host_to_network_order((union ipv6addr *)&ret, &u); + in6_addr ret; + host_to_network_order(reinterpret_cast(&ret), &u); return ret; } - static Addr from_sockaddr(const struct sockaddr_in6 *sa) + static Addr from_sockaddr(const sockaddr_in6 *sa) { Addr ret; - network_to_host_order(&ret.u, (const union ipv6addr *)sa->sin6_addr.s6_addr); + network_to_host_order(&ret.u, reinterpret_cast(sa->sin6_addr.s6_addr)); ret.scope_id_ = sa->sin6_scope_id; return ret; } - struct sockaddr_in6 to_sockaddr(const unsigned short port = 0) const + sockaddr_in6 to_sockaddr(const unsigned short port = 0) const { - struct sockaddr_in6 ret; - std::memset(&ret, 0, sizeof(ret)); + sockaddr_in6 ret = {}; ret.sin6_family = AF_INET6; ret.sin6_port = htons(port); - host_to_network_order((union ipv6addr *)&ret.sin6_addr.s6_addr, &u); + host_to_network_order(reinterpret_cast(&ret.sin6_addr.s6_addr), &u); ret.sin6_scope_id = scope_id_; #ifdef SIN6_LEN /* This is defined on both macOS and FreeBSD that have the sin6_len member */ @@ -173,7 +168,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial if (len < 1 || len > 32) throw ipv6_exception("parse hex error"); size_t di = (len - 1) >> 1; - for (int i = (len & 1) ? -1 : 0; i < int(len); i += 2) + for (int i = (len & 1) ? -1 : 0; i < static_cast(len); i += 2) { const size_t idx = base + i; const int bh = (i >= 0) ? parse_hex_char(s[idx]) : 0; @@ -208,16 +203,15 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial static Addr from_ulong(unsigned long ul) { Addr ret; - ret.u.u64[Endian::e2(0)] = std::uint64_t(ul); + ret.u.u64[Endian::e2(0)] = ul; ret.u.u64[Endian::e2(1)] = 0; return ret; } - // return *this as a unsigned long unsigned long to_ulong() const { - const unsigned long ret = (unsigned long)u.u64[Endian::e2(0)]; - const std::uint64_t cmp = std::uint64_t(ret); + const unsigned long ret = static_cast(u.u64[Endian::e2(0)]); + const auto cmp = std::uint64_t(ret); if (u.u64[Endian::e2(1)] || cmp != u.u64[Endian::e2(0)]) throw ipv6_exception("overflow in conversion from IPv6.Addr to unsigned long"); return ret; @@ -239,7 +233,6 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - // return *this as a long long to_long() const { bool neg = false; @@ -249,14 +242,14 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial a.negate(); neg = true; } - const long ret = (long)a.u.u64[Endian::e2(0)]; - const std::uint64_t cmp = std::uint64_t(ret); + const long ret = static_cast(a.u.u64[Endian::e2(0)]); + const auto cmp = std::uint64_t(ret); if (a.u.u64[Endian::e2(1)] || cmp != a.u.u64[Endian::e2(0)]) throw ipv6_exception("overflow in conversion from IPv6.Addr to long"); return neg ? -(ret + 1) : ret; } - std::string arpa() const + static std::string arpa() { throw ipv6_exception("arpa() not implemented"); } @@ -267,7 +260,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial ipv6addr addr{}; addr.asio_bytes = asio_addr.to_bytes(); network_to_host_order(&ret.u, &addr); - ret.scope_id_ = (unsigned int)asio_addr.scope_id(); + ret.scope_id_ = asio_addr.scope_id(); return ret; } @@ -319,7 +312,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial openvpn_io::ip::address_v6 to_asio() const { - union ipv6addr addr; + ipv6addr addr; host_to_network_order(&addr, &u); return openvpn_io::ip::address_v6(addr.asio_bytes, scope_id_); } @@ -345,7 +338,6 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - // build a netmask using given prefix_len static Addr netmask_from_prefix_len(const unsigned int prefix_len) { Addr ret; @@ -353,15 +345,14 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return ret; } - // build a netmask using given extent - Addr netmask_from_extent() const + Addr netmask_from_this_as_extent() const { const Addr lb = *this - 1; for (size_t i = 4; i-- > 0;) { const std::uint32_t v = lb.u.u32[Endian::e4(i)]; if (v) - return netmask_from_prefix_len(SIZE - (((unsigned int)i << 5) + find_last_set(v))); + return netmask_from_prefix_len(SIZE - ((static_cast(i) << 5) + find_last_set(v))); } return from_zero_complement(); } @@ -483,26 +474,23 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial return !operator==(other); } -#define OPENVPN_IPV6_OPERATOR_REL(OP) \ - bool operator OP(const Addr &other) const \ - { \ - if (u.u64[Endian::e2(1)] == other.u.u64[Endian::e2(1)]) \ - { \ - if (u.u64[Endian::e2(0)] != other.u.u64[Endian::e2(0)]) \ - return u.u64[Endian::e2(0)] OP other.u.u64[Endian::e2(0)]; \ - else \ - return scope_id_ OP other.scope_id_; \ - } \ - else \ - return u.u64[Endian::e2(1)] OP other.u.u64[Endian::e2(1)]; \ - } - - OPENVPN_IPV6_OPERATOR_REL(<) - OPENVPN_IPV6_OPERATOR_REL(>) - OPENVPN_IPV6_OPERATOR_REL(<=) - OPENVPN_IPV6_OPERATOR_REL(>=) -#undef OPENVPN_IPV6_OPERATOR_REL + bool operator<(const Addr &other) const + { + return compare(other, std::less<>{}); + } + bool operator>(const Addr &other) const + { + return compare(other, std::greater<>{}); + } + bool operator<=(const Addr &other) const + { + return compare(other, std::less_equal<>{}); + } + bool operator>=(const Addr &other) const + { + return compare(other, std::greater_equal<>{}); + } bool unspecified() const { @@ -543,8 +531,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial { if (pos < 64) return (u.u64[Endian::e2(0)] & (std::uint64_t(1) << pos)) != 0; - else - return (u.u64[Endian::e2(1)] & (std::uint64_t(1) << (pos - 64))) != 0; + return (u.u64[Endian::e2(1)] & (std::uint64_t(1) << (pos - 64))) != 0; } // number of network bits in netmask, @@ -596,10 +583,9 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial a.one(); return a << hl; } - else if (hl == SIZE) + if (hl == SIZE) return from_zero(); - else - throw ipv6_exception("extent overflow"); + throw ipv6_exception("extent overflow"); } // address size in bits @@ -726,23 +712,23 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial } } - static void div(const Addr &n, const Addr &d, Addr &q, Addr &r) + static void div(const Addr &numerator, const Addr &denominator, Addr "ient, Addr &remainder) { - if (d.all_zeros()) + if (denominator.all_zeros()) throw ipv6_exception("division by 0"); - q = from_zero(); // quotient - r = n; // remainder (init to numerator) - Addr ml = from_zero(); // mask low - Addr mh = d; // mask high (init to denominator) + quotient = from_zero(); + remainder = numerator; + Addr mask_low = from_zero(); + Addr mask_high = denominator; for (unsigned int i = 0; i < SIZE; ++i) { - ml >>= 1; - ml.set_bit(SIZE - 1, mh.bit(0)); - mh >>= 1; - if (mh.all_zeros() && r >= ml) + mask_low >>= 1; + mask_low.set_bit(SIZE - 1, mask_high.bit(0)); + mask_high >>= 1; + if (mask_high.all_zeros() && remainder >= mask_low) { - r -= ml; - q.set_bit((SIZE - 1) - i, true); + remainder -= mask_low; + quotient.set_bit((SIZE - 1) - i, true); } } } @@ -802,11 +788,10 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial { if (prefix_len <= SIZE) return prefix_len_to_netmask_unchecked(prefix_len); - else - throw ipv6_exception("bad prefix len"); + throw ipv6_exception("bad prefix len"); } - static void host_to_network_order(union ipv6addr *dest, const union ipv6addr *src) + static void host_to_network_order(ipv6addr *dest, const ipv6addr *src) { dest->u32[0] = htonl(src->u32[Endian::e4rev(0)]); dest->u32[1] = htonl(src->u32[Endian::e4rev(1)]); @@ -814,7 +799,7 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial dest->u32[3] = htonl(src->u32[Endian::e4rev(3)]); } - static void network_to_host_order(union ipv6addr *dest, const union ipv6addr *src) + static void network_to_host_order(ipv6addr *dest, const ipv6addr *src) { dest->u32[0] = ntohl(src->u32[Endian::e4rev(0)]); dest->u32[1] = ntohl(src->u32[Endian::e4rev(1)]); @@ -907,13 +892,26 @@ class Addr // NOTE: must be union-legal, so default constructor does not initial + (dorigl < dest.u64[Endian::e2(0)]); } - union ipv6addr u; + + template + bool compare(const Addr &other, Comparator comp) const + { + + if (u.u64[Endian::e2(1)] == other.u.u64[Endian::e2(1)]) + { + if (u.u64[Endian::e2(0)] != other.u.u64[Endian::e2(0)]) + return comp(u.u64[Endian::e2(0)], other.u.u64[Endian::e2(0)]); + return comp(scope_id_, other.scope_id_); + } + return comp(u.u64[Endian::e2(1)], other.u.u64[Endian::e2(1)]); + } + + ipv6addr u; unsigned int scope_id_ = 0; }; OPENVPN_OSTREAM(Addr, to_string) -} // namespace IPv6 -} // namespace openvpn +} // namespace openvpn::IPv6 #ifdef USE_OPENVPN_HASH OPENVPN_HASH_METHOD(openvpn::IPv6::Addr, hashval);