// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2017 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see . #ifndef OPENVPN_ADDR_IP_H #define OPENVPN_ADDR_IP_H #include #include // for std::memset #include #include #include #include #include #include #include #include namespace openvpn { // This is our fundamental IP address class that handles IPv4 or IPv6 // IP addresses. It is implemented as a discriminated union of IPv4::Addr // and IPv6::Addr. namespace IP { OPENVPN_EXCEPTION(ip_exception); class Addr { public: enum Version { UNSPEC, V4, V6 }; enum { V4_MASK=(1<<0), V6_MASK=(1<<1) }; typedef unsigned int VersionMask; enum VersionSize { V4_SIZE = IPv4::Addr::SIZE, V6_SIZE = IPv6::Addr::SIZE, }; Addr(const Addr& other, const char *title = nullptr, Version required_version = UNSPEC) : ver(other.ver) { other.validate_version(title, required_version); switch (ver) { case V4: u.v4 = other.u.v4; break; case V6: u.v6 = other.u.v6; break; default: break; } } Addr(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC) : Addr(from_string(ipstr, title, required_version)) { } #ifndef SWIGPYTHON // When calling IP:Addr with None as the second parameter, Swig will // always pick this function and complain about not being able to convert // a null pointer to a const std::string reference. Hide this function, so // swig is forced to take the const char* variant of this function instead Addr(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC) : Addr(from_string(ipstr, title.c_str(), required_version)) { } #endif void validate_version(const char *title, Version required_version) const { if (required_version != UNSPEC && required_version != ver) throw ip_exception(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version")); } #ifndef SWIGPYTHON void validate_version(const std::string& title, Version required_version) const { validate_version(title.c_str(), required_version); } #endif static std::string validate(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC) { Addr a = from_string(ipstr, title, required_version); return a.to_string(); } #ifndef SWIGPYTHON static std::string validate(const std::string& ipstr, const std::string& title, Version required_version = UNSPEC) { return validate(ipstr, title.c_str(), required_version); } #endif 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; } // slow path { openvpn_io::error_code ec; openvpn_io::ip::make_address(ipstr, ec); return !ec; } } static Addr from_string(const std::string& ipstr, const char *title = nullptr, Version required_version = UNSPEC) { openvpn_io::error_code ec; openvpn_io::ip::address a = openvpn_io::ip::make_address(ipstr, ec); if (ec) throw ip_exception(internal::format_error(ipstr, title, "", ec)); const Addr ret = from_asio(a); if (required_version != UNSPEC && required_version != ret.ver) throw ip_exception(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version")); return ret; } static Addr from_hex(Version v, const std::string& s) { if (v == V4) return from_ipv4(IPv4::Addr::from_hex(s)); else if (v == V6) return from_ipv6(IPv6::Addr::from_hex(s)); else throw ip_exception("address unspecified"); } static Addr from_ipv4(IPv4::Addr addr) { Addr a; a.ver = V4; a.u.v4 = std::move(addr); return a; } static Addr from_ipv6(IPv6::Addr addr) { Addr a; a.ver = V6; a.u.v6 = std::move(addr); return a; } const IPv4::Addr& to_ipv4() const { if (ver == V4) return u.v4; else throw ip_exception("address is not IPv4"); } const IPv6::Addr& to_ipv6() const { if (ver == V6) return u.v6; else throw ip_exception("address is not IPv6"); } const IPv4::Addr& to_ipv4_nocheck() const { return u.v4; } const IPv6::Addr& to_ipv6_nocheck() const { return u.v6; } static Addr from_sockaddr(const struct sockaddr *sa) { if (sa->sa_family == AF_INET) return from_ipv4(IPv4::Addr::from_sockaddr((struct sockaddr_in *)sa)); else if (sa->sa_family == AF_INET6) return from_ipv6(IPv6::Addr::from_sockaddr((struct sockaddr_in6 *)sa)); else return Addr(); } static bool sockaddr_defined(const struct sockaddr *sa) { return sa && (sa->sa_family == AF_INET || sa->sa_family == AF_INET6); } static Addr from_ulong(Version v, unsigned long ul) { if (v == V4) return from_ipv4(IPv4::Addr::from_ulong(ul)); else if (v == V6) return from_ipv6(IPv6::Addr::from_ulong(ul)); else throw ip_exception("address unspecified"); } // return *this as a ulong, will raise exception on overflow unsigned long to_ulong() const { if (ver == V4) return u.v4.to_ulong(); else if (ver == V6) return u.v6.to_ulong(); else throw ip_exception("address unspecified"); } static Addr from_long(Version v, long ul) { if (v == V4) return from_ipv4(IPv4::Addr::from_long(ul)); else if (v == V6) return from_ipv6(IPv6::Addr::from_long(ul)); else throw ip_exception("address unspecified"); } // return *this as a long, will raise exception on overflow long to_long() const { if (ver == V4) return u.v4.to_long(); else if (ver == V6) return u.v6.to_long(); else throw ip_exception("address unspecified"); } // return Addr from 16 byte binary string static Addr from_byte_string(const unsigned char *bytestr) { Addr a; if (IPv6::Addr::byte_string_is_v4(bytestr)) { a.ver = V4; a.u.v4 = IPv4::Addr::from_uint32_net(IPv6::Addr::v4_from_byte_string(bytestr)); } else { a.ver = V6; a.u.v6 = IPv6::Addr::from_byte_string(bytestr); } return a; } // convert Addr to 16 byte binary string void to_byte_string(unsigned char *bytestr) const { if (ver == V4) IPv6::Addr::v4_to_byte_string(bytestr, u.v4.to_uint32_net()); else if (ver == V6) u.v6.to_byte_string(bytestr); else std::memset(bytestr, 0, 16); } // convert Addr to variable length byte string void to_byte_string_variable(unsigned char *bytestr) const { if (ver == V4) u.v4.to_byte_string(bytestr); else if (ver == V6) u.v6.to_byte_string(bytestr); else throw ip_exception("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; } // construct an address where all bits are zero static Addr from_zero(Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_zero()); else if (v == V6) return from_ipv6(IPv6::Addr::from_zero()); else throw ip_exception("address unspecified"); } // construct an address where all bits are zero static Addr from_one(Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_one()); else if (v == V6) return from_ipv6(IPv6::Addr::from_one()); else throw ip_exception("address unspecified"); } // construct an address where all bits are one static Addr from_zero_complement(Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_zero_complement()); else if (v == V6) return from_ipv6(IPv6::Addr::from_zero_complement()); else throw ip_exception("address 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; } 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) return from_ipv4(IPv4::Addr::netmask_from_prefix_len(prefix_len)); else if (v == V6) return from_ipv6(IPv6::Addr::netmask_from_prefix_len(prefix_len)); else throw ip_exception("address unspecified"); } // build a netmask using *this as extent Addr netmask_from_extent() const { if (ver == V4) return from_ipv4(u.v4.netmask_from_extent()); else if (ver == V6) return from_ipv6(u.v6.netmask_from_extent()); else throw ip_exception("address unspecified"); } std::string to_string() const { if (ver != UNSPEC) { const openvpn_io::ip::address a = to_asio(); std::string ret = a.to_string(); return ret; } else return "UNSPEC"; } std::string to_string_bracket_ipv6() const { std::string ret; if (ver == V6) ret += '['; ret += to_string(); if (ver == V6) ret += ']'; return ret; } std::string to_hex() const { if (ver == V4) return u.v4.to_hex(); else if (ver == V6) return u.v6.to_hex(); else throw ip_exception("address unspecified"); } std::string arpa() const { if (ver == V4) return u.v4.arpa(); else if (ver == V6) return u.v6.arpa(); else throw ip_exception("address unspecified"); } static Addr from_asio(const openvpn_io::ip::address& addr) { if (addr.is_v4()) { Addr a; a.ver = V4; a.u.v4 = IPv4::Addr::from_asio(addr.to_v4()); return a; } else if (addr.is_v6()) { Addr a; a.ver = V6; a.u.v6 = IPv6::Addr::from_asio(addr.to_v6()); return a; } else throw ip_exception("address unspecified"); } openvpn_io::ip::address to_asio() const { switch (ver) { case V4: return openvpn_io::ip::address_v4(u.v4.to_asio()); case V6: return openvpn_io::ip::address_v6(u.v6.to_asio()); default: throw ip_exception("address unspecified"); } } Addr operator+(const long delta) const { switch (ver) { case V4: { Addr ret; ret.ver = V4; ret.u.v4 = u.v4 + delta; return ret; } case V6: { Addr ret; ret.ver = V6; ret.u.v6 = u.v6 + delta; return ret; } default: throw ip_exception("address unspecified"); } } Addr operator-(const long delta) const { return operator+(-delta); } #define OPENVPN_IP_OPERATOR_BINOP(OP) \ Addr operator OP (const Addr& other) const { \ if (ver != other.ver) \ throw ip_exception("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: \ throw ip_exception("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) { case V4: { Addr ret; ret.ver = V4; ret.u.v4 = u.v4 << shift; return ret; } case V6: { Addr ret; ret.ver = V6; ret.u.v6 = u.v6 << shift; return ret; } default: throw ip_exception("address unspecified"); } } Addr operator>>(const unsigned int shift) const { switch (ver) { case V4: { Addr ret; ret.ver = V4; ret.u.v4 = u.v4 >> shift; return ret; } case V6: { Addr ret; ret.ver = V6; ret.u.v6 = u.v6 >> shift; return ret; } default: throw ip_exception("address unspecified"); } } Addr operator~() const { switch (ver) { case V4: { Addr ret; ret.ver = V4; ret.u.v4 = ~u.v4; return ret; } case V6: { Addr ret; ret.ver = V6; ret.u.v6 = ~u.v6; return ret; } default: throw ip_exception("address unspecified"); } } Addr network_addr(const unsigned int prefix_len) const { switch (ver) { case V4: { Addr ret; ret.ver = V4; ret.u.v4 = u.v4.network_addr(prefix_len); return ret; } case V6: { Addr ret; ret.ver = V6; ret.u.v6 = u.v6.network_addr(prefix_len); return ret; } default: throw ip_exception("address unspecified"); } } bool operator==(const Addr& other) const { switch (ver) { case UNSPEC: return other.ver == UNSPEC; case V4: if (ver == other.ver) return u.v4 == other.u.v4; break; case V6: if (ver == other.ver) return u.v6 == other.u.v6; break; } return false; } bool operator!=(const Addr& other) const { 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; \ } OPENVPN_IP_OPERATOR_REL(<) OPENVPN_IP_OPERATOR_REL(>) OPENVPN_IP_OPERATOR_REL(<=) OPENVPN_IP_OPERATOR_REL(>=) #undef OPENVPN_IP_OPERATOR_REL bool unspecified() const { return all_zeros(); } bool specified() const { return !unspecified(); } bool all_zeros() const { switch (ver) { case V4: return u.v4.all_zeros(); case V6: return u.v6.all_zeros(); default: return true; } } bool all_ones() const { switch (ver) { case V4: return u.v4.all_ones(); case V6: return u.v6.all_ones(); default: return false; } } bool is_loopback() const { switch (ver) { case V4: return u.v4.is_loopback(); case V6: return u.v6.is_loopback(); default: return false; } } bool defined() const { return ver != UNSPEC; } const char *version_string() const { return version_string_static(ver); } static const char *version_string_static(Version ver) { switch (ver) { case V4: return "v4"; case V6: return "v6"; default: return "v?"; } } Version version() const { return ver; } static VersionMask version_mask(const Version ver) { switch (ver) { case V4: return V4_MASK; case V6: return V6_MASK; default: return 0; } } VersionMask version_mask() const { return version_mask(ver); } int version_index() const { switch (ver) { case V4: return 0; case V6: return 1; default: throw ip_exception("version index undefined"); } } int family() const { switch (ver) { case V4: return AF_INET; case V6: return AF_INET6; default: return -1; } } bool is_compatible(const Addr& other) const { return ver == other.ver; } bool is_ipv6() const { return ver == V6; } void verify_version_consistency(const Addr& other) const { if (!is_compatible(other)) throw ip_exception("version inconsistency"); } // throw exception if address is not a valid netmask void validate_netmask() { prefix_len(); } // number of network bits in netmask, // throws exception if addr is not a netmask unsigned int prefix_len() const { switch (ver) { case V4: return u.v4.prefix_len(); case V6: return u.v6.prefix_len(); default: throw ip_exception("address unspecified"); } } // IPv6 scope ID or -1 if not IPv6 int scope_id() const { return ver == V6 ? u.v6.scope_id() : -1; } // number of host bits in netmask unsigned int host_len() const { switch (ver) { case V4: return u.v4.host_len(); case V6: return u.v6.host_len(); default: throw ip_exception("address unspecified"); } } // return the number of host addresses contained within netmask Addr extent_from_netmask() const { switch (ver) { case V4: return from_ipv4(u.v4.extent_from_netmask()); case V6: return from_ipv6(u.v6.extent_from_netmask()); default: throw ip_exception("address unspecified"); } } // address size in bits unsigned int size() const { return version_size(ver); } // address size in bytes unsigned int size_bytes() const { return size() / 8; } // address size in bits of particular IP version static unsigned int version_size(Version v) { if (v == V4) return IPv4::Addr::SIZE; else if (v == V6) return IPv6::Addr::SIZE; else return 0; } template void hash(HASH& h) const { switch (ver) { case Addr::V4: u.v4.hash(h); break; case Addr::V6: u.v6.hash(h); break; default: break; } } #ifdef HAVE_CITYHASH std::size_t hashval() const { HashSizeT h; hash(h); return h.value(); } #endif #ifdef OPENVPN_IP_IMMUTABLE private: #endif Addr() : ver(UNSPEC) { } void reset() { ver = UNSPEC; } Addr& operator=(const Addr& other) { switch (ver = other.ver) { case V4: u.v4 = other.u.v4; break; case V6: u.v6 = other.u.v6; break; default: break; } return *this; } Addr& operator++() { switch (ver) { case V4: ++u.v4; break; case V6: ++u.v6; break; default: break; } return *this; } Addr& operator+=(const long delta) { switch (ver) { case V4: u.v4 += delta; break; case V6: u.v6 += delta; break; default: break; } return *this; } Addr& operator-=(const long delta) { switch (ver) { case V4: u.v4 -= delta; break; case V6: u.v6 -= delta; break; default: break; } return *this; } void reset_ipv4_from_uint32(const IPv4::Addr::base_type addr) { ver = V4; u.v4 = IPv4::Addr::from_uint32(addr); } private: union { IPv4::Addr v4; IPv6::Addr v6; } u; Version ver; }; OPENVPN_OSTREAM(Addr, to_string) } } #ifdef HAVE_CITYHASH OPENVPN_HASH_METHOD(openvpn::IP::Addr, hashval); #endif #endif