[Openvpn-devel] Use exponential backoff for caching in tls_authentication_status
Commit Message
The caching in tls_authentication_status broke the quick reaction to
authentication status in the code paths that did not do caching like
PUSH_REQUEST reply code path.
This patch introduces exponential backoff for the caching so we still
retain the quick reaction while still keeping the benefit of caching.
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
src/openvpn/multi.c | 3 +--
src/openvpn/push.c | 2 +-
src/openvpn/ssl.c | 2 +-
src/openvpn/ssl.h | 3 ---
src/openvpn/ssl_common.h | 5 ++++-
src/openvpn/ssl_verify.c | 25 +++++++++++++++++++++++--
src/openvpn/ssl_verify.h | 8 +++-----
7 files changed, 33 insertions(+), 15 deletions(-)
Comments
Hi,
On 10/05/2021 15:13, Arne Schwabe wrote:
> The caching in tls_authentication_status broke the quick reaction to
> authentication status in the code paths that did not do caching like
> PUSH_REQUEST reply code path.
>
> This patch introduces exponential backoff for the caching so we still
> retain the quick reaction while still keeping the benefit of caching.
>
> Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Very simple and nice patch that removes the static caching interval and
makes it dynamic, following an exp backoff.
Logic is very simple and various latency values are hardcoded in an
array - so no need for any math.
Tests reveal that the delay introduced with the previous patch (Return
cached result in tls_authentication_status) is indeed killed.
Should the deferred auth script/plugin take a bit longer, we will see
some delay being introduced, but still very reasonable.
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Bonus: if fixes a comment and a typ0.
Regards,
As discussed on IRC, this patch fixes most of the shortcomings in
the previous patch (9a430502077).
I still find these code paths very complicated, but after running
the server test side with added logging to see when the cache kicks
in and what sort of delay is induced, I think I can say "this makes
sense" - and it does not break any of the existing test scenarios,
which is good :-)
While still complicated, it actually simplifies the overall flow, as
there is no extra "latency" parameter anymore, which does magic things,
depending on where it's called from.
Also, I was slightly confused on the actual effect of the cache/delay -
this is really only relevant for the initial authentication (and not for
TLS renegotiation) and only while the auth control file status is not yet
known. So for all other code paths, and for "inotify" paths (triggered
check of the ACFs) this just makes the code more simple -> more good.
Your patch has been applied to the master branch.
commit d49df6bdde0592c9f795a2a260f6f04255b32303
Author: Arne Schwabe
Date: Mon May 10 15:13:56 2021 +0200
Use exponential backoff for caching in tls_authentication_status
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Message-Id: <20210510131356.968965-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg22327.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
--
kind regards,
Gert Doering
@@ -2596,8 +2596,7 @@ static const multi_client_connect_handler client_connect_handlers[] = {
static void
multi_connection_established(struct multi_context *m, struct multi_instance *mi)
{
- if (tls_authentication_status(mi->context.c2.tls_multi, TLS_MULTI_AUTH_STATUS_INTERVAL)
- != TLS_AUTHENTICATION_SUCCEEDED)
+ if (tls_authentication_status(mi->context.c2.tls_multi) != TLS_AUTHENTICATION_SUCCEEDED)
{
return;
}
@@ -864,7 +864,7 @@ process_incoming_push_request(struct context *c)
int ret = PUSH_MSG_ERROR;
- if (tls_authentication_status(c->c2.tls_multi, TLS_MULTI_AUTH_STATUS_INTERVAL) == TLS_AUTHENTICATION_FAILED
+ if (tls_authentication_status(c->c2.tls_multi) == TLS_AUTHENTICATION_FAILED
|| c->c2.tls_multi->multi_state == CAS_FAILED)
{
const char *client_reason = tls_client_reason(c->c2.tls_multi);
@@ -3133,7 +3133,7 @@ tls_multi_process(struct tls_multi *multi,
update_time();
- enum tls_auth_status tas = tls_authentication_status(multi, TLS_MULTI_AUTH_STATUS_INTERVAL);
+ enum tls_auth_status tas = tls_authentication_status(multi);
/*
* If lame duck session expires, kill it.
@@ -91,9 +91,6 @@
#define TLS_MULTI_HORIZON 2 /* call tls_multi_process frequently for n seconds after
* every packet sent/received action */
-/* Interval that tls_multi_process should call tls_authentication_status */
-#define TLS_MULTI_AUTH_STATUS_INTERVAL 10
-
/*
* Buffer sizes (also see mtu.h).
*/
@@ -570,10 +570,13 @@ struct tls_multi
char *locked_username;
struct cert_hash_set *locked_cert_hash_set;
- /* Time of last when we updated the cached state of
+ /** Time of last when we updated the cached state of
* tls_authentication_status deferred files */
time_t tas_cache_last_update;
+ /** The number of times we updated the cache */
+ unsigned int tas_cache_num_updates;
+
/*
* An error message to send to client on AUTH_FAILED
*/
@@ -1073,8 +1073,28 @@ key_state_test_auth_control_file(struct auth_deferred_status *ads, bool cached)
return ACF_DISABLED;
}
+/**
+ * The minimum times to have passed to update the cache. Older versions
+ * of OpenVPN had code path that did not do any caching, so we start
+ * with no caching (0) here as well to have the same super quick initial
+ * reaction.
+ */
+static time_t cache_intervals[] = {0, 0, 0, 0, 0, 1, 1, 2, 2, 4, 8};
+
+/**
+ * uses cache_intervals times to determine if we should update the
+ * cache.
+ */
+static bool
+tls_authentication_status_use_cache(struct tls_multi *multi)
+{
+ unsigned int idx = min_uint(multi->tas_cache_num_updates, SIZE(cache_intervals) - 1);
+ time_t latency = cache_intervals[idx];
+ return multi->tas_cache_last_update + latency >= now;
+}
+
enum tls_auth_status
-tls_authentication_status(struct tls_multi *multi, const int latency)
+tls_authentication_status(struct tls_multi *multi)
{
bool deferred = false;
@@ -1087,7 +1107,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency)
/* at least one key already failed authentication */
bool failed_auth = false;
- bool cached = multi->tas_cache_last_update + latency >= now;
+ bool cached = tls_authentication_status_use_cache(multi);
for (int i = 0; i < KEY_SCAN_SIZE; ++i)
{
@@ -1143,6 +1163,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency)
if (!cached)
{
multi->tas_cache_last_update = now;
+ multi->tas_cache_num_updates++;
}
#if 0
@@ -75,7 +75,7 @@ enum tls_auth_status
/**
* Return current session authentication state of the tls_multi structure
* This will return TLS_AUTHENTICATION_SUCCEEDED only if the session is
- * fully authenicated, i.e. VPN traffic is allowed over it.
+ * fully authenticated, i.e. VPN traffic is allowed over it.
*
* Checks the status of all active keys and checks if the deferred
* authentication has succeeded.
@@ -84,15 +84,13 @@ enum tls_auth_status
* from KS_AUTH_DEFERRED to KS_AUTH_FALSE/KS_AUTH_TRUE if the deferred
* authentication has succeeded after last call.
*
- * @param latency if not null, return TLS_AUTHENTICATION_UNDEFINED if
- * the last call for this multi struct has been less
- * than latency seconds ago
+ *
* @param multi the tls_multi struct to operate on
*
* @return Current authentication status of the tls_multi
*/
enum tls_auth_status
-tls_authentication_status(struct tls_multi *multi, const int latency);
+tls_authentication_status(struct tls_multi *multi);
/** Check whether the \a ks \c key_state has finished the key exchange part
* of the OpenVPN hand shake. This is that the key_method_2read/write