RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1984040 - iptables ... (nf_tables): table `...' is incompatible, use 'nft' tool when using conntrack rules
Summary: iptables ... (nf_tables): table `...' is incompatible, use 'nft' tool when us...
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: iptables
Version: 8.3
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: beta
: ---
Assignee: Phil Sutter
QA Contact: qe-baseos-daemons
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2021-07-20 13:43 UTC by Andreas Karis
Modified: 2024-12-20 20:30 UTC (History)
3 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2021-07-20 16:41:51 UTC
Type: Bug
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Knowledge Base (Solution) 6198972 0 None None None 2021-07-20 13:52:23 UTC

Description Andreas Karis 2021-07-20 13:43:11 UTC
Description of problem:

This happens when injecting an iptables rule with -m state, then saving a ruleset with `nft list ruleset` and dumping it to a file, then reloading from the file with `nft -f`. iptables will complain that `table `filter` is incompatible.



Version-Release number of selected component (if applicable):
~~~
[root@rhel8 ~]# rpm -qa | egrep 'kernel|ipt|nft|fire'
kernel-tools-libs-4.18.0-305.7.1.el8_4.x86_64
python3-subscription-manager-rhsm-1.28.13-3.el8_4.x86_64
libnftnl-1.1.5-4.el8.x86_64
kernel-4.18.0-240.1.1.el8_3.x86_64
kernel-core-4.18.0-240.1.1.el8_3.x86_64
subscription-manager-1.28.13-3.el8_4.x86_64
crypto-policies-scripts-20210209-1.gitbfb6bed.el8_3.noarch
kernel-modules-4.18.0-305.7.1.el8_4.x86_64
iptables-libs-1.8.4-17.el8.x86_64
kernel-modules-4.18.0-240.1.1.el8_3.x86_64
iptables-services-1.8.4-17.el8.x86_64
libipt-1.6.1-8.el8.x86_64
dnf-plugin-subscription-manager-1.28.13-3.el8_4.x86_64
kernel-core-4.18.0-305.7.1.el8_4.x86_64
subscription-manager-cockpit-1.28.13-3.el8_4.noarch
iptables-1.8.4-17.el8.x86_64
iptables-ebtables-1.8.4-17.el8.x86_64
subscription-manager-rhsm-certificates-1.28.13-3.el8_4.x86_64
kernel-headers-4.18.0-305.7.1.el8_4.x86_64
kernel-4.18.0-305.7.1.el8_4.x86_64
kernel-tools-4.18.0-305.7.1.el8_4.x86_64
initscripts-10.00.15-1.el8.x86_64
nftables-0.9.3-18.el8.x86_64
~~~

How reproducible:


Steps to Reproduce:
~~~
[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# iptables -t filter -I FORWARD -o virbr0 --dst 192.168.122.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
[root@rhel8 ~]# iptables -t filter -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            192.168.122.0/24     state RELATED,ESTABLISHED

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# nft list ruleset > /etc/nftables/test2.nft
[root@rhel8 ~]# cat /etc/nftables/test2.nft
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# nft -f /etc/nftables/test2.nft
[root@rhel8 ~]# iptables -t filter -L
iptables v1.8.4 (nf_tables): table `filter' is incompatible, use 'nft' tool.

