aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/IPv6ToBinary.cpp
blob: 8d335d893536d78708be4cf78bbaf019329d1fef (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include "IPv6ToBinary.h"
#include <Poco/Net/IPAddress.h>
#include <Poco/ByteOrder.h>

#include <Common/formatIPv6.h>

#include <cstring>
#include <bit>


namespace DB
{

/// Result array could be indexed with all possible uint8 values without extra check.
/// For values greater than 128 we will store same value as for 128 (all bits set).
constexpr size_t IPV6_MASKS_COUNT = 256;
using RawMaskArrayV6 = std::array<uint8_t, IPV6_BINARY_LENGTH>;

void IPv6ToRawBinary(const Poco::Net::IPAddress & address, char * res)
{
    if (Poco::Net::IPAddress::IPv6 == address.family())
    {
        memcpy(res, address.addr(), 16);
    }
    else if (Poco::Net::IPAddress::IPv4 == address.family())
    {
        /// Convert to IPv6-mapped address.
        memset(res, 0, 10);
        res[10] = '\xFF';
        res[11] = '\xFF';
        memcpy(&res[12], address.addr(), 4);
    }
    else
        memset(res, 0, 16);
}

std::array<char, 16> IPv6ToBinary(const Poco::Net::IPAddress & address)
{
    std::array<char, 16> res;
    IPv6ToRawBinary(address, res.data());
    return res;
}

template <typename RawMaskArrayT>
static constexpr RawMaskArrayT generateBitMask(size_t prefix)
{
    RawMaskArrayT arr{0};
    if (prefix >= arr.size() * 8)
        prefix = arr.size() * 8;
    size_t i = 0;
    for (; prefix >= 8; ++i, prefix -= 8)
        arr[i] = 0xff;
    if (prefix > 0)
        arr[i++] = ~(0xff >> prefix);
    while (i < arr.size())
        arr[i++] = 0x00;
    return arr;
}

template <typename RawMaskArrayT, size_t masksCount>
static constexpr std::array<RawMaskArrayT, masksCount> generateBitMasks()
{
    std::array<RawMaskArrayT, masksCount> arr{};
    for (size_t i = 0; i < masksCount; ++i)
        arr[i] = generateBitMask<RawMaskArrayT>(i);
    return arr;
}

const std::array<uint8_t, 16> & getCIDRMaskIPv6(UInt8 prefix_len)
{
    static constexpr auto IPV6_RAW_MASK_ARRAY = generateBitMasks<RawMaskArrayV6, IPV6_MASKS_COUNT>();
    return IPV6_RAW_MASK_ARRAY[prefix_len];
}

bool matchIPv4Subnet(UInt32 addr, UInt32 cidr_addr, UInt8 prefix)
{
    UInt32 mask = (prefix >= 32) ? 0xffffffffu : ~(0xffffffffu >> prefix);
    return (addr & mask) == (cidr_addr & mask);
}

#if defined(__SSE2__)
#include <emmintrin.h>

bool matchIPv6Subnet(const uint8_t * addr, const uint8_t * cidr_addr, UInt8 prefix)
{
    uint16_t mask = _mm_movemask_epi8(_mm_cmpeq_epi8(
        _mm_loadu_si128(reinterpret_cast<const __m128i *>(addr)),
        _mm_loadu_si128(reinterpret_cast<const __m128i *>(cidr_addr))));
    mask = ~mask;

    if (mask)
    {
        auto offset = std::countr_zero(mask);

        if (prefix / 8 != offset)
            return prefix / 8 < offset;

        auto cmpmask = ~(0xff >> (prefix % 8));
        return (addr[offset] & cmpmask) == (cidr_addr[offset] & cmpmask);
    }
    return true;
}

# else

bool matchIPv6Subnet(const uint8_t * addr, const uint8_t * cidr_addr, UInt8 prefix)
{
    if (prefix > IPV6_BINARY_LENGTH * 8U)
        prefix = IPV6_BINARY_LENGTH * 8U;

    size_t i = 0;
    for (; prefix >= 8; ++i, prefix -= 8)
    {
        if (addr[i] != cidr_addr[i])
            return false;
    }
    if (prefix == 0)
        return true;

    auto mask = ~(0xff >> prefix);
    return (addr[i] & mask) == (cidr_addr[i] & mask);
}

#endif  // __SSE2__

}