diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dnsperf.1.in | 25 | ||||
-rw-r--r-- | src/dnsperf.c | 110 | ||||
-rw-r--r-- | src/net_doh.c | 8 | ||||
-rw-r--r-- | src/net_dot.c | 5 | ||||
-rw-r--r-- | src/tsig.c | 43 | ||||
-rw-r--r-- | src/tsig.h | 12 |
6 files changed, 186 insertions, 17 deletions
diff --git a/src/dnsperf.1.in b/src/dnsperf.1.in index c44a5f3..76b694e 100644 --- a/src/dnsperf.1.in +++ b/src/dnsperf.1.in @@ -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) diff --git a/src/dnsperf.c b/src/dnsperf.c index 90b7874..02bdd2c 100644 --- a/src/dnsperf.c +++ b/src/dnsperf.c @@ -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; } diff --git a/src/net_doh.c b/src/net_doh.c index dafd736..2caeab8 100644 --- a/src/net_doh.c +++ b/src/net_doh.c @@ -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 diff --git a/src/net_dot.c b/src/net_dot.c index 7e58a73..02b54e4 100644 --- a/src/net_dot.c +++ b/src/net_dot.c @@ -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: @@ -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; @@ -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); |