edecca
From 477f1de37ff7c911048d8d5e7f7de32f12d30b5d Mon Sep 17 00:00:00 2001
edecca
Message-Id: <477f1de37ff7c911048d8d5e7f7de32f12d30b5d@dist-git>
edecca
From: Laine Stump <laine@laine.org>
edecca
Date: Fri, 1 Feb 2019 20:29:29 -0500
edecca
Subject: [PATCH] util: new virFirewallD APIs + docs
edecca
MIME-Version: 1.0
edecca
Content-Type: text/plain; charset=UTF-8
edecca
Content-Transfer-Encoding: 8bit
edecca
edecca
virFirewallDGetBackend() reports whether firewalld is currently using
edecca
an iptables or an nftables backend.
edecca
edecca
virFirewallDGetVersion() learns the version of the firewalld running
edecca
on this system and returns it as 1000000*major + 1000*minor + micro.
edecca
edecca
virFirewallDGetZones() gets a list of all currently active firewalld
edecca
zones.
edecca
edecca
virFirewallDInterfaceSetZone() sets the firewalld zone of the given
edecca
interface.
edecca
edecca
virFirewallDZoneExists() can be used to learn whether or not a
edecca
particular zone is present and active in firewalld.
edecca
edecca
Signed-off-by: Laine Stump <laine@laine.org>
edecca
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
edecca
(cherry picked from commit 3bba4825c291e51a8cd4d497d6e919ac2ee96ff0)
edecca
edecca
Conflicts: src/util/virfirewalld.c - had to remove VIR_AUTO_PTR(), which doesn't
edecca
   exist in libvirt 4.5.0, and replace with appropriately placed VIR_FREE()
edecca
edecca
https://bugzilla.redhat.com/1650320
edecca
edecca
Signed-off-by: Laine Stump <laine@laine.org>
edecca
Reviewed-by: Ján Tomko <jtomko@redhat.com>
edecca
---
edecca
 src/libvirt_private.syms |   5 +
edecca
 src/util/virfirewalld.c  | 223 +++++++++++++++++++++++++++++++++++++++
edecca
 src/util/virfirewalld.h  |  15 ++-
edecca
 3 files changed, 242 insertions(+), 1 deletion(-)
edecca
edecca
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
edecca
index 57948d8049..624151056a 100644
edecca
--- a/src/libvirt_private.syms
edecca
+++ b/src/libvirt_private.syms
edecca
@@ -1905,7 +1905,12 @@ virFirewallStartTransaction;
edecca
 
edecca
 # util/virfirewalld.h
edecca
 virFirewallDApplyRule;
edecca
+virFirewallDGetBackend;
edecca
+virFirewallDGetVersion;
edecca
+virFirewallDGetZones;
edecca
+virFirewallDInterfaceSetZone;
edecca
 virFirewallDIsRegistered;
edecca
+virFirewallDZoneExists;
edecca
 
edecca
 
edecca
 # util/virfirmware.h
edecca
diff --git a/src/util/virfirewalld.c b/src/util/virfirewalld.c
edecca
index f27ec9c124..8dd6ac2b8a 100644
edecca
--- a/src/util/virfirewalld.c
edecca
+++ b/src/util/virfirewalld.c
edecca
@@ -22,6 +22,7 @@
edecca
 
edecca
 #include <stdarg.h>
edecca
 
edecca
+#include "viralloc.h"
edecca
 #include "virfirewall.h"
edecca
 #include "virfirewalld.h"
edecca
 #define LIBVIRT_VIRFIREWALLDPRIV_H_ALLOW
edecca
@@ -46,6 +47,14 @@ VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST,
edecca
               );
edecca
 
edecca
 
