From 99fce2f77698c3fde675d0eec69c5ebee88f68a6 Mon Sep 17 00:00:00 2001 From: yanjianqing Date: Thu, 18 Sep 2025 20:51:06 +0800 Subject: [PATCH] network: force re-creation of iptables private chains on firewalld restart --- ...-creation-of-iptables-private-chains.patch | 267 ++++++++++++++++++ libvirt.spec | 42 +-- 2 files changed, 290 insertions(+), 19 deletions(-) create mode 100755 backport-network-force-re-creation-of-iptables-private-chains.patch diff --git a/backport-network-force-re-creation-of-iptables-private-chains.patch b/backport-network-force-re-creation-of-iptables-private-chains.patch new file mode 100755 index 0000000..79bb893 --- /dev/null +++ b/backport-network-force-re-creation-of-iptables-private-chains.patch @@ -0,0 +1,267 @@ +From f5418b427e7d2f26803880309478de9103680826 Mon Sep 17 00:00:00 2001 +From: Laine Stump +Date: Thu, 7 May 2020 21:54:39 -0400 +Subject: [PATCH] network: force re-creation of iptables private chains on + firewalld restart +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When firewalld is stopped, it removes *all* iptables rules and chains, +including those added by libvirt. Since restarting firewalld means +stopping and then starting it, any time it is restarted, libvirt needs +to recreate all the private iptables chains it uses, along with all +the rules it adds. + +We already have code in place to call networkReloadFirewallRules() any +time we're notified of a firewalld start, and +networkReloadFirewallRules() will call +networkPreReloadFirewallRules(), which calls +networkSetupPrivateChains(); unfortunately that last call is called +using virOnce(), meaning that it will only be called the first time +through networkPreReloadFirewallRules() after libvirtd starts - so of +course when firewalld is later restarted, the call to +networkSetupPrivateChains() is skipped. + +The neat and tidy way to fix this would be if there was a standard way +to reset a pthread_once_t object so that the next time virOnce was +called, it would think the function hadn't been called, and call it +again. Unfortunately, there isn't any official way of doing that (we +*could* just fill it with 0 and hope for the best, but that doesn't +seem very safe. + +So instead, this patch just adds a static variable called +chainInitDone, which is set to true after networkSetupPrivateChains() +is called for the first time, and then during calls to +networkPreReloadFirewallRules(), if chainInitDone is set, we call +networkSetupPrivateChains() directly instead of via virOnce(). + +It may seem unsafe to directly call a function that is meant to be +called only once, but I think in this case we're safe - there's +nothing in the function that is inherently "once only" - it doesn't +initialize anything that can't safely be re-initialized (as long as +two threads don't try to do it at the same time), and it only happens +when responding to a dbus message that firewalld has been started (and +I don't think it's possible for us to be processing two of those at +once), and even then only if the initial call to the function has +already been completed (so we're safe if we receive a firewalld +restart call at a time when we haven't yet called it, or even if +another thread is already in the process of executing it. The only +problematic bit I can think of is if another thread is in the process +of adding an iptable rule at the time we're executing this function, +but 1) none of those threads will be trying to add chains, and 2) if +there was a concurrency problem with other threads adding iptables +rules while firewalld was being restarted, it would still be a problem +even without this change. + +This is yet another patch that fixes an occurrence of this error: + +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. + +In particular, this resolves: https://bugzilla.redhat.com/1813830 + +Signed-off-by: Laine Stump +Reviewed-by: Daniel P. Berrangé +--- + src/network/bridge_driver.c | 16 ++++--- + src/network/bridge_driver_linux.c | 69 ++++++++++++++++++---------- + src/network/bridge_driver_nop.c | 3 +- + src/network/bridge_driver_platform.h | 2 +- + 4 files changed, 58 insertions(+), 32 deletions(-) + +diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c +index e8f0dcf7d0..47d5d95678 100644 +--- a/src/network/bridge_driver.c ++++ b/src/network/bridge_driver.c +@@ -273,7 +273,9 @@ static int + networkShutdownNetworkExternal(virNetworkObjPtr obj); + + static void +-networkReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup); ++networkReloadFirewallRules(virNetworkDriverStatePtr driver, ++ bool startup, ++ bool force); + + static void + networkRefreshDaemons(virNetworkDriverStatePtr driver); +@@ -689,7 +691,7 @@ firewalld_dbus_filter_bridge(DBusConnection *connection G_GNUC_UNUSED, + + if (reload) { + VIR_DEBUG("Reload in bridge_driver because of firewalld."); +- networkReloadFirewallRules(driver, false); ++ networkReloadFirewallRules(driver, false, true); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +@@ -798,7 +800,7 @@ networkStateInitialize(bool privileged, + virNetworkObjListPrune(network_driver->networks, + VIR_CONNECT_LIST_NETWORKS_INACTIVE | + VIR_CONNECT_LIST_NETWORKS_TRANSIENT); +- networkReloadFirewallRules(network_driver, true); ++ networkReloadFirewallRules(network_driver, true, false); + networkRefreshDaemons(network_driver); + + if (virDriverShouldAutostart(network_driver->stateDir, &autostart) < 0) +@@ -868,7 +870,7 @@ networkStateReload(void) + network_driver->networkConfigDir, + network_driver->networkAutostartDir, + network_driver->xmlopt); +- networkReloadFirewallRules(network_driver, false); ++ networkReloadFirewallRules(network_driver, false, false); + networkRefreshDaemons(network_driver); + virNetworkObjListForEach(network_driver->networks, + networkAutostartConfig, +@@ -2201,14 +2203,16 @@ networkReloadFirewallRulesHelper(virNetworkObjPtr obj, + + + static void +-networkReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup) ++networkReloadFirewallRules(virNetworkDriverStatePtr driver, ++ bool startup, ++ bool force) + { + VIR_INFO("Reloading iptables rules"); + /* Ideally we'd not even register the driver when unprivilegd + * but until we untangle the virt driver that's not viable */ + if (!driver->privileged) + return; +- networkPreReloadFirewallRules(driver, startup); ++ networkPreReloadFirewallRules(driver, startup, force); + virNetworkObjListForEach(driver->networks, + networkReloadFirewallRulesHelper, + NULL); +diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c +index 80bd2409e1..b0bd207250 100644 +--- a/src/network/bridge_driver_linux.c ++++ b/src/network/bridge_driver_linux.c +@@ -36,11 +36,14 @@ VIR_LOG_INIT("network.bridge_driver_linux"); + #define PROC_NET_ROUTE "/proc/net/route" + + static virOnceControl createdOnce; +-static bool createdChains; ++static bool chainInitDone; /* true iff networkSetupPrivateChains was ever called */ ++static bool createdChains; /* true iff networkSetupPrivateChains created chains during most recent call */ + static virErrorPtr errInitV4; + static virErrorPtr errInitV6; + +-/* Only call via virOnce */ ++/* Usually only called via virOnce, but can also be called directly in ++ * response to firewalld reload (if chainInitDone == true) ++ */ + static void networkSetupPrivateChains(void) + { + int rc; +@@ -82,6 +85,8 @@ static void networkSetupPrivateChains(void) + VIR_DEBUG("Global IPv6 chains already exist"); + } + } ++ ++ chainInitDone = true; + } + + +@@ -111,7 +116,10 @@ networkHasRunningNetworks(virNetworkDriverStatePtr driver) + } + + +-void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup) ++void ++networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, ++ bool startup, ++ bool force) + { + /* + * If there are any running networks, we need to +@@ -130,29 +138,42 @@ void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup + * of starting the network though as that makes them + * more likely to be seen by a human + */ +- if (!networkHasRunningNetworks(driver)) { +- VIR_DEBUG("Delayed global rule setup as no networks are running"); +- return; +- } ++ if (chainInitDone && force) { ++ /* The Private chains have already been initialized once ++ * during this run of libvirtd, so 1) we can't do it again via ++ * virOnce(), and 2) we need to re-add the private chains even ++ * if there are currently no running networks, because the ++ * next time a network is started, libvirt will expect that ++ * the chains have already been added. So we call directly ++ * instead of via virOnce(). ++ */ ++ networkSetupPrivateChains(); + +- ignore_value(virOnce(&createdOnce, networkSetupPrivateChains)); ++ } else { ++ if (!networkHasRunningNetworks(driver)) { ++ VIR_DEBUG("Delayed global rule setup as no networks are running"); ++ return; ++ } + +- /* +- * If this is initial startup, and we just created the +- * top level private chains we either +- * +- * - upgraded from old libvirt +- * - freshly booted from clean state +- * +- * In the first case we must delete the old rules from +- * the built-in chains, instead of our new private chains. +- * In the second case it doesn't matter, since no existing +- * rules will be present. Thus we can safely just tell it +- * to always delete from the builin chain +- */ +- if (startup && createdChains) { +- VIR_DEBUG("Requesting cleanup of legacy firewall rules"); +- iptablesSetDeletePrivate(false); ++ ignore_value(virOnce(&createdOnce, networkSetupPrivateChains)); ++ ++ /* ++ * If this is initial startup, and we just created the ++ * top level private chains we either ++ * ++ * - upgraded from old libvirt ++ * - freshly booted from clean state ++ * ++ * In the first case we must delete the old rules from ++ * the built-in chains, instead of our new private chains. ++ * In the second case it doesn't matter, since no existing ++ * rules will be present. Thus we can safely just tell it ++ * to always delete from the builin chain ++ */ ++ if (startup && createdChains) { ++ VIR_DEBUG("Requesting cleanup of legacy firewall rules"); ++ iptablesSetDeletePrivate(false); ++ } + } + } + +diff --git a/src/network/bridge_driver_nop.c b/src/network/bridge_driver_nop.c +index 08d737511f..db89c10023 100644 +--- a/src/network/bridge_driver_nop.c ++++ b/src/network/bridge_driver_nop.c +@@ -20,7 +20,8 @@ + #include + + void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver G_GNUC_UNUSED, +- bool startup G_GNUC_UNUSED) ++ bool startup G_GNUC_UNUSED, ++ bool force G_GNUC_UNUSED) + { + } + +diff --git a/src/network/bridge_driver_platform.h b/src/network/bridge_driver_platform.h +index 169417a6c0..48ab52c160 100644 +--- a/src/network/bridge_driver_platform.h ++++ b/src/network/bridge_driver_platform.h +@@ -62,7 +62,7 @@ struct _virNetworkDriverState { + typedef struct _virNetworkDriverState virNetworkDriverState; + typedef virNetworkDriverState *virNetworkDriverStatePtr; + +-void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup); ++void networkPreReloadFirewallRules(virNetworkDriverStatePtr driver, bool startup, bool force); + void networkPostReloadFirewallRules(bool startup); + + int networkCheckRouteCollision(virNetworkDefPtr def); +-- +2.25.1 diff --git a/libvirt.spec b/libvirt.spec index aa0481e..69c477d 100644 --- a/libvirt.spec +++ b/libvirt.spec @@ -101,7 +101,7 @@ Summary: Library providing a simple virtualization API Name: libvirt Version: 6.2.0 -Release: 25 +Release: 26 License: LGPLv2+ URL: https://libvirt.org/ @@ -162,6 +162,7 @@ Patch0049: interface-fix-udev_device_get_sysattr_value-return-v.patch Patch0050: util-keep-track-of-full-GSource-object-not-source-ID.patch Patch0051: rpc-mark-source-returned-by-virEventGLibAddSocketWat.patch Patch0052: rpc-ensure-temporary-GSource-is-removed-from-client-.patch +Patch0053: backport-network-force-re-creation-of-iptables-private-chains.patch Requires: libvirt-daemon = %{version}-%{release} Requires: libvirt-daemon-config-network = %{version}-%{release} @@ -1896,66 +1897,69 @@ exit 0 %changelog -* Fri May 24 2024 jiangjiacheng +* Thu Sep 18 2025 yanjianqing - 6.2.0-26 +- network: force re-creation of iptables private chains on firewalld restart + +* Fri May 24 2024 jiangjiacheng - 6.2.0-25 - util: keep track of full GSource object not source ID number - rpc: mark source returned by virEventGLibAddSocketWatch as unused - rpc: ensure temporary GSource is removed from client event loop (CVE-2024-4418) -* Wed Apr 10 2024 caozhongwang +* Wed Apr 10 2024 caozhongwang - 6.2.0-24 - interface: fix udev_device_get_sysattr_value return value check (CVE-2024-2496) -* Wed Apr 10 2024 caozhongwang +* Wed Apr 10 2024 caozhongwang - 6.2.0-23 - remote: check for negative array lengths before allocation (CVE-2024-2494) -* Wed Apr 10 2024 caozhongwang +* Wed Apr 10 2024 caozhongwang - 6.2.0-22 - vish:Fix off-by-one error in udevListInterfacesByStatus (CVE-2024-1441) -* Sat Dec 10 2022 yezengruan +* Sat Dec 10 2022 yezengruan - 6.2.0-21 - update the Chinese translation of nwfilter -* Thu Aug 25 2022 yezengruan +* Thu Aug 25 2022 yezengruan - 6.2.0-20 - qemu: Add missing lock in qemuProcessHandleMonitorEOF (CVE-2021-3975) -* Mon Jun 20 2022 yezengruan +* Mon Jun 20 2022 yezengruan - 6.2.0-19 - nwfilter: fix crash when counting number of network filters (CVE-2022-0897) -* Wed Jun 15 2022 yezengruan +* Wed Jun 15 2022 yezengruan - 6.2.0-18 - libvir.spec: build without dtrace -* Tue Mar 08 2022 imxcc +* Tue Mar 08 2022 imxcc - 6.2.0-17 - virsh: Display vhostuser socket path in domblklist -* Wed Dec 08 2021 Euler Robot +* Wed Dec 08 2021 Euler Robot - 6.2.0-16 - virDevMapperGetTargets: Don't ignore EBADF - virdevmapper: Don't cache device-mapper major - virdevmapper: Handle kernel without device-mapper support -* Wed Dec 08 2021 Euler Robot +* Wed Dec 08 2021 Euler Robot - 6.2.0-15 - add phytium 2000plus and s2500 support on arm architecture for capability -* Sun Sep 26 2021 Euler Robot +* Sun Sep 26 2021 Euler Robot -6.2.0-14 - storage_driver: Unlock object on ACL fail in storagePoolLookupByTargetPath - security: fix SELinux label generation logic -* Fri Sep 24 2021 Euler Robot +* Fri Sep 24 2021 Euler Robot - 6.2.0-13 - conf/domain_conf: pin the retry_interval and retry_timeout parameters to xml -* Tue Jul 27 2021 Jingyi Wang +* Tue Jul 27 2021 Jingyi Wang - 6.2.0-12 - add new CPU model Cooperlake -* Fri Apr 23 2021 Chen Qun +* Fri Apr 23 2021 Chen Qun 6.2.0-11 - libvirt: Add 'retry' support for error policy - qemu: Support 'retry' BLOCK_IO_ERROR event. - libvirt/conf: Set default values of retry fileds -* Wed Jan 20 2021 Huawei Technologies Co., Ltd +* Wed Jan 20 2021 Huawei Technologies Co., Ltd - 6.2.0-10 - util: Move virIsDevMapperDevice() to virdevmapper.c - virdevmapper: Don't use libdevmapper to obtain dependencies -* Tue Dec 8 2020 Huawei Technologies Co., Ltd +* Tue Dec 8 2020 Huawei Technologies Co., Ltd - 6.2.0-9 - add --without-firewalld-zone to configure commandline for old firewalld version -* Wed Oct 14 2020 Huawei Technologies Co., Ltd +* Wed Oct 14 2020 Huawei Technologies Co., Ltd - 6.2.0-8 - rpc: gendispatch: handle empty flags - rpc: add support for filtering @acls by uint params - rpc: require write acl for guest agent in virDomainInterfaceAddresses -- Gitee