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 <dionbosschieter@gmail.com> --- 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 :|