0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-14 09:38:34 +00:00
netdata_netdata/src/libnetdata/os/random.c
Costa Tsaousis 4aba1a3c07
better randomness for heartbeat ()
* add 10ms buffering to streaming senders

* queue for 1ms

* added jitter to heartbeat and sender

* remove jitter and add os_random in libnetdata

* cleanup

* cleanup

* fix random for all operating systems

* simpler random number generator

* make microsleep wait at least 1 nsec

* detect random number generators

* use libnetdata os_random()

* use the buffer versions of the random generators; fallback to using random()

* provide random functions per size

* getrandom() can fail; handle this case too

* fix for wrong params
2024-11-04 13:55:02 +02:00

214 lines
5.8 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
#include "libnetdata/libnetdata.h"
#if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_RAND_S)
static SPINLOCK random_lock = NETDATA_SPINLOCK_INITIALIZER;
static __attribute__((constructor)) void seed_random() {
// Use current time and process ID to create a high-entropy seed
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t seed = (uint32_t)(tv.tv_sec ^ tv.tv_usec ^ getpid());
// Seed the random number generator
srandom(seed);
}
#if defined(HAVE_GETRANDOM)
#include <sys/random.h>
void getrandom_helper(void *buf, size_t buflen) {
ssize_t result;
while (buflen > 0) {
result = getrandom(buf, buflen, 0);
if (result == -1) {
if (errno == EINTR) {
// Interrupted, retry
continue;
} else if (errno == EAGAIN) {
// Insufficient entropy; wait and retry
tinysleep();
continue;
} else {
// Fallback to using random() with a spinlock
spinlock_lock(&random_lock);
while (buflen > 0) {
if (buflen >= sizeof(uint32_t)) {
// Generate 4 bytes at a time
uint32_t temp = random();
memcpy(buf, &temp, sizeof(uint32_t));
buf = (uint8_t *)buf + sizeof(uint32_t);
buflen -= sizeof(uint32_t);
} else if (buflen >= sizeof(uint16_t)) {
// Generate 2 bytes at a time
uint16_t temp = random();
memcpy(buf, &temp, sizeof(uint16_t));
buf = (uint8_t *)buf + sizeof(uint16_t);
buflen -= sizeof(uint16_t);
} else {
// Generate remaining bytes
uint32_t temp = random();
for (size_t i = 0; i < buflen; i++) {
((uint8_t *)buf)[i] = temp & 0xFF;
temp >>= 8;
}
buflen = 0;
}
}
spinlock_unlock(&random_lock);
return;
}
}
buf = (uint8_t *)buf + result;
buflen -= result;
}
}
#endif // HAVE_GETRANDOM
#endif // !HAVE_ARC4RANDOM_BUF && !HAVE_RAND_S
// return a random number 0 to max - 1
uint64_t os_random(uint64_t max) {
if (max <= 1) return 0;
uint64_t value;
#if defined(HAVE_ARC4RANDOM_BUF)
if (max <= UINT8_MAX) {
uint8_t v;
arc4random_buf(&v, sizeof(v));
value = v;
} else if(max <= UINT16_MAX) {
uint16_t v;
arc4random_buf(&v, sizeof(v));
value = v;
} else if (max <= UINT32_MAX) {
uint32_t v;
arc4random_buf(&v, sizeof(v));
value = v;
} else
arc4random_buf(&value, sizeof(value));
#elif defined(HAVE_RAND_S)
if (max <= UINT_MAX) {
unsigned int temp;
rand_s(&temp);
value = temp;
} else {
unsigned int temp_lo, temp_hi;
rand_s(&temp_lo);
rand_s(&temp_hi);
value = ((uint64_t)temp_hi << 32) + (uint64_t)temp_lo;
}
#elif defined(HAVE_GETRANDOM)
if (max <= UINT8_MAX) {
uint8_t v;
getrandom_helper(&v, sizeof(v));
value = v;
} else if(max <= UINT16_MAX) {
uint16_t v;
getrandom_helper(&v, sizeof(v));
value = v;
} else if (max <= UINT32_MAX) {
uint32_t v;
getrandom_helper(&v, sizeof(v));
value = v;
} else
getrandom_helper(&value, sizeof(value));
#else
spinlock_lock(&random_lock);
if(max <= INT32_MAX)
value = random();
else
value = ((uint64_t) random() << 33) | ((uint64_t) random() << 2) | (random() & 0x3);
spinlock_unlock(&random_lock);
#endif
return value % max;
}
// Generate an 8-bit random number
uint8_t os_random8(void) {
uint8_t value;
#if defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(&value, sizeof(value));
#elif defined(HAVE_GETRANDOM)
getrandom_helper(&value, sizeof(value));
#elif defined(HAVE_RAND_S)
unsigned int temp;
rand_s(&temp);
value = (uint8_t)temp;
#else
spinlock_lock(&random_lock);
value = (uint8_t)random();
spinlock_unlock(&random_lock);
#endif
return value;
}
// Generate a 16-bit random number
uint16_t os_random16(void) {
uint16_t value;
#if defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(&value, sizeof(value));
#elif defined(HAVE_GETRANDOM)
getrandom_helper(&value, sizeof(value));
#elif defined(HAVE_RAND_S)
unsigned int temp;
rand_s(&temp);
value = (uint16_t)temp;
#else
spinlock_lock(&random_lock);
value = (uint16_t)random();
spinlock_unlock(&random_lock);
#endif
return value;
}
// Generate a 32-bit random number
uint32_t os_random32(void) {
uint32_t value;
#if defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(&value, sizeof(value));
#elif defined(HAVE_GETRANDOM)
getrandom_helper(&value, sizeof(value));
#elif defined(HAVE_RAND_S)
unsigned int temp;
rand_s(&temp);
value = temp;
#else
spinlock_lock(&random_lock);
value = random();
spinlock_unlock(&random_lock);
#endif
return value;
}
// Generate a 64-bit random number
uint64_t os_random64(void) {
uint64_t value;
#if defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(&value, sizeof(value));
#elif defined(HAVE_GETRANDOM)
getrandom_helper(&value, sizeof(value));
#elif defined(HAVE_RAND_S)
unsigned int temp_lo, temp_hi;
rand_s(&temp_lo);
rand_s(&temp_hi);
value = ((uint64_t)temp_hi << 32) | (uint64_t)temp_lo;
#else
spinlock_lock(&random_lock);
value = ((uint64_t)random() << 33) | ((uint64_t)random() << 2) | (random() & 0x3);
spinlock_unlock(&random_lock);
#endif
return value;
}