2

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!

Henrik_er
  • 23
  • 1
  • 4

2 Answers2

1

You just have to add an additional condition to not match for this IP address, thus not performing NAT in the end:

iifname "br0" ip saddr != 192.168.22.5 udp dport 53 counter dnat to 192.168.22.5:53

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 (using snat) 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):

nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; policy accept; }'

the rule would be:

ip daddr 192.168.22.5 udp dport 53 counter masquerade

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

    ip saddr 192.168.22.0/24 ip daddr 192.168.22.5 udp dport 53 ct status dnat counter masquerade
    
  • With a recent enough kernel (>= 5.6) matching on the incoming interface (still while in postrouting) would be sufficient instead:

    iifname "br0" ip daddr 192.168.22.5 udp dport 53 ct status dnat counter masquerade
    
  • or else you can use marks in prerouting and postrouting, if they're not conflicting with other uses:

    prerouting:

    iifname "br0" ip saddr != 192.168.22.5 udp dport 53 counter set mark 1 dnat to 192.168.22.5:53
    

    postrouting:

    mark 1 counter masquerade
    

And finally, as DNS uses TCP as well as UDP, you should probably also apply all the same using tcp dport 53.

A.B
  • 9,037
  • 2
  • 19
  • 37
  • Thanks for a really through reply. It now solves the issues related to br0. However, I still cannot seem to properly pass traffic destined for 192.168.22.5 properly when it is coming from other adapters. It is NATed not "just routed", with the result I cannot see where the traffic is originating from on the DNS, it all appears to come from 192.168.22.1. How would I have to modify the setup below to only counter and NAT port 53 if it is not destined for 192.168.22.5? https://pastebin.com/FycjXefv – Henrik_er May 14 '20 at 19:06
  • To be clear, quoting from my answer: "If you want to affect only previously dnat-ed traffic from br0 **and not from elsewhere**, there are a few options". There are 3 choices to pick from, pick one (but choice 2 is probably not available). – A.B May 14 '20 at 19:24
0

Traffics originated from the host itself doesn't get matched by iifname $outgoing_interface (but iifname lo), so your concern doesn't exist.

Tom Yan
  • 715
  • 2
  • 9