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

Dion Bosschieter posted 5 patches 22 hours ago
[PATCH v2 4/5] nwfilter: allow use of nftables nwfilter driver via nwfilter.conf
Posted by Dion Bosschieter 22 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 nftables to *filter_tech_drivers as an available driver option
for users to choose from.

Signed-off-by: Dion Bosschieter <dionbosschieter@gmail.com>
---
 po/POTFILES                                |   1 +
 src/conf/virnwfilterobj.h                  |  20 --
 src/nwfilter/libvirtd_nwfilter.aug         |  37 ++++
 src/nwfilter/meson.build                   |  37 ++++
 src/nwfilter/nwfilter.conf.in              |  26 +++
 src/nwfilter/nwfilter_driver.c             |  66 +++----
 src/nwfilter/nwfilter_driver_conf.c        | 210 +++++++++++++++++++++
 src/nwfilter/nwfilter_driver_conf.h        |  66 +++++++
 src/nwfilter/nwfilter_ebiptables_driver.h  |   2 +-
 src/nwfilter/nwfilter_gentech_driver.c     |  63 +++----
 src/nwfilter/nwfilter_gentech_driver.h     |   5 +-
 src/nwfilter/nwfilter_tech_driver.c        |   1 -
 src/nwfilter/nwfilter_tech_driver.h        |   1 +
 src/nwfilter/test_libvirtd_nwfilter.aug.in |   5 +
 14 files changed, 441 insertions(+), 99 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/po/POTFILES b/po/POTFILES
index 51dae40cea..69cdba22f9 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..96351d272a
--- /dev/null
+++ b/src/nwfilter/libvirtd_nwfilter.aug
@@ -0,0 +1,37 @@
+(* /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 ]
+
+   (* Each entry in the config is one of the following *)
+   let entry = str_entry "nwfilter_driver"
+   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..8ed5cbaa53
--- /dev/null
+++ b/src/nwfilter/nwfilter.conf.in
@@ -0,0 +1,26 @@
+# Master configuration file for the nwfilter driver.
+# All settings described here are optional - if omitted, sensible
+# defaults are used.
+
+# nwfilter_driver:
+#
+#   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 nwfilter_driver 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 there are active
+#   virtual networks *isn't* supported. The change will take place the
+#   next time that libvirtd/virtnetworkd is restarted - all existing
+#   firewalls remain, and have to be cleaned up manually
+#   reloaded using the new backend.)
+#
+#nwfilter_driver = "@FIREWALL_BACKEND@"
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 522cfda022..6b1100b675 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->firewallBackend) < 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..238e6771f5
--- /dev/null
+++ b/src/nwfilter/nwfilter_driver_conf.c
@@ -0,0 +1,210 @@
+/*
+ * 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, "nwfilter_driver", &fwBackendStr) < 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..e017149663
--- /dev/null
+++ b/src/nwfilter/nwfilter_driver_conf.h
@@ -0,0 +1,66 @@
+/*
+ * 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;
+};
+
+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.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..fdba2393f9 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, virFirewallBackend firewallBackend)
 {
     size_t i = 0;
-    VIR_DEBUG("Initializing NWFilter technology drivers");
-    while (filter_tech_drivers[i]) {
-        if (!(filter_tech_drivers[i]->flags & TECHDRV_FLAG_INITIALIZED))
+    VIR_DEBUG("Initializing NWFilter technology drivers, chosen '%s'",
+              virFirewallBackendTypeToString(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(firewallBackend)))
             filter_tech_drivers[i]->init(privileged);
-        i++;
     }
+
     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..59d76b852c 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, virFirewallBackend firewallBackend);
 void virNWFilterTechDriversShutdown(void);
 
 enum instCase {
diff --git a/src/nwfilter/nwfilter_tech_driver.c b/src/nwfilter/nwfilter_tech_driver.c
index 7b3edff8e6..ece8f90bf8 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 7a85c46339..103f33a3b8 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 {
diff --git a/src/nwfilter/test_libvirtd_nwfilter.aug.in b/src/nwfilter/test_libvirtd_nwfilter.aug.in
new file mode 100644
index 0000000000..a930f9b428
--- /dev/null
+++ b/src/nwfilter/test_libvirtd_nwfilter.aug.in
@@ -0,0 +1,5 @@
+module Test_libvirtd_nwfilter =
+  @CONFIG@
+
+  test Libvirtd_nwfilter.lns get conf =
+{ "nwfilter_driver" = "nftables" }
-- 
2.43.0