edecca
+VIR_ENUM_DECL(virFirewallDBackend);
edecca
+VIR_ENUM_IMPL(virFirewallDBackend, VIR_FIREWALLD_BACKEND_LAST,
edecca
+              "",
edecca
+              "iptables",
edecca
+              "nftables",
edecca
+              );
edecca
+
edecca
+
edecca
 /**
edecca
  * virFirewallDIsRegistered:
edecca
  *
edecca
@@ -57,6 +66,197 @@ virFirewallDIsRegistered(void)
edecca
     return virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE);
edecca
 }
edecca
 
edecca
+/**
edecca
+ * virFirewallDGetVersion:
edecca
+ * @version: pointer to location to save version in the form of:
edecca
+ *           1000000 * major + 1000 * minor + micro
edecca
+ *
edecca
+ * queries the firewalld version property from dbus, and converts it
edecca
+ * from a string into a number.
edecca
+ *
edecca
+ * Returns 0 if version was successfully retrieved, or -1 on error
edecca
+ */
edecca
+int
edecca
+virFirewallDGetVersion(unsigned long *version)
edecca
+{
edecca
+    int ret = -1;
edecca
+    DBusConnection *sysbus = virDBusGetSystemBus();
edecca
+    DBusMessage *reply = NULL;
edecca
+    char * versionStr = NULL;
edecca
+
edecca
+    if (!sysbus)
edecca
+        return -1;
edecca
+
edecca
+    if (virDBusCallMethod(sysbus,
edecca
+                          &reply,
edecca
+                          NULL,
edecca
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
edecca
+                          "/org/fedoraproject/FirewallD1",
edecca
+                          "org.freedesktop.DBus.Properties",
edecca
+                          "Get",
edecca
+                          "ss",
edecca
+                          "org.fedoraproject.FirewallD1",
edecca
+                          "version") < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    if (virDBusMessageRead(reply, "v", "s", &versionStr) < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    if (virParseVersionString(versionStr, version, false) < 0) {
edecca
+        virReportError(VIR_ERR_INTERNAL_ERROR,
edecca
+                       _("Failed to parse firewalld version '%s'"),
edecca
+                       versionStr);
edecca
+        goto cleanup;
edecca
+    }
edecca
+
edecca
+    VIR_DEBUG("FirewallD version: %s - %lu", versionStr, *version);
edecca
+
edecca
+    ret = 0;
edecca
+ cleanup:
edecca
+    VIR_FREE(versionStr);
edecca
+    virDBusMessageUnref(reply);
edecca
+    return ret;
edecca
+}
edecca
+
edecca
+/**
edecca
+ * virFirewallDGetBackend:
edecca
+ *
edecca
+ * Returns virVirewallDBackendType value representing which packet
edecca
+ * filtering backend is currently in use by firewalld, or -1 on error.
edecca
+ */
edecca
+int
edecca
+virFirewallDGetBackend(void)
edecca
+{
edecca
+    DBusConnection *sysbus = virDBusGetSystemBus();
edecca
+    DBusMessage *reply = NULL;
edecca
+    virError error;
edecca
+    char * backendStr = NULL;
edecca
+    int backend = -1;
edecca
+
edecca
+    if (!sysbus)
edecca
+        return -1;
edecca
+
edecca
+    memset(&error, 0, sizeof(error));
edecca
+
edecca
+    if (virDBusCallMethod(sysbus,
edecca
+                          &reply,
edecca
+                          &error,
edecca
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
edecca
+                          "/org/fedoraproject/FirewallD1/config",
edecca
+                          "org.freedesktop.DBus.Properties",
edecca
+                          "Get",
edecca
+                          "ss",
edecca
+                          "org.fedoraproject.FirewallD1.config",
edecca
+                          "FirewallBackend") < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    if (error.level == VIR_ERR_ERROR) {
edecca
+        /* we don't want to log any error in the case that
edecca
+         * FirewallBackend isn't implemented in this firewalld, since
edecca
+         * that just means that it is an old version, and only has an
edecca
+         * iptables backend.
edecca
+         */
edecca
+        VIR_DEBUG("Failed to get FirewallBackend setting, assuming 'iptables'");
edecca
+        backend = VIR_FIREWALLD_BACKEND_IPTABLES;
edecca
+        goto cleanup;
edecca
+    }
edecca
+
edecca
+    if (virDBusMessageRead(reply, "v", "s", &backendStr) < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    VIR_DEBUG("FirewallD backend: %s", backendStr);
edecca
+
edecca
+    if ((backend = virFirewallDBackendTypeFromString(backendStr)) < 0) {
edecca
+        virReportError(VIR_ERR_INTERNAL_ERROR,
edecca
+                       _("Unrecognized firewalld backend type: %s"),
edecca
+                       backendStr);
edecca
+        goto cleanup;
edecca
+    }
edecca
+
edecca
+ cleanup:
edecca
+    VIR_FREE(backendStr);
edecca
+    virResetError(&error);
edecca
+    virDBusMessageUnref(reply);
edecca
+    return backend;
edecca
+}
edecca
+
edecca
+
edecca
+/**
edecca
+ * virFirewallDGetZones:
edecca
+ * @zones: array of char *, each entry is a null-terminated zone name
edecca
+ * @nzones: number of entries in @zones
edecca
+ *
edecca
+ * Get the number of currently active firewalld zones, and their names
edecca
+ * in an array of null-terminated strings. The memory pointed to by
edecca
+ * @zones will belong to the caller, and must be freed.
edecca
+ *
edecca
+ * Returns 0 on success, -1 (and failure logged) on error
edecca
+ */
edecca
+int
edecca
+virFirewallDGetZones(char ***zones, size_t *nzones)
edecca
+{
edecca
+    DBusConnection *sysbus = virDBusGetSystemBus();
edecca
+    DBusMessage *reply = NULL;
edecca
+    int ret = -1;
edecca
+
edecca
+    *nzones = 0;
edecca
+    *zones = NULL;
edecca
+
edecca
+    if (!sysbus)
edecca
+        return -1;
edecca
+
edecca
+    if (virDBusCallMethod(sysbus,
edecca
+                          &reply,
edecca
+                          NULL,
edecca
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
edecca
+                          "/org/fedoraproject/FirewallD1",
edecca
+                          "org.fedoraproject.FirewallD1.zone",
edecca
+                          "getZones",
edecca
+                          NULL) < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    if (virDBusMessageRead(reply, "a&s", nzones, zones) < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    ret = 0;
edecca
+ cleanup:
edecca
+    virDBusMessageUnref(reply);
edecca
+    return ret;
edecca
+}
edecca
+
edecca
+
edecca
+/**
edecca
+ * virFirewallDZoneExists:
edecca
+ * @match: name of zone to look for
edecca
+ *
edecca
+ * Returns true if the requested zone exists, or false if it doesn't exist
edecca
+ */
edecca
+bool
edecca
+virFirewallDZoneExists(const char *match)
edecca
+{
edecca
+    size_t nzones = 0, i;
edecca
+    char **zones = NULL;
edecca
+    bool result = false;
edecca
+
edecca
+    return true;
edecca
+
edecca
+    if (virFirewallDGetZones(&zones, &nzones) < 0)
edecca
+        goto cleanup;
edecca
+
edecca
+    for (i = 0; i < nzones; i++) {
edecca
+        if (STREQ_NULLABLE(zones[i], match))
edecca
+            result = true;
edecca
+    }
edecca
+
edecca
+ cleanup:
edecca
+    VIR_DEBUG("Requested zone '%s' %s exist",
edecca
+              match, result ? "does" : "doesn't");
edecca
+    for (i = 0; i < nzones; i++)
edecca
+       VIR_FREE(zones[i]);
edecca
+    VIR_FREE(zones);
edecca
+    return result;
edecca
+}
edecca
+
edecca
 
edecca
 /**
edecca
  * virFirewallDApplyRule:
edecca
@@ -149,3 +349,26 @@ virFirewallDApplyRule(virFirewallLayer layer,
edecca
     virDBusMessageUnref(reply);
edecca
     return ret;
edecca
 }
edecca
+
edecca
+
edecca
+int
edecca
+virFirewallDInterfaceSetZone(const char *iface,
edecca
+                             const char *zone)
edecca
+{
edecca
+    DBusConnection *sysbus = virDBusGetSystemBus();
edecca
+    DBusMessage *reply = NULL;
edecca
+
edecca
+    if (!sysbus)
edecca
+        return -1;
edecca
+
edecca
+    return virDBusCallMethod(sysbus,
edecca
+                             &reply,
edecca
+                             NULL,
edecca
+                             VIR_FIREWALL_FIREWALLD_SERVICE,
edecca
+                             "/org/fedoraproject/FirewallD1",
edecca
+                             "org.fedoraproject.FirewallD1.zone",
edecca
+                             "changeZoneOfInterface",
edecca
+                             "ss",
edecca
+                             zone,
edecca
+                             iface);
edecca
+}
edecca
diff --git a/src/util/virfirewalld.h b/src/util/virfirewalld.h
edecca
index 83fe1149cc..f05f5f2f08 100644
edecca
--- a/src/util/virfirewalld.h
edecca
+++ b/src/util/virfirewalld.h
edecca
@@ -23,11 +23,24 @@
edecca
 
edecca
 # define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1"
edecca
 
edecca
-int virFirewallDIsRegistered(void);
edecca
+typedef enum {
edecca
+    VIR_FIREWALLD_BACKEND_NONE,
edecca
+    VIR_FIREWALLD_BACKEND_IPTABLES,
edecca
+    VIR_FIREWALLD_BACKEND_NFTABLES,
edecca
+    VIR_FIREWALLD_BACKEND_LAST,
edecca
+} virFirewallDBackendType;
edecca
 
edecca
+int virFirewallDGetVersion(unsigned long *version);
edecca
+int virFirewallDGetBackend(void);
edecca
+int virFirewallDIsRegistered(void);
edecca
+int virFirewallDGetZones(char ***zones, size_t *nzones);
edecca
+bool virFirewallDZoneExists(const char *match);
edecca
 int virFirewallDApplyRule(virFirewallLayer layer,
edecca
                           char **args, size_t argsLen,
edecca
                           bool ignoreErrors,
edecca
                           char **output);
edecca
 
edecca
+int virFirewallDInterfaceSetZone(const char *iface,
edecca
+                                 const char *zone);
edecca
+
edecca
 #endif /* LIBVIRT_VIRFIREWALLD_H */
edecca
-- 
edecca
2.20.1
edecca