Why does no packet traverse the nat chain in the output or postrouting hook with this ruleset?
0
votes
1
answer
78
views
I have a machine with the network interface
enp0s3
which is assigned the IPv4 address 192.168.20.254
. Furthermore, on another machine there is a DNS server listening on the IPv4 address 192.168.20.10
. The network between the two machines works without issues, the machines can reach each other. On both machines, IPv6 is completely disabled via kernel parameter ipv6.disable=1
.
On the first machine, I have the following nftables ruleset:
root@charon /etc/network # nft list ruleset
table ip t_IP {
chain output-route {
type route hook output priority mangle; policy accept;
ip daddr 192.168.20.10 meta nftrace set 1
}
chain output-filter {
type filter hook output priority filter; policy accept;
ip daddr 192.168.20.10 meta nftrace set 1
}
chain output-nat {
type nat hook output priority 100; policy accept;
ip daddr 192.168.20.10 meta nftrace set 1
}
chain postrouting-filter {
type filter hook postrouting priority filter; policy accept;
ip daddr 192.168.20.10 meta nftrace set 1
}
chain postrouting-nat {
type nat hook postrouting priority srcnat; policy accept;
ip daddr 192.168.20.10 meta nftrace set 1
}
}
The purpose of this ruleset is to accept all packets no matter what, and to be able to trace all packets that originate at the machine through all chains that are part of the packet output path.
I have put meta nftrace set 1
in every chain because I don't know which the first chain is that the packets traverse. To avoid too much noise, tracing is enabled only for DNS packets (which here can easily be identified either by their destination IP address or their destination port; the above ruleset does the former).
nft monitor trace
shows the following output when I enter host blahblah
in a second terminal on the machine (I have inserted a blank line to add a bit of structure):
trace id b00605cc ip t_IP output-route packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 8386 ip length 67 udp sport 37348 udp dport 53 udp length 47 @th,64,96 0x69ea01000001000000000000
trace id b00605cc ip t_IP output-route rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id b00605cc ip t_IP output-route verdict continue
trace id b00605cc ip t_IP output-route policy accept
trace id b00605cc ip t_IP output-filter packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 8386 ip length 67 udp sport 37348 udp dport 53 udp length 47 @th,64,96 0x69ea01000001000000000000
trace id b00605cc ip t_IP output-filter rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id b00605cc ip t_IP output-filter verdict continue
trace id b00605cc ip t_IP output-filter policy accept
trace id b00605cc ip t_IP postrouting-filter packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 8386 ip length 67 udp sport 37348 udp dport 53 udp length 47 @th,64,96 0x69ea01000001000000000000
trace id b00605cc ip t_IP postrouting-filter rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id b00605cc ip t_IP postrouting-filter verdict continue
trace id b00605cc ip t_IP postrouting-filter policy accept
trace id 4e72ea91 ip t_IP output-route packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 42220 ip length 50 udp sport 44559 udp dport 53 udp length 30 @th,64,96 0x395901000001000000000000
trace id 4e72ea91 ip t_IP output-route rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id 4e72ea91 ip t_IP output-route verdict continue
trace id 4e72ea91 ip t_IP output-route policy accept
trace id 4e72ea91 ip t_IP output-filter packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 42220 ip length 50 udp sport 44559 udp dport 53 udp length 30 @th,64,96 0x395901000001000000000000
trace id 4e72ea91 ip t_IP output-filter rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id 4e72ea91 ip t_IP output-filter verdict continue
trace id 4e72ea91 ip t_IP output-filter policy accept
trace id 4e72ea91 ip t_IP postrouting-filter packet: oif "enp0s3" ip saddr 192.168.20.254 ip daddr 192.168.20.10 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 42220 ip length 50 udp sport 44559 udp dport 53 udp length 30 @th,64,96 0x395901000001000000000000
trace id 4e72ea91 ip t_IP postrouting-filter rule ip daddr 192.168.20.10 meta nftrace set 1 (verdict continue)
trace id 4e72ea91 ip t_IP postrouting-filter verdict continue
trace id 4e72ea91 ip t_IP postrouting-filter policy accept
The trace shows how two different packets flow through the chains. Both packets are typical DNS client packets that originate at the local machine (192.168.20.254
, random non-privileged source port) and go to the DNS server (192.168.20.10
, destination port 53
).
Each packet first reaches the output hook. At that hook, it first traverses the route
chain, then the filter
chain, according to the priorities these chains are registered at.
But no packet ever goes through the nat
chain at the output
hook. **Why does this not happen?**
Likewise, each packet, after having left the output
hook, reaches the postrouting
hook and traverses the filter
chain that is registered there. But no packet ever traverses the nat
chain that is also registered at the postrouting
hook. **Again, why is this not the case?**
The documentation (in section "Verdict Statement") says that an accept
verdict for a packet stops the evaluation of further rules in the current chain, but that the packet nonetheless traverses later chains that are registered at the same hook and all chains at later hooks, until it meets another verdict which is *really* final, e.g. a drop
verdict. This means that an accept
verdict is final only for the chain scope, but not for the whole hook scope or the global scope. In contrast, a drop
verdict is final and immediately stops *any* further evaluation.
Since every chain in the ruleset above has an accept
policy and no explicit verdicts, at least the first packet of every connection should go through the nat
chains at the output
or postrouting
hook. But that is not the case.
What am I missing?
Asked by Binarus
(3891 rep)
Feb 3, 2025, 08:48 PM
Last activity: Feb 4, 2025, 03:38 PM
Last activity: Feb 4, 2025, 03:38 PM