Skip to content

net_consomme: reply to forwarded UDP clients from the published port#3902

Open
benhillis wants to merge 1 commit into
microsoft:mainfrom
benhillis:user/benhillis/consomme-udp-reply-port
Open

net_consomme: reply to forwarded UDP clients from the published port#3902
benhillis wants to merge 1 commit into
microsoft:mainfrom
benhillis:user/benhillis/consomme-udp-reply-port

Conversation

@benhillis

Copy link
Copy Markdown
Member

Summary

Fixes microsoft/WSL#40999. UDP replies from a wslc container on a published port came back to the host client from a random ephemeral source port instead of the published port, so connected UDP clients (ENet, QUIC, DTLS, most game netcode) rejected them and timed out. TCP publishing was unaffected.

Root cause

wslc UDP port publishing routes through consomme (the wslrelay path is TCP only). When the container replies to a forwarded client, handle_udp resolves the destination back to the host client (a local address) but then falls through to get_or_insert, which binds a fresh per-connection ephemeral socket and sends the reply from it. The client, using a connected UDP socket, only accepts datagrams from the exact address it sent to (the published port), so the reply is dropped.

Inbound forwarding was fine because the listener socket is bound to the published port; only the return path picked the wrong socket.

Fix

In handle_udp, when a datagram's source port is published to the host and its destination is a local address, send it from the listener's host socket. The reply's source then matches the published port, matching Docker's behavior. GSO size is tracked on the listener the same way it is on per-connection sockets. Non-local (genuine guest-initiated outbound) flows are unaffected and continue to use per-connection sockets.

Testing

  • New unit test test_udp_reply_source_port: binds a listener, injects a guest datagram whose source is the published port and destination is a host client on loopback, and verifies the client receives the reply from the published host port and that no per-connection socket is created.
  • cargo test -p consomme: 100/100 pass.
  • cargo clippy -p consomme --all-targets and cargo fmt -p consomme -- --check clean.

Copilot AI review requested due to automatic review settings July 9, 2026 15:19
@benhillis benhillis requested a review from a team as a code owner July 9, 2026 15:19

Copilot AI 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.

Pull request overview

This PR fixes UDP port publishing behavior in net_consomme (consomme’s UDP forwarding path) so that replies from a guest service running on a published UDP port egress from the published host port, rather than from a per-connection ephemeral socket. This aligns behavior with connected UDP client expectations (e.g., QUIC/DTLS/game networking) and addresses microsoft/WSL#40999.

Changes:

  • Track UDP GSO state on UdpListener sockets (similar to existing per-connection tracking).
  • In handle_udp, when sending a packet from a published guest port to a host-local destination, send via the listener’s host socket to preserve the published source port.
  • Add a unit test validating the reply source port and ensuring no per-connection UDP socket is created for this reply path.

When a guest replied to a UDP client on a published port, consomme sent the
reply through a per-connection ephemeral socket rather than the listener's
host socket. The reply's source port therefore did not match the published
port. A connected UDP client only accepts datagrams from the exact address it
sent to, so the reply was dropped and the client timed out. This broke ENet,
QUIC, DTLS, and most game netcode running in wslc containers (microsoft/WSL#40999).

handle_udp now detects datagrams whose source port is published to the host and
destined for a local address, and sends them from the listener's host socket so
the reply's source matches the published port. Adds a unit test covering the
reply source port.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 9, 2026 15:28
@benhillis benhillis force-pushed the user/benhillis/consomme-udp-reply-port branch from 25fe4e2 to 72a9466 Compare July 9, 2026 15:29

Copilot AI 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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

dst_sock_addr.set_port(listener.host_addr.port());
}

// Guest reply from a published port to a local client: send from the listener's

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.

I am not sure this should be local only; wouldn't we want the same behavior for a reply to a remote client?

We should be able to structure this a bit differently as well to get rid of all this duplicated code. Maybe move all the shared fields from UdpListener/UdpConnection into a common structure that they both include so we can return a &mut to that and then run the same code

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.

wslc: UDP port publishing rewrites reply source port, breaks connected UDP sockets (ENet/QUIC/games)

3 participants