Skip to content

Add control channel support for server latency checks and mtu discovery#30

Open
schwabe wants to merge 1 commit into
masterfrom
schwabe/oob_contol_data
Open

Add control channel support for server latency checks and mtu discovery#30
schwabe wants to merge 1 commit into
masterfrom
schwabe/oob_contol_data

Conversation

@schwabe

@schwabe schwabe commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@rzvncj rzvncj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec looks good to me. I just had a few minor suggestions related to the text (accidentaly missing words, clarity).

Comment thread openvpn-wire-protocol.xml Outdated
Comment thread openvpn-wire-protocol.xml Outdated
Comment thread openvpn-wire-protocol.xml Outdated
Comment thread openvpn-wire-protocol.xml
Comment thread openvpn-wire-protocol.xml
@schwabe
schwabe force-pushed the schwabe/oob_contol_data branch 2 times, most recently from 98bee04 to 0e1ad6c Compare June 10, 2026 11:36

@rzvncj rzvncj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments!

Comment thread openvpn-wire-protocol.xml Outdated
The embedding follows the same format as normal P_CONTROL_V1 packets but without the fields required for reliability layer. I.e. a normal
packet without tls-auth or tls-crypt will still have the Session ID but not have the message packet id or acked message ids:
<artwork>
struct oob_ontrol_v1_no_auth {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Comment thread openvpn-wire-protocol.xml Outdated
</artwork>
Example for tls-crypt:
<artwork>
struct tlscrypt_control {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oob_tlscrypt_control ?

Comment thread openvpn-wire-protocol.xml
struct oob_ontrol_v1_no_auth {
uint opcode:5;
uint key_id:3;
uint64 own_session_id;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should state explicitly what goes into own_session_id and replay_packet_id since OOB packets don't belong to any established session.

Comment thread openvpn-wire-protocol.xml Outdated
select the server according to their weight value.
</t>
<t>
The cookie_lifetime value indicates how long (in seconds) the server considers the ping

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the logic of cookies in details? What is the cookie value here? Is it own_session_id from the outer packet? The TLV only carries client session id.

How does the server enforce cookie lifetime, does it keep per-probe state? But then we'll have exhaustion problem that cookie exists to avoid.

What exactly client sends when it skips the handshake? third packet of the three-way handshake - does it mean ACK?

With tls-crypt-v2, stateless server has no WKc when packet 3 arrives. Does packet 3 after probe-skip have to be P_CONTROL_WKC_V1?

@schwabe
schwabe force-pushed the schwabe/oob_contol_data branch from 0e1ad6c to 881a326 Compare June 11, 2026 13:25
Comment thread openvpn-wire-protocol.xml
The CONTROL_OOB_WKC_V1 variant will have the wrapped client key after the enc_oob_payload. The enc_oob_payload is the same as
the oob_payload in the previous two example, just with the required tls-crypt based encryption.
</t>
<t>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With some help of AI, I would reword this section as following:

The own_session_id MUST be freshly generated and random for each probe,
since the replay protection of the PROBE_REPLY relies on it being
unpredictable. The client MAY later reuse this session ID when
establishing a connection to the same server, and MUST reuse it if it
wants to skip the three-way handshake as described in the probe reply
section.

For tls-auth and tls-crypt the replay_packet_id is not tied to a
connection but is global state shared by all packets protected with the
same key. The client uses the same rules for OOB packets as for any
other control channel packet: replay_packet_id values MUST NOT be
reused and MUST NOT go backwards.

@schwabe schwabe Jun 12, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the AI stuff made it worse and is even getting things wrong. It sounds nice but is just not better at all. Let's try not to make our stuff worse by AI.

@schwabe
schwabe force-pushed the schwabe/oob_contol_data branch 2 times, most recently from 74dc013 to 9ab2ac4 Compare June 23, 2026 13:23
Comment thread openvpn-wire-protocol.xml
@schwabe
schwabe force-pushed the schwabe/oob_contol_data branch from 9ab2ac4 to 39553ee Compare June 24, 2026 13:02
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
@schwabe
schwabe force-pushed the schwabe/oob_contol_data branch from 39553ee to ff2013f Compare July 22, 2026 16:26
Comment thread openvpn-wire-protocol.xml
struct probe_reply {
int optional:1 = 0;
int type:15 = 0x201;
uint16 length = 18;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This length value does not seem to be correct. The minimum length is 8 + 2 + 2 + 2 +2 + 4= 20. Probably wasn't updated after added max_latency_diff?

Comment thread openvpn-wire-protocol.xml
These messages can also replace the typical three handshake messages. In that case the client and server
need to continue to session as if the CONTROL_HARD_RESET_CLIENT_V3/CONTROL_HARD_RESET_CLIENT_V2 and
CONTROL_HARD_RESET_SERVER_V2 had been exchanged. Especially they need to assume that the message-id
0 of the peer has been received and acknowledged.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"assume received and acknowledged" - read literally it suggests that no ACK for message-id 0 needs to be sent", but that cannot work. A packet without ACKs cannot carry the peer session-id as per spec:

uint64_t peer_session_id; [only if acked_pktid_len > 0]

(and per code, reliable_ack_parse()). That peer session-id is the server session-id from the PROBE_REPLY - for the stateless server, an HMAC cookie. On the shortcut the server recomputes the expected cookie, but without the echoed copy in the packet is has nothing to compare against, so it drops the packet. How about following wording:

These messages can also replace the typical three handshake messages. In
that case both peers continue the session as if the
CONTROL_HARD_RESET_CLIENT_V3/CONTROL_HARD_RESET_CLIENT_V2 and
CONTROL_HARD_RESET_SERVER_V2 had been exchanged: message-id 0 of the peer
counts as received, and the message-id and tls-auth/tls-crypt
replay_packet_id counters advance as if one packet had been sent in each
direction.

The client MUST still acknowledge the server's message-id 0 in its first
control packet, which carries the TLS ClientHello as message-id 1
(CONTROL_WKC_V1 if bit 0 of the probe reply flags is set). The
acknowledgment is required because the peer_session_id field is only
present in packets that acknowledge something, and it returns the server
session id from the PROBE_REPLY — the cookie a stateless server validates.
The client MUST also reuse the client session id and source IP and port
of the SERVER_PROBE.

Comment thread openvpn-wire-protocol.xml
request/response to be valid as part of a three-way handshake instead of
CONTROL_HARD_RESET_CLIENT_V3/CONTROL_HARD_RESET_CLIENT_V2 and CONTROL_HARD_RESET_SERVER_V2. If set to 0
or more than connect_lifetime seconds have passed the client must initiate a regular three-way handshake.
If the client can send from the same source IP and port and use the same client and server session ID, the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"use the same client and server session ID" - it doesn't say where client gets server session ID from. It is own_session_id of the PROBE_REPLY packet itself. The probe_reply TLV only echoes the client session id back (peer_session_id).

How about:

The server session ID is the own_session_id carried in the PROBE_REPLY packet header; the client MUST return it unchanged when skipping the handshake. A stateless server MAY derive this value as an HMAC over the client session id, the client's source IP and port, and a coarse timestamp — the same SYN-cookie mechanism used for the regular three-way handshake — in which case connect_lifetime corresponds to the validity window of that cookie and requires no per-client state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants