Skip to content

Add QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET - #49

Merged
masa-koz merged 1 commit into
seera-mainfrom
masa-koz/unconnected-socket-param-seera
Jul 29, 2026
Merged

Add QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET#49
masa-koz merged 1 commit into
seera-mainfrom
masa-koz/unconnected-socket-param-seera

Conversation

@masa-koz

Copy link
Copy Markdown

Description

A client connection's UDP socket is connected to the server's address, so the binding underneath it can only ever be shared by connections to that same address. Connections to different servers each need their own local port. On Windows that is a hard limit rather than a preference: the stack will not hand out the same local port to a second connected socket.

This adds QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET, a connection parameter that leaves the socket unconnected, so one binding — and therefore one local port — can carry connections to different remote addresses.

This is the parameterised form of the ShareBinding ? NULL : &Path->Route.RemoteAddress change in 55cfd6a. Rather than tying the behaviour to ShareBinding, it is opted into explicitly, so existing shared-binding users are unaffected.

How it works

The mode is passed through by omitting the remote address from CXPLAT_UDP_CONFIG, which is what already marks a binding as unconnected (Binding->Connected = UdpConfig->RemoteAddress == NULL) and makes QuicLibraryLookupBinding match on local port alone. The same is done on the rebind path taken when QUIC_PARAM_CONN_LOCAL_ADDRESS is set on a started connection.

Incoming packets on a client-owned binding are already matched by destination connection ID (QuicBindingDeliverDatagrams), so demultiplexing several connections off one unconnected socket needs nothing further.

Requirements

Two things are required of the application, and both are enforced.

QUIC_PARAM_CONN_SHARE_UDP_BINDING must be set. An unconnected socket receives datagrams from any remote address, so packets are matched to a connection by connection ID alone, and only a shared binding gives the connection a non-zero length source connection ID (QuicCidNewRandomSource rather than QuicCidNewNullSource). Setting the new parameter without one fails synchronously with QUIC_STATUS_INVALID_STATE.

A specific local address must be set, via QUIC_PARAM_CONN_LOCAL_ADDRESS. A connected socket takes its source address from the kernel when it is connected; an unconnected one does not, and the connection's first packet goes out before anything has been learned from the peer. Left wildcard, the send carries an unspecified address in IPV6_PKTINFO and the kernel rejects it:

sendmsg(555, {msg_name={... "::ffff:127.0.0.1" ...},
  msg_control=[{cmsg_level=SOL_IPV6, cmsg_type=IPV6_TCLASS, ...},
               {cmsg_level=SOL_IPV6, cmsg_type=IPV6_PKTINFO, cmsg_data=<all zero>}]
  }, 0) = -1 EINVAL (Invalid argument)

A server never hits this because it learns a concrete local address from the received packet's PKTINFO before it sends. Starting a connection with an unconnected socket and no local address, or a wildcard one, now fails the connection with QUIC_STATUS_INVALID_STATE rather than leaving it to fail obscurely in the datapath. The port may be left as 0 to let the stack choose one.

Notes

  • The parameter is under QUIC_API_ENABLE_PREVIEW_FEATURES and uses 0x05000025, the first free connection parameter id on this branch.
  • MsQuicConnection::SetUnconnectedUdpSocket() is added alongside SetShareUdpBinding().

Testing

  • Basic/WithFamilyArgs.ConnectUnconnectedSocket (v4 and v6) starts two listeners on different ports, connects to both from one local port, and checks both handshakes complete and both connections report the same local port.
  • Basic.UnconnectedSocketRequirements covers the two requirements: the parameter is rejected without a shared binding, and starting without a specific local address fails the connection with QUIC_STATUS_INVALID_STATE.
  • QuicTest_QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET is added to ParameterValidation.ValidateConnectionParam for the usual set/get validation.

*UnconnectedSocket*:*ConnectionParam*:*Basic* passes in full on Linux: 494 tests, no failures. No compiler warnings.

Worth naming: on Linux the functional test would also pass without this change, because the stack will reuse a local port across separate connected sockets with different destinations. It is a positive test that the mode works, not a Linux regression test. Windows is where the difference shows.

Documentation

docs/Settings.md gains the parameter in the connection parameter table, plus a section covering both requirements and how to put several connections on one local port.

A client connection's UDP socket is connected to the server's address, so
the binding underneath it can only ever be shared by connections to that
same address. Connections to different servers each need their own local
port, which on Windows is a hard limit rather than a preference: the stack
will not hand out the same local port to a second connected socket.

Add a connection parameter that leaves the socket unconnected, so one
binding, and therefore one local port, can carry connections to different
remote addresses. It is passed through to QuicLibraryGetBinding by omitting
the remote address, which is what already marks a binding as unconnected
and lets the lookup match on local port alone.

Two things are required of the application, and both are enforced.

QUIC_PARAM_CONN_SHARE_UDP_BINDING has to be set: an unconnected socket
receives datagrams from any remote address, so packets are matched to a
connection by connection ID alone, and only a shared binding gives the
connection a non-zero length source connection ID. Setting the new
parameter without one fails synchronously.

A specific local address has to be set. A connected socket takes its source
address from the kernel when it is connected; an unconnected one does not,
and the connection's first packet goes out before anything has been learned
from the peer. Left wildcard, the send carries an unspecified address in
IPV6_PKTINFO and the kernel rejects it with EINVAL against a v4-mapped
destination. Starting such a connection fails it with
QUIC_STATUS_INVALID_STATE instead.

Covered by QuicTestConnectUnconnectedSocket, which puts connections to two
different servers on one local port, and QuicTestUnconnectedSocketRequirements,
which covers both requirements above.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@masa-koz
masa-koz merged commit 937c965 into seera-main Jul 29, 2026
444 of 483 checks passed
@masa-koz
masa-koz deleted the masa-koz/unconnected-socket-param-seera branch July 29, 2026 15:01
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.85714% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/connection.c 82.85% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

1 participant