Re: "nft list hooks" not showing the custom nat chains, and unexpected priority for nf_nat_ipv4_local_fn
"Kerin Millar" <[email protected]> Sat, 13 Jun 2026 17:52:17 +0100
| Newsgroups | gmane.comp.security.firewalls.netfilter.general |
|---|---|
| Message-ID | <[email protected]> |
Hi,
On Sat, 13 Jun 2026, at 5:16 PM, Binarus wrote:
> Dear all,
>
> I am still struggling with understanding the packet flow through the=20
> netfilter hooks and chains. Therefore, I have set up a very simple tes=
t=20
> scenario. Before listing the ruleset, a few remarks:
>
> The following relates to netfilter 1.1.3 with kernel 6.12.90 on Debian=20
> trixie. I had to modify the kernel to enable the hook listing, but=20
> that's the only change compared to a Debian trixie vanilla system.
>
> The test system has a ppp0 interface (default route / internet access)=20
> and a physical NIC interface (network 192.168.20.0/24, IP=20
> 192.168.20.249). The following ruleset is totally useless for any=20
> practical application, but I hope it's appropriate for investigations:
>
> root@charon /etc/network # nft list ruleset
> table ip t_IP {
> chain output-route {
> type route hook output priority mangle; policy drop;
> ip saddr 192.168.20.249 ip protocol icmp meta nftrace=
set 1
> accept
> }
>
> chain output-filter {
> type filter hook output priority filter; policy drop;
> ip saddr 192.168.20.249 ip protocol icmp meta nftrace=
set 1
> accept
> }
>
> chain output-nat {
> type nat hook output priority 100; policy drop;
> ip saddr 192.168.20.249 ip protocol icmp meta nftrace=
set 1
> accept
> }
>
> chain postrouting-filter {
> type filter hook postrouting priority filter; policy =
drop;
> ip saddr 192.168.20.249 ip protocol icmp meta nftrace=
set 1
> accept
> }
>
> chain postrouting-nat {
> type nat hook postrouting priority srcnat; policy dro=
p;
> ip saddr 192.168.20.249 ip protocol icmp meta nftrace=20
> set 1
> oifname "ppp0" ip protocol { tcp, udp } masquerade to=20
> :61000-64999
> oifname "ppp0" ip protocol icmp masquerade
> accept
> }
> }
>
> The goal is to investigate the flow of an outbound icmp packet that is=20
> locally generated. I believe that the above ruleset includes all pairs=20
> of hooks and chain types that exist for outbound local packets. I have=20
> included the "nftrace" line in every chain because I wanted to see all=20
> chains the packet goes through even in case I had totally misunderstoo=
d=20
> the order.
>
> But before the actual test, I looked at the output of "nft list hooks"=
.=20
> This is the result:
>
> root@charon /etc/network # nft list hooks
> family ip {
> hook prerouting {
> -0000000400 ipv4_conntrack_defrag [nf_defrag_ipv4]
> -0000000200 ipv4_conntrack_in [nf_conntrack]
> -0000000100 nf_nat_ipv4_pre_routing [nf_nat]
> }
> hook input {
> +0000000100 nf_nat_ipv4_local_in [nf_nat]
> +2147483647 nf_confirm [nf_conntrack]
> }
> hook output {
> -0000000400 ipv4_conntrack_defrag [nf_defrag_ipv4]
> -0000000200 ipv4_conntrack_local [nf_conntrack]
> -0000000150 chain ip t_IP output-route [nf_tables]
> -0000000100 nf_nat_ipv4_local_fn [nf_nat]
> 0000000000 chain ip t_IP output-filter [nf_tables]
> }
> hook postrouting {
> 0000000000 chain ip t_IP postrouting-filter [nf_tabl=
es]
> +0000000100 nf_nat_ipv4_out [nf_nat]
> +2147483647 nf_confirm [nf_conntrack]
> }
> }
>
> Now this leaves me totally clueless. Focusing on the output and the=20
> postrouting hook only (because I'd like to learn about these two first=
):
>
> 1.
> My own custom output-route, output-filter and postrouting-filter are=20
> listed. But neither the output-nat nor the postrouting-nat chain are=20
> listed; at their place, the respective internal netfilter functions ar=
e=20
> listed.
>
> I then have changed the priorities for these two chains (setting them=20
> to the shown value from the ruleset =C2=B11 each) in case the default=20
> priorities must not be used. But that did not change anything: The=20
> custom nat chains still were not listed in the output of "nft list=20
> hooks".
>
> Could somebody please explain where my misunderstanding is? In further=20
> tests, I have verified that these custom chains were indeed active, so=20
> why aren't they listed?
It is enumerating the hook functions registered by the Netfilter core. T=
he names of your nat chains are not shown because they are subordinate t=
o the nf_nat hook functions. In your case, the relevant ones are "nf_nat=
_ipv4_local_fn" for output, and "nf_nat_ipv4_out" for postrouting.
>
> 2.
> According to my research, the nat chain in the output hook is for=20
> source nat, not for destination nat, and therefore its priority should=20
> be 100, as given in the ruleset. The famous "netfilter hooks" graphics=20
> in the nftables wiki says the same, at least in my understanding.
>
> But "nft list hooks" says that the priority of nf_nat_ipv4_local_fn is=20
> -100, as if the nat chain at this hook would be for destination nat=20
> instead of source nat.
>
> Could somebody please explain which type of nat the nat chain at the=20
> output hook is meant for (snat or dnat), and if it is snat, why the=20
> priority is -100 instead of 100 as expected?
It is destination NAT (dstnat). To rewrite the source addresses of local=
ly generated packets, you would do it in your "postrouting-nat" chain in=
stead.
--
Kerin Millar