[PATCH v4 4/5] nwfilter: allow use of nftables nwfilter driver via nwfilter.conf

Dion Bosschieter posted 5 patches 15 hours ago
[PATCH v4 4/5] nwfilter: allow use of nftables nwfilter driver via nwfilter.conf
Posted by Dion Bosschieter 15 hours ago
Change the nwfilter driver loading mechanism to read from nwfilter.conf.
By default, it will use the nftables driver, which follows the
firewall_backend bridge driver config logic.

Added extra nwfilter config options "enable_trace" and "enable_counters"
to allow users to toggle nftable driver specific features.

Added nftables to *filter_tech_drivers as an available driver option
for users to choose from.

Signed-off-by: Dion Bosschieter <dionbosschieter@gmail.com>
---
 libvirt.spec.in                            |   7 +
 po/POTFILES                                |   1 +
 src/conf/virnwfilterobj.h                  |  20 --
 src/nwfilter/libvirtd_nwfilter.aug         |  45 +++++
 src/nwfilter/meson.build                   |  37 ++++
 src/nwfilter/nwfilter.conf.in              |  50 +++++
 src/nwfilter/nwfilter_driver.c             |  66 +++----
 src/nwfilter/nwfilter_driver_conf.c        | 214 +++++++++++++++++++++
 src/nwfilter/nwfilter_driver_conf.h        |  69 +++++++
 src/nwfilter/nwfilter_ebiptables_driver.c  |   4 +-
 src/nwfilter/nwfilter_ebiptables_driver.h  |   2 +-
 src/nwfilter/nwfilter_gentech_driver.c     |  65 +++----
 src/nwfilter/nwfilter_gentech_driver.h     |   5 +-
 src/nwfilter/nwfilter_nftables_driver.c    |  35 ++--
 src/nwfilter/nwfilter_tech_driver.c        |   1 -
 src/nwfilter/nwfilter_tech_driver.h        |   3 +-
 src/nwfilter/test_libvirtd_nwfilter.aug.in |   7 +
 17 files changed, 511 insertions(+), 120 deletions(-)
 create mode 100644 src/nwfilter/libvirtd_nwfilter.aug
 create mode 100644 src/nwfilter/nwfilter.conf.in
 create mode 100644 src/nwfilter/nwfilter_driver_conf.c
 create mode 100644 src/nwfilter/nwfilter_driver_conf.h
 create mode 100644 src/nwfilter/test_libvirtd_nwfilter.aug.in

diff --git a/libvirt.spec.in b/libvirt.spec.in
index 22c9975d9f..01c0df7cda 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -617,8 +617,12 @@ bridge capabilities.
 Summary: Nwfilter driver plugin for the libvirtd daemon
 Requires: libvirt-daemon-common = %{version}-%{release}
 Requires: libvirt-libs = %{version}-%{release}
+    %if %{prefer_nftables}
+Requires: nftables
+    %else
 Requires: iptables
 Requires: ebtables
+    %endif
 
 %description daemon-driver-nwfilter
 The nwfilter driver plugin for the libvirtd daemon, providing
@@ -2232,6 +2236,9 @@ exit 0
 %config(noreplace) %{_sysconfdir}/libvirt/virtnwfilterd.conf
 %{_datadir}/augeas/lenses/virtnwfilterd.aug
 %{_datadir}/augeas/lenses/tests/test_virtnwfilterd.aug
+%config(noreplace) %{_sysconfdir}/libvirt/nwfilter.conf
+%{_datadir}/augeas/lenses/libvirtd_nwfilter.aug
+%{_datadir}/augeas/lenses/tests/test_libvirtd_nwfilter.aug
 %{_unitdir}/virtnwfilterd.service
 %{_unitdir}/virtnwfilterd.socket
 %{_unitdir}/virtnwfilterd-ro.socket
diff --git a/po/POTFILES b/po/POTFILES
index 5d4c27ac00..b77229d9df 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -159,6 +159,7 @@ src/node_device/node_device_driver.c
 src/node_device/node_device_udev.c
 src/nwfilter/nwfilter_dhcpsnoop.c
 src/nwfilter/nwfilter_driver.c
+src/nwfilter/nwfilter_driver_conf.c
 src/nwfilter/nwfilter_ebiptables_driver.c
 src/nwfilter/nwfilter_gentech_driver.c
 src/nwfilter/nwfilter_learnipaddr.c
diff --git a/src/conf/virnwfilterobj.h b/src/conf/virnwfilterobj.h
index b67dc017c5..2c5df3e9ac 100644
--- a/src/conf/virnwfilterobj.h
+++ b/src/conf/virnwfilterobj.h
@@ -28,26 +28,6 @@ typedef struct _virNWFilterObj virNWFilterObj;
 
 typedef struct _virNWFilterObjList virNWFilterObjList;
 
-typedef struct _virNWFilterDriverState virNWFilterDriverState;
-struct _virNWFilterDriverState {
-    bool privileged;
-
-    /* pid file FD, ensures two copies of the driver can't use the same root */
-    int lockFD;
-
-    virNWFilterObjList *nwfilters;
-
-    virNWFilterBindingObjList *bindings;
-
-    char *stateDir;
-    char *configDir;
-    char *bindingDir;
-
-    /* Recursive. Hold for filter changes, instantiation or deletion */
-    virMutex updateLock;
-    bool updateLockInitialized;
-};
-
 virNWFilterDef *
 virNWFilterObjGetDef(virNWFilterObj *obj);
 
