systemd-resolved
binds to the lo
interface:
# ss -aunp src == 127.0.0.53 sport == 53
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=44157,fd=17))
This limits available routes even once net.ipv4.conf.br0.route_localnet=1
is applied to those set on the lo
interface:
$ ip -4 route show table all dev lo
broadcast 127.0.0.0 table local proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 table local proto kernel scope host src 127.0.0.1
local 127.0.0.1 table local proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 table local proto kernel scope link src 127.0.0.1
None will match.
The source address would need to be changed. While the rarely used type nat hook input
chain type would allow to change the source address before the application received it, it's already too late: this happens after routing was done and packet is already dropped. So stateful NAT can't handle this case.
Instead a proxy could be used for this (after removing all the specific nat settings). Here's an example using socat
. socat
not being a dedicated application, there are caveats especially for UDP.
TCP handling (OP forgot that DNS uses TCP too)
socat TCP4-LISTEN:53,bind=192.168.1.2,reuseaddr,fork TCP4:127.0.0.53:53
Can't bind to IN_ADDR_ANY
because 127.0.0.53:53 is already bound, so bind to OP's provided address (for the wrong reasons): 192.168.1.2. Beside this, it's quite simple.
UDP handling
socat -T 20 UDP4-LISTEN:53,bind=192.168.1.2,reuseaddr,fork UDP4:127.0.0.53:53
The 20s timeout is here because socat
can't be told to cease right after receiving the single UDP packet answer and would keep all the forked socat
commands accumulating over time.
While UDP doesn't have to bind to an address in this case, binding to an address using UDP avoids the caveat related to multi-homing and the need to use the IP_PKTINFO
socket option.