Attention is currently required from: ralf_lici, stipa.
Hello plaisthos, ralf_lici,
I'd like you to reexamine a change. Please visit
http://gerrit.openvpn.net/c/openvpn/+/1771?usp=email
to look at the new patch set (#27).
Change subject: oob: Fall back quickly when a probe-started handshake is ignored
......................................................................
oob: Fall back quickly when a probe-started handshake is ignored
If the server does not accept the third packet of a handshake started
from a probe reply, the client stalled for the full handshake_window
(~60s) before recovering.
In a normal handshake, no answer could mean the server is down, so
waiting is right. Here we know it is up, since it answered a probe a
moment ago -- so no answer means it did not accept the reply as a reset,
and waiting 60s is pointless. That happens when a load balancer sends
the probe and the handshake to different instances, when NAT changes the
source port the cookie is bound to, or when the server restarted and
rotated its session-id HMAC key.
Give such a session a short first-response deadline
(min(handshake_window, 5s)); once the server answers, tls_pre_decrypt()
restores the full window so a slow-but-working handshake is not cut off.
If no response arrives the session times out quickly and the client
restarts the attempt rather than retrying in place: the restart moves on
to the next resolved address or connection entry, so the address that
answered the probe is not retried this cycle (with --persist-remote-ip
the restart keeps it, as for any restart). A rejected reply therefore
costs a few seconds instead of ~60.
Change-Id: Icec4696ff263ab39ebab06e8d478395a5ddfaab9
Signed-off-by: Lev Stipakov <[email protected]>
---
M doc/man-sections/client-options.rst
M src/openvpn/ssl.c
M src/openvpn/ssl_common.h
3 files changed, 51 insertions(+), 2 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/71/1771/27
diff --git a/doc/man-sections/client-options.rst
b/doc/man-sections/client-options.rst
index 80f760f..e35de8c 100644
--- a/doc/man-sections/client-options.rst
+++ b/doc/man-sections/client-options.rst
@@ -660,7 +660,16 @@
- the server honours its reply only for a limited time (see
``--server-probe-reply``), so an unusually slow start-up, such as a
private key passphrase or token prompt, can outlast it; the client
- then falls back to a normal handshake.
+ then falls back to a normal handshake;
+ - a handshake started this way is given only a few seconds to draw a
+ response, rather than ``--hand-window``. If none arrives the client
+ logs a key negotiation timeout, restarts the attempt and moves on to
+ the next address or remote, so the address that answered the probe is
+ not retried during this cycle. The server answered a probe moments
+ earlier, so silence means it did not accept the reply as a reset,
+ which happens when a load balancer sends the probe and the handshake
+ to different instances, when NAT changes the source port, or when the
+ server rotated its session id key.
The handshake shortcut is not used on Windows while DCO is active.
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7d54130..8f5e0d1 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -2588,6 +2588,11 @@
return session_move_pre_start(session, ks, true);
}
+/* Seconds to wait for the server's first response to a probe-started handshake
+ * before giving up and falling back to a normal handshake. A couple of
+ * control-channel retransmits; capped at handshake_window by the caller. */
+#define OOB_PROBE_START_FALLBACK_SECS 5
+
bool
session_skip_to_pre_start_client(struct tls_session *session, const struct
session_id *client_sid,
const struct session_id *server_sid,
@@ -2640,6 +2645,10 @@
return false;
}
ks->state = S_PRE_START;
+
+ /* Fail fast if the server ignores it: wait seconds, not handshake_window.
+ * tls_pre_decrypt() restores the full window once the server answers. */
+ ks->oob_probe_start = true;
return true;
}
@@ -2880,12 +2889,31 @@
continue_tls_process = session_move_pre_start(session, ks, false);
}
+ /* Start the probe-started fallback clock only once our first control
packet
+ * is queued: opening the tun, plugin init and chroot all happen before
that
+ * and must not be counted against a deadline meant for the server's
reply. */
+ if (ks->oob_probe_start && !reliable_empty(ks->send_reliable))
+ {
+ const time_t deadline =
+ now + min_int(session->opt->handshake_window,
OOB_PROBE_START_FALLBACK_SECS);
+ if (ks->must_negotiate > deadline)
+ {
+ ks->must_negotiate = deadline;
+ }
+ }
+
/* Are we timed out on receive? */
if (now >= ks->must_negotiate && ks->state >= S_UNDEF && ks->state <
S_ACTIVE)
{
+ /* Report the window that actually applied: an unanswered probe-started
+ * handshake times out on the short fallback deadline, not
+ * handshake_window. */
+ int window = ks->oob_probe_start
+ ? min_int(session->opt->handshake_window,
OOB_PROBE_START_FALLBACK_SECS)
+ : session->opt->handshake_window;
msg(D_TLS_ERRORS,
"TLS Error: TLS key negotiation failed to occur within %d seconds
(check your network connectivity)",
- session->opt->handshake_window);
+ window);
goto error;
}
@@ -3997,6 +4025,16 @@
goto error;
}
+ /* First valid response to a probe-started handshake: the server accepted
it,
+ * so restore the normal negotiation window (it was shortened to fail fast
if
+ * the probe reply had been ignored). Only after the source address
matched,
+ * or any forged packet could re-arm the long window. */
+ if (ks->oob_probe_start)
+ {
+ ks->oob_probe_start = false;
+ ks->must_negotiate = now + session->opt->handshake_window;
+ }
+
/*
* Should we do a retransmit of all unacknowledged packets in
* the send buffer? This improves the start-up efficiency of the
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index f6392f9..905006c 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -227,6 +227,8 @@
time_t initial; /* when we created this session */
time_t established; /* when our state went S_ACTIVE */
time_t must_negotiate; /* key negotiation times out if not
finished before this time */
+ bool oob_probe_start; /* probe-started handshake:
must_negotiate is a short
+ * first-response deadline, not the
full window */
time_t must_die; /* this object is destroyed at this
time */
time_t peer_last_packet; /* Last time we received a packet
in this control session */
--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1771?usp=email
To unsubscribe, or for help writing mail filters, visit
http://gerrit.openvpn.net/settings?usp=email
Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: Icec4696ff263ab39ebab06e8d478395a5ddfaab9
Gerrit-Change-Number: 1771
Gerrit-PatchSet: 27
Gerrit-Owner: stipa <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-Reviewer: ralf_lici <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: ralf_lici <[email protected]>
Gerrit-Attention: stipa <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel