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 :|

Reply via email to