On 4/8/26 10:08 AM, Dion Bosschieter wrote:
Hi,
I'd like to propose switching from individual nft commands to nft -f in the nftables firewall backend — either as a new backend or as an extension of the existing one.
The main motivation is performance. In benchmarking, nft -f is about 46% faster:
Interesting - I've always assumed the difference would be much greater. We talked about doing that with the iptables backend of the network driver at some point many years ago. I don't remember if we decided against it for some concrete reason, or simply because nobody ever had sufficient motivation to do it. (I think maybe even someone did a proof of concept at some time?) One thing that I've started a few messages about wrt your nwfilter nftables backend (and then got interrupted in the middle, never sent it, and lost the message :-() is the idea of keeping track of rules that have been added so they can be properly removed before new rules are added; this is especially important when restarting the daemon after switching the backend from iptables to nftables or vice versa. This may seem unimportant ("just reboot the host to clean everything out"), but users with many guests running on their host will *not* be happy about needing to migrate, suspend, or shut off all their guests in order to reboot just because the new libvirt version has switched the default nwfilter backend from iptables to nftables! :-O. (I did notice you and Daniel conversing about it, but was just breezing past the message, though "I should come back to this!" to myself, but then completely and totally forgot :-/ Anyway, I digress. The reason I bring this is up is because in the case of iptables, you can remove a rule or a chain simply by repeating the command you used to *add* the rule, replacing "-A" or "-I" with "-D". nftables doesn't allow this though - while you can remove an entire table, or a chain, by regurgitating the same command with s/add/delete/, but you can't remove a rule that way. Instead you need to provide the table, chain and "handle to "nft delete rule". The handle can be retrieved from stdout when the nft add rule command is run with "-a". (it can also be parsed out of "nft -a list ruleset", but that would be even more complicated). So, for any rules that are in a self-contained chain, you can just "nft delete chain blah", but if there are any individual rules, you'll need to add them with nft -a add rule, keep track of the handle, and delete them with "nft delete rule $table $chain $handle" (or something like that - it's been awhile) .....aaaannnnnndddd now I've digressed *even further*!! What I've been working up to saying is that adding all the rules at once from a file may preclude retrieving the handles for those rules, and they may actually be essential. If so, maybe there could be a hybrid approach, where everything that's self-contained in a chain used only for this particular interface is added using nft -f and a file, while a few rules that are mixed in with other rules in a common chain could be added individually so their handles could be saved. Alternately, another possibility would be to skip exec'ing of the nft command completely, and directly use the same API that the nft command uses (libnftables) (firewalld also uses this API - via python bindings of course). You'd get the advantages of atomic operations, and also likely even better performance than nft -f. I think in the long run this would be much nicer to have (although I don't know how handles of individual rules are handled in the libnftables API when you add multiple rules at a time). So, I guess my response to your question is "Yes, but..." :-)
The following commands are run on the .args files in the nwfilter nftables v6 patch.
$ time for i in *.args; do n=$(echo $i | sed s/.args$//); sudo ./ reset-tables.sh; sudo sh -e $i; done real 0m8.335s user 0m0.320s sys 0m0.564s
$ time for i in *.output; do sudo ./reset-tables.sh skipvmap; sudo nft -f $i; done real 0m4.518s user 0m0.274s sys 0m0.498s
On top of that, loading a full ruleset via nft -f is atomic for the whole set of changes, which allows us to remove the rollback logic and removes the need for tmp rules.
One issue I see is that the current approach allows certain commands to fail silently, deleting something that doesn't exist won't abort the operation. That's not the case with nft -f, where a failure stops the whole load. One approach here is to only use nft -f for sections that don't contain ignore-errors commands, and run those separately as individual commands.
As described earlier: to handle the current non-atomic nature, we have a few extra commands in place — a temporary jump rule, delete vmap entry, add vmap entry. The tmp jump can be replaced by running nft -f instead.
I wonder what your opinions are about adding nft -f.
Regards,
Dion