Add QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET - #49
Merged
Conversation
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>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.RemoteAddresschange in 55cfd6a. Rather than tying the behaviour toShareBinding, 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 makesQuicLibraryLookupBindingmatch on local port alone. The same is done on the rebind path taken whenQUIC_PARAM_CONN_LOCAL_ADDRESSis 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_BINDINGmust 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 (QuicCidNewRandomSourcerather thanQuicCidNewNullSource). Setting the new parameter without one fails synchronously withQUIC_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 inIPV6_PKTINFOand the kernel rejects it:A server never hits this because it learns a concrete local address from the received packet's
PKTINFObefore it sends. Starting a connection with an unconnected socket and no local address, or a wildcard one, now fails the connection withQUIC_STATUS_INVALID_STATErather than leaving it to fail obscurely in the datapath. The port may be left as 0 to let the stack choose one.Notes
QUIC_API_ENABLE_PREVIEW_FEATURESand uses0x05000025, the first free connection parameter id on this branch.MsQuicConnection::SetUnconnectedUdpSocket()is added alongsideSetShareUdpBinding().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.UnconnectedSocketRequirementscovers the two requirements: the parameter is rejected without a shared binding, and starting without a specific local address fails the connection withQUIC_STATUS_INVALID_STATE.QuicTest_QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKETis added toParameterValidation.ValidateConnectionParamfor 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.mdgains the parameter in the connection parameter table, plus a section covering both requirements and how to put several connections on one local port.