[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
		oifname "virbr0" @nh,128,24 12626042 ct state established,related counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
~~~

Compare this to a rule which does not track the conntrack state:
~~~
[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# iptables -t filter -I FORWARD -o virbr0 --dst 192.168.122.0/24 -j ACCEPT
[root@rhel8 ~]# iptables -t filter -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            192.168.122.0/24    

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# nft list ruleset > /etc/nftables/test2.nft
[root@rhel8 ~]# cat /etc/nftables/test2.nft
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# nft -f /etc/nftables/test2.nft
[root@rhel8 ~]#  iptables -t filter -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             192.168.122.0/24    
ACCEPT     all  --  anywhere             192.168.122.0/24    

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 counter packets 0 bytes 0 accept
		oifname "virbr0" @nh,128,24 12626042 counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
~~~

Actual results:


Expected results:


Additional info:

This impacts tools that use the iptables compatibility layers, such as podman.

Comment 1 Andreas Karis 2021-07-20 13:45:13 UTC
This is also described in the following Debian bug: 
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=970267

Comment 2 Phil Sutter 2021-07-20 14:14:46 UTC
(In reply to Andreas Karis from comment #0)
> This happens when injecting an iptables rule with -m state, then saving a
> ruleset with `nft list ruleset` and dumping it to a file, then reloading
> from the file with `nft -f`. iptables will complain that `table `filter` is
> incompatible.

That's a sure way to kill one's ruleset: iptables-nft uses xtables extensions
for most matches/targets. nft might be able to translate them using libxtables
but that's not guaranteed. In doubt it merely prints the extension's name in a
comment at rule end, so restoring such a ruleset would just drop the respective
matches/targets.

In this specific case, a translation is available. So restoring the ruleset
turns the xtables extension into a native nftables expression which
iptables-nft in turn isn't able to interpret correctly.

Is this just a case of wrong usage or are you relying upon this behaviour
somehow? In general, one should treat iptables-nft like it wasn't related to
nftables and use iptables-nft-save etc. Mixing the tools is rarely sane.

Comment 5 Andreas Karis 2021-07-20 14:59:49 UTC
Further clarification (hopefully) in the private comments above.

What I don't understand is this:
~~~
[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# iptables -t filter -I FORWARD -o virbr0 --dst 192.168.122.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
~~~

Hence, the rule translates cleanly to:
~~~
table ip filter {
	chain FORWARD {
		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
	}
}
~~~

And it can be shown:
~~~
[root@rhel8 ~]# iptables -t filter -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             192.168.122.0/24     state RELATED,ESTABLISHED

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 
~~~

Why does this break if I insert the rule with nft, even though the `nft list ruleset` looks exactly the same?
~~~
[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# nft list ruleset
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft add rule ip filter FORWARD oifname "virbr0" @nh,128,24 12626042 ct state related,established counter packets 0 bytes 0 accept
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" @nh,128,24 12626042 ct state established,related counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# iptables -t filter -L FORWARD
iptables v1.8.4 (nf_tables): chain `FORWARD' in table `filter' is incompatible, use 'nft' tool.
~~~

Something there must be happening under the hood which is invisible to the end user. Because if I compare that to the earlier output, `nft list ruleset` is exactly the same in both cases, but iptables can translate the rules in the first case, but not in the latter case.

Comment 6 Andreas Karis 2021-07-20 15:11:52 UTC
The same if I leave out the @nh... part:

[root@rhel8 ~]# nft flush ruleset
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]#  nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# iptables -t filter -L FORWARD
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
[root@rhel8 ~]#  nft add rule ip filter FORWARD oifname "virbr0" ct state related,established counter packets 0 bytes 0 accept
[root@rhel8 ~]# nft list ruleset
table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
		oifname "virbr0" ct state established,related counter packets 0 bytes 0 accept
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
[root@rhel8 ~]# iptables -t filter -L FORWARD
iptables v1.8.4 (nf_tables): chain `FORWARD' in table `filter' is incompatible, use 'nft' tool.

Comment 8 Phil Sutter 2021-07-20 15:26:55 UTC
(In reply to Andreas Karis from comment #5)
> Further clarification (hopefully) in the private comments above.
> 
> What I don't understand is this:
> ~~~
> [root@rhel8 ~]# nft flush ruleset
> [root@rhel8 ~]# iptables -t filter -I FORWARD -o virbr0 --dst
> 192.168.122.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
> [root@rhel8 ~]# nft list ruleset
> table ip filter {
> 	chain INPUT {
> 		type filter hook input priority filter; policy accept;
> 	}
> 
> 	chain FORWARD {
> 		type filter hook forward priority filter; policy accept;
> 		oifname "virbr0" @nh,128,24 12626042 ct state related,established counter
> packets 0 bytes 0 accept

Here we see an older nft tool that doesn't understand iptables-nft's optimized
prefix match yet. Hence why you see '@nh,128,24' (for "look at network header
offset 128bits length 24 bits").

The reasone why it breaks iptables after restoring with nft is because the 'ct
state' match you see above is actually a compat expression calling xt_CONNTRACK
extension. nft has xtables support built in and uses libxtables' xlate callback
to translate the extension into native nftables syntax. So effectively it
prints '-m state --state ESTABLISHED,RELATED' as 'ct state related,established'
although it is not the same (technically). When you restore the output, nft
creates a "real" nftables ct state expression. And iptables-nft can't handle
that, therefore the error message.

> Something there must be happening under the hood which is invisible to the
> end user. Because if I compare that to the earlier output, `nft list
> ruleset` is exactly the same in both cases, but iptables can translate the
> rules in the first case, but not in the latter case.


Yes, that's right. But it's an inherent dilemma: iptables-nft, in order to be
as compatible to legacy iptables as possible, has to call iptables extensions
in kernel for at least some matches/targets. nft in turn does not offer to call
those, so there's no syntax we could use to list those compat expressions
created by iptables-nft. Up until now there are but two possible outcomes: One
is the "# CHECKSUM fill" you see in comment 3, this is nft telling you there's
a compat expression calling an extension that doesn't have a translate callback
in libxtables. The other is the misleading 'ct state' output you see above. It
is nft telling you what the rule does, but still omits the fact that it is a
compat expression internally.

From a technical point of view there is no good solution to this. :(

Comment 9 Andreas Karis 2021-07-20 16:41:51 UTC
Hi,

Thank you so much for the detailed explanation. I think I'll have to do some further reading about this but I'll close this BZ as WON'T FIX and I will help the customer clean up their iptables rules and switch to iptables.service instead of nftables.service on system boot and see if that helps. Or other wise hunt down the service that's using nft instead of iptables-nft.

Thanks,

Andreas


Note You need to log in before you can comment on or make changes to this bug.