Re: "nft list hooks" not showing the custom nat chains, and unexpected priority for nf_nat_ipv4_local_fn
"Kerin Millar" <[email protected]> Wed, 17 Jun 2026 21:15:36 +0100
| Newsgroups | gmane.comp.security.firewalls.netfilter.general |
|---|---|
| Message-ID | <[email protected]> |
On Mon, 15 Jun 2026, at 2:44 PM, Binarus wrote:
> At first, thank you very much for taking the time.
>
> On 13.06.2026 18:52, Kerin Millar wrote:
>> Hi,
>>=20
>> On Sat, 13 Jun 2026, at 5:16 PM, Binarus wrote:
>>> Dear all,
>>>
>>> I am still struggling with understanding the packet flow through the
>>> netfilter hooks and chains. Therefore, I have set up a very simple t=
est
>>> scenario. Before listing the ruleset, a few remarks:
>>>
>>> The following relates to netfilter 1.1.3 with kernel 6.12.90 on Debi=
an
>>> trixie. I had to modify the kernel to enable the hook listing, but
>>> that's the only change compared to a Debian trixie vanilla system.
>>>
>>> The test system has a ppp0 interface (default route / internet acces=
s)
>>> and a physical NIC interface (network 192.168.20.0/24, IP
>>> 192.168.20.249). The following ruleset is totally useless for any
>>> practical application, but I hope it's appropriate for investigation=
s:
>>>
>>> root@charon /etc/network # nft list ruleset
>>> table ip t_IP {
>>> chain output-route {
>>> type route hook output priority mangle; policy dro=
p;
>>> ip saddr 192.168.20.249 ip protocol icmp meta nftr=
ace set 1
>>> accept
>>> }
>>>
>>> chain output-filter {
>>> type filter hook output priority filter; policy dr=
op;
>>> ip saddr 192.168.20.249 ip protocol icmp meta nftr=
ace set 1
>>> accept
>>> }
>>>
>>> chain output-nat {
>>> type nat hook output priority 100; policy drop;
>>> ip saddr 192.168.20.249 ip protocol icmp meta nftr=
ace set 1
>>> accept
>>> }
>>>
>>> chain postrouting-filter {
>>> type filter hook postrouting priority filter; poli=
cy drop;
>>> ip saddr 192.168.20.249 ip protocol icmp meta nftr=
ace set 1
>>> accept
>>> }
>>>
>>> chain postrouting-nat {
>>> type nat hook postrouting priority srcnat; policy =
drop;
>>> ip saddr 192.168.20.249 ip protocol icmp meta nftr=
ace
>>> set 1
>>> oifname "ppp0" ip protocol { tcp, udp } masquerade=
to
>>> :61000-64999
>>> oifname "ppp0" ip protocol icmp masquerade
>>> accept
>>> }
>>> }
>>>
>>> The goal is to investigate the flow of an outbound icmp packet that =
is
>>> locally generated. I believe that the above ruleset includes all pai=
rs
>>> of hooks and chain types that exist for outbound local packets. I ha=
ve
>>> included the "nftrace" line in every chain because I wanted to see a=
ll
>>> chains the packet goes through even in case I had totally misunderst=
ood
>>> the order.
>>>
>>> But before the actual test, I looked at the output of "nft list hook=
s".
>>> 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_t=
ables]
>>> +0000000100 nf_nat_ipv4_out [nf_nat]
>>> +2147483647 nf_confirm [nf_conntrack]
>>> }
>>> }
>>>
>>> Now this leaves me totally clueless. Focusing on the output and the
>>> postrouting hook only (because I'd like to learn about these two fir=
st):
>>>
>>> 1.
>>> My own custom output-route, output-filter and postrouting-filter are
>>> listed. But neither the output-nat nor the postrouting-nat chain are
>>> listed; at their place, the respective internal netfilter functions =
are
>>> listed.
>>>
>>> I then have changed the priorities for these two chains (setting them
>>> to the shown value from the ruleset =C2=B11 each) in case the default
>>> priorities must not be used. But that did not change anything: The
>>> custom nat chains still were not listed in the output of "nft list
>>> hooks".
>>>
>>> Could somebody please explain where my misunderstanding is? In furth=
er
>>> tests, I have verified that these custom chains were indeed active, =
so
>>> why aren't they listed?
>>=20
>> It is enumerating the hook functions registered by the Netfilter core=
. The names of your nat chains are not shown because they are subordinat=
e to 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.
>
> Thank you very much for the explanation. Then that's obviously also th=
e=20
> reason why changing the priority didn't change anything.
>
> I suppose that netfilter still takes the chain priority into account=20
> when calling the subordinate chains. That is, the nat hook functions=20
> are always at the same priority, but in turn call the registered=20
> subordinate chains in the order of their priorities. Is this correct?
Provided the word, same, as taken as meaning "at a fixed, per-hook prior=
ity" then yes, this is entirely correct.
--=20
Kerin Millar