diff --git a/src/nwfilter/libvirtd_nwfilter.aug b/src/nwfilter/libvirtd_nwfilter.aug
new file mode 100644
index 0000000000..2a8475b680
--- /dev/null
+++ b/src/nwfilter/libvirtd_nwfilter.aug
@@ -0,0 +1,45 @@
+(* /etc/libvirt/nwfilter.conf *)
+
+module Libvirtd_nwfilter =
+   autoload xfm
+
+   let eol   = del /[ \t]*\n/ "\n"
+   let value_sep   = del /[ \t]*=[ \t]*/  " = "
+   let indent = del /[ \t]*/ ""
+
+   let array_sep  = del /,[ \t\n]*/ ", "
+   let array_start = del /\[[ \t\n]*/ "[ "
+   let array_end = del /\]/ "]"
+
+   let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\""
+   let bool_val = store /0|1/
+   let int_val = store /[0-9]+/
+   let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/ ""
+   let str_array_val = counter "el" . array_start . ( str_array_element . ( array_sep . str_array_element ) * ) ? . array_end
+
+   let str_entry       (kw:string) = [ key kw . value_sep . str_val ]
+   let bool_entry      (kw:string) = [ key kw . value_sep . bool_val ]
+   let int_entry       (kw:string) = [ key kw . value_sep . int_val ]
+   let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
+
+   (* Config entries *)
+   let firewall_backend_entry = str_entry "firewall_backend"
+   let trace_entry = bool_entry "enable_trace"
+   let counters_entry = bool_entry "enable_counters"
+
+   (* Each entry in the config is one of the following ... *)
+   let entry = firewall_backend_entry
+          | trace_entry
+          | counters_entry
+
+   let comment = [ label "#comment" . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
+   let empty = [ label "#empty" . eol ]
+
+   let record = indent . entry . eol
+
+   let lns = ( record | comment | empty ) *
+
+   let filter = incl "/etc/libvirt/nwfilter.conf"
+              . Util.stdexcl
+
+   let xfm = transform lns filter
diff --git a/src/nwfilter/meson.build b/src/nwfilter/meson.build
index a94d72d570..4d8abc7deb 100644
--- a/src/nwfilter/meson.build
+++ b/src/nwfilter/meson.build
@@ -1,5 +1,6 @@
 nwfilter_driver_sources = [
   'nwfilter_driver.c',
+  'nwfilter_driver_conf.c',
   'nwfilter_gentech_driver.c',
   'nwfilter_tech_driver.c',
   'nwfilter_dhcpsnoop.c',
@@ -46,6 +47,42 @@ if conf.has('WITH_NWFILTER')
     ],
   }
 
+  nwfilter_options_conf = configuration_data({
+    'FIREWALL_BACKEND_PRIORITY': ', '.join(firewall_backend_priority),
+    'FIREWALL_BACKEND': firewall_backend_priority[0],
+  })
+
+  nwfilter_conf = configure_file(
+    input: 'nwfilter.conf.in',
+    output: 'nwfilter.conf',
+    configuration: nwfilter_options_conf,
+  )
+
+  nwfilter_options_hack_conf = configuration_data({
+    'FIREWALL_BACKEND_PRIORITY': ', '.join(firewall_backend_priority),
+    'FIREWALL_BACKEND': firewall_backend_priority[0],
+    # This hack is necessary because the output file is going to be
+    # used as input for another configure_file() call later, which
+    # will take care of substituting @CONFIG@ with useful data
+    'CONFIG': '@CONFIG@',
+  })
+  test_libvirtd_network_aug_tmp = configure_file(
+    input: 'test_libvirtd_nwfilter.aug.in',
+    output: 'test_libvirtd_nwfilter.aug.tmp',
+    configuration: nwfilter_options_hack_conf,
+  )
+
+  virt_conf_files += nwfilter_conf
+  virt_aug_files += files('libvirtd_nwfilter.aug')
+  virt_test_aug_files += {
+    'name': 'test_libvirtd_nwfilter.aug',
+    'aug': test_libvirtd_network_aug_tmp,
+    'conf': nwfilter_conf,
+    'test_name': 'libvirtd_nwfilter',
+    'test_srcdir': meson.current_source_dir(),
+    'test_builddir': meson.current_build_dir(),
+  }
+
   virt_daemon_confs += {
     'name': 'virtnwfilterd',
   }
diff --git a/src/nwfilter/nwfilter.conf.in b/src/nwfilter/nwfilter.conf.in
new file mode 100644
index 0000000000..f535a34dae
--- /dev/null
+++ b/src/nwfilter/nwfilter.conf.in
@@ -0,0 +1,50 @@
+# Master configuration file for the nwfilter driver.
+# All settings described here are optional - if omitted, sensible
+# defaults are used.
+
+# firewall_backend:
+#
+#   determines which driver to use to setup nwfilter firewall rules
+#
+#   Supported settings:
+#
+#     iptables - use ebtables and iptables commands to construct the user
+#				 defined firewall
+#     nftables - use nft commands to construct the user defined firewall
+#
+#   If firewall_backend isn't configured, libvirt will choose the
+#   first available backend from the following list:
+#
+#     [@FIREWALL_BACKEND_PRIORITY@]
+#
+#   (NB: switching from one backend to another while libvirtd/virtnwfilterd
+#   has started *isn't* supported. The change will take place the
+#   next time that libvirtd/virtnwfilterd is restarted - all existing created
+#   firewall rules remain and have to be cleaned up manually.)
+#
+#firewall_backend = "@FIREWALL_BACKEND@"
+
+# enable_trace:
+#
+#   Enables packet tracing for debugging firewall rules
+#
+#   Supported backends:
+#
+#     nftables - Creates nftables tables root chains with "meta nftrace set 1;" setting
+#
+#   Possible values are 0 or 1. Default value is 0.
+#
+#enable_trace = 0
+
+# enable_counters:
+#
+#   Enables adding a counter to each rule, helpfull for counting
+#   how many tracking packets traversed each rule
+#
+#   Supported backends:
+#
+#     nftables - Adds the "counter" argument to nft rules nwfilter creates
+#
+#   Possible values are 0 or 1. Default value is 0.
+#
+#enable_counters = 0
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 522cfda022..66f5fa7c18 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -26,17 +26,15 @@
 
 #include "virgdbus.h"
 #include "virlog.h"
