Our "sstring_view" is an historic alias for the standard std::string_view. The patch changes the last remaining random uses of this old alias across our source directory to the standard type name. After this patch, there are no more uses of the "sstring_view" alias. It will be removed in the following patch. Refs #4062. Signed-off-by: Nadav Har'El <nyh@scylladb.com>
168 lines
5.6 KiB
C++
168 lines
5.6 KiB
C++
/*
|
|
*
|
|
* Modified by ScyllaDB
|
|
* Copyright (C) 2015-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: (AGPL-3.0-or-later 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
|