[Openvpn-devel] [S] Change in openvpn[master]: oob: advertise a connect_lifetime in the probe reply

"stipa \(Code Review\) via Openvpn-devel" <[email protected]> Tue, 28 Jul 2026 15:03:23 +0000
Newsgroups net.sourceforge.lists.openvpn-devel
Message-ID <e90033473a49df3e45f747641e2295b1457db3bd-EmailReplacePatchSet-HTML@gerrit.openvpn.net>
Attention is currently required from: stipa.

Hello plaisthos, 

I'd like you to reexamine a change. Please visit

    http://gerrit.openvpn.net/c/openvpn/+/1768?usp=email

to look at the new patch set (#9).


Change subject: oob: advertise a connect_lifetime in the probe reply
......................................................................

oob: advertise a connect_lifetime in the probe reply

A server answering an out-of-band SERVER_PROBE now also advertises a
connect_lifetime in the PROBE_REPLY: how long, in seconds, a probing client may
use the reply as the server's HARD_RESET when it starts a handshake.

The value is inferred, not configurable: the reply is only usable as that reset
while its stateless SYN-cookie is valid, i.e. the guaranteed cookie window of
~handshake_window (2 quantised buckets; see check_session_hmac_and_pkt_id), so
the server advertises exactly that. A tls-crypt-v2 probe (unwrapped via its
WKc) also sets OOB_PROBE_REPLY_FLAG_RESEND_WKC, telling the client to resend
the WKc when completing the handshake, since the server keeps no state.

Only the wire advertisement and its plumbing through oob_build_probe_reply()
are added here; the client side that acts on it follows.

Change-Id: Ib2b6c2246f9d9c0a505292ee8d879f714901ffae
Signed-off-by: Lev Stipakov <[email protected]>
---
M src/openvpn/mudp.c
M src/openvpn/oob.h
M tests/unit_tests/openvpn/test_oob.c
3 files changed, 26 insertions(+), 2 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/68/1768/9

diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 2f48fff..9a3acb5 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -243,11 +243,27 @@
         /* Out-of-band server probe. state->newbuf points at the TLV payload
          * (read_control_auth has stripped the opcode, session id and any
          * tls-auth/tls-crypt wrapping). Answer it without creating a session. */
+
+        /* A tls-crypt-v2 client must resend the WKc if it later uses this reply
+         * to start a handshake, since we keep no state. */
+        uint32_t reply_flags =
+            (verdict == VERDICT_VALID_OOB_WKC_V1) ? OOB_PROBE_REPLY_FLAG_RESEND_WKC : 0;
+
+        /* The client's third packet validates only while its SYN-cookie does, so
+         * the advertised connect_lifetime is inferred (not configurable): the
+         * guaranteed cookie window of ~handshake_window (2 quantised buckets; see
+         * check_session_hmac_and_pkt_id). Advertising more would make the client
+         * trust an already-expired cookie. (RFC: connect_lifetime is how long the
+         * server considers the reply valid.) */
+        int connect_lifetime = 2 * ((handwindow + 1) / 2);
+
         /* what we advertise; oob_build_probe_reply() adds the peer's session id */
         struct oob_probe_reply reply = {
             .priority = (uint16_t)m->top.options.server_probe_reply_priority,
             .weight = (uint16_t)m->top.options.server_probe_reply_weight,
             .max_latency_diff = (uint16_t)m->top.options.server_probe_reply_max_latency_diff,
+            .connect_lifetime = (uint16_t)connect_lifetime,
+            .flags = reply_flags,
         };
         if (!oob_build_probe_reply(&state->newbuf, (uint64_t)now, (uint64_t)handwindow,
                                    &state->peer_session_id, &reply))
diff --git a/src/openvpn/oob.h b/src/openvpn/oob.h
index a8652ea..e4af7cb 100644
--- a/src/openvpn/oob.h
+++ b/src/openvpn/oob.h
@@ -157,6 +157,12 @@
  */
 bool oob_timestamp_in_window(uint64_t probe_ts, uint64_t now, uint64_t window_secs);
 
+/* probe_reply flags (the reply TLV's 32-bit flags field) */
+/* bit 0: the client must resend the wrapped client key (via P_CONTROL_WKC_V1)
+ * when it completes the handshake started from this reply. Set
+ * by a tls-crypt-v2 server, which is stateless and discarded the WKc. */
+#define OOB_PROBE_REPLY_FLAG_RESEND_WKC 0x1
+
 /**
  * Process the TLV payload of a received SERVER_PROBE and decide whether to
  * answer it. Combines oob_server_probe_read() and oob_timestamp_in_window():
diff --git a/tests/unit_tests/openvpn/test_oob.c b/tests/unit_tests/openvpn/test_oob.c
index c8910b8..1f9f0ce 100644
--- a/tests/unit_tests/openvpn/test_oob.c
+++ b/tests/unit_tests/openvpn/test_oob.c
@@ -390,13 +390,15 @@
         .priority = 5,
         .weight = 50,
         .max_latency_diff = 25,
+        .connect_lifetime = 120,
+        .flags = OOB_PROBE_REPLY_FLAG_RESEND_WKC,
     };
     assert_true(oob_build_probe_reply(&buf, now, 30, &peer, &reply));
     assert_memory_equal(reply.peer_session_id.id, peer.id, SID_SIZE);
     assert_int_equal(reply.priority, 5);
     assert_int_equal(reply.weight, 50);
-    assert_int_equal(reply.connect_lifetime, 0);
-    assert_int_equal(reply.flags, 0);
+    assert_int_equal(reply.connect_lifetime, 120);
+    assert_int_equal(reply.flags, OOB_PROBE_REPLY_FLAG_RESEND_WKC);
     assert_int_equal(reply.max_latency_diff, 25);
 
     gc_free(&gc);

-- 
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1768?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: Ib2b6c2246f9d9c0a505292ee8d879f714901ffae
Gerrit-Change-Number: 1768
Gerrit-PatchSet: 9
Gerrit-Owner: stipa <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: stipa <[email protected]>

_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel