Today I wanted to transparantly redirect the DNS requests coming at the output of a tunnel to a local caching DNS resolver. The caching DNS was listening only on the loopback as port 53 was already bound to other interfaces. That would be fairly simple on Linux:
echo 1 > /proc/sys/net/ipv4/ip_forward iptables -t nat -A PREROUTING -i tun0 -p udp --dport 53 -j DNAT --to-destination 127.0.0.1 iptables -A FORWARD -i tun0 -o lo -p udp --dport 53 -j ACCEPT
But… The kernel will refuse to route packets with the loopback as source or destination because this qualify as a martian packet. The solution was to enable the
route_localnet flag. As stated in the kernel documentation:
route_localnet – BOOLEAN: Do not consider loopback addresses as martian source or destination while routing. This enables the use of 127/8 for local routing purposes (default FALSE).
This is per interface. So I just had to enable this on the tunnel interface:
echo 1 > /proc/sys/net/ipv4/conf/tun0/route_localnet