Blob Blame History Raw
From 477f1de37ff7c911048d8d5e7f7de32f12d30b5d Mon Sep 17 00:00:00 2001
Message-Id: <477f1de37ff7c911048d8d5e7f7de32f12d30b5d@dist-git>
From: Laine Stump <laine@laine.org>
Date: Fri, 1 Feb 2019 20:29:29 -0500
Subject: [PATCH] util: new virFirewallD APIs + docs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

virFirewallDGetBackend() reports whether firewalld is currently using
an iptables or an nftables backend.

virFirewallDGetVersion() learns the version of the firewalld running
on this system and returns it as 1000000*major + 1000*minor + micro.

virFirewallDGetZones() gets a list of all currently active firewalld
zones.

virFirewallDInterfaceSetZone() sets the firewalld zone of the given
interface.

virFirewallDZoneExists() can be used to learn whether or not a
particular zone is present and active in firewalld.

Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 3bba4825c291e51a8cd4d497d6e919ac2ee96ff0)

Conflicts: src/util/virfirewalld.c - had to remove VIR_AUTO_PTR(), which doesn't
   exist in libvirt 4.5.0, and replace with appropriately placed VIR_FREE()

https://bugzilla.redhat.com/1650320

Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
---
 src/libvirt_private.syms |   5 +
 src/util/virfirewalld.c  | 223 +++++++++++++++++++++++++++++++++++++++
 src/util/virfirewalld.h  |  15 ++-
 3 files changed, 242 insertions(+), 1 deletion(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 57948d8049..624151056a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1905,7 +1905,12 @@ virFirewallStartTransaction;
 
 # util/virfirewalld.h
 virFirewallDApplyRule;
+virFirewallDGetBackend;
+virFirewallDGetVersion;
+virFirewallDGetZones;
+virFirewallDInterfaceSetZone;
 virFirewallDIsRegistered;
+virFirewallDZoneExists;
 
 
 # util/virfirmware.h
diff --git a/src/util/virfirewalld.c b/src/util/virfirewalld.c
index f27ec9c124..8dd6ac2b8a 100644
--- a/src/util/virfirewalld.c
+++ b/src/util/virfirewalld.c
@@ -22,6 +22,7 @@
 
 #include <stdarg.h>
 
+#include "viralloc.h"
 #include "virfirewall.h"
 #include "virfirewalld.h"
 #define LIBVIRT_VIRFIREWALLDPRIV_H_ALLOW
@@ -46,6 +47,14 @@ VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST,
               );
 
 
+VIR_ENUM_DECL(virFirewallDBackend);
+VIR_ENUM_IMPL(virFirewallDBackend, VIR_FIREWALLD_BACKEND_LAST,
+              "",
+              "iptables",
+              "nftables",
+              );
+
+
 /**
  * virFirewallDIsRegistered:
  *
@@ -57,6 +66,197 @@ virFirewallDIsRegistered(void)
     return virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE);
 }
 
+/**
+ * virFirewallDGetVersion:
+ * @version: pointer to location to save version in the form of:
+ *           1000000 * major + 1000 * minor + micro
+ *
+ * queries the firewalld version property from dbus, and converts it
+ * from a string into a number.
+ *
+ * Returns 0 if version was successfully retrieved, or -1 on error
+ */
+int
+virFirewallDGetVersion(unsigned long *version)
+{
+    int ret = -1;
+    DBusConnection *sysbus = virDBusGetSystemBus();
+    DBusMessage *reply = NULL;
+    char * versionStr = NULL;
+
+    if (!sysbus)
+        return -1;
+
+    if (virDBusCallMethod(sysbus,
+                          &reply,
+                          NULL,
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
+                          "/org/fedoraproject/FirewallD1",
+                          "org.freedesktop.DBus.Properties",
+                          "Get",
+                          "ss",
+                          "org.fedoraproject.FirewallD1",
+                          "version") < 0)
+        goto cleanup;
+
+    if (virDBusMessageRead(reply, "v", "s", &versionStr) < 0)
+        goto cleanup;
+
+    if (virParseVersionString(versionStr, version, false) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to parse firewalld version '%s'"),
+                       versionStr);
+        goto cleanup;
+    }
+
+    VIR_DEBUG("FirewallD version: %s - %lu", versionStr, *version);
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(versionStr);
+    virDBusMessageUnref(reply);
+    return ret;
+}
+
+/**
+ * virFirewallDGetBackend:
+ *
+ * Returns virVirewallDBackendType value representing which packet
+ * filtering backend is currently in use by firewalld, or -1 on error.
+ */
+int
+virFirewallDGetBackend(void)
+{
+    DBusConnection *sysbus = virDBusGetSystemBus();
+    DBusMessage *reply = NULL;
+    virError error;
+    char * backendStr = NULL;
+    int backend = -1;
+
+    if (!sysbus)
+        return -1;
+
+    memset(&error, 0, sizeof(error));
+
+    if (virDBusCallMethod(sysbus,
+                          &reply,
+                          &error,
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
+                          "/org/fedoraproject/FirewallD1/config",
+                          "org.freedesktop.DBus.Properties",
+                          "Get",
+                          "ss",
+                          "org.fedoraproject.FirewallD1.config",
+                          "FirewallBackend") < 0)
+        goto cleanup;
+
+    if (error.level == VIR_ERR_ERROR) {
+        /* we don't want to log any error in the case that
+         * FirewallBackend isn't implemented in this firewalld, since
+         * that just means that it is an old version, and only has an
+         * iptables backend.
+         */
+        VIR_DEBUG("Failed to get FirewallBackend setting, assuming 'iptables'");
+        backend = VIR_FIREWALLD_BACKEND_IPTABLES;
+        goto cleanup;
+    }
+
+    if (virDBusMessageRead(reply, "v", "s", &backendStr) < 0)
+        goto cleanup;
+
+    VIR_DEBUG("FirewallD backend: %s", backendStr);
+
+    if ((backend = virFirewallDBackendTypeFromString(backendStr)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unrecognized firewalld backend type: %s"),
+                       backendStr);
+        goto cleanup;
+    }
+
+ cleanup:
+    VIR_FREE(backendStr);
+    virResetError(&error);
+    virDBusMessageUnref(reply);
+    return backend;
+}
+
+
+/**
+ * virFirewallDGetZones:
+ * @zones: array of char *, each entry is a null-terminated zone name
+ * @nzones: number of entries in @zones
+ *
+ * Get the number of currently active firewalld zones, and their names
+ * in an array of null-terminated strings. The memory pointed to by
+ * @zones will belong to the caller, and must be freed.
+ *
+ * Returns 0 on success, -1 (and failure logged) on error
+ */
+int
+virFirewallDGetZones(char ***zones, size_t *nzones)
+{
+    DBusConnection *sysbus = virDBusGetSystemBus();
+    DBusMessage *reply = NULL;
+    int ret = -1;
+
+    *nzones = 0;
+    *zones = NULL;
+
+    if (!sysbus)
+        return -1;
+
+    if (virDBusCallMethod(sysbus,
+                          &reply,
+                          NULL,
+                          VIR_FIREWALL_FIREWALLD_SERVICE,
+                          "/org/fedoraproject/FirewallD1",
+                          "org.fedoraproject.FirewallD1.zone",
+                          "getZones",
+                          NULL) < 0)
+        goto cleanup;
+
+    if (virDBusMessageRead(reply, "a&s", nzones, zones) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    virDBusMessageUnref(reply);
+    return ret;
+}
+
+
+/**
+ * virFirewallDZoneExists:
+ * @match: name of zone to look for
+ *
+ * Returns true if the requested zone exists, or false if it doesn't exist
+ */
+bool
+virFirewallDZoneExists(const char *match)
+{
+    size_t nzones = 0, i;
+    char **zones = NULL;
+    bool result = false;
+
+    return true;
+
+    if (virFirewallDGetZones(&zones, &nzones) < 0)
+        goto cleanup;
+
+    for (i = 0; i < nzones; i++) {
+        if (STREQ_NULLABLE(zones[i], match))
+            result = true;
+    }
+
+ cleanup:
+    VIR_DEBUG("Requested zone '%s' %s exist",
+              match, result ? "does" : "doesn't");
+    for (i = 0; i < nzones; i++)
+       VIR_FREE(zones[i]);
+    VIR_FREE(zones);
+    return result;
+}
+
 
 /**
  * virFirewallDApplyRule:
@@ -149,3 +349,26 @@ virFirewallDApplyRule(virFirewallLayer layer,
     virDBusMessageUnref(reply);
     return ret;
 }
+
+
+int
+virFirewallDInterfaceSetZone(const char *iface,
+                             const char *zone)
+{
+    DBusConnection *sysbus = virDBusGetSystemBus();
+    DBusMessage *reply = NULL;
+
+    if (!sysbus)
+        return -1;
+
+    return virDBusCallMethod(sysbus,
+                             &reply,
+                             NULL,
+                             VIR_FIREWALL_FIREWALLD_SERVICE,
+                             "/org/fedoraproject/FirewallD1",
+                             "org.fedoraproject.FirewallD1.zone",
+                             "changeZoneOfInterface",
+                             "ss",
+                             zone,
+                             iface);
+}
diff --git a/src/util/virfirewalld.h b/src/util/virfirewalld.h
index 83fe1149cc..f05f5f2f08 100644
--- a/src/util/virfirewalld.h
+++ b/src/util/virfirewalld.h
@@ -23,11 +23,24 @@
 
 # define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1"
 
-int virFirewallDIsRegistered(void);
+typedef enum {
+    VIR_FIREWALLD_BACKEND_NONE,
+    VIR_FIREWALLD_BACKEND_IPTABLES,
+    VIR_FIREWALLD_BACKEND_NFTABLES,
+    VIR_FIREWALLD_BACKEND_LAST,
+} virFirewallDBackendType;
 
+int virFirewallDGetVersion(unsigned long *version);
+int virFirewallDGetBackend(void);
+int virFirewallDIsRegistered(void);
+int virFirewallDGetZones(char ***zones, size_t *nzones);
+bool virFirewallDZoneExists(const char *match);
 int virFirewallDApplyRule(virFirewallLayer layer,
                           char **args, size_t argsLen,
                           bool ignoreErrors,
                           char **output);
 
+int virFirewallDInterfaceSetZone(const char *iface,
+                                 const char *zone);
+
 #endif /* LIBVIRT_VIRFIREWALLD_H */
-- 
2.20.1