2013-03-04 16:30:40 +00:00
|
|
|
/*
|
|
|
|
* virfirewall.c: integration with firewalls
|
|
|
|
*
|
2015-05-21 13:36:18 -04:00
|
|
|
* Copyright (C) 2013-2015 Red Hat, Inc.
|
2013-03-04 16:30:40 +00:00
|
|
|
*
|
|
|
|
* 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 <stdarg.h>
|
|
|
|
|
2018-12-13 14:53:50 +00:00
|
|
|
#define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW
|
2013-03-04 16:30:40 +00:00
|
|
|
#include "virfirewallpriv.h"
|
2019-01-09 14:11:32 -05:00
|
|
|
#include "virfirewalld.h"
|
2019-04-01 16:28:05 +02:00
|
|
|
#include "viralloc.h"
|
2013-03-04 16:30:40 +00:00
|
|
|
#include "virerror.h"
|
|
|
|
#include "virstring.h"
|
|
|
|
#include "vircommand.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virthread.h"
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_FIREWALL
|
|
|
|
|
|
|
|
VIR_LOG_INIT("util.firewall");
|
|
|
|
|
|
|
|
typedef struct _virFirewallGroup virFirewallGroup;
|
|
|
|
|
2019-01-20 11:04:56 -05:00
|
|
|
VIR_ENUM_DECL(virFirewallLayerCommand);
|
2019-03-16 14:20:32 -04:00
|
|
|
VIR_ENUM_IMPL(virFirewallLayerCommand,
|
|
|
|
VIR_FIREWALL_LAYER_LAST,
|
2013-03-04 16:30:40 +00:00
|
|
|
EBTABLES_PATH,
|
|
|
|
IPTABLES_PATH,
|
2019-01-20 11:30:15 -05:00
|
|
|
IP6TABLES_PATH,
|
|
|
|
);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
struct _virFirewallRule {
|
|
|
|
virFirewallLayer layer;
|
|
|
|
|
|
|
|
virFirewallQueryCallback queryCB;
|
|
|
|
void *queryOpaque;
|
|
|
|
bool ignoreErrors;
|
|
|
|
|
|
|
|
size_t argsAlloc;
|
|
|
|
size_t argsLen;
|
|
|
|
char **args;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _virFirewallGroup {
|
|
|
|
unsigned int actionFlags;
|
|
|
|
unsigned int rollbackFlags;
|
|
|
|
|
|
|
|
size_t naction;
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRule **action;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
size_t nrollback;
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRule **rollback;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
bool addingRollback;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct _virFirewall {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
size_t ngroups;
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup **groups;
|
2013-03-04 16:30:40 +00:00
|
|
|
size_t currentGroup;
|
|
|
|
};
|
|
|
|
|
|
|
|
static virFirewallBackend currentBackend = VIR_FIREWALL_BACKEND_AUTOMATIC;
|
|
|
|
static virMutex ruleLock = VIR_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
static int
|
|
|
|
virFirewallValidateBackend(virFirewallBackend backend);
|
|
|
|
|
|
|
|
static int
|
|
|
|
virFirewallOnceInit(void)
|
|
|
|
{
|
|
|
|
return virFirewallValidateBackend(currentBackend);
|
|
|
|
}
|
|
|
|
|
2019-01-20 12:23:29 -05:00
|
|
|
VIR_ONCE_GLOBAL_INIT(virFirewall);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
virFirewallValidateBackend(virFirewallBackend backend)
|
|
|
|
{
|
2020-11-16 20:17:05 -05:00
|
|
|
const char *commands[] = {
|
|
|
|
IPTABLES_PATH, IP6TABLES_PATH, EBTABLES_PATH
|
|
|
|
};
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(commands); i++) {
|
|
|
|
if (!virFileIsExecutable(commands[i])) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("%s not available, firewall backend will not function"),
|
|
|
|
commands[i]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VIR_DEBUG("found iptables/ip6tables/ebtables");
|
|
|
|
|
2013-03-04 16:30:40 +00:00
|
|
|
if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC ||
|
|
|
|
backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
|
2019-01-09 14:11:32 -05:00
|
|
|
int rv = virFirewallDIsRegistered();
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("Firewalld is registered ? %d", rv);
|
2020-11-16 20:17:05 -05:00
|
|
|
|
|
|
|
if (rv == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (rv == -2) {
|
|
|
|
if (backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("firewalld backend requested, but service is not running"));
|
2013-03-04 16:30:40 +00:00
|
|
|
return -1;
|
2020-11-16 20:17:05 -05:00
|
|
|
} else {
|
|
|
|
VIR_DEBUG("firewalld service not running, using direct backend");
|
|
|
|
backend = VIR_FIREWALL_BACKEND_DIRECT;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("firewalld service running, using firewalld backend");
|
|
|
|
backend = VIR_FIREWALL_BACKEND_FIREWALLD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
currentBackend = backend;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virFirewallSetBackend(virFirewallBackend backend)
|
|
|
|
{
|
|
|
|
currentBackend = backend;
|
|
|
|
|
|
|
|
if (virFirewallInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return virFirewallValidateBackend(backend);
|
|
|
|
}
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virFirewallGroup *
|
2013-03-04 16:30:40 +00:00
|
|
|
virFirewallGroupNew(void)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
2020-10-05 19:09:12 +02:00
|
|
|
group = g_new0(virFirewallGroup, 1);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallNew:
|
|
|
|
*
|
|
|
|
* Creates a new firewall ruleset for changing rules
|
|
|
|
* of @layer. This should be followed by a call to
|
|
|
|
* virFirewallStartTransaction before adding
|
|
|
|
* any rules
|
|
|
|
*
|
|
|
|
* Returns the new firewall ruleset
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewall *virFirewallNew(void)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewall *firewall;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
2014-11-11 12:34:57 +00:00
|
|
|
if (virFirewallInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2020-10-05 19:09:12 +02:00
|
|
|
firewall = g_new0(virFirewall, 1);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
return firewall;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRuleFree(virFirewallRule *rule)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!rule)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < rule->argsLen; i++)
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(rule->args[i]);
|
|
|
|
g_free(rule->args);
|
|
|
|
g_free(rule);
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroupFree(virFirewallGroup *group)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!group)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < group->naction; i++)
|
|
|
|
virFirewallRuleFree(group->action[i]);
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(group->action);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
for (i = 0; i < group->nrollback; i++)
|
|
|
|
virFirewallRuleFree(group->rollback[i]);
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(group->rollback);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(group);
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallFree:
|
|
|
|
*
|
|
|
|
* Release all memory associated with the firewall
|
|
|
|
* ruleset
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallFree(virFirewall *firewall)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!firewall)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < firewall->ngroups; i++)
|
|
|
|
virFirewallGroupFree(firewall->groups[i]);
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(firewall->groups);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
2021-02-03 14:32:34 -05:00
|
|
|
g_free(firewall);
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 13:09:47 +01:00
|
|
|
#define VIR_FIREWALL_RETURN_IF_ERROR(firewall) \
|
|
|
|
do { \
|
|
|
|
if (!firewall || firewall->err) \
|
|
|
|
return; \
|
2013-03-04 16:30:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule)\
|
2017-11-03 13:09:47 +01:00
|
|
|
do { \
|
|
|
|
if (!firewall || firewall->err || !rule) \
|
|
|
|
return; \
|
2013-03-04 16:30:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
2017-11-03 13:09:47 +01:00
|
|
|
#define VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall) \
|
|
|
|
do { \
|
|
|
|
if (!firewall || firewall->err) \
|
|
|
|
return NULL; \
|
2013-03-04 16:30:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
2017-11-03 13:09:47 +01:00
|
|
|
#define ADD_ARG(rule, str) \
|
|
|
|
do { \
|
2021-03-20 00:37:01 +01:00
|
|
|
VIR_RESIZE_N(rule->args, rule->argsAlloc, rule->argsLen, 1); \
|
2019-10-20 13:49:46 +02:00
|
|
|
rule->args[rule->argsLen++] = g_strdup(str); \
|
2013-03-04 16:30:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virFirewallRule *
|
|
|
|
virFirewallAddRuleFullV(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
virFirewallLayer layer,
|
|
|
|
bool ignoreErrors,
|
|
|
|
virFirewallQueryCallback cb,
|
|
|
|
void *opaque,
|
|
|
|
va_list args)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group;
|
|
|
|
virFirewallRule *rule;
|
2013-03-04 16:30:40 +00:00
|
|
|
char *str;
|
|
|
|
|
|
|
|
VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall);
|
|
|
|
|
|
|
|
if (firewall->ngroups == 0) {
|
2014-04-30 08:51:29 +02:00
|
|
|
firewall->err = EINVAL;
|
2013-03-04 16:30:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
group = firewall->groups[firewall->currentGroup];
|
|
|
|
|
|
|
|
|
2020-10-05 19:09:12 +02:00
|
|
|
rule = g_new0(virFirewallRule, 1);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
rule->layer = layer;
|
|
|
|
rule->queryCB = cb;
|
|
|
|
rule->queryOpaque = opaque;
|
|
|
|
rule->ignoreErrors = ignoreErrors;
|
|
|
|
|
2014-11-11 12:34:57 +00:00
|
|
|
switch (rule->layer) {
|
|
|
|
case VIR_FIREWALL_LAYER_ETHERNET:
|
util/tests: enable locking on iptables/ebtables commandlines by default
iptables and ip6tables have had a "-w" commandline option to grab a
systemwide lock that prevents two iptables invocations from modifying
the iptables chains since 2013 (upstream commit 93587a04 in
iptables-1.4.20). Similarly, ebtables has had a "--concurrent"
commandline option for the same purpose since 2011 (in the upstream
ebtables commit f9b4bcb93, which was present in ebtables-2.0.10.4).
Libvirt added code to conditionally use the commandline option for
iptables/ip6tables in upstream commit ba95426d6f (libvirt-1.2.0,
November 2013), and for ebtables in upstream commit dc33e6e4a5
(libvirt-1.2.11, November 2014) (the latter actually *re*-added the
locking for iptables/ip6tables, as it had accidentally been removed
during a refactor of firewall code in the interim).
I say "conditionally" because a check was made during firewall module
initialization that tried executing a test command with the
-w/--concurrent option, and only continued using it for actual
commands if that test command completed successfully. At the time the
code was added this was a reasonable thing to do, as it had been less
than a year since introduction of -w to iptables, so many distros
supported by libvirt were still using iptables (and possibly even
ebtables) versions too old to have the new commandline options.
It is now 2020, and as far as I can discern from repology.org (and
manually examining a RHEL7.9 system), every version of every distro
that is supported by libvirt now uses new enough versions of both
iptables and ebtables that they all have support for -w/--concurrent.
That means we can finally remove the conditional code and simply
always use them.
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-16 20:02:43 -05:00
|
|
|
ADD_ARG(rule, "--concurrent");
|
2014-11-11 12:34:57 +00:00
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_LAYER_IPV4:
|
util/tests: enable locking on iptables/ebtables commandlines by default
iptables and ip6tables have had a "-w" commandline option to grab a
systemwide lock that prevents two iptables invocations from modifying
the iptables chains since 2013 (upstream commit 93587a04 in
iptables-1.4.20). Similarly, ebtables has had a "--concurrent"
commandline option for the same purpose since 2011 (in the upstream
ebtables commit f9b4bcb93, which was present in ebtables-2.0.10.4).
Libvirt added code to conditionally use the commandline option for
iptables/ip6tables in upstream commit ba95426d6f (libvirt-1.2.0,
November 2013), and for ebtables in upstream commit dc33e6e4a5
(libvirt-1.2.11, November 2014) (the latter actually *re*-added the
locking for iptables/ip6tables, as it had accidentally been removed
during a refactor of firewall code in the interim).
I say "conditionally" because a check was made during firewall module
initialization that tried executing a test command with the
-w/--concurrent option, and only continued using it for actual
commands if that test command completed successfully. At the time the
code was added this was a reasonable thing to do, as it had been less
than a year since introduction of -w to iptables, so many distros
supported by libvirt were still using iptables (and possibly even
ebtables) versions too old to have the new commandline options.
It is now 2020, and as far as I can discern from repology.org (and
manually examining a RHEL7.9 system), every version of every distro
that is supported by libvirt now uses new enough versions of both
iptables and ebtables that they all have support for -w/--concurrent.
That means we can finally remove the conditional code and simply
always use them.
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-16 20:02:43 -05:00
|
|
|
ADD_ARG(rule, "-w");
|
2014-11-11 12:34:57 +00:00
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_LAYER_IPV6:
|
util/tests: enable locking on iptables/ebtables commandlines by default
iptables and ip6tables have had a "-w" commandline option to grab a
systemwide lock that prevents two iptables invocations from modifying
the iptables chains since 2013 (upstream commit 93587a04 in
iptables-1.4.20). Similarly, ebtables has had a "--concurrent"
commandline option for the same purpose since 2011 (in the upstream
ebtables commit f9b4bcb93, which was present in ebtables-2.0.10.4).
Libvirt added code to conditionally use the commandline option for
iptables/ip6tables in upstream commit ba95426d6f (libvirt-1.2.0,
November 2013), and for ebtables in upstream commit dc33e6e4a5
(libvirt-1.2.11, November 2014) (the latter actually *re*-added the
locking for iptables/ip6tables, as it had accidentally been removed
during a refactor of firewall code in the interim).
I say "conditionally" because a check was made during firewall module
initialization that tried executing a test command with the
-w/--concurrent option, and only continued using it for actual
commands if that test command completed successfully. At the time the
code was added this was a reasonable thing to do, as it had been less
than a year since introduction of -w to iptables, so many distros
supported by libvirt were still using iptables (and possibly even
ebtables) versions too old to have the new commandline options.
It is now 2020, and as far as I can discern from repology.org (and
manually examining a RHEL7.9 system), every version of every distro
that is supported by libvirt now uses new enough versions of both
iptables and ebtables that they all have support for -w/--concurrent.
That means we can finally remove the conditional code and simply
always use them.
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-16 20:02:43 -05:00
|
|
|
ADD_ARG(rule, "-w");
|
2014-11-11 12:34:57 +00:00
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_LAYER_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-11-13 15:28:18 +01:00
|
|
|
while ((str = va_arg(args, char *)) != NULL)
|
2013-03-04 16:30:40 +00:00
|
|
|
ADD_ARG(rule, str);
|
|
|
|
|
|
|
|
if (group->addingRollback) {
|
2021-02-23 17:38:18 +01:00
|
|
|
ignore_value(VIR_APPEND_ELEMENT_COPY(group->rollback,
|
|
|
|
group->nrollback,
|
|
|
|
rule));
|
2013-03-04 16:30:40 +00:00
|
|
|
} else {
|
2021-02-23 17:38:18 +01:00
|
|
|
ignore_value(VIR_APPEND_ELEMENT_COPY(group->action,
|
|
|
|
group->naction,
|
|
|
|
rule));
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallAddRuleFull:
|
|
|
|
* @firewall: firewall ruleset to add to
|
|
|
|
* @layer: the firewall layer to change
|
|
|
|
* @ignoreErrors: true to ignore failure of the command
|
|
|
|
* @cb: callback to invoke with result of query
|
|
|
|
* @opaque: data passed into @cb
|
|
|
|
* @...: NULL terminated list of strings for the rule
|
|
|
|
*
|
|
|
|
* Add any type of rule to the firewall ruleset. Any output
|
|
|
|
* generated by the addition will be fed into the query
|
|
|
|
* callback @cb. This callback is permitted to create new
|
|
|
|
* rules by invoking the virFirewallAddRule method, but
|
|
|
|
* is not permitted to start new transactions.
|
|
|
|
*
|
|
|
|
* If @ignoreErrors is set to TRUE, then any failure of
|
|
|
|
* the command is ignored. If it is set to FALSE, then
|
|
|
|
* the behaviour upon failure is determined by the flags
|
|
|
|
* set when the transaction was started.
|
|
|
|
*
|
|
|
|
* Returns the new rule
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRule *virFirewallAddRuleFull(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
virFirewallLayer layer,
|
|
|
|
bool ignoreErrors,
|
|
|
|
virFirewallQueryCallback cb,
|
|
|
|
void *opaque,
|
|
|
|
...)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRule *rule;
|
2013-03-04 16:30:40 +00:00
|
|
|
va_list args;
|
|
|
|
va_start(args, opaque);
|
|
|
|
rule = virFirewallAddRuleFullV(firewall, layer, ignoreErrors, cb, opaque, args);
|
|
|
|
va_end(args);
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallRemoveRule:
|
|
|
|
* @firewall: firewall ruleset to remove from
|
|
|
|
* @rule: the rule to remove
|
|
|
|
*
|
|
|
|
* Remove a rule from the current transaction
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallRemoveRule(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
/* Explicitly not checking firewall->err too,
|
|
|
|
* because if rule was partially created
|
|
|
|
* before hitting error we must still remove
|
|
|
|
* it to avoid leaking 'rule'
|
|
|
|
*/
|
|
|
|
if (!firewall)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (firewall->ngroups == 0)
|
|
|
|
return;
|
|
|
|
group = firewall->groups[firewall->currentGroup];
|
|
|
|
|
|
|
|
if (group->addingRollback) {
|
|
|
|
for (i = 0; i < group->nrollback; i++) {
|
|
|
|
if (group->rollback[i] == rule) {
|
|
|
|
VIR_DELETE_ELEMENT(group->rollback,
|
|
|
|
i,
|
|
|
|
group->nrollback);
|
|
|
|
virFirewallRuleFree(rule);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < group->naction; i++) {
|
|
|
|
if (group->action[i] == rule) {
|
|
|
|
VIR_DELETE_ELEMENT(group->action,
|
|
|
|
i,
|
|
|
|
group->naction);
|
|
|
|
virFirewallRuleFree(rule);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallRuleAddArg(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
const char *arg)
|
|
|
|
{
|
|
|
|
VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
|
|
|
|
|
|
|
|
ADD_ARG(rule, arg);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallRuleAddArgFormat(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
2019-10-15 15:16:31 +02:00
|
|
|
g_autofree char *arg = NULL;
|
2013-03-04 16:30:40 +00:00
|
|
|
va_list list;
|
|
|
|
|
|
|
|
VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
|
|
|
|
|
|
|
|
va_start(list, fmt);
|
2019-10-22 14:11:15 +02:00
|
|
|
arg = g_strdup_vprintf(fmt, list);
|
|
|
|
va_end(list);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
ADD_ARG(rule, arg);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallRuleAddArgSet(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
const char *const *args)
|
|
|
|
{
|
|
|
|
VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
|
|
|
|
|
|
|
|
while (*args) {
|
|
|
|
ADD_ARG(rule, *args);
|
|
|
|
args++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallRuleAddArgList(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
...)
|
|
|
|
{
|
|
|
|
va_list list;
|
|
|
|
const char *str;
|
|
|
|
|
|
|
|
VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
|
|
|
|
|
|
|
|
va_start(list, rule);
|
|
|
|
|
2014-11-13 15:28:18 +01:00
|
|
|
while ((str = va_arg(list, char *)) != NULL)
|
2013-03-04 16:30:40 +00:00
|
|
|
ADD_ARG(rule, str);
|
|
|
|
|
|
|
|
va_end(list);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
size_t virFirewallRuleGetArgCount(virFirewallRule *rule)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
if (!rule)
|
|
|
|
return 0;
|
|
|
|
return rule->argsLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallStartTransaction:
|
|
|
|
* @firewall: the firewall ruleset
|
|
|
|
* @flags: bitset of virFirewallTransactionFlags
|
|
|
|
*
|
|
|
|
* Start a new transaction with associated rollback
|
|
|
|
* block.
|
|
|
|
*
|
|
|
|
* Should be followed by calls to add various rules to
|
|
|
|
* the transaction. Then virFirwallStartRollback should
|
|
|
|
* be used to provide rules to rollback upon transaction
|
|
|
|
* failure
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallStartTransaction(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
VIR_FIREWALL_RETURN_IF_ERROR(firewall);
|
|
|
|
|
2021-02-23 17:36:36 +01:00
|
|
|
group = virFirewallGroupNew();
|
2013-03-04 16:30:40 +00:00
|
|
|
group->actionFlags = flags;
|
|
|
|
|
2021-03-20 00:37:03 +01:00
|
|
|
VIR_EXPAND_N(firewall->groups, firewall->ngroups, 1);
|
2013-03-04 16:30:40 +00:00
|
|
|
firewall->groups[firewall->ngroups - 1] = group;
|
|
|
|
firewall->currentGroup = firewall->ngroups - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFirewallBeginRollback:
|
|
|
|
* @firewall: the firewall ruleset
|
|
|
|
* @flags: bitset of virFirewallRollbackFlags
|
|
|
|
*
|
|
|
|
* Mark the beginning of a set of rules able to rollback
|
|
|
|
* changes in this and all earlier transactions.
|
|
|
|
*
|
|
|
|
* Should be followed by calls to add various rules needed
|
|
|
|
* to rollback state. Then virFirewallStartTransaction
|
|
|
|
* should be used to indicate the beginning of the next
|
|
|
|
* transactional ruleset.
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
void virFirewallStartRollback(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
unsigned int flags)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
VIR_FIREWALL_RETURN_IF_ERROR(firewall);
|
|
|
|
|
|
|
|
if (firewall->ngroups == 0) {
|
2014-04-30 08:51:29 +02:00
|
|
|
firewall->err = EINVAL;
|
2013-03-04 16:30:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
group = firewall->groups[firewall->ngroups-1];
|
|
|
|
group->rollbackFlags = flags;
|
|
|
|
group->addingRollback = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRuleToString(virFirewallRule *rule)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
|
2020-07-02 22:30:20 -04:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2013-03-04 16:30:40 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
virBufferAdd(&buf, bin, -1);
|
|
|
|
for (i = 0; i < rule->argsLen; i++) {
|
|
|
|
virBufferAddLit(&buf, " ");
|
|
|
|
virBufferAdd(&buf, rule->args[i], -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallApplyRuleDirect(virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
bool ignoreErrors,
|
|
|
|
char **output)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
|
2019-10-15 14:47:50 +02:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2013-03-04 16:30:40 +00:00
|
|
|
int status;
|
2019-10-15 15:16:31 +02:00
|
|
|
g_autofree char *error = NULL;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
if (!bin) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unknown firewall layer %d"),
|
|
|
|
rule->layer);
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cmd = virCommandNewArgList(bin, NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < rule->argsLen; i++)
|
|
|
|
virCommandAddArg(cmd, rule->args[i]);
|
|
|
|
|
|
|
|
virCommandSetOutputBuffer(cmd, output);
|
|
|
|
virCommandSetErrorBuffer(cmd, &error);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
if (ignoreErrors) {
|
|
|
|
VIR_DEBUG("Ignoring error running command");
|
|
|
|
} else {
|
2019-10-15 15:16:31 +02:00
|
|
|
g_autofree char *args = virCommandToString(cmd, false);
|
2013-03-04 16:30:40 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to apply firewall rules %s: %s"),
|
|
|
|
NULLSTR(args), NULLSTR(error));
|
|
|
|
VIR_FREE(*output);
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 21:22:17 +05:30
|
|
|
return 0;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
util: call iptables directly rather than via firewalld
When libvirt added support for firewalld, we were unable to use
firewalld's higher level rules, because they weren't detailed enough
and could not be applied to the iptables FORWARD or OUTPUT chains
(only to the INPUT chain). Instead we changed our code so that rather
than running the iptables/ip6tables/ebtables binaries ourselves, we
would send these commands to firewalld as "passthrough commands", and
firewalld would run the appropriate program on our behalf.
This was done under the assumption that firewalld was somehow tracking
all these rules, and that this tracking was benefitting proper
operation of firewalld and the system in general.
Several years later this came up in a discussion on IRC, and we
learned from the firewalld developers that, in fact, adding iptables
and ebtables rules with firewalld's passthrough commands actually has
*no* advantage; firewalld doesn't keep track of these rules in any
way, and doesn't use them to tailor the construction of its own rules.
Meanwhile, users have been complaining for some time that whenever
firewalld is restarted on a system with libvirt virtual networks
and/or nwfilter rules active, the system logs would be flooded with
warning messages whining that [lots of different rules] could not be
deleted because they didn't exist. For example:
firewalld[3536040]: WARNING: COMMAND_FAILED:
'/usr/sbin/iptables -w10 -w --table filter --delete LIBVIRT_OUT
--out-interface virbr4 --protocol udp --destination-port 68
--jump ACCEPT' failed: iptables: Bad rule
(does a matching rule exist in that chain?).
(See https://bugzilla.redhat.com/1790837 for many more examples and a
discussion)
Note that these messages are created by iptables, but are logged by
firewalld - when an iptables/ebtables command fails, firewalld grabs
whatever is in stderr of the program, and spits it out to the system
log as a warning. We've requested that firewalld not do this (and
instead leave it up to the calling application to do the appropriate
logging), but this request has been respectfully denied.
But combining the two problems above ( 1) firewalld doesn't do
anything useful when you use it as a proxy to add/remove iptables
rules, 2) firewalld often insists on logging lots of
annoying/misleading/useless "error" messages when you use it as a
proxy to remove iptables rules that don't already exist), leads to a
solution - simply stop using firewalld to add and remove iptables
rules. Instead, exec iptables/ip6tables/ebtables directly in the same
way we do when firewalld isn't active.
We still need to keep track of whether or not firewalld is active, as
there are some things that must be done, e.g. we need to add some
actual firewalld rules in the firewalld "libvirt" zone, and we need to
take notice when firewalld restarts, so that we can reload all our
rules.
This patch doesn't remove the infrastructure that allows having
different firewall backends that perform their functions in different
ways, as that will very possibly come in handy in the future when we
want to have an nftables direct backend, and possibly a "pure"
firewalld backend (now that firewalld supports more complex rules, and
can add those rules to the FORWARD and OUTPUT chains). Instead, it
just changes the action when the selected backend is "firewalld" so
that it adds rules directly rather than through firewalld, while
leaving as much of the existing code intact as possible.
In order for tests to still pass, virfirewalltest also had to be
modified to behave in a different way (i.e. by capturing the generated
commandline as it does for the DIRECT backend, rather than capturing
dbus messages using a mocked dbus API).
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-17 10:55:12 -05:00
|
|
|
static int G_GNUC_UNUSED
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallApplyRuleFirewallD(virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
bool ignoreErrors,
|
|
|
|
char **output)
|
|
|
|
{
|
2019-01-09 14:11:32 -05:00
|
|
|
/* wrapper necessary because virFirewallRule is a private struct */
|
|
|
|
return virFirewallDApplyRule(rule->layer, rule->args, rule->argsLen, ignoreErrors, output);
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
util: synchronize with firewalld before we start calling iptables directly
When it is starting up, firewalld will delete all existing iptables
rules and chains before adding its own rules. If libvirtd were to try
to directly add iptables rules during the time before firewalld has
finished initializing, firewalld would end up deleting the rules that
libvirtd has just added.
Currently this isn't a problem, since libvirtd only adds iptables
rules via the firewalld "passthrough command" API, and so firewalld is
able to properly serialize everything. However, we will soon be
changing libvirtd to add its iptables and ebtables rules by directly
calling iptables/ebtables rather than via firewalld, thus removing the
serialization of libvirtd adding rules vs. firewalld deleting rules.
This will especially apparent (if we don't fix it in advance, as this
patch does) when libvirtd is responding to the dbus NameOwnerChanged
event, which is used to learn when firewalld has been restarted. In
that case, dbus sends the event before firewalld has been able to
complete its initialization, so when libvirt responds to the event by
adding back its iptables rules (with direct calls to
/usr/bin/iptables), some of those rules are added before firewalld has
a chance to do its "remove everything" startup protocol. The usual
result of this is that libvirt will successfully add its private
chains (e.g. LIBVIRT_INP, etc), but then fail when it tries to add a
rule jumping to one of those chains (because in the interim, firewalld
has deleted the new chains).
The solution is for libvirt to preface it's direct calling to iptables
with a iptables command sent via firewalld's passthrough command
API. Since commands sent to firewalld are completed synchronously, and
since firewalld won't service them until it has completed its own
initialization, this will assure that by the time libvirt starts
calling iptables to add rules, that firewalld will not be following up
by deleting any of those rules.
To minimize the amount of extra overhead, we request the simplest
iptables command possible: "iptables -V" (and aside from logging a
debug message, we ignore the result, for good measure).
(This patch is being done *before* the patch that switches to calling
iptables directly, so that everything will function properly with any
fractional part of the series applied).
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-23 14:39:40 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
virFirewallBackendSynchronize(void)
|
|
|
|
{
|
|
|
|
const char *arg = "-V";
|
|
|
|
g_autofree char *output = NULL;
|
|
|
|
|
|
|
|
switch (currentBackend) {
|
|
|
|
case VIR_FIREWALL_BACKEND_DIRECT:
|
|
|
|
/* nobody to synchronize with */
|
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_BACKEND_FIREWALLD:
|
|
|
|
/* Send a simple rule via firewalld's passthrough iptables
|
|
|
|
* command so that we'll be sure firewalld has fully
|
|
|
|
* initialized and caught up with its internal queue of
|
|
|
|
* iptables commands. Waiting for this will prevent our own
|
|
|
|
* directly-executed iptables commands from being run while
|
|
|
|
* firewalld is still initializing.
|
|
|
|
*/
|
|
|
|
ignore_value(virFirewallDApplyRule(VIR_FIREWALL_LAYER_IPV4,
|
|
|
|
(char **)&arg, 1, true, &output));
|
|
|
|
VIR_DEBUG("Result of 'iptables -V' via firewalld: %s", NULLSTR(output));
|
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_BACKEND_AUTOMATIC:
|
|
|
|
case VIR_FIREWALL_BACKEND_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 16:30:40 +00:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallApplyRule(virFirewall *firewall,
|
|
|
|
virFirewallRule *rule,
|
2013-03-04 16:30:40 +00:00
|
|
|
bool ignoreErrors)
|
|
|
|
{
|
2019-10-15 15:16:31 +02:00
|
|
|
g_autofree char *output = NULL;
|
|
|
|
g_autofree char *str = virFirewallRuleToString(rule);
|
2020-12-01 09:21:32 +01:00
|
|
|
g_auto(GStrv) lines = NULL;
|
2013-03-04 16:30:40 +00:00
|
|
|
VIR_INFO("Applying rule '%s'", NULLSTR(str));
|
|
|
|
|
|
|
|
if (rule->ignoreErrors)
|
|
|
|
ignoreErrors = rule->ignoreErrors;
|
|
|
|
|
|
|
|
switch (currentBackend) {
|
|
|
|
case VIR_FIREWALL_BACKEND_DIRECT:
|
|
|
|
if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_FIREWALL_BACKEND_FIREWALLD:
|
util: call iptables directly rather than via firewalld
When libvirt added support for firewalld, we were unable to use
firewalld's higher level rules, because they weren't detailed enough
and could not be applied to the iptables FORWARD or OUTPUT chains
(only to the INPUT chain). Instead we changed our code so that rather
than running the iptables/ip6tables/ebtables binaries ourselves, we
would send these commands to firewalld as "passthrough commands", and
firewalld would run the appropriate program on our behalf.
This was done under the assumption that firewalld was somehow tracking
all these rules, and that this tracking was benefitting proper
operation of firewalld and the system in general.
Several years later this came up in a discussion on IRC, and we
learned from the firewalld developers that, in fact, adding iptables
and ebtables rules with firewalld's passthrough commands actually has
*no* advantage; firewalld doesn't keep track of these rules in any
way, and doesn't use them to tailor the construction of its own rules.
Meanwhile, users have been complaining for some time that whenever
firewalld is restarted on a system with libvirt virtual networks
and/or nwfilter rules active, the system logs would be flooded with
warning messages whining that [lots of different rules] could not be
deleted because they didn't exist. For example:
firewalld[3536040]: WARNING: COMMAND_FAILED:
'/usr/sbin/iptables -w10 -w --table filter --delete LIBVIRT_OUT
--out-interface virbr4 --protocol udp --destination-port 68
--jump ACCEPT' failed: iptables: Bad rule
(does a matching rule exist in that chain?).
(See https://bugzilla.redhat.com/1790837 for many more examples and a
discussion)
Note that these messages are created by iptables, but are logged by
firewalld - when an iptables/ebtables command fails, firewalld grabs
whatever is in stderr of the program, and spits it out to the system
log as a warning. We've requested that firewalld not do this (and
instead leave it up to the calling application to do the appropriate
logging), but this request has been respectfully denied.
But combining the two problems above ( 1) firewalld doesn't do
anything useful when you use it as a proxy to add/remove iptables
rules, 2) firewalld often insists on logging lots of
annoying/misleading/useless "error" messages when you use it as a
proxy to remove iptables rules that don't already exist), leads to a
solution - simply stop using firewalld to add and remove iptables
rules. Instead, exec iptables/ip6tables/ebtables directly in the same
way we do when firewalld isn't active.
We still need to keep track of whether or not firewalld is active, as
there are some things that must be done, e.g. we need to add some
actual firewalld rules in the firewalld "libvirt" zone, and we need to
take notice when firewalld restarts, so that we can reload all our
rules.
This patch doesn't remove the infrastructure that allows having
different firewall backends that perform their functions in different
ways, as that will very possibly come in handy in the future when we
want to have an nftables direct backend, and possibly a "pure"
firewalld backend (now that firewalld supports more complex rules, and
can add those rules to the FORWARD and OUTPUT chains). Instead, it
just changes the action when the selected backend is "firewalld" so
that it adds rules directly rather than through firewalld, while
leaving as much of the existing code intact as possible.
In order for tests to still pass, virfirewalltest also had to be
modified to behave in a different way (i.e. by capturing the generated
commandline as it does for the DIRECT backend, rather than capturing
dbus messages using a mocked dbus API).
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2020-11-17 10:55:12 -05:00
|
|
|
/* Since we are using raw iptables rules, there is no
|
|
|
|
* advantage to going through firewalld, so instead just add
|
|
|
|
* them directly rather that via dbus calls to firewalld. This
|
|
|
|
* has the useful side effect of eliminating extra unwanted
|
|
|
|
* warning messages in the system logs when trying to delete
|
|
|
|
* rules that don't exist (which is something that happens
|
|
|
|
* often when libvirtd is started, and *always* when firewalld
|
|
|
|
* is restarted)
|
|
|
|
*/
|
|
|
|
if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
|
2013-03-04 16:30:40 +00:00
|
|
|
return -1;
|
|
|
|
break;
|
2018-02-14 09:43:59 +00:00
|
|
|
|
|
|
|
case VIR_FIREWALL_BACKEND_AUTOMATIC:
|
|
|
|
case VIR_FIREWALL_BACKEND_LAST:
|
2013-03-04 16:30:40 +00:00
|
|
|
default:
|
2018-02-14 09:43:59 +00:00
|
|
|
virReportEnumRangeError(virFirewallBackend, currentBackend);
|
2013-03-04 16:30:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rule->queryCB && output) {
|
2021-02-05 18:35:07 +01:00
|
|
|
if (!(lines = g_strsplit(output, "\n", -1)))
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("Invoking query %p with '%s'", rule->queryCB, output);
|
2018-12-04 16:33:28 +00:00
|
|
|
if (rule->queryCB(firewall, rule->layer, (const char *const *)lines, rule->queryOpaque) < 0)
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
if (firewall->err) {
|
|
|
|
virReportSystemError(firewall->err, "%s",
|
|
|
|
_("Unable to create rule"));
|
2018-07-24 21:22:17 +05:30
|
|
|
return -1;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-07-24 21:22:17 +05:30
|
|
|
return 0;
|
2013-03-04 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallApplyGroup(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
size_t idx)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group = firewall->groups[idx];
|
2013-03-04 16:30:40 +00:00
|
|
|
bool ignoreErrors = (group->actionFlags & VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
|
|
|
|
size_t i;
|
|
|
|
|
2017-09-25 11:43:33 +01:00
|
|
|
VIR_INFO("Starting transaction for firewall=%p group=%p flags=0x%x",
|
2014-11-11 12:34:57 +00:00
|
|
|
firewall, group, group->actionFlags);
|
2013-03-04 16:30:40 +00:00
|
|
|
firewall->currentGroup = idx;
|
|
|
|
group->addingRollback = false;
|
|
|
|
for (i = 0; i < group->naction; i++) {
|
|
|
|
if (virFirewallApplyRule(firewall,
|
|
|
|
group->action[i],
|
|
|
|
ignoreErrors) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallRollbackGroup(virFirewall *firewall,
|
2013-03-04 16:30:40 +00:00
|
|
|
size_t idx)
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallGroup *group = firewall->groups[idx];
|
2013-03-04 16:30:40 +00:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
VIR_INFO("Starting rollback for group %p", group);
|
|
|
|
firewall->currentGroup = idx;
|
|
|
|
group->addingRollback = true;
|
|
|
|
for (i = 0; i < group->nrollback; i++) {
|
|
|
|
ignore_value(virFirewallApplyRule(firewall,
|
|
|
|
group->rollback[i],
|
|
|
|
true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2021-03-11 08:16:13 +01:00
|
|
|
virFirewallApply(virFirewall *firewall)
|
2013-03-04 16:30:40 +00:00
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
virMutexLock(&ruleLock);
|
|
|
|
|
2015-05-21 13:36:18 -04:00
|
|
|
if (currentBackend == VIR_FIREWALL_BACKEND_AUTOMATIC) {
|
|
|
|
/* a specific backend should have been set when the firewall
|
|
|
|
* object was created. If not, it means none was found.
|
|
|
|
*/
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Failed to initialize a valid firewall backend"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2021-02-23 17:39:50 +01:00
|
|
|
if (!firewall || firewall->err) {
|
2021-03-05 10:38:49 +01:00
|
|
|
int err = EINVAL;
|
|
|
|
|
|
|
|
if (firewall)
|
|
|
|
err = firewall->err;
|
|
|
|
|
|
|
|
virReportSystemError(err, "%s", _("Unable to create rule"));
|
2013-03-04 16:30:40 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Applying groups for %p", firewall);
|
|
|
|
for (i = 0; i < firewall->ngroups; i++) {
|
|
|
|
if (virFirewallApplyGroup(firewall, i) < 0) {
|
|
|
|
size_t first = i;
|
2018-12-06 12:33:39 -05:00
|
|
|
virErrorPtr saved_error;
|
|
|
|
|
2020-08-03 17:28:06 +02:00
|
|
|
VIR_DEBUG("Rolling back groups up to %zu for %p", i, firewall);
|
|
|
|
|
2018-12-06 12:33:39 -05:00
|
|
|
virErrorPreserveLast(&saved_error);
|
2013-03-04 16:30:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Look at any inheritance markers to figure out
|
|
|
|
* what the first rollback group we need to apply is
|
|
|
|
*/
|
|
|
|
for (j = 0; j < i; j++) {
|
|
|
|
VIR_DEBUG("Checking inheritance of group %zu", i - j);
|
|
|
|
if (firewall->groups[i - j]->rollbackFlags &
|
|
|
|
VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS)
|
|
|
|
first = (i - j) - 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now apply all rollback groups in order
|
|
|
|
*/
|
|
|
|
for (j = first; j <= i; j++) {
|
|
|
|
VIR_DEBUG("Rolling back group %zu", j);
|
|
|
|
virFirewallRollbackGroup(firewall, j);
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:33:39 -05:00
|
|
|
virErrorRestore(&saved_error);
|
2013-03-04 16:30:40 +00:00
|
|
|
VIR_DEBUG("Done rolling back groups for %p", firewall);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Done applying groups for %p", firewall);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virMutexUnlock(&ruleLock);
|
|
|
|
return ret;
|
|
|
|
}
|