forked from scylladb/scylladb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUUID_gen.cc
167 lines (144 loc) · 5.66 KB
/
UUID_gen.cc
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
*
* Modified by ScyllaDB
* Copyright (C) 2015-present ScyllaDB
*/
/*
* SPDX-License-Identifier: (LicenseRef-ScyllaDB-Source-Available-1.0 and Apache-2.0)
*/
#include "UUID_gen.hh"
#ifdef __linux__
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
#endif // __linux__
#include <atomic>
#include <stdlib.h>
#include "utils/hashers.hh"
namespace utils {
static int64_t make_thread_local_node(int64_t node) {
// An atomic counter to issue thread identifiers.
// We should take current core number into consideration
// because create_time_safe() doesn't synchronize across cores and
// it's easy to get duplicates. Use an own counter since
// seastar::this_shard_id() may not yet be available.
static std::atomic<int64_t> thread_id_counter;
static thread_local int64_t thread_id = thread_id_counter.fetch_add(1);
// Mix in the core number into Organisational Unique
// Identifier, to leave NIC intact, assuming tampering
// with NIC is more likely to lead to collision within
// a single network than tampering with OUI.
//
// Make sure the result fits into 6 bytes reserved for MAC
// (adding the core number may overflow the original
// value).
return (node + (thread_id << 32)) & 0xFFFF'FFFF'FFFFL;
}
static int64_t make_random_node() {
static int64_t random_node = [] {
int64_t node = 0;
std::random_device rndgen;
do {
auto i = rndgen();
node = i;
if (sizeof(i) < sizeof(node)) {
node = (node << 32) + rndgen();
}
} while (node == 0); // 0 may mean "node is uninitialized", so avoid it.
return node;
}();
return random_node;
}
static int64_t make_node() {
static int64_t global_node = [] {
int64_t node = 0;
#ifdef __linux__
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd >= 0) {
// Get a hardware address for an interface, if there is more than one, use any
struct ifreq ifr_list[32];
struct ifconf ifc;
ifc.ifc_req = ifr_list;
ifc.ifc_len = sizeof(ifr_list)/sizeof(ifr_list[0]);
if (ioctl(fd, SIOCGIFCONF, static_cast<void*>(&ifc)) >= 0) {
for (struct ifreq *ifr = ifr_list; ifr < ifr_list + ifc.ifc_len; ifr++) {
// Go over available addresses and pick any
// valid one, except loopback
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) {
continue;
}
if (ifr->ifr_flags & IFF_LOOPBACK) { // don't count loopback
continue;
}
if (ioctl(fd, SIOCGIFHWADDR, ifr) < 0) {
continue;
}
auto macaddr = ifr->ifr_hwaddr.sa_data;
for (auto c = macaddr; c < macaddr + 6; c++) {
// Avoid little-big-endian differences
node = (node << 8) + static_cast<unsigned char>(*c);
}
if (node) {
break; // Success
}
}
}
close(fd);
}
#endif
if (node == 0) {
node = make_random_node();
}
return node;
}();
return make_thread_local_node(global_node);
}
static int64_t make_clock_seq_and_node()
{
// The original Java code did this, shuffling the number of millis
// since the epoch, and taking 14 bits of it. We don't do exactly
// the same, but the idea is the same.
//long clock = new Random(System.currentTimeMillis()).nextLong();
unsigned int seed = std::chrono::system_clock::now().time_since_epoch().count();
int clock = rand_r(&seed);
long lsb = 0;
lsb |= 0x8000000000000000L; // variant (2 bits)
lsb |= (clock & 0x0000000000003FFFL) << 48; // clock sequence (14 bits)
lsb |= make_node(); // 6 bytes
return lsb;
}
UUID UUID_gen::get_name_UUID(bytes_view b) {
return get_name_UUID(reinterpret_cast<const unsigned char*>(b.begin()), b.size());
}
UUID UUID_gen::get_name_UUID(std::string_view s) {
static_assert(sizeof(char) == sizeof(std::string_view::value_type), "Assumed that str.size() counts in chars");
return get_name_UUID(reinterpret_cast<const unsigned char*>(s.begin()), s.size());
}
UUID UUID_gen::get_name_UUID(const unsigned char *s, size_t len) {
bytes digest = md5_hasher::calculate(std::string_view(reinterpret_cast<const char*>(s), len));
// set version to 3
digest[6] &= 0x0f;
digest[6] |= 0x30;
// set variant to IETF variant
digest[8] &= 0x3f;
digest[8] |= 0x80;
return get_UUID(digest);
}
UUID UUID_gen::negate(UUID o) {
auto lsb = o.get_least_significant_bits();
const long clock_mask = 0x0000000000003FFFL;
// We flip the node-and-clock-seq octet of the UUID for time-UUIDs. This
// creates a virtual node with a time which cannot be generated anymore, so
// is safe against collisions.
// For name UUIDs we flip the same octet. Name UUIDs being an md5 hash over
// a buffer, flipping any bit should be safe against collisions.
long clock = (lsb >> 48) & clock_mask;
clock = ~clock & clock_mask;
lsb &= ~(clock_mask << 48); // zero current clock
lsb |= (clock << 48); // write new clock
return UUID(o.get_most_significant_bits(), lsb);
}
const thread_local int64_t UUID_gen::spoof_node = make_thread_local_node(make_random_node());
const thread_local int64_t UUID_gen::clock_seq_and_node = make_clock_seq_and_node();
thread_local UUID_gen UUID_gen::_instance;
} // namespace utils