Merging upstream version 2.12.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-09 09:08:15 +01:00
parent 8ce9b70a48
commit 31166912ef
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
9 changed files with 233 additions and 28 deletions

View file

@ -485,6 +485,8 @@ available in interval statistics.
Number of answers received within \fIstats_interval\fR can legitimately
exceed number of queries sent, depending on answer latency, configured
\fItimeout\fR, and \fIstats_interval\fR.
Only available in \fBdnsperf\fR.
.RE
\fBlatency-histogram\fR
@ -496,6 +498,29 @@ range for individual bins increases logarithmically.
This is done to to limit amount of memory required for histograms
and also allows to visualize latency using logarithmic percentile histograms
with minimal postprocessing.
Only available in \fBdnsperf\fR and if compile time support was detected.
.RE
\fBqps-threshold-wait=<microseconds>\fR
.br
.RS
When using \fB-Q\fR to rate limit queries sent, this option control the
minimum time (in microseconds) there should be between queries to use
\fInanosleep()\fR to wait until sending the next query.
Setting to zero (0) will disable any call to \fInanosleep()\fR between
sending queries.
If the time between queries is lower then this, then no wait is performed
in order to have more precision on when to send the next query.
This is because during high QPS rate limiting it can take more time just
calling the functions to wait for when to send the next query then the
actually time between queries.
If not set, the average call-time to \fInanosleep()\fR will be measured
during startup.
Only available in \fBdnsperf\fR.
.RE
.SH "SEE ALSO"
\fBresperf\fR(1)

View file