-
 #include "internal.h"
-
 #include "virerror.h"
 #include "datatypes.h"
 #include "nwfilter_driver.h"
+#include "nwfilter_driver_conf.h"
 #include "nwfilter_gentech_driver.h"
 #include "configmake.h"
 #include "virpidfile.h"
 #include "viraccessapicheck.h"
-
 #include "nwfilter_ipaddrmap.h"
 #include "nwfilter_dhcpsnoop.h"
 #include "nwfilter_learnipaddr.h"
@@ -159,9 +157,12 @@ virNWFilterTriggerRebuildImpl(void *opaque)
 static int
 nwfilterStateCleanupLocked(void)
 {
+    g_autoptr(virNWFilterDriverConfig) cfg = NULL;
     if (!driver)
         return -1;
 
+    cfg = virNWFilterDriverGetConfig(driver);
+
     if (driver->privileged) {
         virNWFilterConfLayerShutdown();
         virNWFilterDHCPSnoopShutdown();
@@ -171,11 +172,7 @@ nwfilterStateCleanupLocked(void)
         nwfilterDriverRemoveDBusMatches();
 
         if (driver->lockFD != -1)
-            virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
-
-        g_free(driver->stateDir);
-        g_free(driver->configDir);
-        g_free(driver->bindingDir);
+            virPidFileRelease(cfg->stateDir, "driver", driver->lockFD);
     }
 
     virObjectUnref(driver->bindings);
@@ -216,6 +213,7 @@ nwfilterStateInitialize(bool privileged,
                         void *opaque G_GNUC_UNUSED)
 {
     VIR_LOCK_GUARD lock = virLockGuardLock(&driverMutex);
+    virNWFilterDriverConfig *cfg;
     GDBusConnection *sysbus = NULL;
 
     if (root != NULL) {
@@ -236,6 +234,9 @@ nwfilterStateInitialize(bool privileged,
     driver->updateLockInitialized = true;
     driver->privileged = privileged;
 
+    if (!(driver->config = cfg = virNWFilterDriverConfigNew(privileged)))
+        goto error;
+
     if (!(driver->nwfilters = virNWFilterObjListNew()))
         goto error;
 
@@ -245,16 +246,8 @@ nwfilterStateInitialize(bool privileged,
     if (!privileged)
         return VIR_DRV_STATE_INIT_SKIPPED;
 
-    driver->stateDir = g_strdup(RUNSTATEDIR "/libvirt/nwfilter");
-
-    if (g_mkdir_with_parents(driver->stateDir, S_IRWXU) < 0) {
-        virReportSystemError(errno, _("cannot create state directory '%1$s'"),
-                             driver->stateDir);
-        goto error;
-    }
-
     if ((driver->lockFD =
-         virPidFileAcquire(driver->stateDir, "driver", getpid())) < 0)
+         virPidFileAcquire(cfg->stateDir, "driver", getpid())) < 0)
         goto error;
 
     if (virNWFilterIPAddrMapInit() < 0)
@@ -266,7 +259,7 @@ nwfilterStateInitialize(bool privileged,
     if (virNWFilterDHCPSnoopInit() < 0)
         goto error;
 
-    if (virNWFilterTechDriversInit(privileged) < 0)
+    if (virNWFilterTechDriversInit(privileged, cfg) < 0)
         goto error;
 
     if (virNWFilterConfLayerInit(virNWFilterTriggerRebuildImpl, driver) < 0)
@@ -279,26 +272,10 @@ nwfilterStateInitialize(bool privileged,
     if (sysbus)
         nwfilterDriverInstallDBusMatches(sysbus);
 
-    driver->configDir = g_strdup(SYSCONFDIR "/libvirt/nwfilter");
-
-    if (g_mkdir_with_parents(driver->configDir, S_IRWXU) < 0) {
-        virReportSystemError(errno, _("cannot create config directory '%1$s'"),
-                             driver->configDir);
-        goto error;
-    }
-
-    driver->bindingDir = g_strdup(RUNSTATEDIR "/libvirt/nwfilter-binding");
-
-    if (g_mkdir_with_parents(driver->bindingDir, S_IRWXU) < 0) {
-        virReportSystemError(errno, _("cannot create config directory '%1$s'"),
-                             driver->bindingDir);
+    if (virNWFilterObjListLoadAllConfigs(driver->nwfilters, cfg->configDir) < 0)
         goto error;
-    }
 
-    if (virNWFilterObjListLoadAllConfigs(driver->nwfilters, driver->configDir) < 0)
-        goto error;
-
-    if (virNWFilterBindingObjListLoadAllConfigs(driver->bindings, driver->bindingDir) < 0)
+    if (virNWFilterBindingObjListLoadAllConfigs(driver->bindings, cfg->bindingDir) < 0)
         goto error;
 
     if (virNWFilterBuildAll(driver, false) < 0)
@@ -320,19 +297,22 @@ nwfilterStateInitialize(bool privileged,
 static int
 nwfilterStateReload(void)
 {
+    g_autoptr(virNWFilterDriverConfig) cfg = NULL;
     if (!driver)
         return -1;
 
     if (!driver->privileged)
         return 0;
 
+    cfg = virNWFilterDriverGetConfig(driver);
+
     virNWFilterDHCPSnoopEnd(NULL);
     /* shut down all threads -- they will be restarted if necessary */
     virNWFilterLearnThreadsTerminate(true);
 
     VIR_WITH_MUTEX_LOCK_GUARD(&driverMutex) {
         VIR_WITH_MUTEX_LOCK_GUARD(&driver->updateLock) {
-            virNWFilterObjListLoadAllConfigs(driver->nwfilters, driver->configDir);
+            virNWFilterObjListLoadAllConfigs(driver->nwfilters, cfg->configDir);
         }
 
 
@@ -535,6 +515,7 @@ nwfilterDefineXMLFlags(virConnectPtr conn,
     virNWFilterObj *obj = NULL;
     virNWFilterDef *objdef;
     virNWFilterPtr nwfilter = NULL;
+    g_autoptr(virNWFilterDriverConfig) cfg = virNWFilterDriverGetConfig(driver);
 
     virCheckFlags(VIR_NWFILTER_DEFINE_VALIDATE, NULL);
 
@@ -558,7 +539,7 @@ nwfilterDefineXMLFlags(virConnectPtr conn,
     def = NULL;
     objdef = virNWFilterObjGetDef(obj);
 
-    if (virNWFilterSaveConfig(driver->configDir, objdef) < 0) {
+    if (virNWFilterSaveConfig(cfg->configDir, objdef) < 0) {
         virNWFilterObjListRemove(driver->nwfilters, obj);
         goto cleanup;
     }
@@ -588,6 +569,7 @@ nwfilterUndefine(virNWFilterPtr nwfilter)
     virNWFilterObj *obj;
     virNWFilterDef *def;
     int ret = -1;
+    g_autoptr(virNWFilterDriverConfig) cfg = virNWFilterDriverGetConfig(driver);
 
     VIR_WITH_MUTEX_LOCK_GUARD(&driver->updateLock) {
         if (!(obj = nwfilterObjFromNWFilter(nwfilter->uuid)))
@@ -604,7 +586,7 @@ nwfilterUndefine(virNWFilterPtr nwfilter)
             goto cleanup;
         }
 
-        if (virNWFilterDeleteDef(driver->configDir, def) < 0)
+        if (virNWFilterDeleteDef(cfg->configDir, def) < 0)
             goto cleanup;
 
         virNWFilterObjListRemove(driver->nwfilters, obj);
@@ -730,6 +712,7 @@ nwfilterBindingCreateXML(virConnectPtr conn,
     virNWFilterBindingDef *def;
     virNWFilterBindingObj *obj = NULL;
     virNWFilterBindingPtr ret = NULL;
+    g_autoptr(virNWFilterDriverConfig) cfg = virNWFilterDriverGetConfig(driver);
 
     virCheckFlags(VIR_NWFILTER_BINDING_CREATE_VALIDATE, NULL);
 
@@ -772,7 +755,7 @@ nwfilterBindingCreateXML(virConnectPtr conn,
         }
     }
 
-    virNWFilterBindingObjSave(obj, driver->bindingDir);
+    virNWFilterBindingObjSave(obj, cfg->bindingDir);
 
  cleanup:
     if (!obj)
@@ -799,6 +782,7 @@ nwfilterBindingDelete(virNWFilterBindingPtr binding)
     virNWFilterBindingObj *obj;
     virNWFilterBindingDef *def;
     int ret = -1;
+    g_autoptr(virNWFilterDriverConfig) cfg = virNWFilterDriverGetConfig(driver);
 
     obj = virNWFilterBindingObjListFindByPortDev(driver->bindings, binding->portdev);
     if (!obj) {
@@ -814,7 +798,7 @@ nwfilterBindingDelete(virNWFilterBindingPtr binding)
     VIR_WITH_MUTEX_LOCK_GUARD(&driver->updateLock) {
         virNWFilterTeardownFilter(def);
     }
-    virNWFilterBindingObjDelete(obj, driver->bindingDir);
+    virNWFilterBindingObjDelete(obj, cfg->bindingDir);
     virNWFilterBindingObjListRemove(driver->bindings, obj);
 
     ret = 0;
diff --git a/src/nwfilter/nwfilter_driver_conf.c b/src/nwfilter/nwfilter_driver_conf.c
new file mode 100644
index 0000000000..88ed10bfdb
--- /dev/null
+++ b/src/nwfilter/nwfilter_driver_conf.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * nwfilter_driver_conf.c: nwfilter.conf config file inspection
+ *
+ * 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 "configmake.h"
+#include "datatypes.h"
+#include "virlog.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virutil.h"
+#include "virfirewall.h" /* for binary names */
+#include "nwfilter_driver_conf.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+VIR_LOG_INIT("nwfilter.nwfilter_driver");
+
+static virClass *virNWFilterDriverConfigClass;
+static void virNWFilterDriverConfigDispose(void *obj);
+
+static int
+virNWFilterConfigOnceInit(void)
+{
+    if (!VIR_CLASS_NEW(virNWFilterDriverConfig, virClassForObject()))
+        return -1;
+
+    return 0;
+}
+
+
+VIR_ONCE_GLOBAL_INIT(virNWFilterConfig);
+
+
+static int
+virNWFilterLoadDriverConfig(virNWFilterDriverConfig *cfg,
+                            const char *filename)
+{
+    g_autoptr(virConf) conf = NULL;
+    g_autofree char *fwBackendStr = NULL;
+    bool fwBackendSelected = false;
+    size_t i;
+    int fwBackends[] = {
+        FIREWALL_BACKENDS
+    };
+    G_STATIC_ASSERT(G_N_ELEMENTS(fwBackends) > 0 &&
+                    G_N_ELEMENTS(fwBackends) <= VIR_FIREWALL_BACKEND_LAST);
+    int nFwBackends = G_N_ELEMENTS(fwBackends);
+
+    if (access(filename, R_OK) == 0) {
+
+        conf = virConfReadFile(filename, 0);
+        if (!conf)
+            return -1;
+
+        /* use virConfGetValue*(conf, ...) functions to read any settings into cfg */
+
+        if (virConfGetValueString(conf, "firewall_backend", &fwBackendStr) < 0)
+            return -1;
+        if (virConfGetValueBool(conf, "enable_trace", &cfg->firewallTracing) < 0)
+            return -1;
+        if (virConfGetValueBool(conf, "enable_counters", &cfg->ruleCounters) < 0)
+            return -1;
+
+        if (fwBackendStr) {
+            fwBackends[0] = virFirewallBackendTypeFromString(fwBackendStr);
+            nFwBackends = 1;
+
+            if (fwBackends[0] < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("unrecognized nwfilter_driver = '%1$s' set in nwfilter driver config file %2$s"),
+                               fwBackendStr, filename);
+                return -1;
+            }
+            VIR_DEBUG("nwfilter_driver setting requested from config file %s: '%s'",
+                      filename, virFirewallBackendTypeToString(fwBackends[0]));
+        }
+    }
+
+    for (i = 0; i < nFwBackends && !fwBackendSelected; i++) {
+        switch ((virFirewallBackend)fwBackends[i]) {
+        case VIR_FIREWALL_BACKEND_NONE:
+            fwBackendSelected = true;
+            break;
+
+        case VIR_FIREWALL_BACKEND_IPTABLES: {
+            g_autofree char *iptablesInPath = virFindFileInPath(IPTABLES);
+
+            if (iptablesInPath)
+                fwBackendSelected = true;
+            break;
+        }
+
+        case VIR_FIREWALL_BACKEND_NFTABLES: {
+            g_autofree char *nftablesInPath = virFindFileInPath(NFT);
+
+            if (nftablesInPath)
+                fwBackendSelected = true;
+            break;
+        }
+
+        case VIR_FIREWALL_BACKEND_PF: {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("unsupported nwfilter driver PF"));
+            return -1;
+        }
+
+        case VIR_FIREWALL_BACKEND_LAST:
+            virReportEnumRangeError(virFirewallBackend, fwBackends[i]);
+            return -1;
+        }
+
+        if (fwBackendSelected)
+            cfg->firewallBackend = fwBackends[i];
+    }
+
+    if (fwBackendSelected) {
+        VIR_INFO("using nwfilter_driver: '%s'",
+                 virFirewallBackendTypeToString(cfg->firewallBackend));
+        return 0;
+    } else if (fwBackendStr) {
+        /* the explicitly requested driver wasn't found - this is a failure */
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("requested nwfilter_driver '%1$s' is not available"),
+                       fwBackendStr);
+        return -1;
+    } else {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("could not find a usable nwfilter driver"));
+        return -1;
+    }
+}
+
+
+virNWFilterDriverConfig *
+virNWFilterDriverConfigNew(bool privileged)
+{
+    g_autoptr(virNWFilterDriverConfig) cfg = NULL;
+    g_autofree char *configdir = NULL;
+    g_autofree char *configfile = NULL;
+
+    if (virNWFilterConfigInitialize() < 0)
+        return NULL;
+
+    if (!(cfg = virObjectNew(virNWFilterDriverConfigClass)))
+        return NULL;
+
+    if (!privileged)
+        return g_steal_pointer(&cfg);
+
+    cfg->stateDir = g_strdup(RUNSTATEDIR "/libvirt/nwfilter");
+    cfg->configDir = g_strdup(SYSCONFDIR "/libvirt/nwfilter");
+    cfg->bindingDir = g_strdup(RUNSTATEDIR "/libvirt/nwfilter-binding");
+    configfile = g_strdup(SYSCONFDIR "/libvirt/nwfilter.conf");
+
+    if (virNWFilterLoadDriverConfig(cfg, configfile) < 0)
+        return NULL;
+
+    if (g_mkdir_with_parents(cfg->stateDir, S_IRWXU) < 0) {
+        virReportSystemError(errno, _("cannot create state directory '%1$s'"),
+                             cfg->stateDir);
+        return NULL;
+    }
+
+    if (g_mkdir_with_parents(cfg->configDir, S_IRWXU) < 0) {
+        virReportSystemError(errno, _("cannot create config directory '%1$s'"),
+                             cfg->configDir);
+        return NULL;
+    }
+
+    if (g_mkdir_with_parents(cfg->bindingDir, S_IRWXU) < 0) {
+        virReportSystemError(errno, _("cannot create config directory '%1$s'"),
+                             cfg->bindingDir);
+        return NULL;
+    }
+
+    return g_steal_pointer(&cfg);
+}
+
+
+virNWFilterDriverConfig *
+virNWFilterDriverGetConfig(virNWFilterDriverState *driver)
+{
+    return virObjectRef(driver->config);
+}
+
+
+static void
+virNWFilterDriverConfigDispose(void *obj)
+{
+    virNWFilterDriverConfig *cfg = obj;
+
+    g_free(cfg->stateDir);
+    g_free(cfg->configDir);
+    g_free(cfg->bindingDir);
+}
diff --git a/src/nwfilter/nwfilter_driver_conf.h b/src/nwfilter/nwfilter_driver_conf.h
new file mode 100644
index 0000000000..356b3cb71e
--- /dev/null
+++ b/src/nwfilter/nwfilter_driver_conf.h
@@ -0,0 +1,69 @@
+/*
+ * nwfilter_driver_conf.h: nwfilter driver state and config objects
+ *
+ * Copyright (C) 2006-2013 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include "libvirt_internal.h"
+#include "virthread.h"
+#include "virnwfilterobj.h"
+#include "virfirewall.h"
+#include "virinhibitor.h"
+
+typedef struct _virNWFilterDriverConfig virNWFilterDriverConfig;
+struct _virNWFilterDriverConfig {
+    virObject parent;
+
+    /* Immutable pointers, Immutable objects */
+    char *stateDir;
+    char *configDir;
+    char *bindingDir;
+
+    virFirewallBackend firewallBackend;
+
+    bool firewallTracing;
+    bool ruleCounters;
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNWFilterDriverConfig, virObjectUnref);
+
+/* Main driver state */
+typedef struct _virNWFilterDriverState virNWFilterDriverState;
+struct _virNWFilterDriverState {
+    bool privileged;
+
+    /* pid file FD, ensures two copies of the driver can't use the same root */
+    int lockFD;
+
+    virNWFilterObjList *nwfilters;
+
+    virNWFilterBindingObjList *bindings;
+
+    virNWFilterDriverConfig *config;
+
+    /* Recursive. Hold for filter changes, instantiation or deletion */
+    virMutex updateLock;
+    bool updateLockInitialized;
+};
+
+virNWFilterDriverConfig *
+virNWFilterDriverConfigNew(bool privileged);
+virNWFilterDriverConfig *
+virNWFilterDriverGetConfig(virNWFilterDriverState *driver);
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c
index 6769a2fb42..6b6b3cbd69 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.c
+++ b/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -78,7 +78,7 @@ VIR_LOG_INIT("nwfilter.nwfilter_ebiptables_driver");
 #define MATCH_PHYSDEV_OUT_OLD_FW  "-m", "physdev", "--physdev-out"
 
 static int ebtablesRemoveBasicRules(const char *ifname);
-static int ebiptablesDriverInit(bool privileged);
+static int ebiptablesDriverInit(bool privileged, virNWFilterDriverConfig *config G_GNUC_UNUSED);
 static void ebiptablesDriverShutdown(void);
 static int ebtablesCleanAll(const char *ifname);
 static int ebiptablesAllTeardown(const char *ifname);
@@ -3400,7 +3400,7 @@ virNWFilterTechDriver ebiptables_driver = {
 };
 
 static int
-ebiptablesDriverInit(bool privileged)
+ebiptablesDriverInit(bool privileged, virNWFilterDriverConfig *config G_GNUC_UNUSED)
 {
     if (!privileged)
         return 0;
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.h b/src/nwfilter/nwfilter_ebiptables_driver.h
index cb146f9f97..dfaea5af3d 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.h
+++ b/src/nwfilter/nwfilter_ebiptables_driver.h
@@ -27,6 +27,6 @@
 
 extern virNWFilterTechDriver ebiptables_driver;
 
-#define EBIPTABLES_DRIVER_ID "ebiptables"
+#define EBIPTABLES_DRIVER_ID "iptables"
 
 #define IPTABLES_MAX_COMMENT_LENGTH  256
diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c
index 1465734a54..29f80a8677 100644
--- a/src/nwfilter/nwfilter_gentech_driver.c
+++ b/src/nwfilter/nwfilter_gentech_driver.c
@@ -32,6 +32,7 @@
 #include "nwfilter_dhcpsnoop.h"
 #include "nwfilter_ipaddrmap.h"
 #include "nwfilter_learnipaddr.h"
+#include "nwfilter_nftables_driver.h"
 #include "virnetdev.h"
 
 #define VIR_FROM_THIS VIR_FROM_NWFILTER
@@ -48,18 +49,23 @@ static int _virNWFilterTeardownFilter(const char *ifname);
 
 static virNWFilterTechDriver *filter_tech_drivers[] = {
     &ebiptables_driver,
-    NULL
+    &nftables_driver,
 };
 
-int virNWFilterTechDriversInit(bool privileged)
+int virNWFilterTechDriversInit(bool privileged, virNWFilterDriverConfig *config)
 {
     size_t i = 0;
-    VIR_DEBUG("Initializing NWFilter technology drivers");
-    while (filter_tech_drivers[i]) {
-        if (!(filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
-            filter_tech_drivers[i]->init(privileged);
-        i++;
+    VIR_DEBUG("Initializing NWFilter technology drivers, chosen '%s'",
+              virFirewallBackendTypeToString(config->firewallBackend));
+
+    for (i = 0; i < G_N_ELEMENTS(filter_tech_drivers); i++) {
+        if (filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED)
+            continue;
+        if (STREQ(filter_tech_drivers[i]->name,
+            virFirewallBackendTypeToString(config->firewallBackend)))
+            filter_tech_drivers[i]->init(privileged, config);
     }
+
     return 0;
 }
 
@@ -67,25 +73,20 @@ int virNWFilterTechDriversInit(bool privileged)
 void virNWFilterTechDriversShutdown(void)
 {
     size_t i = 0;
-    while (filter_tech_drivers[i]) {
+    for (i = 0; i < G_N_ELEMENTS(filter_tech_drivers); i++) {
         if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
             filter_tech_drivers[i]->shutdown();
-        i++;
     }
 }
 
 
 static virNWFilterTechDriver *
-virNWFilterTechDriverForName(const char *name)
+virNWFilterInitializedTechDriver(void)
 {
     size_t i = 0;
-    while (filter_tech_drivers[i]) {
-        if (STREQ(filter_tech_drivers[i]->name, name)) {
-            if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED) == 0)
-                break;
+    for (i = 0; i < G_N_ELEMENTS(filter_tech_drivers); i++) {
+        if ((filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
             return filter_tech_drivers[i];
-        }
-        i++;
     }
     return NULL;
 }
@@ -617,7 +618,6 @@ virNWFilterInstantiateFilterUpdate(virNWFilterDriverState *driver,
                                    bool *foundNewFilter)
 {
     int rc = -1;
-    const char *drvname = EBIPTABLES_DRIVER_ID;
     virNWFilterTechDriver *techdriver;
     virNWFilterObj *obj;
     virNWFilterDef *filter;
@@ -625,12 +625,11 @@ virNWFilterInstantiateFilterUpdate(virNWFilterDriverState *driver,
     char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
     virNWFilterVarValue *ipaddr;
 
-    techdriver = virNWFilterTechDriverForName(drvname);
+    techdriver = virNWFilterInitializedTechDriver();
 
     if (!techdriver) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Could not get access to ACL tech driver '%1$s'"),
-                       drvname);
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not get access to ACL tech driver"));
         return -1;
     }
 
@@ -768,15 +767,13 @@ virNWFilterUpdateInstantiateFilter(virNWFilterDriverState *driver,
 static int
 virNWFilterRollbackUpdateFilter(virNWFilterBindingDef *binding)
 {
-    const char *drvname = EBIPTABLES_DRIVER_ID;
     int ifindex;
     virNWFilterTechDriver *techdriver;
 
-    techdriver = virNWFilterTechDriverForName(drvname);
+    techdriver = virNWFilterInitializedTechDriver();
     if (!techdriver) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Could not get access to ACL tech driver '%1$s'"),
-                       drvname);
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not get access to ACL tech driver"));
         return -1;
     }
 
@@ -793,15 +790,13 @@ virNWFilterRollbackUpdateFilter(virNWFilterBindingDef *binding)
 static int
 virNWFilterTearOldFilter(virNWFilterBindingDef *binding)
 {
-    const char *drvname = EBIPTABLES_DRIVER_ID;
     int ifindex;
     virNWFilterTechDriver *techdriver;
 
-    techdriver = virNWFilterTechDriverForName(drvname);
+    techdriver = virNWFilterInitializedTechDriver();
     if (!techdriver) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Could not get access to ACL tech driver '%1$s'"),
-                       drvname);
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not get access to ACL tech driver"));
         return -1;
     }
 
@@ -818,14 +813,12 @@ virNWFilterTearOldFilter(virNWFilterBindingDef *binding)
 static int
 _virNWFilterTeardownFilter(const char *ifname)
 {
-    const char *drvname = EBIPTABLES_DRIVER_ID;
     virNWFilterTechDriver *techdriver;
-    techdriver = virNWFilterTechDriverForName(drvname);
+    techdriver = virNWFilterInitializedTechDriver();
 
     if (!techdriver) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Could not get access to ACL tech driver '%1$s'"),
-                       drvname);
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not get access to ACL tech driver"));
         return -1;
     }
 
diff --git a/src/nwfilter/nwfilter_gentech_driver.h b/src/nwfilter/nwfilter_gentech_driver.h
index 946d5d3d56..0059ca6bdb 100644
--- a/src/nwfilter/nwfilter_gentech_driver.h
+++ b/src/nwfilter/nwfilter_gentech_driver.h
@@ -24,8 +24,11 @@
 
 #include "virnwfilterobj.h"
 #include "virnwfilterbindingdef.h"
+#include "nwfilter_driver_conf.h"
+#include "virfirewall.h"
 
-int virNWFilterTechDriversInit(bool privileged);
+
+int virNWFilterTechDriversInit(bool privileged, virNWFilterDriverConfig *config);
 void virNWFilterTechDriversShutdown(void);
 
 enum instCase {
diff --git a/src/nwfilter/nwfilter_nftables_driver.c b/src/nwfilter/nwfilter_nftables_driver.c
index 79feb91aac..bd2a4ee465 100644
--- a/src/nwfilter/nwfilter_nftables_driver.c
+++ b/src/nwfilter/nwfilter_nftables_driver.c
@@ -38,16 +38,20 @@
 
 #define VIR_FROM_THIS VIR_FROM_NWFILTER
 
-/* define nftable root table */
+/* define nftable root tables */
 #define NF_ETHERNET_TABLE "libvirt_nwfilter_ethernet"
 #define NF_INET_TABLE     "libvirt_nwfilter_inet"
+
 #define NF_COMMENT \
     "{ comment \"Managed by libvirt for network filters: " \
     "https://libvirt.org/firewall.html#the-network-filter-driver\"; }"
+
 /* nftables counter can be enabled for firewalls transparency */
-#ifndef NF_COUNTER
-# define NF_COUNTER       0
-#endif
+static bool counters_enabled;
+
+/* nftables tracing can be enabled for firewall debugging,
+* to find out where packets are flowing towards */
+static bool trace_enabled;
 
 /* define chains */
 #define IN_CHAIN          "postrouting"
@@ -67,14 +71,7 @@
 
 #define DEFAULT_POLICY    "accept"
 
-#ifndef NF_TRACE
-# define NF_TRACE         0
-#endif
-#if NF_TRACE
-# define TRACE_SETTING    "meta nftrace set 1;"
-#else
-# define TRACE_SETTING    ""
-#endif
+#define TRACE_SETTING    "meta nftrace set 1;"
 
 #define CHAINSETTINGS     "{ }"
 
@@ -84,7 +81,7 @@
 
 #define ROOT_CHAINSETTINGS(chain, defaultPolicy) \
     "{ type filter hook "chain" priority %d;" \
-    " policy "defaultPolicy"; "TRACE_SETTING" }"
+    " policy "defaultPolicy"; %s }"
 
 VIR_LOG_INIT("nwfilter.nwfilter_nftables_driver");
 
@@ -126,6 +123,7 @@ static void nftablesCreateTable(virFirewall *fw,
                                 const char *tableName)
 {
     virFirewallCmd *fwrule = NULL;
+    const char *traceSetting = trace_enabled ? TRACE_SETTING : "";
     int tablePriority = STREQ(tableName, NF_ETHERNET_TABLE) ? 0 : 1;
 
     /* define table */
@@ -144,12 +142,12 @@ static void nftablesCreateTable(virFirewall *fw,
                                tableName, IN_CHAIN, NULL);
     virFirewallCmdAddArgFormat(fw, fwrule,
                                ROOT_CHAINSETTINGS(IN_CHAIN, DEFAULT_POLICY),
-                               tablePriority);
+                               tablePriority, traceSetting);
     fwrule = virFirewallAddCmd(fw, layer, "add", "chain", "bridge",
                                tableName, OUT_CHAIN, NULL);
     virFirewallCmdAddArgFormat(fw, fwrule,
                                ROOT_CHAINSETTINGS(OUT_CHAIN, DEFAULT_POLICY),
-                               tablePriority);
+                               tablePriority, traceSetting);
 
     /* add the one jump rule based on the vmap */
     fwrule = virFirewallAddCmd(fw, layer, "add", "rule", "bridge", tableName,
@@ -1350,7 +1348,7 @@ nftablesCreateRuleInstance(virFirewall *fw,
             goto cleanup;
     }
 
-    if (NF_COUNTER)
+    if (counters_enabled)
         virFirewallCmdAddArg(fw, fwrule, "counter");
 
     /* specify the action for this rule */
@@ -2481,11 +2479,14 @@ nftablesDropAllRules(const char *ifname)
 }
 
 static int
-nftablesDriverInit(bool privileged)
+nftablesDriverInit(bool privileged, virNWFilterDriverConfig *config G_GNUC_UNUSED)
 {
     if (!privileged)
         return 0;
 
+    trace_enabled = config->firewallTracing;
+    counters_enabled = config->ruleCounters;
+
     nftables_driver.flags = TECHDRV_FLAG_INITIALIZED;
 
     return 0;
diff --git a/src/nwfilter/nwfilter_tech_driver.c b/src/nwfilter/nwfilter_tech_driver.c
index cfe016aef0..f337af05da 100644
--- a/src/nwfilter/nwfilter_tech_driver.c
+++ b/src/nwfilter/nwfilter_tech_driver.c
@@ -19,7 +19,6 @@
 #include <config.h>
 
 #include "nwfilter_tech_driver.h"
-#include "nwfilter_conf.h"
 
 #define VIR_FROM_THIS VIR_FROM_NWFILTER
 
diff --git a/src/nwfilter/nwfilter_tech_driver.h b/src/nwfilter/nwfilter_tech_driver.h
index 8f9d605529..e351b7a798 100644
--- a/src/nwfilter/nwfilter_tech_driver.h
+++ b/src/nwfilter/nwfilter_tech_driver.h
@@ -25,6 +25,7 @@
 
 #include "virnwfilterobj.h"
 #include "virstring.h"
+#include "nwfilter_driver_conf.h"
 
 typedef struct _virNWFilterRuleInst virNWFilterRuleInst;
 struct _virNWFilterRuleInst {
@@ -61,7 +62,7 @@ enum virNWFilterProtoIdx {
 
 #define virNWFilterUShortMapEntryIdx(IDX, ATT, VAL) [IDX] = { .attr = ATT, .val = VAL }
 
-typedef int (*virNWFilterTechDrvInit)(bool privileged);
+typedef int (*virNWFilterTechDrvInit)(bool privileged, virNWFilterDriverConfig *config);
 typedef void (*virNWFilterTechDrvShutdown)(void);
 
 typedef int (*virNWFilterRuleApplyNewRules)(const char *ifname,
diff --git a/src/nwfilter/test_libvirtd_nwfilter.aug.in b/src/nwfilter/test_libvirtd_nwfilter.aug.in
new file mode 100644
index 0000000000..e6e62550e4
--- /dev/null
+++ b/src/nwfilter/test_libvirtd_nwfilter.aug.in
@@ -0,0 +1,7 @@
+module Test_libvirtd_nwfilter =
+  @CONFIG@
+
+  test Libvirtd_nwfilter.lns get conf =
+{ "firewall_backend" = "nftables" }
+{ "enable_trace" = "0" }
+{ "enable_counters" = "0" }
-- 
2.43.0