I have been searching, but cannot seem to find an answer to my specific issue. I have the following rule today under prerouting:
iifname "br0" udp dport 53 counter dnat to 192.168.22.5:53
However, I have one issue, the IP address 192.168.22.5 is also on "br0". So the question at hand is, how can I make nftables ignore this IP and allow it access port 53 on the net (this is my pihole), but redirect all other port 53 udp data to it?
Appreciate your replies!
You just have to add an additional condition to not match for this IP address, thus not performing NAT in the end:
BUT... if both client and DNS server are in the same LAN, then the reply would be done directly from the DNS server to the client. For example with a client at 192.168.22.101 being set to use google's public resolver (with all the involved privacy issues) you'd get this:
query: 192.168.22.101 to 8.8.8.8 -> routed and dnat-ed -> 192.168.22.5
reply: 192.168.22.5 -> bridged (or not seen at all by the router, in both cases) not seen by the ip family rules -> 192.168.22.101
But as 192.168.22.101 expects an answer fro 8.8.8.8, not from 192.168.22.5, it will fail.
So you must also snat the query. Any IP address making packets use the router is fine: router's internal IP (
masquerade
), because the router owns this IP, or any Internet IP (usingsnat
) because they are routed through the router. You could even imagine using some TEST-NET IP for this (eg: 192.0.2.2): the IP address will never be seen outside of the local network anyway. The drawback is that the DNS sever can't identify what IP made the query. Newer kernels (upcoming 5.7 or else 5.8) will have the equivalent of iptables's NETMAP available if this is really important.Let's use
masquerade
here for simplicity.So inside the equivalent postrouting chain which could be created with something similar to (please adapt to your own naming conventions):
the rule would be:
This won't affect any return traffic initiated from the DNS server, because its conntrack entry will not be in state NEW anymore and this nat rule won't be traversed.
If you want to affect only previously dnat-ed traffic from br0 and not from elsewhere, there are a few options:
check the source IP is from 192.168.22.0/24 and dnat-ed
With a recent enough kernel (>= 5.6) matching on the incoming interface (still while in postrouting) would be sufficient instead:
or else you can use marks in prerouting and postrouting, if they're not conflicting with other uses:
prerouting:
postrouting:
And finally, as DNS uses TCP as well as UDP, you should probably also apply all the same using
tcp dport 53
.Traffics originated from the host itself doesn't get matched by
iifname $outgoing_interface
(butiifname lo
), so your concern doesn't exist.