|
|
8c03ec |
From 021167719bebe7fb7a0e366c371b6c7057ebed7e Mon Sep 17 00:00:00 2001
|
|
|
8c03ec |
Message-Id: <021167719bebe7fb7a0e366c371b6c7057ebed7e@dist-git>
|
|
|
8c03ec |
From: Laine Stump <laine@redhat.com>
|
|
|
8c03ec |
Date: Wed, 14 Apr 2021 23:25:34 -0400
|
|
|
8c03ec |
Subject: [PATCH] network: force re-creation of iptables private chains on
|
|
|
8c03ec |
firewalld restart
|
|
|
8c03ec |
MIME-Version: 1.0
|
|
|
8c03ec |
Content-Type: text/plain; charset=UTF-8
|
|
|
8c03ec |
Content-Transfer-Encoding: 8bit
|
|
|
8c03ec |
|
|
|
8c03ec |
When firewalld is stopped, it removes *all* iptables rules and chains,
|
|
|
8c03ec |
including those added by libvirt. Since restarting firewalld means
|
|
|
8c03ec |
stopping and then starting it, any time it is restarted, libvirt needs
|
|
|
8c03ec |
to recreate all the private iptables chains it uses, along with all
|
|
|
8c03ec |
the rules it adds.
|
|
|
8c03ec |
|
|
|
8c03ec |
We already have code in place to call networkReloadFirewallRules() any
|
|
|
8c03ec |
time we're notified of a firewalld start, and
|
|
|
8c03ec |
networkReloadFirewallRules() will call
|
|
|
8c03ec |
networkPreReloadFirewallRules(), which calls
|
|
|
8c03ec |
networkSetupPrivateChains(); unfortunately that last call is called
|
|
|
8c03ec |
using virOnce(), meaning that it will only be called the first time
|
|
|
8c03ec |
through networkPreReloadFirewallRules() after libvirtd starts - so of
|
|
|
8c03ec |
course when firewalld is later restarted, the call to
|
|
|
8c03ec |
networkSetupPrivateChains() is skipped.
|
|
|
8c03ec |
|
|
|
8c03ec |
The neat and tidy way to fix this would be if there was a standard way
|
|
|
8c03ec |
to reset a pthread_once_t object so that the next time virOnce was
|
|
|
8c03ec |
called, it would think the function hadn't been called, and call it
|
|
|
8c03ec |
again. Unfortunately, there isn't any official way of doing that (we
|
|
|
8c03ec |
*could* just fill it with 0 and hope for the best, but that doesn't
|
|
|
8c03ec |
seem very safe.
|
|
|
8c03ec |
|
|
|
8c03ec |
So instead, this patch just adds a static variable called
|
|
|
8c03ec |
chainInitDone, which is set to true after networkSetupPrivateChains()
|
|
|
8c03ec |
is called for the first time, and then during calls to
|
|
|
8c03ec |
networkPreReloadFirewallRules(), if chainInitDone is set, we call
|
|
|
8c03ec |
networkSetupPrivateChains() directly instead of via virOnce().
|
|
|
8c03ec |
|
|
|
8c03ec |
It may seem unsafe to directly call a function that is meant to be
|
|
|
8c03ec |
called only once, but I think in this case we're safe - there's
|
|
|
8c03ec |
nothing in the function that is inherently "once only" - it doesn't
|
|
|
8c03ec |
initialize anything that can't safely be re-initialized (as long as
|
|
|
8c03ec |
two threads don't try to do it at the same time), and it only happens
|
|
|
8c03ec |
when responding to a dbus message that firewalld has been started (and
|
|
|
8c03ec |
I don't think it's possible for us to be processing two of those at
|
|
|
8c03ec |
once), and even then only if the initial call to the function has
|
|
|
8c03ec |
already been completed (so we're safe if we receive a firewalld
|
|
|
8c03ec |
restart call at a time when we haven't yet called it, or even if
|
|
|
8c03ec |
another thread is already in the process of executing it. The only
|
|
|
8c03ec |
problematic bit I can think of is if another thread is in the process
|
|
|
8c03ec |
of adding an iptable rule at the time we're executing this function,
|
|
|
8c03ec |
but 1) none of those threads will be trying to add chains, and 2) if
|
|
|
8c03ec |
there was a concurrency problem with other threads adding iptables
|
|
|
8c03ec |
rules while firewalld was being restarted, it would still be a problem
|
|
|
8c03ec |
even without this change.
|
|
|
8c03ec |
|
|
|
8c03ec |
This is yet another patch that fixes an occurrence of this error:
|
|
|
8c03ec |
|
|
|
8c03ec |
COMMAND_FAILED: '/usr/sbin/iptables -w10 -w --table filter --insert LIBVIRT_INP --in-interface virbr0 --protocol tcp --destination-port 67 --jump ACCEPT' failed: iptables: No chain/target/match by that name.
|
|
|
8c03ec |
|
|
|
8c03ec |
Signed-off-by: Laine Stump <laine@redhat.com>
|
|
|
8c03ec |
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
8c03ec |
(cherry picked from commit f5418b427e7d2f26803880309478de9103680826)
|
|
|
8c03ec |
|
|
|
8c03ec |
https://bugzilla.redhat.com/1942805
|
|
|
8c03ec |
(cloned from the RHEL-AV version: https://bugzilla.redhat.com/1813830 )
|
|
|
8c03ec |
|
|
|
8c03ec |
Conflicts:
|
|
|
8c03ec |
src/network/bridge_driver.c:
|
|
|
8c03ec |
In one place a later commit was backported prior to this commit,
|
|
|
8c03ec |
removing a VIR_DEBUG line and some { }. (see upstream commit
|
|
|
8c03ec |
c102bbd3efc35, which was backported for
|
|
|
8c03ec |
https://bugzilla.redhat.com/1607929
|
|
|
8c03ec |
|
|
|
8c03ec |
Signed-off-by: Laine Stump <laine@redhat.com>
|
|
|
8c03ec |
Message-Id: <20210415032534.723202-3-laine@redhat.com>
|
|
|
8c03ec |
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
|
|
|
8c03ec |
---
|
|
|
8c03ec |
src/network/bridge_driver.c | 16 ++++---
|
|
|
8c03ec |
src/network/bridge_driver_linux.c | 69 ++++++++++++++++++----------
|
|
|
8c03ec |
src/network/bridge_driver_nop.c | 3 +-
|
|
|
8c03ec |
src/network/bridge_driver_platform.h | 2 +-
|
|
|
8c03ec |
4 files changed, 58 insertions(+), 32 deletions(-)
|
|
|
8c03ec |
|
|
|
8c03ec |
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
|
|
|
8c03ec |
index 5995396f78..b8118067d1 100644
|
|
|
8c03ec |
--- a/src/network/bridge_driver.c
|
|
|
8c03ec |
+++ b/src/network/bridge_driver.c
|
|
|
8c03ec |
@@ -271,7 +271,9 @@ static int
|
|
|
8c03ec |
networkShutdownNetworkExternal(virNetworkObjPtr obj);
|
|
|
8c03ec |
|
|
|
8c03ec |
static void
|
|
|
8c03ec |
-networkReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup);
|
|
|
8c03ec |
+networkReloadFirewallRules(virNetworkDriverStatePtr driver,
|
|
|
8c03ec |
+ bool startup,
|
|
|
8c03ec |
+ bool force);
|
|
|
8c03ec |
|
|
|
8c03ec |
static void
|
|
|
8c03ec |
networkRefreshDaemons(virNetworkDriverStatePtr driver);
|
|
|
8c03ec |
@@ -690,7 +692,7 @@ firewalld_dbus_filter_bridge(DBusConnection *connection G_GNUC_UNUSED,
|
|
|
8c03ec |
}
|
|
|
8c03ec |
|
|
|
8c03ec |
if (reload)
|
|
|
8c03ec |
- networkReloadFirewallRules(driver, false);
|
|
|
8c03ec |
+ networkReloadFirewallRules(driver, false, true);
|
|
|
8c03ec |
|
|
|
8c03ec |
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
8c03ec |
}
|
|
|
8c03ec |
@@ -791,7 +793,7 @@ networkStateInitialize(bool privileged,
|
|
|
8c03ec |
virNetworkObjListPrune(network_driver->networks,
|
|
|
8c03ec |
VIR_CONNECT_LIST_NETWORKS_INACTIVE |
|
|
|
8c03ec |
VIR_CONNECT_LIST_NETWORKS_TRANSIENT);
|
|
|
8c03ec |
- networkReloadFirewallRules(network_driver, true);
|
|
|
8c03ec |
+ networkReloadFirewallRules(network_driver, true, false);
|
|
|
8c03ec |
networkRefreshDaemons(network_driver);
|
|
|
8c03ec |
|
|
|
8c03ec |
if (virDriverShouldAutostart(network_driver->stateDir, &autostart) < 0)
|
|
|
8c03ec |
@@ -861,7 +863,7 @@ networkStateReload(void)
|
|
|
8c03ec |
network_driver->networkConfigDir,
|
|
|
8c03ec |
network_driver->networkAutostartDir,
|
|
|
8c03ec |
network_driver->xmlopt);
|
|
|
8c03ec |
- networkReloadFirewallRules(network_driver, false);
|
|
|
8c03ec |
+ networkReloadFirewallRules(network_driver, false, false);
|
|
|
8c03ec |
networkRefreshDaemons(network_driver);
|
|
|
8c03ec |
virNetworkObjListForEach(network_driver->networks,
|
|
|
8c03ec |
networkAutostartConfig,
|
|
|
8c03ec |
@@ -2229,14 +2231,16 @@ networkReloadFirewallRulesHelper(virNetworkObjPtr obj,
|
|
|
8c03ec |
|
|
|
8c03ec |
|
|
|
8c03ec |
static void
|
|
|
8c03ec |
-networkReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup)
|
|
|
8c03ec |
+networkReloadFirewallRules(virNetworkDriverStatePtr driver,
|
|
|
8c03ec |
+ bool startup,
|
|
|
8c03ec |
+ bool force)
|
|
|
8c03ec |
{
|
|
|
8c03ec |
VIR_INFO("Reloading iptables rules");
|
|
|
8c03ec |
/* Ideally we'd not even register the driver when unprivilegd
|
|
|
8c03ec |
* but until we untangle the virt driver that's not viable */
|
|
|
8c03ec |
if (!driver->privileged)
|
|
|
8c03ec |
return;
|
|
|
8c03ec |
- networkPreReloadFirewallRules(driver, startup);
|
|
|
8c03ec |
+ networkPreReloadFirewallRules(driver, startup, force);
|
|
|
8c03ec |
virNetworkObjListForEach(driver->networks,
|
|
|
8c03ec |
networkReloadFirewallRulesHelper,
|
|
|
8c03ec |
NULL);
|
|
|
8c03ec |
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c
|
|
|
8c03ec |
index b6b324d1d5..f707bf8e47 100644
|
|
|
8c03ec |
--- a/src/network/bridge_driver_linux.c
|
|
|
8c03ec |
+++ b/src/network/bridge_driver_linux.c
|
|
|
8c03ec |
@@ -36,11 +36,14 @@ VIR_LOG_INIT("network.bridge_driver_linux");
|
|
|
8c03ec |
#define PROC_NET_ROUTE "/proc/net/route"
|
|
|
8c03ec |
|
|
|
8c03ec |
static virOnceControl createdOnce;
|
|
|
8c03ec |
-static bool createdChains;
|
|
|
8c03ec |
+static bool chainInitDone; /* true iff networkSetupPrivateChains was ever called */
|
|
|
8c03ec |
+static bool createdChains; /* true iff networkSetupPrivateChains created chains during most recent call */
|
|
|
8c03ec |
static virErrorPtr errInitV4;
|
|
|
8c03ec |
static virErrorPtr errInitV6;
|
|
|
8c03ec |
|
|
|
8c03ec |
-/* Only call via virOnce */
|
|
|
8c03ec |
+/* Usually only called via virOnce, but can also be called directly in
|
|
|
8c03ec |
+ * response to firewalld reload (if chainInitDone == true)
|
|
|
8c03ec |
+ */
|
|
|
8c03ec |
static void networkSetupPrivateChains(void)
|
|
|
8c03ec |
{
|
|
|
8c03ec |
int rc;
|
|
|
8c03ec |
@@ -82,6 +85,8 @@ static void networkSetupPrivateChains(void)
|
|
|
8c03ec |
VIR_DEBUG("Global IPv6 chains already exist");
|
|
|
8c03ec |
}
|
|
|
8c03ec |
}
|
|
|
8c03ec |
+
|
|
|
8c03ec |
+ chainInitDone = true;
|
|
|
8c03ec |
}
|
|
|
8c03ec |
|
|
|
8c03ec |
|
|
|
8c03ec |
@@ -111,7 +116,10 @@ networkHasRunningNetworks(virNetworkDriverStatePtr driver)
|
|
|
8c03ec |
}
|
|
|
8c03ec |
|
|
|
8c03ec |
|
|
|
8c03ec |
-void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup)
|
|
|
8c03ec |
+void
|
|
|
8c03ec |
+networkPreReloadFirewallRules(virNetworkDriverStatePtr driver,
|
|
|
8c03ec |
+ bool startup,
|
|
|
8c03ec |
+ bool force)
|
|
|
8c03ec |
{
|
|
|
8c03ec |
/*
|
|
|
8c03ec |
* If there are any running networks, we need to
|
|
|
8c03ec |
@@ -130,29 +138,42 @@ void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup
|
|
|
8c03ec |
* of starting the network though as that makes them
|
|
|
8c03ec |
* more likely to be seen by a human
|
|
|
8c03ec |
*/
|
|
|
8c03ec |
- if (!networkHasRunningNetworks(driver)) {
|
|
|
8c03ec |
- VIR_DEBUG("Delayed global rule setup as no networks are running");
|
|
|
8c03ec |
- return;
|
|
|
8c03ec |
- }
|
|
|
8c03ec |
+ if (chainInitDone && force) {
|
|
|
8c03ec |
+ /* The Private chains have already been initialized once
|
|
|
8c03ec |
+ * during this run of libvirtd, so 1) we can't do it again via
|
|
|
8c03ec |
+ * virOnce(), and 2) we need to re-add the private chains even
|
|
|
8c03ec |
+ * if there are currently no running networks, because the
|
|
|
8c03ec |
+ * next time a network is started, libvirt will expect that
|
|
|
8c03ec |
+ * the chains have already been added. So we call directly
|
|
|
8c03ec |
+ * instead of via virOnce().
|
|
|
8c03ec |
+ */
|
|
|
8c03ec |
+ networkSetupPrivateChains();
|
|
|
8c03ec |
|
|
|
8c03ec |
- ignore_value(virOnce(&createdOnce, networkSetupPrivateChains));
|
|
|
8c03ec |
+ } else {
|
|
|
8c03ec |
+ if (!networkHasRunningNetworks(driver)) {
|
|
|
8c03ec |
+ VIR_DEBUG("Delayed global rule setup as no networks are running");
|
|
|
8c03ec |
+ return;
|
|
|
8c03ec |
+ }
|
|
|
8c03ec |
|
|
|
8c03ec |
- /*
|
|
|
8c03ec |
- * If this is initial startup, and we just created the
|
|
|
8c03ec |
- * top level private chains we either
|
|
|
8c03ec |
- *
|
|
|
8c03ec |
- * - upgraded from old libvirt
|
|
|
8c03ec |
- * - freshly booted from clean state
|
|
|
8c03ec |
- *
|
|
|
8c03ec |
- * In the first case we must delete the old rules from
|
|
|
8c03ec |
- * the built-in chains, instead of our new private chains.
|
|
|
8c03ec |
- * In the second case it doesn't matter, since no existing
|
|
|
8c03ec |
- * rules will be present. Thus we can safely just tell it
|
|
|
8c03ec |
- * to always delete from the builin chain
|
|
|
8c03ec |
- */
|
|
|
8c03ec |
- if (startup && createdChains) {
|
|
|
8c03ec |
- VIR_DEBUG("Requesting cleanup of legacy firewall rules");
|
|
|
8c03ec |
- iptablesSetDeletePrivate(false);
|
|
|
8c03ec |
+ ignore_value(virOnce(&createdOnce, networkSetupPrivateChains));
|
|
|
8c03ec |
+
|
|
|
8c03ec |
+ /*
|
|
|
8c03ec |
+ * If this is initial startup, and we just created the
|
|
|
8c03ec |
+ * top level private chains we either
|
|
|
8c03ec |
+ *
|
|
|
8c03ec |
+ * - upgraded from old libvirt
|
|
|
8c03ec |
+ * - freshly booted from clean state
|
|
|
8c03ec |
+ *
|
|
|
8c03ec |
+ * In the first case we must delete the old rules from
|
|
|
8c03ec |
+ * the built-in chains, instead of our new private chains.
|
|
|
8c03ec |
+ * In the second case it doesn't matter, since no existing
|
|
|
8c03ec |
+ * rules will be present. Thus we can safely just tell it
|
|
|
8c03ec |
+ * to always delete from the builin chain
|
|
|
8c03ec |
+ */
|
|
|
8c03ec |
+ if (startup && createdChains) {
|
|
|
8c03ec |
+ VIR_DEBUG("Requesting cleanup of legacy firewall rules");
|
|
|
8c03ec |
+ iptablesSetDeletePrivate(false);
|
|
|
8c03ec |
+ }
|
|
|
8c03ec |
}
|
|
|
8c03ec |
}
|
|
|
8c03ec |
|
|
|
8c03ec |
diff --git a/src/network/bridge_driver_nop.c b/src/network/bridge_driver_nop.c
|
|
|
8c03ec |
index 08d737511f..db89c10023 100644
|
|
|
8c03ec |
--- a/src/network/bridge_driver_nop.c
|
|
|
8c03ec |
+++ b/src/network/bridge_driver_nop.c
|
|
|
8c03ec |
@@ -20,7 +20,8 @@
|
|
|
8c03ec |
#include <config.h>
|
|
|
8c03ec |
|
|
|
8c03ec |
void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver G_GNUC_UNUSED,
|
|
|
8c03ec |
- bool startup G_GNUC_UNUSED)
|
|
|
8c03ec |
+ bool startup G_GNUC_UNUSED,
|
|
|
8c03ec |
+ bool force G_GNUC_UNUSED)
|
|
|
8c03ec |
{
|
|
|
8c03ec |
}
|
|
|
8c03ec |
|
|
|
8c03ec |
diff --git a/src/network/bridge_driver_platform.h b/src/network/bridge_driver_platform.h
|
|
|
8c03ec |
index 169417a6c0..48ab52c160 100644
|
|
|
8c03ec |
--- a/src/network/bridge_driver_platform.h
|
|
|
8c03ec |
+++ b/src/network/bridge_driver_platform.h
|
|
|
8c03ec |
@@ -62,7 +62,7 @@ struct _virNetworkDriverState {
|
|
|
8c03ec |
typedef struct _virNetworkDriverState virNetworkDriverState;
|
|
|
8c03ec |
typedef virNetworkDriverState *virNetworkDriverStatePtr;
|
|
|
8c03ec |
|
|
|
8c03ec |
-void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup);
|
|
|
8c03ec |
+void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup, bool force);
|
|
|
8c03ec |
void networkPostReloadFirewallRules(bool startup);
|
|
|
8c03ec |
|
|
|
8c03ec |
int networkCheckRouteCollision(virNetworkDefPtr def);
|
|
|
8c03ec |
--
|
|
|
8c03ec |
2.31.1
|
|
|
8c03ec |
|