Skip to content

Fix UDP response source IP symmetry on multi-homed hosts#1739

Open
IngmarStein wants to merge 1 commit intoTechnitiumSoftware:masterfrom
IngmarStein:fix/udp-source-ip-symmetry
Open

Fix UDP response source IP symmetry on multi-homed hosts#1739
IngmarStein wants to merge 1 commit intoTechnitiumSoftware:masterfrom
IngmarStein:fix/udp-source-ip-symmetry

Conversation

@IngmarStein
Copy link
Contributor

This change enables the 'PacketInformation' socket option (IP_PKTINFO / IPV6_RECVPKTINFO) to capture the destination IP address of incoming DNS and DHCP UDP requests. The captured IPPacketInformation is then used to select the most appropriate socket for sending the response.

On multi-homed hosts, if the server is bound to multiple specific IP addresses, this ensures that the response is sent from the exact same IP address that received the request, preventing clients from discarding the response due to IP mismatch.

Summary:

  • Enabled PacketInformation socket options on UDP and DHCP listeners.
  • Captured IPPacketInformation using ReceiveMessageFromAsync.
  • Added logic to select a specifically-bound socket (if available) for the response to ensure source IP symmetry.
  • Updated SendToAsync calls to use ReadOnlyMemory for better performance.

@ShreyasZare
Copy link
Member

Thanks for the PR. If you configure DNS Server Local End Points to listen on all required IP addresses then this issue does not occur. The response exits the correct interface with this config. So, these changes do not make any difference.

For DHCP server case of relay agents, the relay agent and the end client is expected to be able to reach the DHCP server via proper routing. So, this issue does not occur here as the server is expected to be able to route to the packet properly.

@IngmarStein
Copy link
Contributor Author

IngmarStein commented Feb 19, 2026

Thanks for the PR. If you configure DNS Server Local End Points to listen on all required IP addresses then this issue does not occur. The response exits the correct interface with this config. So, these changes do not make any difference.

There's a difference: this change also works with [::] or 0.0.0.0 as local endpoints - it'll select the right IP also in that case.
Adding specific IPs as local endpoints has some consequence, for example this requires root permissions (for privileged) ports on macOS whereas 0.0.0.0 doesn't.

Oh and as far as I know, all other major DNS implementation (BIND, Unbound, PowerDNS, dnsdist, Knot, …) use this approach.

@ShreyasZare
Copy link
Member

Thanks for the PR. If you configure DNS Server Local End Points to listen on all required IP addresses then this issue does not occur. The response exits the correct interface with this config. So, these changes do not make any difference.

There's a difference: this change also works with [::] or 0.0.0.0 as local endpoints - it'll select the right IP also in that case. Adding specific IPs as local endpoints has some consequence, for example this requires root permissions (for privileged) ports on macOS whereas 0.0.0.0 doesn't.

The code is searching for socket that is bound to the address in packet info. So that requires you to configure DNS Server Local End Points. Also the PR description requires server to be bound to multiple specific IP addresses. So, when you configure the local end point to listen on multiple IP addresses, it already works without these changes.

@ShreyasZare
Copy link
Member

ShreyasZare commented Feb 19, 2026

The ProcessUdpRequestAsync is called independently for each udpListener socket in existing code. There is no point in searching the list of sockets to select the correct one since the correct one is always the udpListener since it the object that received the UDP packet.

@IngmarStein
Copy link
Contributor Author

IngmarStein commented Feb 19, 2026

Searching through the sockets is for completeness. The crucial part is setting the SocketOptionName.PacketInformation socket option. I'd be happy to reduce the PR to just that.

@ShreyasZare
Copy link
Member

Searching through the sockets is for completeness. The crucial part is setting the SocketOptionName.PacketInformation socket option. I'd be happy to reduce the PR to just that.

The PacketInformation is already being read with the udpListener.ReceiveMessageFromAsync call which is used for setting metadata for DNS request object. Setting the socket option explicitly is not required as its already working.

@IngmarStein IngmarStein force-pushed the fix/udp-source-ip-symmetry branch 2 times, most recently from d6be82b to bce3fe7 Compare February 19, 2026 14:03
@IngmarStein
Copy link
Contributor Author

So I think there are two concepts here:

  1. the IP addresses the DNS server listens on (the DNS Server Local End Points)
  2. the IP addresses the DNS server uses as source address of DNS replies

They are related, but not the same. If 1) is set to specific addresses, then 2) is already solved - the source address is the same as the matching endpoint. However, 2) can also be solved when 1) is set to ANY - we just need to set the correct source address.

https://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses has more details.

@ShreyasZare
Copy link
Member

Ya, this is an issue which would be good to solve to do away user needing to do additional config.

Thanks for the link. Will read it and get back.

This change enables the 'PacketInformation' socket option (IP_PKTINFO / IPV6_RECVPKTINFO)
to capture the destination IP address of incoming DNS and DHCP UDP requests.
The captured IPPacketInformation is then used to select the most appropriate
socket for sending the response.

On multi-homed hosts, if the server is bound to multiple specific IP addresses,
this ensures that the response is sent from the exact same IP address that
received the request, preventing clients from discarding the response due to
IP mismatch.

Summary:
- Enabled PacketInformation socket options on UDP and DHCP listeners.
- Captured IPPacketInformation using ReceiveMessageFromAsync.
- Added logic to select a specifically-bound socket (if available) for the response
  to ensure source IP symmetry.
- Updated SendToAsync calls to use ReadOnlyMemory<byte> for better performance.
@IngmarStein IngmarStein force-pushed the fix/udp-source-ip-symmetry branch from bce3fe7 to 34129ab Compare February 19, 2026 22:38
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.

2 participants

Comments