From 66dc2b6d6b36a9714fad4a5a77ce65dbf8d52bec Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 25 Mar 2020 17:01:27 +0100 Subject: [PATCH] nts: rework NTS-KE retry interval Make the NTS-KE retry interval exponentially increasing, using a factor provided by the NKE session. Use shorter intervals when the server is refusing TCP connections or the connection is closed or timing out before the TLS handshake. --- nts_ke.h | 4 ++++ nts_ke_client.c | 8 ++++++++ nts_ke_client.h | 3 +++ nts_ke_session.c | 19 +++++++++++++++++++ nts_ke_session.h | 4 ++++ nts_ntp_client.c | 36 +++++++++++++++++++++++++++++------- test/unit/nts_ntp_client.c | 2 +- 7 files changed, 68 insertions(+), 8 deletions(-) diff --git a/nts_ke.h b/nts_ke.h index f00fcef..55cf47e 100644 --- a/nts_ke.h +++ b/nts_ke.h @@ -56,6 +56,10 @@ #define NKE_MAX_COOKIES 8 #define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH +#define NKE_RETRY_FACTOR2_CONNECT 4 +#define NKE_RETRY_FACTOR2_TLS 10 +#define NKE_MAX_RETRY_INTERVAL2 19 + typedef struct { int length; unsigned char key[NKE_MAX_KEY_LENGTH]; diff --git a/nts_ke_client.c b/nts_ke_client.c index 29421de..517b06c 100644 --- a/nts_ke_client.c +++ b/nts_ke_client.c @@ -393,3 +393,11 @@ NKC_GetNtsData(NKC_Instance inst, return i; } + +/* ================================================== */ + +int +NKC_GetRetryFactor(NKC_Instance inst) +{ + return NKSN_GetRetryFactor(inst->session); +} diff --git a/nts_ke_client.h b/nts_ke_client.h index 8d58775..7622252 100644 --- a/nts_ke_client.h +++ b/nts_ke_client.h @@ -55,4 +55,7 @@ extern int NKC_GetNtsData(NKC_Instance inst, NKE_Cookie *cookies, int *num_cookies, int max_cookies, IPSockAddr *ntp_address); +/* Get a factor to calculate retry interval (in log2 seconds) */ +extern int NKC_GetRetryFactor(NKC_Instance inst); + #endif diff --git a/nts_ke_session.c b/nts_ke_session.c index 224355f..bd1b001 100644 --- a/nts_ke_session.c +++ b/nts_ke_session.c @@ -75,6 +75,7 @@ struct NKSN_Instance_Record { char *label; gnutls_session_t tls_session; SCH_TimeoutID timeout_id; + int retry_factor; struct Message message; int new_message; @@ -382,6 +383,12 @@ handle_event(NKSN_Instance inst, int event) LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "TLS handshake with %s failed : %s", inst->label, gnutls_strerror(r)); stop_session(inst); + + /* Increase the retry interval if the handshake did not fail due + to the other end closing the connection */ + if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION) + inst->retry_factor = NKE_RETRY_FACTOR2_TLS; + return 0; } @@ -391,6 +398,8 @@ handle_event(NKSN_Instance inst, int event) return 0; } + inst->retry_factor = NKE_RETRY_FACTOR2_TLS; + if (DEBUG) { char *description = gnutls_session_get_desc(inst->tls_session); DEBUG_LOG("Handshake with %s completed %s", @@ -644,6 +653,7 @@ NKSN_CreateInstance(int server_mode, const char *server_name, inst->label = NULL; inst->tls_session = NULL; inst->timeout_id = 0; + inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT; return inst; } @@ -677,6 +687,7 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, inst->label = Strdup(label); inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst); + inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT; reset_message(&inst->message); inst->new_message = 0; @@ -783,3 +794,11 @@ NKSN_StopSession(NKSN_Instance inst) { stop_session(inst); } + +/* ================================================== */ + +int +NKSN_GetRetryFactor(NKSN_Instance inst) +{ + return inst->retry_factor; +} diff --git a/nts_ke_session.h b/nts_ke_session.h index bf1b469..49d95b4 100644 --- a/nts_ke_session.h +++ b/nts_ke_session.h @@ -80,4 +80,8 @@ extern int NKSN_IsStopped(NKSN_Instance inst); /* Stop the session */ extern void NKSN_StopSession(NKSN_Instance inst); +/* Get a factor to calculate retry interval (in log2 seconds) + based on the session state or how it was terminated */ +extern int NKSN_GetRetryFactor(NKSN_Instance inst); + #endif diff --git a/nts_ntp_client.c b/nts_ntp_client.c index 2feff7e..1e23f77 100644 --- a/nts_ntp_client.c +++ b/nts_ntp_client.c @@ -54,7 +54,8 @@ struct NNC_Instance_Record { SIV_Instance siv_s2c; NKC_Instance nke; - double last_nke_attempt; + int nke_attempts; + double next_nke_attempt; double last_nke_success; NKE_Cookie cookies[NTS_MAX_COOKIES]; int num_cookies; @@ -70,7 +71,8 @@ struct NNC_Instance_Record { static void reset_instance(NNC_Instance inst) { - inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL; + inst->nke_attempts = 0; + inst->next_nke_attempt = 0.0; inst->last_nke_success = 0.0; inst->num_cookies = 0; inst->cookie_index = 0; @@ -167,6 +169,21 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address) /* ================================================== */ +static void +update_next_nke_attempt(NNC_Instance inst, double now) +{ + int factor, interval; + + if (!inst->nke) + return; + + factor = NKC_GetRetryFactor(inst->nke); + interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2); + inst->next_nke_attempt = now + UTI_Log2ToDouble(interval); +} + +/* ================================================== */ + static int get_nke_data(NNC_Instance inst) { @@ -181,8 +198,9 @@ get_nke_data(NNC_Instance inst) now = SCH_GetLastEventMonoTime(); if (!inst->nke) { - if (now - inst->last_nke_attempt < MIN_NKE_RETRY_INTERVAL) { - DEBUG_LOG("Limiting NTS-KE request rate"); + if (now < inst->next_nke_attempt) { + DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)", + inst->next_nke_attempt - now); return 0; } @@ -194,12 +212,15 @@ get_nke_data(NNC_Instance inst) inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name); + inst->nke_attempts++; + update_next_nke_attempt(inst, now); + if (!NKC_Start(inst->nke)) return 0; - - inst->last_nke_attempt = now; } + update_next_nke_attempt(inst, now); + if (NKC_IsActive(inst->nke)) return 0; @@ -418,7 +439,8 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, /* At this point we know the client interoperates with the server. Allow a new NTS-KE session to be started as soon as the cookies run out. */ - inst->last_nke_attempt = -MIN_NKE_RETRY_INTERVAL; + inst->nke_attempts = 0; + inst->next_nke_attempt = 0.0; return 1; } diff --git a/test/unit/nts_ntp_client.c b/test/unit/nts_ntp_client.c index 06050f8..2503c2f 100644 --- a/test/unit/nts_ntp_client.c +++ b/test/unit/nts_ntp_client.c @@ -85,7 +85,7 @@ get_request(NNC_Instance inst) TEST_CHECK(!NNC_GenerateRequestAuth(inst, &packet, &info)); while (!NNC_PrepareForAuth(inst)) { - inst->last_nke_attempt = random() % 100000 - 50000; + inst->next_nke_attempt = SCH_GetLastEventMonoTime() + random() % 10 - 7; } TEST_CHECK(inst->num_cookies > 0);