From a84b83f20f0bb5ee69a06e6e94d266fd85ac6a94 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 21 Aug 2018 10:39:54 +0200 Subject: [PATCH] net/IPv6Address: new class --- Makefile.am | 1 + src/net/IPv6Address.cxx | 84 +++++++++++++++++ src/net/IPv6Address.hxx | 203 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 src/net/IPv6Address.cxx create mode 100644 src/net/IPv6Address.hxx diff --git a/Makefile.am b/Makefile.am index db18baf69..2ee3063ab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -600,6 +600,7 @@ libnet_a_SOURCES = \ src/net/StaticSocketAddress.cxx src/net/StaticSocketAddress.hxx \ src/net/AllocatedSocketAddress.cxx src/net/AllocatedSocketAddress.hxx \ src/net/IPv4Address.cxx src/net/IPv4Address.hxx \ + src/net/IPv6Address.cxx src/net/IPv6Address.hxx \ src/net/SocketAddress.cxx src/net/SocketAddress.hxx \ src/net/SocketUtil.cxx src/net/SocketUtil.hxx \ src/net/SocketDescriptor.cxx src/net/SocketDescriptor.hxx \ diff --git a/src/net/IPv6Address.cxx b/src/net/IPv6Address.cxx new file mode 100644 index 000000000..410259f82 --- /dev/null +++ b/src/net/IPv6Address.cxx @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012-2017 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IPv6Address.hxx" + +#include +#include + +static const struct sockaddr_in6 * +CastToIPv6(const struct sockaddr *p) noexcept +{ + assert(p->sa_family == AF_INET6); + + /* cast through void to work around the bogus alignment warning */ + const void *q = reinterpret_cast(p); + return reinterpret_cast(q); +} + +IPv6Address::IPv6Address(SocketAddress src) noexcept + :address(*CastToIPv6(src.GetAddress())) {} + +bool +IPv6Address::IsAny() const noexcept +{ + assert(IsValid()); + + return memcmp(&address.sin6_addr, + &in6addr_any, sizeof(in6addr_any)) == 0; +} + +template +static void +BitwiseAndT(T *dest, const T *a, const T *b, size_t n) +{ + while (n-- > 0) + *dest++ = *a++ & *b++; +} + +static void +BitwiseAnd32(void *dest, const void *a, const void *b, size_t n) +{ + using value_type = uint32_t; + using pointer = value_type *; + using const_pointer = const value_type *; + + BitwiseAndT(pointer(dest), const_pointer(a), const_pointer(b), + n / sizeof(value_type)); +} + +IPv6Address +IPv6Address::operator&(const IPv6Address &other) const +{ + IPv6Address result; + BitwiseAnd32(&result, this, &other, + sizeof(result)); + return result; +} diff --git a/src/net/IPv6Address.hxx b/src/net/IPv6Address.hxx new file mode 100644 index 000000000..2b5cfdccf --- /dev/null +++ b/src/net/IPv6Address.hxx @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012-2017 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IPV6_ADDRESS_HXX +#define IPV6_ADDRESS_HXX + +#include "SocketAddress.hxx" +#include "system/ByteOrder.hxx" +#include "util/Compiler.h" + +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +/** + * An OO wrapper for struct sockaddr_in. + */ +class IPv6Address { + struct sockaddr_in6 address; + + static constexpr struct in6_addr Construct(uint16_t a, uint16_t b, + uint16_t c, uint16_t d, + uint16_t e, uint16_t f, + uint16_t g, uint16_t h) noexcept { + return {{{ + uint8_t(a >> 8), uint8_t(a), + uint8_t(b >> 8), uint8_t(b), + uint8_t(c >> 8), uint8_t(c), + uint8_t(d >> 8), uint8_t(d), + uint8_t(e >> 8), uint8_t(e), + uint8_t(f >> 8), uint8_t(f), + uint8_t(g >> 8), uint8_t(g), + uint8_t(h >> 8), uint8_t(h), + }}}; + } + + static constexpr struct sockaddr_in6 Construct(struct in6_addr address, + uint16_t port, + uint32_t scope_id) noexcept { + return { +#if defined(__APPLE__) + sizeof(struct sockaddr_in6), +#endif + AF_INET6, + ToBE16(port), + 0, + address, + scope_id, + }; + } + +public: + IPv6Address() = default; + + constexpr IPv6Address(struct in6_addr _address, uint16_t port, + uint32_t scope_id=0) noexcept + :address(Construct(_address, port, scope_id)) {} + + constexpr explicit IPv6Address(uint16_t port, + uint32_t scope_id=0) noexcept + :IPv6Address(IN6ADDR_ANY_INIT, port, scope_id) {} + + + constexpr IPv6Address(uint16_t a, uint16_t b, uint16_t c, uint16_t d, + uint16_t e, uint16_t f, uint16_t g, uint16_t h, + uint16_t port, uint32_t scope_id=0) noexcept + :IPv6Address(Construct(a, b, c, d, e, f, g, h), + port, scope_id) {} + + /** + * Construct with data copied from a #SocketAddress. Its + * address family must be AF_INET6. + */ + explicit IPv6Address(SocketAddress src) noexcept; + + /** + * Generate a (net-)mask with the specified prefix length. + */ + static constexpr IPv6Address MaskFromPrefix(unsigned prefix_length) noexcept { + return IPv6Address(MaskWord(prefix_length, 0), + MaskWord(prefix_length, 16), + MaskWord(prefix_length, 32), + MaskWord(prefix_length, 48), + MaskWord(prefix_length, 64), + MaskWord(prefix_length, 80), + MaskWord(prefix_length, 96), + MaskWord(prefix_length, 112), + ~uint16_t(0), + ~uint32_t(0)); + } + + /** + * Return a downcasted reference to the address. This call is + * only legal after verifying SocketAddress::GetFamily(). + */ + static constexpr const IPv6Address &Cast(const SocketAddress src) noexcept { + /* this reinterpret_cast works because this class is + just a wrapper for struct sockaddr_in6 */ + return *(const IPv6Address *)(const void *)src.GetAddress(); + } + + constexpr operator SocketAddress() const noexcept { + return SocketAddress((const struct sockaddr *)&address, + sizeof(address)); + } + + constexpr SocketAddress::size_type GetSize() const noexcept { + return sizeof(address); + } + + constexpr bool IsDefined() const noexcept { + return address.sin6_family != AF_UNSPEC; + } + + constexpr bool IsValid() const noexcept { + return address.sin6_family == AF_INET6; + } + + constexpr uint16_t GetPort() const noexcept { + return FromBE16(address.sin6_port); + } + + void SetPort(uint16_t port) noexcept { + address.sin6_port = ToBE16(port); + } + + constexpr const struct in6_addr &GetAddress() const noexcept { + return address.sin6_addr; + } + + constexpr uint32_t GetScopeId() const noexcept { + return address.sin6_scope_id; + } + + /** + * Is this the IPv6 wildcard address (in6addr_any)? + */ + gcc_pure + bool IsAny() const noexcept; + + /** + * Is this an IPv4 address mapped inside struct sockaddr_in6? + */ +#if defined(__linux__) && !GCC_OLDER_THAN(5,0) + constexpr +#endif + bool IsV4Mapped() const noexcept { + return IN6_IS_ADDR_V4MAPPED(&address.sin6_addr); + } + + /** + * Bit-wise AND of two addresses. This is useful for netmask + * calculations. + */ + gcc_pure + IPv6Address operator&(const IPv6Address &other) const; + +private: + /** + * Helper function for MaskFromPrefix(). + */ + static constexpr uint16_t MaskWord(unsigned prefix_length, + unsigned offset) noexcept { + return prefix_length <= offset + ? 0 + : (prefix_length >= offset + 16 + ? 0xffff + : (0xffff << (offset + 16 - prefix_length))); + } +}; + +#endif