2013-07-24 12:22:54 +00:00
|
|
|
/*
|
|
|
|
* bridge_driver_linux.c: Linux implementation of bridge driver
|
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "viralloc.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "viriptables.h"
|
|
|
|
#include "virstring.h"
|
2014-02-28 12:16:17 +00:00
|
|
|
#include "virlog.h"
|
2014-03-06 17:01:13 +00:00
|
|
|
#include "virfirewall.h"
|
network: set firewalld zone of bridges to "libvirt" zone when appropriate
This patch restores broken guest network connectivity after a host
firewalld is switched to using an nftables backend. It does this by
adding libvirt networks' bridge interfaces to the new "libvirt" zone
in firewalld.
After this patch, the bridge interface of any network created by
libvirt (when firewalld is active) will be added to the firewalld
zone called "libvirt" if it exists (regardless of the firewalld
backend setting). This behavior does *not* depend on whether or not
libvirt has installed the libvirt zone file (set with
"--with[out]-firewalld-zone" during the configure phase of the package
build).
If the libvirt zone doesn't exist (either because the package was
configured to not install it, or possibly it was installed, but
firewalld doesn't support rule priorities, resulting in a parse
error), the bridge will remain in firewalld's default zone, which
could be innocuous (in the case that the firewalld backend is
iptables, guest networking will still function properly with the
bridge in the default zone), or it could be disastrous (if the
firewalld backend is nftables, we can be assured that guest networking
will fail). In order to be unobtrusive in the former case, and
informative in the latter, when the libvirt zone doesn't exist we
then check the firewalld version to see if it's new enough to support
the nftables backend, and then if the backend is actually set to
nftables, before logging an error (and failing the net-start
operation, since the network couldn't possibly work anyway).
When the libvirt zone is used, network behavior is *slightly*
different from behavior of previous libvirt. In the past, libvirt
network behavior would be affected by the configuration of firewalld's
default zone (usually "public"), but now it is affected only by the
"libvirt" zone), and thus almost surely warrants a release note for
any distro upgrading to libvirt 5.1 or above. Although it's
unfortunate that we have to deal with a mandatory behavior change, the
architecture of multiple hooks makes it impossible to *not* change
behavior in some way, and the new behavior is arguably better (since
it will now be possible to manage access to the host from virtual
machines vs from public interfaces separately).
Creates-and-Resolves: https://bugzilla.redhat.com/1650320
Resolves: https://bugzilla.redhat.com/1638342
Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2018-10-16 00:31:02 +00:00
|
|
|
#include "virfirewalld.h"
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("network.bridge_driver_linux");
|
|
|
|
|
2013-07-24 12:22:54 +00:00
|
|
|
#define PROC_NET_ROUTE "/proc/net/route"
|
|
|
|
|
2019-05-21 10:37:37 +00:00
|
|
|
static virOnceControl createdOnce;
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
static bool chainInitDone; /* true iff networkSetupPrivateChains was ever called */
|
|
|
|
static bool createdChains; /* true iff networkSetupPrivateChains created chains during most recent call */
|
2019-03-18 16:49:32 +00:00
|
|
|
static virErrorPtr errInitV4;
|
|
|
|
static virErrorPtr errInitV6;
|
2019-03-18 17:31:21 +00:00
|
|
|
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
/* Usually only called via virOnce, but can also be called directly in
|
|
|
|
* response to firewalld reload (if chainInitDone == true)
|
|
|
|
*/
|
2019-05-21 10:37:37 +00:00
|
|
|
static void networkSetupPrivateChains(void)
|
2018-12-05 13:29:07 +00:00
|
|
|
{
|
2019-03-18 17:31:21 +00:00
|
|
|
int rc;
|
|
|
|
|
2019-05-22 12:08:13 +00:00
|
|
|
VIR_DEBUG("Setting up global firewall chains");
|
|
|
|
|
2019-05-21 10:37:37 +00:00
|
|
|
createdChains = false;
|
2020-05-08 02:32:59 +00:00
|
|
|
virFreeError(errInitV4);
|
|
|
|
errInitV4 = NULL;
|
|
|
|
virFreeError(errInitV6);
|
|
|
|
errInitV6 = NULL;
|
2019-05-21 10:37:37 +00:00
|
|
|
|
2019-03-18 16:49:32 +00:00
|
|
|
rc = iptablesSetupPrivateChains(VIR_FIREWALL_LAYER_IPV4);
|
2019-03-18 17:31:21 +00:00
|
|
|
if (rc < 0) {
|
2019-05-22 12:08:13 +00:00
|
|
|
VIR_DEBUG("Failed to create global IPv4 chains: %s",
|
|
|
|
virGetLastErrorMessage());
|
2019-03-18 16:49:32 +00:00
|
|
|
errInitV4 = virSaveLastError();
|
2019-03-18 17:31:21 +00:00
|
|
|
virResetLastError();
|
2019-04-12 15:53:50 +00:00
|
|
|
} else {
|
2019-05-22 12:08:13 +00:00
|
|
|
if (rc) {
|
|
|
|
VIR_DEBUG("Created global IPv4 chains");
|
2019-05-21 10:37:37 +00:00
|
|
|
createdChains = true;
|
2019-05-22 12:08:13 +00:00
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Global IPv4 chains already exist");
|
|
|
|
}
|
2019-03-18 17:31:21 +00:00
|
|
|
}
|
2019-03-18 16:49:32 +00:00
|
|
|
|
|
|
|
rc = iptablesSetupPrivateChains(VIR_FIREWALL_LAYER_IPV6);
|
|
|
|
if (rc < 0) {
|
2019-05-22 12:08:13 +00:00
|
|
|
VIR_DEBUG("Failed to create global IPv6 chains: %s",
|
|
|
|
virGetLastErrorMessage());
|
2019-03-18 16:49:32 +00:00
|
|
|
errInitV6 = virSaveLastError();
|
|
|
|
virResetLastError();
|
2019-04-12 15:53:50 +00:00
|
|
|
} else {
|
2019-05-22 12:08:13 +00:00
|
|
|
if (rc) {
|
|
|
|
VIR_DEBUG("Created global IPv6 chains");
|
2019-05-21 10:37:37 +00:00
|
|
|
createdChains = true;
|
2019-05-22 12:08:13 +00:00
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Global IPv6 chains already exist");
|
|
|
|
}
|
2019-03-18 16:49:32 +00:00
|
|
|
}
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
|
|
|
|
chainInitDone = true;
|
2019-05-21 10:37:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-21 11:40:13 +00:00
|
|
|
|
|
|
|
static int
|
2020-06-05 17:11:02 +00:00
|
|
|
networkHasRunningNetworksWithFWHelper(virNetworkObjPtr obj,
|
2019-05-21 11:40:13 +00:00
|
|
|
void *opaque)
|
|
|
|
{
|
2020-06-05 17:11:02 +00:00
|
|
|
bool *activeWithFW = opaque;
|
2019-05-21 11:40:13 +00:00
|
|
|
|
|
|
|
virObjectLock(obj);
|
2020-06-05 17:11:02 +00:00
|
|
|
if (virNetworkObjIsActive(obj)) {
|
|
|
|
virNetworkDefPtr def = virNetworkObjGetDef(obj);
|
|
|
|
|
|
|
|
switch ((virNetworkForwardType) def->forward.type) {
|
|
|
|
case VIR_NETWORK_FORWARD_NONE:
|
|
|
|
case VIR_NETWORK_FORWARD_NAT:
|
|
|
|
case VIR_NETWORK_FORWARD_ROUTE:
|
|
|
|
*activeWithFW = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_NETWORK_FORWARD_OPEN:
|
|
|
|
case VIR_NETWORK_FORWARD_BRIDGE:
|
|
|
|
case VIR_NETWORK_FORWARD_PRIVATE:
|
|
|
|
case VIR_NETWORK_FORWARD_VEPA:
|
|
|
|
case VIR_NETWORK_FORWARD_PASSTHROUGH:
|
|
|
|
case VIR_NETWORK_FORWARD_HOSTDEV:
|
|
|
|
case VIR_NETWORK_FORWARD_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 11:40:13 +00:00
|
|
|
virObjectUnlock(obj);
|
|
|
|
|
2020-06-05 17:11:02 +00:00
|
|
|
/*
|
|
|
|
* terminate ForEach early once we find an active network that
|
|
|
|
* adds Firewall rules (return status is ignored)
|
|
|
|
*/
|
|
|
|
if (*activeWithFW)
|
|
|
|
return -1;
|
|
|
|
|
2019-05-21 11:40:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
2020-06-05 17:11:02 +00:00
|
|
|
networkHasRunningNetworksWithFW(virNetworkDriverStatePtr driver)
|
2019-05-21 11:40:13 +00:00
|
|
|
{
|
2020-06-05 17:11:02 +00:00
|
|
|
bool activeWithFW = false;
|
|
|
|
|
2019-05-21 11:40:13 +00:00
|
|
|
virNetworkObjListForEach(driver->networks,
|
2020-06-05 17:11:02 +00:00
|
|
|
networkHasRunningNetworksWithFWHelper,
|
|
|
|
&activeWithFW);
|
|
|
|
return activeWithFW;
|
2019-05-21 11:40:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
void
|
|
|
|
networkPreReloadFirewallRules(virNetworkDriverStatePtr driver,
|
|
|
|
bool startup,
|
|
|
|
bool force)
|
2019-05-21 10:37:37 +00:00
|
|
|
{
|
2019-05-21 11:40:13 +00:00
|
|
|
/*
|
|
|
|
* If there are any running networks, we need to
|
|
|
|
* create the global rules upfront. This allows us
|
|
|
|
* convert rules created by old libvirt into the new
|
|
|
|
* format.
|
|
|
|
*
|
|
|
|
* If there are not any running networks, then we
|
|
|
|
* must not create rules, because the rules will
|
|
|
|
* cause the conntrack kernel module to be loaded.
|
|
|
|
* This imposes a significant performance hit on
|
|
|
|
* the networking stack. Thus we will only create
|
|
|
|
* rules if a network is later startup.
|
2019-05-21 10:37:37 +00:00
|
|
|
*
|
|
|
|
* Any errors here are saved to be reported at time
|
|
|
|
* of starting the network though as that makes them
|
|
|
|
* more likely to be seen by a human
|
|
|
|
*/
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
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();
|
2019-05-21 11:40:13 +00:00
|
|
|
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
} else {
|
2020-06-05 17:11:02 +00:00
|
|
|
if (!networkHasRunningNetworksWithFW(driver)) {
|
|
|
|
VIR_DEBUG("Delayed global rule setup as no networks with firewall rules are running");
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-12-05 15:53:55 +00:00
|
|
|
|
network: force re-creation of iptables private chains on firewalld restart
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 <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-05-08 01:54:39 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-05-22 12:08:13 +00:00
|
|
|
}
|
2018-12-05 13:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-14 12:45:33 +00:00
|
|
|
void networkPostReloadFirewallRules(bool startup G_GNUC_UNUSED)
|
2018-12-05 13:29:07 +00:00
|
|
|
{
|
2018-12-05 15:53:55 +00:00
|
|
|
iptablesSetDeletePrivate(true);
|
2018-12-05 13:29:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-24 12:22:54 +00:00
|
|
|
/* XXX: This function can be a lot more exhaustive, there are certainly
|
|
|
|
* other scenarios where we can ruin host network connectivity.
|
|
|
|
* XXX: Using a proper library is preferred over parsing /proc
|
|
|
|
*/
|
2014-03-19 16:56:35 +00:00
|
|
|
int networkCheckRouteCollision(virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
|
|
|
int ret = 0, len;
|
|
|
|
char *cur, *buf = NULL;
|
|
|
|
/* allow for up to 100000 routes (each line is 128 bytes) */
|
|
|
|
enum {MAX_ROUTE_SIZE = 128*100000};
|
|
|
|
|
|
|
|
/* Read whole routing table into memory */
|
|
|
|
if ((len = virFileReadAll(PROC_NET_ROUTE, MAX_ROUTE_SIZE, &buf)) < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Dropping the last character shouldn't hurt */
|
|
|
|
if (len > 0)
|
|
|
|
buf[len-1] = '\0';
|
|
|
|
|
|
|
|
VIR_DEBUG("%s output:\n%s", PROC_NET_ROUTE, buf);
|
|
|
|
|
|
|
|
if (!STRPREFIX(buf, "Iface"))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* First line is just headings, skip it */
|
|
|
|
cur = strchr(buf, '\n');
|
|
|
|
if (cur)
|
|
|
|
cur++;
|
|
|
|
|
|
|
|
while (cur) {
|
|
|
|
char iface[17], dest[128], mask[128];
|
|
|
|
unsigned int addr_val, mask_val;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef;
|
2016-06-14 17:40:04 +00:00
|
|
|
virNetDevIPRoutePtr routedef;
|
2013-07-24 12:22:54 +00:00
|
|
|
int num;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* NUL-terminate the line, so sscanf doesn't go beyond a newline. */
|
|
|
|
char *nl = strchr(cur, '\n');
|
2014-11-13 14:27:11 +00:00
|
|
|
if (nl)
|
2013-07-24 12:22:54 +00:00
|
|
|
*nl++ = '\0';
|
|
|
|
|
|
|
|
num = sscanf(cur, "%16s %127s %*s %*s %*s %*s %*s %127s",
|
|
|
|
iface, dest, mask);
|
|
|
|
cur = nl;
|
|
|
|
|
|
|
|
if (num != 3) {
|
|
|
|
VIR_DEBUG("Failed to parse %s", PROC_NET_ROUTE);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(dest, NULL, 16, &addr_val) < 0) {
|
|
|
|
VIR_DEBUG("Failed to convert network address %s to uint", dest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(mask, NULL, 16, &mask_val) < 0) {
|
|
|
|
VIR_DEBUG("Failed to convert network mask %s to uint", mask);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr_val &= mask_val;
|
|
|
|
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipdef = virNetworkDefGetIPByIndex(def, AF_INET, i));
|
2013-07-24 12:22:54 +00:00
|
|
|
i++) {
|
|
|
|
|
|
|
|
unsigned int net_dest;
|
|
|
|
virSocketAddr netmask;
|
|
|
|
|
2016-06-08 16:48:50 +00:00
|
|
|
if (virNetworkIPDefNetmask(ipdef, &netmask) < 0) {
|
2013-07-24 12:22:54 +00:00
|
|
|
VIR_WARN("Failed to get netmask of '%s'",
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge);
|
2013-07-24 12:22:54 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_dest = (ipdef->address.data.inet4.sin_addr.s_addr &
|
|
|
|
netmask.data.inet4.sin_addr.s_addr);
|
|
|
|
|
|
|
|
if ((net_dest == addr_val) &&
|
|
|
|
(netmask.data.inet4.sin_addr.s_addr == mask_val)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Network is already in use by interface %s"),
|
|
|
|
iface);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2015-07-09 13:53:25 +00:00
|
|
|
|
|
|
|
for (i = 0;
|
|
|
|
(routedef = virNetworkDefGetRouteByIndex(def, AF_INET, i));
|
|
|
|
i++) {
|
|
|
|
|
|
|
|
virSocketAddr r_mask, r_addr;
|
2016-06-14 17:40:04 +00:00
|
|
|
virSocketAddrPtr tmp_addr = virNetDevIPRouteGetAddress(routedef);
|
|
|
|
int r_prefix = virNetDevIPRouteGetPrefix(routedef);
|
2015-07-09 13:53:25 +00:00
|
|
|
|
|
|
|
if (!tmp_addr ||
|
|
|
|
virSocketAddrMaskByPrefix(tmp_addr, r_prefix, &r_addr) < 0 ||
|
|
|
|
virSocketAddrPrefixToNetmask(r_prefix, &r_mask, AF_INET) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((r_addr.data.inet4.sin_addr.s_addr == addr_val) &&
|
|
|
|
(r_mask.data.inet4.sin_addr.s_addr == mask_val)) {
|
|
|
|
char *addr_str = virSocketAddrFormat(&r_addr);
|
|
|
|
if (!addr_str)
|
|
|
|
virResetLastError();
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Route address '%s' conflicts "
|
|
|
|
"with IP address for '%s'"),
|
|
|
|
NULLSTR(addr_str), iface);
|
|
|
|
VIR_FREE(addr_str);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:56:13 +00:00
|
|
|
out:
|
2013-07-24 12:22:54 +00:00
|
|
|
VIR_FREE(buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-06-08 13:40:15 +00:00
|
|
|
static const char networkLocalMulticastIPv4[] = "224.0.0.0/24";
|
|
|
|
static const char networkLocalMulticastIPv6[] = "ff02::/16";
|
2013-09-25 10:45:26 +00:00
|
|
|
static const char networkLocalBroadcast[] = "255.255.255.255/32";
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
static int
|
2014-03-06 17:01:13 +00:00
|
|
|
networkAddMasqueradingFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
int prefix = virNetworkIPDefPrefix(ipdef);
|
2014-03-19 16:56:35 +00:00
|
|
|
const char *forwardIf = virNetworkDefForwardIf(def, 0);
|
2020-06-08 13:40:15 +00:00
|
|
|
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
if (prefix < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid prefix or netmask for '%s'"),
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge);
|
2014-03-06 17:01:13 +00:00
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* allow forwarding packets from the bridge interface */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardAllowOut(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* allow forwarding packets to the bridge interface if they are
|
|
|
|
* part of an existing connection
|
|
|
|
*/
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardAllowRelatedIn(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable masquerading.
|
|
|
|
*
|
2013-09-25 10:45:26 +00:00
|
|
|
* We need to end up with 5 rules in the table in this order
|
|
|
|
*
|
|
|
|
* 1. do not masquerade packets targeting 224.0.0.0/24
|
|
|
|
* 2. do not masquerade packets targeting 255.255.255.255/32
|
|
|
|
* 3. masquerade protocol=tcp with sport mapping restriction
|
|
|
|
* 4. masquerade protocol=udp with sport mapping restriction
|
|
|
|
* 5. generic, masquerade any protocol
|
2013-07-24 12:22:54 +00:00
|
|
|
*
|
2013-09-25 10:45:26 +00:00
|
|
|
* 224.0.0.0/24 is the local network multicast range. Packets are not
|
|
|
|
* forwarded outside.
|
|
|
|
*
|
|
|
|
* 255.255.255.255/32 is the broadcast address of any local network. Again,
|
|
|
|
* such packets are never forwarded, but strict DHCP clients don't accept
|
|
|
|
* DHCP replies with changed source ports.
|
2013-07-24 12:22:54 +00:00
|
|
|
*
|
|
|
|
* The sport mappings are required, because default IPtables
|
|
|
|
* MASQUERADE maintain port numbers unchanged where possible.
|
|
|
|
*
|
|
|
|
* NFS can be configured to only "trust" port numbers < 1023.
|
|
|
|
*
|
|
|
|
* Guests using NAT thus need to be prevented from having port
|
|
|
|
* numbers < 1023, otherwise they can bypass the NFS "security"
|
|
|
|
* check on the source port number.
|
|
|
|
*
|
|
|
|
* Since we use '--insert' to add rules to the header of the
|
|
|
|
* chain, we actually need to add them in the reverse of the
|
|
|
|
* order just mentioned !
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* First the generic masquerade rule for other protocols */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* UDP with a source port restriction */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
"udp") < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* TCP with a source port restriction */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
"tcp") < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2013-09-25 10:45:26 +00:00
|
|
|
/* exempt local network broadcast address as destination */
|
2020-06-08 13:40:15 +00:00
|
|
|
if (isIPv4 &&
|
|
|
|
iptablesAddDontMasquerade(fw,
|
2014-03-06 17:01:13 +00:00
|
|
|
&ipdef->address,
|
2013-09-25 10:45:26 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-06 17:01:13 +00:00
|
|
|
networkLocalBroadcast) < 0)
|
|
|
|
return -1;
|
2013-09-25 10:45:26 +00:00
|
|
|
|
|
|
|
/* exempt local multicast range as destination */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddDontMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-09-25 10:45:26 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2020-06-08 13:40:15 +00:00
|
|
|
isIPv4 ? networkLocalMulticastIPv4 :
|
|
|
|
networkLocalMulticastIPv6) < 0)
|
2014-03-06 17:01:13 +00:00
|
|
|
return -1;
|
2013-09-25 10:45:26 +00:00
|
|
|
|
2013-07-24 12:22:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
static int
|
|
|
|
networkRemoveMasqueradingFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
int prefix = virNetworkIPDefPrefix(ipdef);
|
2014-03-19 16:56:35 +00:00
|
|
|
const char *forwardIf = virNetworkDefForwardIf(def, 0);
|
2020-06-08 13:40:15 +00:00
|
|
|
bool isIPv4 = VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
if (prefix < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (iptablesRemoveDontMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-09-25 10:45:26 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2020-06-08 13:40:15 +00:00
|
|
|
isIPv4 ? networkLocalMulticastIPv4 :
|
|
|
|
networkLocalMulticastIPv6) < 0)
|
2014-03-06 17:01:13 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-06-08 13:40:15 +00:00
|
|
|
if (isIPv4 &&
|
|
|
|
iptablesRemoveDontMasquerade(fw,
|
2014-03-06 17:01:13 +00:00
|
|
|
&ipdef->address,
|
2013-09-25 10:45:26 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-06 17:01:13 +00:00
|
|
|
networkLocalBroadcast) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (iptablesRemoveForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
"tcp") < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (iptablesRemoveForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
"udp") < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (iptablesRemoveForwardMasquerade(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
|
|
|
forwardIf,
|
2014-03-19 16:56:35 +00:00
|
|
|
&def->forward.addr,
|
|
|
|
&def->forward.port,
|
2014-03-06 17:01:13 +00:00
|
|
|
NULL) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesRemoveForwardAllowRelatedIn(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (iptablesRemoveForwardAllowOut(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
static int
|
2014-03-06 17:01:13 +00:00
|
|
|
networkAddRoutingFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
int prefix = virNetworkIPDefPrefix(ipdef);
|
2014-03-19 16:56:35 +00:00
|
|
|
const char *forwardIf = virNetworkDefForwardIf(def, 0);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
if (prefix < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid prefix or netmask for '%s'"),
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge);
|
2014-03-06 17:01:13 +00:00
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* allow routing packets from the bridge interface */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardAllowOut(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* allow routing packets to the bridge interface */
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesAddForwardAllowIn(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
static int
|
|
|
|
networkRemoveRoutingFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
int prefix = virNetworkIPDefPrefix(ipdef);
|
2014-03-19 16:56:35 +00:00
|
|
|
const char *forwardIf = virNetworkDefForwardIf(def, 0);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
if (prefix < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (iptablesRemoveForwardAllowIn(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
if (iptablesRemoveForwardAllowOut(fw,
|
|
|
|
&ipdef->address,
|
2013-07-24 12:22:54 +00:00
|
|
|
prefix,
|
2014-03-19 16:56:35 +00:00
|
|
|
def->bridge,
|
2014-03-06 17:01:13 +00:00
|
|
|
forwardIf) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
networkAddGeneralIPv4FirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2014-03-06 17:01:13 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipv4def;
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
|
|
|
|
/* We support dhcp config on 1 IPv4 interface only. */
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
|
2014-03-06 17:01:13 +00:00
|
|
|
i++) {
|
|
|
|
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-09-27 16:10:34 +00:00
|
|
|
/* allow DHCP requests through to dnsmasq & back out */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
|
|
|
|
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2019-09-27 16:10:34 +00:00
|
|
|
/* allow DNS requests through to dnsmasq & back out */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
|
|
|
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
|
|
|
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2020-01-24 20:30:04 +00:00
|
|
|
/* allow TFTP requests through to dnsmasq if necessary & back out */
|
2019-09-27 16:10:34 +00:00
|
|
|
if (ipv4def && ipv4def->tftproot) {
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
|
|
|
|
}
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
/* Catch all rules to block forwarding to/from bridges */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
|
|
|
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
/* Allow traffic between guests on the same bridge */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
2014-03-06 17:01:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
networkRemoveGeneralIPv4FirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2014-03-06 17:01:13 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipv4def;
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
|
2014-03-06 17:01:13 +00:00
|
|
|
i++) {
|
|
|
|
if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot)
|
|
|
|
break;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
|
|
|
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
|
|
|
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2019-09-27 16:10:34 +00:00
|
|
|
if (ipv4def && ipv4def->tftproot) {
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
|
|
|
|
}
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
|
|
|
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
|
|
|
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
|
|
|
|
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2013-07-24 12:22:54 +00:00
|
|
|
/* Add all once/network rules required for IPv6.
|
|
|
|
* If no IPv6 addresses are defined and <network ipv6='yes'> is
|
2015-03-19 15:53:00 +00:00
|
|
|
* specified, then allow IPv6 communications between virtual systems.
|
2013-07-24 12:22:54 +00:00
|
|
|
* If any IPv6 addresses are defined, then add the rules for regular operation.
|
|
|
|
*/
|
2014-03-06 17:01:13 +00:00
|
|
|
static void
|
|
|
|
networkAddGeneralIPv6FirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
|
2014-03-19 16:56:35 +00:00
|
|
|
!def->ipv6nogw) {
|
2014-03-06 17:01:13 +00:00
|
|
|
return;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Catch all rules to block forwarding to/from bridges */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
|
|
|
iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* Allow traffic between guests on the same bridge */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2016-06-08 16:48:50 +00:00
|
|
|
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
|
2019-09-27 16:10:34 +00:00
|
|
|
/* allow DNS over IPv6 & back out */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
|
|
|
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
|
|
|
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
|
|
|
/* allow DHCPv6 & back out */
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-03-06 17:01:13 +00:00
|
|
|
networkRemoveGeneralIPv6FirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2016-06-08 16:48:50 +00:00
|
|
|
if (!virNetworkDefGetIPByIndex(def, AF_INET6, 0) &&
|
2014-03-19 16:56:35 +00:00
|
|
|
!def->ipv6nogw) {
|
2013-07-24 12:22:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-03-06 17:01:13 +00:00
|
|
|
|
2016-06-08 16:48:50 +00:00
|
|
|
if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
|
2019-09-27 16:10:34 +00:00
|
|
|
iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
|
|
|
iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
|
|
|
iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* the following rules are there if no IPv6 address has been defined
|
2014-03-19 16:56:35 +00:00
|
|
|
* but def->ipv6nogw == true
|
2013-07-24 12:22:54 +00:00
|
|
|
*/
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
|
|
|
iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
|
|
|
iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
static void
|
|
|
|
networkAddGeneralFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2014-03-06 17:01:13 +00:00
|
|
|
{
|
2014-03-19 16:56:35 +00:00
|
|
|
networkAddGeneralIPv4FirewallRules(fw, def);
|
|
|
|
networkAddGeneralIPv6FirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
networkRemoveGeneralFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2014-03-06 17:01:13 +00:00
|
|
|
{
|
2014-03-19 16:56:35 +00:00
|
|
|
networkRemoveGeneralIPv4FirewallRules(fw, def);
|
|
|
|
networkRemoveGeneralIPv6FirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
networkAddChecksumFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipv4def;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
|
|
|
|
/* We support dhcp config on 1 IPv4 interface only. */
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
|
2013-07-24 12:22:54 +00:00
|
|
|
i++) {
|
2014-03-06 17:01:13 +00:00
|
|
|
if (ipv4def->nranges || ipv4def->nhosts)
|
2013-07-24 12:22:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are doing local DHCP service on this network, attempt to
|
|
|
|
* add a rule that will fixup the checksum of DHCP response
|
|
|
|
* packets back to the guests (but report failure without
|
|
|
|
* aborting, since not all iptables implementations support it).
|
|
|
|
*/
|
2014-03-06 17:01:13 +00:00
|
|
|
if (ipv4def)
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesAddOutputFixUdpChecksum(fw, def->bridge, 68);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
|
|
|
static void
|
2014-03-06 17:01:13 +00:00
|
|
|
networkRemoveChecksumFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipv4def;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
/* First look for first IPv4 address that has dhcp or tftpboot defined. */
|
|
|
|
/* We support dhcp config on 1 IPv4 interface only. */
|
2013-07-24 12:22:54 +00:00
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipv4def = virNetworkDefGetIPByIndex(def, AF_INET, i));
|
2013-07-24 12:22:54 +00:00
|
|
|
i++) {
|
2014-03-06 17:01:13 +00:00
|
|
|
if (ipv4def->nranges || ipv4def->nhosts)
|
2013-07-24 12:22:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
if (ipv4def)
|
2014-03-19 16:56:35 +00:00
|
|
|
iptablesRemoveOutputFixUdpChecksum(fw, def->bridge, 68);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
|
|
|
static int
|
2016-06-08 16:48:50 +00:00
|
|
|
networkAddIPSpecificFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
|
|
|
/* NB: in the case of IPv6, routing rules are added when the
|
|
|
|
* forward mode is NAT. This is because IPv6 has no NAT.
|
|
|
|
*/
|
|
|
|
|
2014-03-19 16:56:35 +00:00
|
|
|
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
|
2020-06-08 13:40:15 +00:00
|
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
|
|
|
|
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
|
2014-03-19 16:56:35 +00:00
|
|
|
return networkAddMasqueradingFirewallRules(fw, def, ipdef);
|
2013-07-24 12:22:54 +00:00
|
|
|
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
|
2014-03-19 16:56:35 +00:00
|
|
|
return networkAddRoutingFirewallRules(fw, def, ipdef);
|
|
|
|
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
|
|
|
|
return networkAddRoutingFirewallRules(fw, def, ipdef);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
static int
|
2016-06-08 16:48:50 +00:00
|
|
|
networkRemoveIPSpecificFirewallRules(virFirewallPtr fw,
|
2014-03-19 16:56:35 +00:00
|
|
|
virNetworkDefPtr def,
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2014-03-19 16:56:35 +00:00
|
|
|
if (def->forward.type == VIR_NETWORK_FORWARD_NAT) {
|
2020-06-08 13:40:15 +00:00
|
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) ||
|
|
|
|
def->forward.natIPv6 == VIR_TRISTATE_BOOL_YES)
|
2014-03-19 16:56:35 +00:00
|
|
|
return networkRemoveMasqueradingFirewallRules(fw, def, ipdef);
|
2013-07-24 12:22:54 +00:00
|
|
|
else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
|
2014-03-19 16:56:35 +00:00
|
|
|
return networkRemoveRoutingFirewallRules(fw, def, ipdef);
|
|
|
|
} else if (def->forward.type == VIR_NETWORK_FORWARD_ROUTE) {
|
|
|
|
return networkRemoveRoutingFirewallRules(fw, def, ipdef);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
2014-03-06 17:01:13 +00:00
|
|
|
return 0;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 12:03:46 +00:00
|
|
|
|
2013-07-24 12:22:54 +00:00
|
|
|
/* Add all rules for all ip addresses (and general rules) on a network */
|
2014-03-19 16:56:35 +00:00
|
|
|
int networkAddFirewallRules(virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
2014-03-06 17:01:13 +00:00
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef;
|
2014-03-06 17:01:13 +00:00
|
|
|
virFirewallPtr fw = NULL;
|
|
|
|
int ret = -1;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2019-05-21 11:40:13 +00:00
|
|
|
if (virOnce(&createdOnce, networkSetupPrivateChains) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-03-18 16:49:32 +00:00
|
|
|
if (errInitV4 &&
|
|
|
|
(virNetworkDefGetIPByIndex(def, AF_INET, 0) ||
|
|
|
|
virNetworkDefGetRouteByIndex(def, AF_INET, 0))) {
|
|
|
|
virSetError(errInitV4);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errInitV6 &&
|
|
|
|
(virNetworkDefGetIPByIndex(def, AF_INET6, 0) ||
|
|
|
|
virNetworkDefGetRouteByIndex(def, AF_INET6, 0) ||
|
|
|
|
def->ipv6nogw)) {
|
|
|
|
virSetError(errInitV6);
|
2019-03-18 17:31:21 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-01-09 21:51:31 +00:00
|
|
|
if (def->bridgeZone) {
|
|
|
|
|
|
|
|
/* if a firewalld zone has been specified, fail/log an error
|
|
|
|
* if we can't honor it
|
|
|
|
*/
|
|
|
|
if (virFirewallDIsRegistered() < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("zone %s requested for network %s "
|
|
|
|
"but firewalld is not active"),
|
|
|
|
def->bridgeZone, def->name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFirewallDInterfaceSetZone(def->bridge, def->bridgeZone) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* if firewalld is active, try to set the "libvirt" zone. This is
|
|
|
|
* desirable (for consistency) if firewalld is using the iptables
|
|
|
|
* backend, but is necessary (for basic network connectivity) if
|
|
|
|
* firewalld is using the nftables backend
|
network: set firewalld zone of bridges to "libvirt" zone when appropriate
This patch restores broken guest network connectivity after a host
firewalld is switched to using an nftables backend. It does this by
adding libvirt networks' bridge interfaces to the new "libvirt" zone
in firewalld.
After this patch, the bridge interface of any network created by
libvirt (when firewalld is active) will be added to the firewalld
zone called "libvirt" if it exists (regardless of the firewalld
backend setting). This behavior does *not* depend on whether or not
libvirt has installed the libvirt zone file (set with
"--with[out]-firewalld-zone" during the configure phase of the package
build).
If the libvirt zone doesn't exist (either because the package was
configured to not install it, or possibly it was installed, but
firewalld doesn't support rule priorities, resulting in a parse
error), the bridge will remain in firewalld's default zone, which
could be innocuous (in the case that the firewalld backend is
iptables, guest networking will still function properly with the
bridge in the default zone), or it could be disastrous (if the
firewalld backend is nftables, we can be assured that guest networking
will fail). In order to be unobtrusive in the former case, and
informative in the latter, when the libvirt zone doesn't exist we
then check the firewalld version to see if it's new enough to support
the nftables backend, and then if the backend is actually set to
nftables, before logging an error (and failing the net-start
operation, since the network couldn't possibly work anyway).
When the libvirt zone is used, network behavior is *slightly*
different from behavior of previous libvirt. In the past, libvirt
network behavior would be affected by the configuration of firewalld's
default zone (usually "public"), but now it is affected only by the
"libvirt" zone), and thus almost surely warrants a release note for
any distro upgrading to libvirt 5.1 or above. Although it's
unfortunate that we have to deal with a mandatory behavior change, the
architecture of multiple hooks makes it impossible to *not* change
behavior in some way, and the new behavior is arguably better (since
it will now be possible to manage access to the host from virtual
machines vs from public interfaces separately).
Creates-and-Resolves: https://bugzilla.redhat.com/1650320
Resolves: https://bugzilla.redhat.com/1638342
Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2018-10-16 00:31:02 +00:00
|
|
|
*/
|
2019-01-09 21:51:31 +00:00
|
|
|
if (virFirewallDIsRegistered() == 0) {
|
|
|
|
|
|
|
|
/* if the "libvirt" zone exists, then set it. If not, and
|
|
|
|
* if firewalld is using the nftables backend, then we
|
|
|
|
* need to log an error because the combination of
|
|
|
|
* nftables + default zone means that traffic cannot be
|
|
|
|
* forwarded (and even DHCP and DNS from guest to host
|
|
|
|
* will probably no be permitted by the default zone
|
network: set firewalld zone of bridges to "libvirt" zone when appropriate
This patch restores broken guest network connectivity after a host
firewalld is switched to using an nftables backend. It does this by
adding libvirt networks' bridge interfaces to the new "libvirt" zone
in firewalld.
After this patch, the bridge interface of any network created by
libvirt (when firewalld is active) will be added to the firewalld
zone called "libvirt" if it exists (regardless of the firewalld
backend setting). This behavior does *not* depend on whether or not
libvirt has installed the libvirt zone file (set with
"--with[out]-firewalld-zone" during the configure phase of the package
build).
If the libvirt zone doesn't exist (either because the package was
configured to not install it, or possibly it was installed, but
firewalld doesn't support rule priorities, resulting in a parse
error), the bridge will remain in firewalld's default zone, which
could be innocuous (in the case that the firewalld backend is
iptables, guest networking will still function properly with the
bridge in the default zone), or it could be disastrous (if the
firewalld backend is nftables, we can be assured that guest networking
will fail). In order to be unobtrusive in the former case, and
informative in the latter, when the libvirt zone doesn't exist we
then check the firewalld version to see if it's new enough to support
the nftables backend, and then if the backend is actually set to
nftables, before logging an error (and failing the net-start
operation, since the network couldn't possibly work anyway).
When the libvirt zone is used, network behavior is *slightly*
different from behavior of previous libvirt. In the past, libvirt
network behavior would be affected by the configuration of firewalld's
default zone (usually "public"), but now it is affected only by the
"libvirt" zone), and thus almost surely warrants a release note for
any distro upgrading to libvirt 5.1 or above. Although it's
unfortunate that we have to deal with a mandatory behavior change, the
architecture of multiple hooks makes it impossible to *not* change
behavior in some way, and the new behavior is arguably better (since
it will now be possible to manage access to the host from virtual
machines vs from public interfaces separately).
Creates-and-Resolves: https://bugzilla.redhat.com/1650320
Resolves: https://bugzilla.redhat.com/1638342
Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2018-10-16 00:31:02 +00:00
|
|
|
*/
|
2019-01-09 21:51:31 +00:00
|
|
|
if (virFirewallDZoneExists("libvirt")) {
|
|
|
|
if (virFirewallDInterfaceSetZone(def->bridge, "libvirt") < 0)
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
unsigned long version;
|
|
|
|
int vresult = virFirewallDGetVersion(&version);
|
|
|
|
|
|
|
|
if (vresult < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Support for nftables backend was added in firewalld
|
|
|
|
* 0.6.0. Support for rule priorities (required by the
|
|
|
|
* 'libvirt' zone, which should be installed by a
|
|
|
|
* libvirt package, *not* by firewalld) was not added
|
|
|
|
* until firewalld 0.7.0 (unless it was backported).
|
|
|
|
*/
|
|
|
|
if (version >= 6000 &&
|
|
|
|
virFirewallDGetBackend() == VIR_FIREWALLD_BACKEND_NFTABLES) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("firewalld is set to use the nftables "
|
|
|
|
"backend, but the required firewalld "
|
|
|
|
"'libvirt' zone is missing. Either set "
|
|
|
|
"the firewalld backend to 'iptables', or "
|
|
|
|
"ensure that firewalld has a 'libvirt' "
|
|
|
|
"zone by upgrading firewalld to a "
|
|
|
|
"version supporting rule priorities "
|
|
|
|
"(0.7.0+) and/or rebuilding "
|
|
|
|
"libvirt with --with-firewalld-zone"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
network: set firewalld zone of bridges to "libvirt" zone when appropriate
This patch restores broken guest network connectivity after a host
firewalld is switched to using an nftables backend. It does this by
adding libvirt networks' bridge interfaces to the new "libvirt" zone
in firewalld.
After this patch, the bridge interface of any network created by
libvirt (when firewalld is active) will be added to the firewalld
zone called "libvirt" if it exists (regardless of the firewalld
backend setting). This behavior does *not* depend on whether or not
libvirt has installed the libvirt zone file (set with
"--with[out]-firewalld-zone" during the configure phase of the package
build).
If the libvirt zone doesn't exist (either because the package was
configured to not install it, or possibly it was installed, but
firewalld doesn't support rule priorities, resulting in a parse
error), the bridge will remain in firewalld's default zone, which
could be innocuous (in the case that the firewalld backend is
iptables, guest networking will still function properly with the
bridge in the default zone), or it could be disastrous (if the
firewalld backend is nftables, we can be assured that guest networking
will fail). In order to be unobtrusive in the former case, and
informative in the latter, when the libvirt zone doesn't exist we
then check the firewalld version to see if it's new enough to support
the nftables backend, and then if the backend is actually set to
nftables, before logging an error (and failing the net-start
operation, since the network couldn't possibly work anyway).
When the libvirt zone is used, network behavior is *slightly*
different from behavior of previous libvirt. In the past, libvirt
network behavior would be affected by the configuration of firewalld's
default zone (usually "public"), but now it is affected only by the
"libvirt" zone), and thus almost surely warrants a release note for
any distro upgrading to libvirt 5.1 or above. Although it's
unfortunate that we have to deal with a mandatory behavior change, the
architecture of multiple hooks makes it impossible to *not* change
behavior in some way, and the new behavior is arguably better (since
it will now be possible to manage access to the host from virtual
machines vs from public interfaces separately).
Creates-and-Resolves: https://bugzilla.redhat.com/1650320
Resolves: https://bugzilla.redhat.com/1638342
Signed-off-by: Laine Stump <laine@laine.org>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2018-10-16 00:31:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
fw = virFirewallNew();
|
|
|
|
|
|
|
|
virFirewallStartTransaction(fw, 0);
|
|
|
|
|
2014-03-19 16:56:35 +00:00
|
|
|
networkAddGeneralFirewallRules(fw, def);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
|
2013-07-24 12:22:54 +00:00
|
|
|
i++) {
|
2016-06-08 16:48:50 +00:00
|
|
|
if (networkAddIPSpecificFirewallRules(fw, def, ipdef) < 0)
|
2014-03-06 17:01:13 +00:00
|
|
|
goto cleanup;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
virFirewallStartRollback(fw, 0);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
|
2014-03-06 17:01:13 +00:00
|
|
|
i++) {
|
2016-06-08 16:48:50 +00:00
|
|
|
if (networkRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
|
2014-03-06 17:01:13 +00:00
|
|
|
goto cleanup;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
2014-03-19 16:56:35 +00:00
|
|
|
networkRemoveGeneralFirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
|
2014-03-19 16:56:35 +00:00
|
|
|
networkAddChecksumFirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
if (virFirewallApply(fw) < 0)
|
|
|
|
goto cleanup;
|
2013-07-24 12:22:54 +00:00
|
|
|
|
2014-03-06 17:01:13 +00:00
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virFirewallFree(fw);
|
|
|
|
return ret;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove all rules for all ip addresses (and general rules) on a network */
|
2014-03-19 16:56:35 +00:00
|
|
|
void networkRemoveFirewallRules(virNetworkDefPtr def)
|
2013-07-24 12:22:54 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2016-06-08 16:48:50 +00:00
|
|
|
virNetworkIPDefPtr ipdef;
|
2014-03-06 17:01:13 +00:00
|
|
|
virFirewallPtr fw = NULL;
|
|
|
|
|
|
|
|
fw = virFirewallNew();
|
|
|
|
|
|
|
|
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
|
2014-03-19 16:56:35 +00:00
|
|
|
networkRemoveChecksumFirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
|
2013-07-24 12:22:54 +00:00
|
|
|
|
|
|
|
for (i = 0;
|
2016-06-08 16:48:50 +00:00
|
|
|
(ipdef = virNetworkDefGetIPByIndex(def, AF_UNSPEC, i));
|
2013-07-24 12:22:54 +00:00
|
|
|
i++) {
|
2016-06-08 16:48:50 +00:00
|
|
|
if (networkRemoveIPSpecificFirewallRules(fw, def, ipdef) < 0)
|
2014-03-06 17:01:13 +00:00
|
|
|
goto cleanup;
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|
2014-03-19 16:56:35 +00:00
|
|
|
networkRemoveGeneralFirewallRules(fw, def);
|
2014-03-06 17:01:13 +00:00
|
|
|
|
|
|
|
virFirewallApply(fw);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virFirewallFree(fw);
|
2013-07-24 12:22:54 +00:00
|
|
|
}
|