@ -102,6 +102,7 @@ typedef struct {
#ifdef USE_HISTOGRAMS
bool latency_histogram;
#endif
int qps_threshold_wait;
} config_t;
typedef struct {
@ -437,12 +438,16 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats, u
printf("\n");
if (!stats->num_conn_completed) {
if (!stats->num_conn_completed && !stats->num_conn_reconnect) {
fflush(stdout);
return;
}
printf("Connection Statistics:\n\n");
printf(" Reconnections: %" PRIu64 "\n\n", stats->num_conn_reconnect);
printf(" Reconnections: %" PRIu64 " (%.2lf%% of %" PRIu64 " connections)\n\n",
stats->num_conn_reconnect,
PERF_SAFE_DIV(100.0 * stats->num_conn_reconnect, stats->num_conn_completed),
stats->num_conn_completed);
latency_avg = PERF_SAFE_DIV(stats->conn_latency_sum, stats->num_conn_completed);
printf(" Average Latency (s): %u.%06u",
(unsigned int)(latency_avg / MILLION),
@ -466,6 +471,7 @@ print_statistics(const config_t* config, const times_t* times, stats_t* stats, u
}
printf("\n");
fflush(stdout);
}
/*
@ -532,6 +538,40 @@ stringify(unsigned int value)
return buf;
}
static int
measure_nanosleep(config_t* config)
{
struct timespec start, stop, wait = { 0, 0 };
int err;
int i = 100;
if ((err = clock_gettime(CLOCK_REALTIME, &start))) {
return err;
}
for (; i; i--) {
if ((err = nanosleep(&wait, NULL))) {
return err;
}
}
if ((err = clock_gettime(CLOCK_REALTIME, &stop))) {
return err;
}
// Total time for 100 nanosleep() + 2 clock_gettime()
config->qps_threshold_wait = ((stop.tv_sec - start.tv_sec) * 1000000000 + stop.tv_nsec - start.tv_nsec)
// divided by 100 runs
/ 100
// add fudge
* 3
// converted to microseconds
/ 1000;
if (config->qps_threshold_wait < 0) {
config->qps_threshold_wait = 0;
}
return 0;
}
static void
setup(int argc, char** argv, config_t* config)
{
@ -559,6 +599,8 @@ setup(int argc, char** argv, config_t* config)
config->max_outstanding = DEFAULT_MAX_OUTSTANDING;
config->mode = sock_udp;
config->qps_threshold_wait = -1;
perf_opt_add('f', perf_opt_string, "family",
"address family of DNS transport, inet or inet6", "any",
&family);
@ -637,6 +679,8 @@ setup(int argc, char** argv, config_t* config)
perf_long_opt_add("latency-histogram", perf_opt_boolean, NULL,
"collect and print detailed latency histograms", NULL, &config->latency_histogram);
#endif
perf_long_opt_add("qps-threshold-wait", perf_opt_zpint, "microseconds",
"minimum threshold for enabling wait in rate limiting", stringify(config->qps_threshold_wait), &config->qps_threshold_wait);
bool log_stdout = false;
perf_opt_add('W', perf_opt_boolean, NULL, "log warnings and errors to stdout instead of stderr", NULL, &log_stdout);
@ -730,6 +774,14 @@ setup(int argc, char** argv, config_t* config)
perf_log_fatal("Unable to dynamic update, support not built in");
}
#endif
if (config->qps_threshold_wait < 0) {
int err = measure_nanosleep(config);
if (err) {
char __s[256];
perf_log_fatal("Unable to measure nanosleep(): %s", perf_strerror_r(errno, __s, sizeof(__s)));
}
}
}
static void
@ -803,7 +855,7 @@ do_send(void* arg)
stats_t* stats;
unsigned int max_packet_size;
perf_buffer_t msg;
uint64_t now, run_time, req_time;
uint64_t now, req_time, wait_us, q_sent = 0, q_step = 0, q_slice;
char input_data[MAX_INPUT_DATA];
perf_buffer_t lines;
perf_region_t used;
@ -824,8 +876,13 @@ do_send(void* arg)
perf_buffer_init(&msg, packet_buffer, max_packet_size);
perf_buffer_init(&lines, input_data, sizeof(input_data));
if (tinfo->max_qps > 0) {
q_step = MILLION / tinfo->max_qps;
}
wait_for_start();
now = perf_get_time();
now = perf_get_time();
req_time = now;
q_slice = now + MILLION;
while (!interrupted && now < times->stop_time) {
/* Avoid flooding the network too quickly. */
if (stats->num_sent < tinfo->max_outstanding && stats->num_sent % 2 == 1) {
@ -838,13 +895,49 @@ do_send(void* arg)
/* Rate limiting */
if (tinfo->max_qps > 0) {
run_time = now - times->start_time;
req_time = (MILLION * stats->num_sent) / tinfo->max_qps;
if (req_time > run_time) {
usleep(req_time - run_time);
/* the 1 second time slice where q_sent is calculated over */
if (q_slice <= now) {
q_slice += MILLION;
q_sent = 0;
req_time = now; // reset stepping, in case of clock sliding
}
/* limit QPS over the 1 second slice */
if (q_sent >= tinfo->max_qps) {
wait_us = q_slice - now;
if (config->qps_threshold_wait && wait_us > config->qps_threshold_wait) {
wait_us -= config->qps_threshold_wait;
struct timespec ts = { 0, 0 };
if (wait_us >= MILLION) {
ts.tv_sec = wait_us / MILLION;
ts.tv_nsec = (wait_us % MILLION) * 1000;
} else {
ts.tv_sec = 0;
ts.tv_nsec = wait_us * 1000;
}
nanosleep(&ts, NULL);
}
now = perf_get_time();
continue;
}
/* handle stepping to the next window to send a query on */
if (req_time > now) {
wait_us = req_time - now;
if (config->qps_threshold_wait && wait_us > config->qps_threshold_wait) {
wait_us -= config->qps_threshold_wait;
struct timespec ts = { 0, 0 };
if (wait_us >= MILLION) {
ts.tv_sec = wait_us / MILLION;
ts.tv_nsec = (wait_us % MILLION) * 1000;
} else {
ts.tv_sec = 0;
ts.tv_nsec = wait_us * 1000;
}
nanosleep(&ts, NULL);
}
now = perf_get_time();
continue;
}
req_time += q_step;
}
PERF_LOCK(&tinfo->lock);
@ -980,6 +1073,7 @@ do_send(void* arg)
continue;
}
stats->num_sent++;
q_sent++;
stats->total_request_size += length;
}

View file

@ -100,7 +100,7 @@ struct perf__doh_socket {
bool is_ready, is_conn_ready, is_ssl_ready, is_sending, is_post_sending;
// bool have_more; TODO
bool do_reconnect;
bool do_reconnect, do_connect;
perf_sockaddr_t server, local;
size_t bufsize;
@ -254,7 +254,7 @@ static void perf__doh_reconnect(struct perf_net_socket* sock)
self->http2_code = 0;
self->http2_is_dns = false;
perf__doh_connect(sock);
self->do_connect = true;
}
// static ssize_t _recv_callback(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *sock)
@ -623,6 +623,10 @@ static int perf__doh_sockready(struct perf_net_socket* sock, int pipe_fd, int64_
perf__doh_reconnect(sock);
self->do_reconnect = false;
}
if (self->do_connect) {
perf__doh_connect(sock);
self->do_connect = false;
}
if (self->is_ready) {
// do nghttp2 I/O send to flush outstanding frames

View file

@ -175,6 +175,11 @@ static ssize_t perf__dot_recv(struct perf_net_socket* sock, void* buf, size_t le
case SSL_ERROR_WANT_READ:
errno = EAGAIN;
break;
#if OPENSSL_VERSION_NUMBER > 0x30000000L
case SSL_ERROR_SSL:
// OpenSSL 3.0+ returns this on EOF, treat everything as bad fd and reconnect
errno = EBADF;
#endif
case SSL_ERROR_SYSCALL:
switch (errno) {
case EBADF:

View file

@ -183,15 +183,27 @@ perf_tsigkey_t* perf_tsig_parsekey(const char* arg)
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC context failed to be created");
}
HMAC_CTX_init(tsigkey->hmac);
#else
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
if (!(tsigkey->hmac = HMAC_CTX_new())) {
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC context failed to be created");
}
#else
if (!(tsigkey->pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, 0, key, keylen))) {
perf_log_fatal("unable to setup TSIG, OpenSSL EVP PKEY failed to be created");
}
if (!(tsigkey->mdctx = EVP_MD_CTX_create())) {
perf_log_fatal("unable to setup TSIG, OpenSSL EVP MD context failed to be created");
}
if (!EVP_DigestSignInit(tsigkey->mdctx, 0, md, 0, tsigkey->pkey)) {
perf_log_fatal("unable to setup TSIG, OpenSSL EVP DigestSign init failed");
}
#endif
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (!HMAC_Init_ex(tsigkey->hmac, key, keylen, md, 0)) {
perf_log_fatal("unable to setup TSIG, OpenSSL HMAC init failed");
}
#endif
free(key);
@ -206,8 +218,11 @@ void perf_tsig_destroykey(perf_tsigkey_t** tsigkeyp)
#if OPENSSL_VERSION_NUMBER < 0x10100000L
HMAC_CTX_cleanup((*tsigkeyp)->hmac);
free((*tsigkeyp)->hmac);
#else
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
HMAC_CTX_free((*tsigkeyp)->hmac);
#else
EVP_MD_CTX_free((*tsigkeyp)->mdctx);
EVP_PKEY_free((*tsigkeyp)->pkey);
#endif
free(*tsigkeyp);
@ -222,20 +237,27 @@ perf_result_t perf_add_tsig(perf_buffer_t* packet, perf_tsigkey_t* tsigkey)
unsigned char* base;
size_t rdlen, totallen;
unsigned char tmpdata[512], md[EVP_MAX_MD_SIZE];
unsigned int mdlen;
perf_buffer_t tmp;
uint32_t now;
perf_result_t result;
now = time(NULL);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (!HMAC_Init_ex(tsigkey->hmac, 0, 0, 0, 0)) {
perf_log_fatal("adding TSIG: OpenSSL HMAC reinit failed");
}
if (!HMAC_Update(tsigkey->hmac, perf_buffer_base(packet), perf_buffer_usedlength(packet))) {
perf_log_fatal("adding TSIG: OpenSSL HMAC update failed");
}
#else
if (!EVP_DigestSignInit(tsigkey->mdctx, 0, 0, 0, 0)) {
perf_log_fatal("adding TSIG: OpenSSL EVP DigestSign reinit failed");
}
if (!EVP_DigestSignUpdate(tsigkey->mdctx, perf_buffer_base(packet), perf_buffer_usedlength(packet))) {
perf_log_fatal("adding TSIG: OpenSSL EVP DigestSign update failed");
}
#endif
/* Digest the TSIG record */
perf_buffer_init(&tmp, tmpdata, sizeof tmpdata);
@ -267,14 +289,23 @@ perf_result_t perf_add_tsig(perf_buffer_t* packet, perf_tsigkey_t* tsigkey)
perf_buffer_putuint16(&tmp, 0); /* error */
perf_buffer_putuint16(&tmp, 0); /* other length */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
unsigned int mdlen = sizeof(md);
if (!HMAC_Update(tsigkey->hmac, perf_buffer_base(&tmp), perf_buffer_usedlength(&tmp))) {
perf_log_fatal("adding TSIG: OpenSSL HMAC update failed");
}
mdlen = sizeof(md);
if (!HMAC_Final(tsigkey->hmac, md, &mdlen)) {
perf_log_fatal("adding TSIG: OpenSSL HMAC final failed");
}
#else
size_t mdlen = sizeof(md);
if (!EVP_DigestSignUpdate(tsigkey->mdctx, perf_buffer_base(&tmp), perf_buffer_usedlength(&tmp))) {
perf_log_fatal("adding TSIG: OpenSSL EVP DigestSign update failed");
}
if (!EVP_DigestSignFinal(tsigkey->mdctx, md, &mdlen)) {
perf_log_fatal("adding TSIG: OpenSSL EVP DigestSign final failed");
}
#endif
/* Make sure everything will fit */
rdlen = tsigkey->alglen + 18 + mdlen;

View file

@ -23,13 +23,23 @@
#ifndef PERF_TSIG_H
#define PERF_TSIG_H 1
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x30000000L
#include <openssl/hmac.h>
#else
#include <openssl/evp.h>
#endif
typedef struct perf_tsigkey {
char name[256];
size_t namelen, alglen;
const char* alg;
HMAC_CTX* hmac;
#if OPENSSL_VERSION_NUMBER < 0x30000000L
HMAC_CTX* hmac;
#else
EVP_PKEY* pkey;
EVP_MD_CTX* mdctx;
#endif
} perf_tsigkey_t;
perf_tsigkey_t* perf_tsig_parsekey(const char* arg);