On Thu, Dec 11, 2025 at 03:00:52PM +0100, Dion Bosschieter wrote:
> Resolves issue: https://gitlab.com/libvirt/libvirt/-/issues/603
> Benchmarks showed that the amount of iifname jumps for each
> interface is the cause for this.
> Switched the nftables driver towards a vmap (verdict map) so we
> can have 1 rule that jumps to the correct root input/output chain
> per interface. Which improves throughput as when the number of
> interface check and jump rules increases the throughput decreases.
> The issue describes the interface matching works using the interface
> name and the majority of the effort is the strncpy, this commit also
> switches nftables to an interface_index compare instead.
> However, just using the interface_index is not enough, the amount of
> oif and iif jump rules causes quite a performance issue,
> the vmap instead solves this.
>
> Split rules into separate tables: "libvirt-nwfilter-ethernet" and
> "libvirt-nwfilter-other" to preserve existing ebip firewall behavior.
>
> Reworked chain logic for clarity with root -input/-output chains per
> interface. input in the VM interface is filtered in the -input
> chain(s), output out of the VM inteface is filtered in the -output
> chain(s).
>
> Sticked with 2 tables for compatibility reasons with eb iptables,
> unifying into 1 table will break users firewall definitions, which
> depend on being able to do accepts on ethernet rules
> (which currently get defined via ebtables) and additional filtering
> via the ip rules (which currently get defined via ip(6)tables).
> The nwfilter_nftables_driver keeps splitting the ethernet and
> non ethernet (other) rules in seperate tables
> “libvirt-nwfilter-ethernet” and “libvirt-nwfilter-other”.
>
> Rewrote chain logic, so it is easier to understand,
> input in the VM interface is filtered in the -input
> chain(s), output out of the VM inteface is filtered in the -output
> chain(s). -ethernet and -other table follow the same style and
> hook in the same way.
>
> Simplified conntrack handling: rules with accept+conntrack are
> duplicated to the opposite chain for symmetric behavior, to support
> the existing ebiptables logic.
>
> Firewall updates continue use tmp names for atomic replacement.
>
> Unsupported nwfilter features (for now):
> - STP filtering
> - Gratuitous ARP filtering
> - IPSets (potential future support via nft sets)
>
> Signed-off-by: Dion Bosschieter <[email protected]>
> ---
> po/POTFILES | 2 +
> src/nwfilter/meson.build | 1 +
> src/nwfilter/nwfilter_nftables_driver.c | 2374 +++++++++++++++++++++++
> src/nwfilter/nwfilter_nftables_driver.h | 28 +
> 4 files changed, 2405 insertions(+)
> create mode 100644 src/nwfilter/nwfilter_nftables_driver.c
> create mode 100644 src/nwfilter/nwfilter_nftables_driver.h
>
> diff --git a/po/POTFILES b/po/POTFILES
> index f0aad35c8c..51dae40cea 100644
> --- a/po/POTFILES
> +++ b/po/POTFILES
> @@ -162,6 +162,8 @@ src/nwfilter/nwfilter_driver.c
> src/nwfilter/nwfilter_ebiptables_driver.c
> src/nwfilter/nwfilter_gentech_driver.c
> src/nwfilter/nwfilter_learnipaddr.c
> +src/nwfilter/nwfilter_nftables_driver.c
> +src/nwfilter/nwfilter_tech_driver.c
> src/openvz/openvz_conf.c
> src/openvz/openvz_driver.c
> src/openvz/openvz_util.c
> diff --git a/src/nwfilter/meson.build b/src/nwfilter/meson.build
> index 9e8a4797c5..a94d72d570 100644
> --- a/src/nwfilter/meson.build
> +++ b/src/nwfilter/meson.build
> @@ -5,6 +5,7 @@ nwfilter_driver_sources = [
> 'nwfilter_dhcpsnoop.c',
> 'nwfilter_ebiptables_driver.c',
> 'nwfilter_learnipaddr.c',
> + 'nwfilter_nftables_driver.c',
> ]
>
> driver_source_files += files(nwfilter_driver_sources)
> diff --git a/src/nwfilter/nwfilter_nftables_driver.c
> b/src/nwfilter/nwfilter_nftables_driver.c
> new file mode 100644
> index 0000000000..36a6c63f22
> --- /dev/null
> +++ b/src/nwfilter/nwfilter_nftables_driver.c
> @@ -0,0 +1,2374 @@
> +/*
> + * nwfilter_nftables_driver.c: driver for nftables on tap devices
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <config.h>
> +
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "internal.h"
> +
> +#include "virbuffer.h"
> +#include "viralloc.h"
> +#include "virlog.h"
> +#include "virerror.h"
> +#include "nwfilter_conf.h"
> +#include "nwfilter_nftables_driver.h"
> +#include "nwfilter_tech_driver.h"
> +#include "virfile.h"
> +#include "configmake.h"
> +#include "virstring.h"
> +#include "virfirewall.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_NWFILTER
> +
> +/* define nftable root table */
> +#define NF_ETHERNET_TABLE "libvirt-nwfilter-ethernet"
> +#define NF_OTHER_TABLE "libvirt-nwfilter-other"
The network driver uses underscores instead of hyphens,
and with my other comment, lets call these
libvirt_nwfilter_ethernet
libvirt_nwfilter_inet
> +#define NF_COMMENT "{ comment \"this table is managed by libvirt\"; }"
Expand that to
#define NF_COMMENT \
"{ comment \"Managed by libvirt for network filters: " \
"https://libvirt.org/firewall.html#the-network-filter-driver\"; }"
(I've just proposed the equiv for the virtual network driver)
> +/* nftables counter can be enabled for firewalls transparency */
> +#ifndef NF_COUNTER
> +# define NF_COUNTER 0
> +#endif
This should be wired up as a 'enable_counters = 0|1'
setting in /etc/libvirt/nwfilter.conf
> +
> +/* define chains */
> +#define IN_CHAIN "postrouting"
> +#define OUT_CHAIN "prerouting"
> +#define FORWARD_CHAIN "forward"
> +
> +#define IN_IFMATCH "oif"
> +#define OUT_IFMATCH "iif"
> +
> +#define DEFAULT_POLICY "accept"
> +
> +#ifndef NF_TRACE
> +# define NF_TRACE 0
> +#endif
This should also be wired up as "enable_trace = 0|1'
setting in /etc/libvirt/nwfilter.conf
> +#if NF_TRACE
> +# define TRACE_SETTING "meta nftrace set 1;"
> +#else
> +# define TRACE_SETTING ""
> +#endif
> +
> +#define CHAINSETTINGS "{ }"
> +
> +#define VMAP_IN "vmap-oif"
> +#define VMAP_OUT "vmap-iif"
> +#define VMAPSETTINGS "{ type iface_index: verdict; }"
> +
> +#define ROOT_CHAINSETTINGS(chain, defaultPolicy) \
> + "{ type filter hook "chain" priority %d;" \
> + " policy "defaultPolicy"; "TRACE_SETTING" }"
> +
> +VIR_LOG_INIT("nwfilter.nwfilter_nftables_driver");
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|