[libvirt] [PATCH 11/26] Introduce an object for managing firewall rulesets
John Ferlan
jferlan at redhat.com
Sat Apr 26 15:14:06 UTC 2014
On 04/08/2014 11:38 AM, Daniel P. Berrange wrote:
> The network and nwfilter drivers both have a need to update
> firewall rules. The currently share no code for interacting
> with iptables / firewalld. The nwfilter driver is fairly
> tied to the concept of creating shell scripts to execute
> which makes it very hard to port to talk to firewalld via
> DBus APIs.
>
> This patch introduces a virFirewallPtr object which is able
> to represent a complete sequence of rule changes, with the
> ability to have multiple transactional checkpoints with
> rollbacks. By formally separating the definition of the rules
> to be applied from the mechanism used to apply them, it is
> also possible to write a firewall engine that uses firewalld
> DBus APIs natively instead of via the slow firewalld-cmd.
>
> Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
> ---
> include/libvirt/virterror.h | 1 +
> po/POTFILES.in | 1 +
> src/Makefile.am | 2 +
> src/libvirt_private.syms | 17 +
> src/util/virerror.c | 1 +
> src/util/virfirewall.c | 922 +++++++++++++++++++++++++++++++++
> src/util/virfirewall.h | 109 ++++
> src/util/virfirewallpriv.h | 45 ++
> tests/Makefile.am | 7 +
> tests/testutils.c | 18 +-
> tests/virfirewalltest.c | 1186 +++++++++++++++++++++++++++++++++++++++++++
> 11 files changed, 2305 insertions(+), 4 deletions(-)
> create mode 100644 src/util/virfirewall.c
> create mode 100644 src/util/virfirewall.h
> create mode 100644 src/util/virfirewallpriv.h
> create mode 100644 tests/virfirewalltest.c
>
> diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
> index 495c121..be90797 100644
> --- a/include/libvirt/virterror.h
> +++ b/include/libvirt/virterror.h
> @@ -122,6 +122,7 @@ typedef enum {
> VIR_FROM_SYSTEMD = 56, /* Error from systemd code */
> VIR_FROM_BHYVE = 57, /* Error from bhyve driver */
> VIR_FROM_CRYPTO = 58, /* Error from crypto code */
> + VIR_FROM_FIREWALL = 59, /* Error from firewall */
>
> # ifdef VIR_ENUM_SENTINELS
> VIR_ERR_DOMAIN_LAST
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 122b853..e35eb82 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -161,6 +161,7 @@ src/util/virdbus.c
> src/util/virdnsmasq.c
> src/util/vireventpoll.c
> src/util/virfile.c
> +src/util/virfirewall.c
> src/util/virhash.c
> src/util/virhook.c
> src/util/virhostdev.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 7e9a702..7615294 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -106,6 +106,8 @@ UTIL_SOURCES = \
> util/virevent.c util/virevent.h \
> util/vireventpoll.c util/vireventpoll.h \
> util/virfile.c util/virfile.h \
> + util/virfirewall.c util/virfirewall.h \
> + util/virfirewallpriv.h \
> util/virhash.c util/virhash.h \
> util/virhashcode.c util/virhashcode.h \
> util/virhook.c util/virhook.h \
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 0c2cf75..18be0e1 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1277,6 +1277,23 @@ virFileWriteStr;
> virFindFileInPath;
>
>
> +# util/virfirewall.h
> +virFirewallAddRule;
> +virFirewallAddRuleFull;
> +virFirewallApply;
> +virFirewallFree;
> +virFirewallNew;
> +virFirewallRemoveRule;
> +virFirewallRuleAddArg;
> +virFirewallRuleAddArgFormat;
> +virFirewallRuleAddArgList;
> +virFirewallRuleAddArgSet;
> +virFirewallRuleGetArgCount;
> +virFirewallSetBackend;
> +virFirewallStartRollback;
> +virFirewallStartTransaction;
> +
> +
> # util/virhash.h
> virHashAddEntry;
> virHashCreate;
> diff --git a/src/util/virerror.c b/src/util/virerror.c
> index cbbaa83..e0bc970 100644
> --- a/src/util/virerror.c
> +++ b/src/util/virerror.c
> @@ -129,6 +129,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
> "Systemd",
> "Bhyve",
> "Crypto",
> + "Firewall",
> )
>
>
> diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c
> new file mode 100644
> index 0000000..b558d2f
> --- /dev/null
> +++ b/src/util/virfirewall.c
> @@ -0,0 +1,922 @@
> +/*
> + * virfirewall.c: integration with firewalls
> + *
> + * Copyright (C) 2013 Red Hat, Inc.
> + *
> + * 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/>.
> + *
> + * Authors:
> + * Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#define __VIR_FIREWALL_PRIV_H_ALLOW__
> +
> +#include <stdarg.h>
> +
> +#include "viralloc.h"
> +#include "virfirewallpriv.h"
> +#include "virerror.h"
> +#include "virutil.h"
> +#include "virstring.h"
> +#include "vircommand.h"
> +#include "virlog.h"
> +#include "virdbus.h"
> +#include "virfile.h"
> +#include "virthread.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_FIREWALL
> +
> +VIR_LOG_INIT("util.firewall");
> +
> +typedef struct _virFirewallGroup virFirewallGroup;
> +typedef virFirewallGroup *virFirewallGroupPtr;
> +
> +VIR_ENUM_DECL(virFirewallLayerCommand)
> +VIR_ENUM_IMPL(virFirewallLayerCommand, VIR_FIREWALL_LAYER_LAST,
> + EBTABLES_PATH,
> + IPTABLES_PATH,
> + IP6TABLES_PATH);
> +
> +VIR_ENUM_DECL(virFirewallLayerFirewallD)
> +VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST,
> + "eb", "ipv4", "ipv6")
> +
> +
> +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;
> + virFirewallRulePtr *action;
> +
> + size_t nrollback;
> + virFirewallRulePtr *rollback;
> +
> + bool addingRollback;
> +};
> +
> +
> +struct _virFirewall {
> + int err;
> +
> + size_t ngroups;
> + virFirewallGroupPtr *groups;
> + 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);
> +}
> +
> +VIR_ONCE_GLOBAL_INIT(virFirewall)
> +
> +static int
> +virFirewallValidateBackend(virFirewallBackend backend)
> +{
> + VIR_DEBUG("Validating backend %d", backend);
> +#if WITH_DBUS
> + if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC ||
> + backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
> + int rv = virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE);
> + VIR_DEBUG("Firewalled is registered ? %d", rv);
> + if (rv < 0) {
> + if (rv == -2) {
> + if (backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("firewalld firewall backend requested, but service is not running"));
> + return -1;
> + } else {
> + VIR_DEBUG("firewalld service not running, trying direct backend");
> + backend = VIR_FIREWALL_BACKEND_DIRECT;
> + }
> + } else {
> + return -1;
> + }
> + } else {
> + VIR_DEBUG("firewalld service running, using firewalld backend");
> + backend = VIR_FIREWALL_BACKEND_FIREWALLD;
> + }
> + }
> +#else
> + if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC) {
> + VIR_DEBUG("DBus support disabled, trying direct backend");
> + backend = VIR_FIREWALL_BACKEND_DIRECT;
> + } else if (backend == VIR_FIREWALL_BACKEND_FIREWALLD) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("firewalld firewall backend requested, but DBus support disabled"));
> + return -1;
> + }
> +#endif
> +
> + if (backend == VIR_FIREWALL_BACKEND_DIRECT) {
> + const char *commands[] = {
> + IPTABLES_PATH, IP6TABLES_PATH, EBTABLES_PATH
> + };
> + size_t i;
> + for (i = 0; i < ARRAY_CARDINALITY(commands); i++) {
> + if (!virFileIsExecutable(commands[i])) {
> + virReportSystemError(errno,
> + _("direct firewall backend requested, but %s is not available"),
> + commands[i]);
> + return -1;
> + }
> + }
> + VIR_DEBUG("found iptables/ip6tables/ebtables, using direct backend");
> + }
> +
> + currentBackend = backend;
> + return 0;
> +}
> +
> +int
> +virFirewallSetBackend(virFirewallBackend backend)
> +{
> + currentBackend = backend;
> +
> + if (virFirewallInitialize() < 0)
> + return -1;
> +
> + return virFirewallValidateBackend(backend);
> +}
> +
> +static virFirewallGroupPtr
> +virFirewallGroupNew(void)
> +{
> + virFirewallGroupPtr group;
> +
> + if (VIR_ALLOC(group) < 0)
> + return NULL;
> +
> + 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
> + */
> +virFirewallPtr virFirewallNew(void)
> +{
> + virFirewallPtr firewall;
> +
> + if (VIR_ALLOC(firewall) < 0)
> + return NULL;
> +
> + return firewall;
> +}
> +
> +
> +static void
> +virFirewallRuleFree(virFirewallRulePtr rule)
> +{
> + size_t i;
> +
> + if (!rule)
> + return;
> +
> + for (i = 0; i < rule->argsLen; i++)
> + VIR_FREE(rule->args[i]);
> + VIR_FREE(rule->args);
> + VIR_FREE(rule);
> +}
> +
> +
> +static void
> +virFirewallGroupFree(virFirewallGroupPtr group)
> +{
> + size_t i;
> +
> + if (!group)
> + return;
> +
> + for (i = 0; i < group->naction; i++)
> + virFirewallRuleFree(group->action[i]);
> + VIR_FREE(group->action);
> +
> + for (i = 0; i < group->nrollback; i++)
> + virFirewallRuleFree(group->rollback[i]);
> + VIR_FREE(group->rollback);
> +
> + VIR_FREE(group);
> +}
> +
> +
> +/**
> + * virFirewallFree:
> + *
> + * Release all memory associated with the firewall
> + * ruleset
> + */
> +void virFirewallFree(virFirewallPtr firewall)
> +{
> + size_t i;
> +
> + if (!firewall)
> + return;
> +
> + for (i = 0; i < firewall->ngroups; i++)
> + virFirewallGroupFree(firewall->groups[i]);
> + VIR_FREE(firewall->groups);
> +
> + VIR_FREE(firewall);
> +}
> +
> +#define VIR_FIREWALL_RETURN_IF_ERROR(firewall) \
> + if (!firewall || firewall->err) \
> + return;
> +
> +#define VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, ruel)\
> + if (!firewall || firewall->err || !rule) \
> + return;
> +
> +#define VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall) \
> + if (!firewall || firewall->err) \
> + return NULL;
> +
> +#define ADD_ARG(rule, str) \
> + if (VIR_RESIZE_N(rule->args, \
> + rule->argsAlloc, \
> + rule->argsLen, 1) < 0) \
> + goto no_memory; \
> + \
> + if (VIR_STRDUP(rule->args[rule->argsLen++], str) < 0)\
> + goto no_memory;
> +
> +static virFirewallRulePtr
> +virFirewallAddRuleFullV(virFirewallPtr firewall,
> + virFirewallLayer layer,
> + bool ignoreErrors,
> + virFirewallQueryCallback cb,
> + void *opaque,
> + va_list args)
> +{
> + virFirewallGroupPtr group;
> + virFirewallRulePtr rule;
> + char *str;
> +
> + VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall);
> +
> + if (firewall->ngroups == 0) {
> + firewall->err = ENODATA;
> + return NULL;
> + }
> + group = firewall->groups[firewall->currentGroup];
> +
> +
> + if (VIR_ALLOC(rule) < 0)
> + goto no_memory;
> +
> + rule->layer = layer;
> + rule->queryCB = cb;
> + rule->queryOpaque = opaque;
> + rule->ignoreErrors = ignoreErrors;
> +
> + while ((str = va_arg(args, char *)) != NULL) {
> + ADD_ARG(rule, str);
> + }
> +
> + if (group->addingRollback) {
> + if (VIR_APPEND_ELEMENT_COPY(group->rollback,
> + group->nrollback,
> + rule) < 0)
> + goto no_memory;
> + } else {
> + if (VIR_APPEND_ELEMENT_COPY(group->action,
> + group->naction,
> + rule) < 0)
> + goto no_memory;
> + }
> +
> +
> + return rule;
> +
> + no_memory:
> + firewall->err = ENOMEM;
> + virFirewallRuleFree(rule);
> + return NULL;
> +}
> +
> +/**
> + * virFirewallAddRule:
> + * @firewall: firewall ruleset to add to
> + * @layer: the firewall layer to change
> + * @...: NULL terminated list of strings for the rule
> + *
> + * Add any type of rule to the firewall ruleset.
> + *
> + * Returns the new rule
> + */
> +virFirewallRulePtr
> +virFirewallAddRule(virFirewallPtr firewall,
> + virFirewallLayer layer,
> + ...)
> +{
> + virFirewallRulePtr rule;
> + va_list args;
> + va_start(args, layer);
> + rule = virFirewallAddRuleFullV(firewall, layer, false, NULL, NULL, args);
> + va_end(args);
> + 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
> + */
> +virFirewallRulePtr virFirewallAddRuleFull(virFirewallPtr firewall,
> + virFirewallLayer layer,
> + bool ignoreErrors,
> + virFirewallQueryCallback cb,
> + void *opaque,
> + ...)
> +{
> + virFirewallRulePtr rule;
> + 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
> + */
> +void virFirewallRemoveRule(virFirewallPtr firewall,
> + virFirewallRulePtr rule)
> +{
> + size_t i;
> + virFirewallGroupPtr group;
> +
> + /* 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;
> + }
> + }
> + }
> +}
> +
> +
> +void virFirewallRuleAddArg(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *arg)
> +{
> + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
> +
> + ADD_ARG(rule, arg);
> +
> + return;
> +
> + no_memory:
> + firewall->err = ENOMEM;
> +}
> +
> +
> +void virFirewallRuleAddArgFormat(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *fmt, ...)
> +{
> + char *arg;
> + va_list list;
> +
> + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
> +
> + va_start(list, fmt);
> +
> + if (virVasprintf(&arg, fmt, list) < 0)
> + goto no_memory;
> +
> + ADD_ARG(rule, arg);
> +
> + va_end(list);
> +
> + VIR_FREE(arg);
> + return;
> +
> + no_memory:
> + firewall->err = ENOMEM;
> + va_end(list);
> + VIR_FREE(arg);
> +}
> +
> +
> +void virFirewallRuleAddArgSet(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *const *args)
> +{
> + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
> +
> + while (*args) {
> + ADD_ARG(rule, *args);
> + args++;
> + }
> +
> + return;
> +
> + no_memory:
> + firewall->err = ENOMEM;
> +}
> +
> +
> +void virFirewallRuleAddArgList(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + ...)
> +{
> + va_list list;
> + const char *str;
> +
> + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule);
> +
> + va_start(list, rule);
> +
> + while ((str = va_arg(list, char *)) != NULL) {
> + ADD_ARG(rule, str);
> + }
> +
> + va_end(list);
> +
> + return;
> +
> + no_memory:
> + firewall->err = ENOMEM;
> + va_end(list);
> +}
> +
> +
> +size_t virFirewallRuleGetArgCount(virFirewallRulePtr rule)
> +{
> + 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
> + */
> +void virFirewallStartTransaction(virFirewallPtr firewall,
> + unsigned int flags)
> +{
> + virFirewallGroupPtr group;
> +
> + VIR_FIREWALL_RETURN_IF_ERROR(firewall);
> +
> + if (!(group = virFirewallGroupNew())) {
> + firewall->err = ENOMEM;
> + return;
> + }
> + group->actionFlags = flags;
> +
> + if (VIR_EXPAND_N(firewall->groups,
> + firewall->ngroups, 1) < 0) {
> + firewall->err = ENOMEM;
> + virFirewallGroupFree(group);
> + return;
> + }
> + 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.
> + */
> +void virFirewallStartRollback(virFirewallPtr firewall,
> + unsigned int flags)
> +{
> + virFirewallGroupPtr group;
> +
> + VIR_FIREWALL_RETURN_IF_ERROR(firewall);
> +
> + if (firewall->ngroups == 0) {
> + firewall->err = ENODATA;
> + return;
> + }
> +
> + group = firewall->groups[firewall->ngroups-1];
> + group->rollbackFlags = flags;
> + group->addingRollback = true;
> +}
> +
> +
> +static char *
> +virFirewallRuleToString(virFirewallRulePtr rule)
> +{
> + const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
> + virBuffer buf = VIR_BUFFER_INITIALIZER;
> + 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
> +virFirewallApplyRuleDirect(virFirewallRulePtr rule,
> + bool ignoreErrors,
> + char **output)
> +{
> + size_t i;
> + const char *bin = virFirewallLayerCommandTypeToString(rule->layer);
> + virCommandPtr cmd = NULL;
> + int status;
> + int ret = -1;
> + char *error = NULL;
> +
> + if (!bin) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unknown firewall layer %d"),
> + rule->layer);
> + goto cleanup;
> + }
> +
> + 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)
> + goto cleanup;
> +
> + if (status != 0) {
> + if (ignoreErrors) {
> + VIR_DEBUG("Ignoring error running command");
> + } else {
> + char *args = virCommandToString(cmd);
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Failed to apply firewall rules %s: %s"),
> + NULLSTR(args), NULLSTR(error));
> + VIR_FREE(args);
> + VIR_FREE(*output);
> + goto cleanup;
> + }
> + }
> +
> + ret = 0;
> + cleanup:
> + VIR_FREE(error);
> + virCommandFree(cmd);
> + return ret;
> +}
> +
> +
> +#ifdef WITH_DBUS
> +static int
> +virFirewallApplyRuleFirewallD(virFirewallRulePtr rule,
> + bool ignoreErrors,
> + char **output)
> +{
> + const char *ipv = virFirewallLayerFirewallDTypeToString(rule->layer);
> + DBusConnection *sysbus = virDBusGetSystemBus();
> + DBusMessage *reply = NULL;
> + DBusError error;
> + int ret = -1;
> +
> + if (!sysbus)
> + return -1;
> +
> + dbus_error_init(&error);
> +
> + if (!ipv) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unknown firewall layer %d"),
> + rule->layer);
> + goto cleanup;
> + }
> +
> + if (virDBusCallMethod(sysbus,
> + &reply,
> + &error,
> + VIR_FIREWALL_FIREWALLD_SERVICE,
> + "/org/fedoraproject/FirewallD1",
> + "org.fedoraproject.FirewallD1.direct",
> + "passthrough",
> + "sa&s",
> + ipv,
> + (int)rule->argsLen,
> + rule->args) < 0)
> + goto cleanup;
> +
> + if (dbus_error_is_set(&error)) {
> + /*
> + * As of firewalld-0.3.9.3-1.fc20.noarch the name and
> + * message fields in the error look like
> + *
> + * name="org.freedesktop.DBus.Python.dbus.exceptions.DBusException"
> + * message="COMMAND_FAILED: '/sbin/iptables --table filter --delete
> + * INPUT --in-interface virbr0 --protocol udp --destination-port 53
> + * --jump ACCEPT' failed: iptables: Bad rule (does a matching rule
> + * exist in that chain?)."
> + *
> + * We'd like to only ignore DBus errors precisely related to the failure
> + * of iptables/ebtables commands. A well designed DBus interface would
> + * return specific named exceptions not the top level generic python dbus
> + * exception name. With this current scheme our only option is todo a
> + * sub-string match for 'COMMAND_FAILED' on the message. eg like
> + *
> + * if (ignoreErrors &&
> + * STREQ(error.name,
> + * "org.freedesktop.DBus.Python.dbus.exceptions.DBusException") &&
> + * STRPREFIX(error.message, "COMMAND_FAILED"))
> + * ...
> + *
> + * But this risks our error detecting code being broken if firewalld changes
> + * ever alter the message string, so we're avoiding doing that.
> + */
> + if (ignoreErrors) {
> + VIR_DEBUG("Ignoring error '%s': '%s'",
> + error.name, error.message);
> + } else {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unable to apply rule '%s'"),
> + error.message);
> + goto cleanup;
> + }
> + } else {
> + if (virDBusMessageRead(reply, "s", output) < 0)
> + goto cleanup;
> + }
> +
> + ret = 0;
> +
> + cleanup:
> + dbus_error_free(&error);
> + if (reply)
> + dbus_message_unref(reply);
> + return ret;
> +}
> +#endif
> +
> +static int
> +virFirewallApplyRule(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + bool ignoreErrors)
> +{
> + char *output = NULL;
> + char **lines = NULL;
> + int ret = -1;
> + char *str = virFirewallRuleToString(rule);
> + VIR_INFO("Applying rule '%s'", NULLSTR(str));
> + VIR_FREE(str);
> +
> + if (rule->ignoreErrors)
> + ignoreErrors = rule->ignoreErrors;
> +
> + switch (currentBackend) {
> + case VIR_FIREWALL_BACKEND_DIRECT:
> + if (virFirewallApplyRuleDirect(rule, ignoreErrors, &output) < 0)
> + return -1;
> + break;
> +#if WITH_DBUS
> + case VIR_FIREWALL_BACKEND_FIREWALLD:
> + if (virFirewallApplyRuleFirewallD(rule, ignoreErrors, &output) < 0)
> + return -1;
> + break;
> +#endif
> + default:
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("Unexpected firewall engine backend"));
> + return -1;
> + }
> +
> + if (rule->queryCB && output) {
> + if (!(lines = virStringSplit(output, "\n", -1)))
> + goto cleanup;
> +
> + VIR_DEBUG("Invoking query %p with '%s'", rule->queryCB, output);
> + if (rule->queryCB(firewall, (const char *const *)lines, rule->queryOpaque) < 0)
> + goto cleanup;
> +
> + if (firewall->err == ENOMEM) {
> + virReportOOMError();
> + goto cleanup;
> + }
> + if (firewall->err) {
> + virReportSystemError(firewall->err, "%s",
> + _("Unable to create rule"));
> + goto cleanup;
> + }
> +
> + }
> +
> + ret = 0;
> + cleanup:
> + virStringFreeList(lines);
> + VIR_FREE(output);
> + return ret;
> +}
> +
> +static int
> +virFirewallApplyGroup(virFirewallPtr firewall,
> + size_t idx)
> +{
> + virFirewallGroupPtr group = firewall->groups[idx];
> + bool ignoreErrors = (group->actionFlags & VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
> + size_t i;
> +
> + VIR_INFO("Starting transaction for %p flags=%x",
> + group, group->actionFlags);
> + 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
> +virFirewallRollbackGroup(virFirewallPtr firewall,
> + size_t idx)
> +{
> + virFirewallGroupPtr group = firewall->groups[idx];
> + 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
> +virFirewallApply(virFirewallPtr firewall)
> +{
> + size_t i, j;
> + int ret = -1;
> +
> + virMutexLock(&ruleLock);
> + if (virFirewallInitialize() < 0)
> + goto cleanup;
> +
> + if (!firewall || firewall->err == ENOMEM) {
> + virReportOOMError();
> + goto cleanup;
> + }
> + if (firewall->err) {
> + virReportSystemError(firewall->err, "%s",
> + _("Unable to create rule"));
> + goto cleanup;
> + }
> +
> + VIR_DEBUG("Applying groups for %p", firewall);
> + for (i = 0; i < firewall->ngroups; i++) {
> + if (virFirewallApplyGroup(firewall, i) < 0) {
> + VIR_DEBUG("Rolling back groups upto %zu for %p", i, firewall);
> + size_t first = i;
> + virErrorPtr saved_error = virSaveLastError();
> +
> + /*
> + * 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);
> + }
> +
> + virSetError(saved_error);
> + virFreeError(saved_error);
> + 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;
> +}
> diff --git a/src/util/virfirewall.h b/src/util/virfirewall.h
> new file mode 100644
> index 0000000..5ca66fa
> --- /dev/null
> +++ b/src/util/virfirewall.h
> @@ -0,0 +1,109 @@
> + /*
> + * virfirewall.h: integration with firewalls
> + *
> + * Copyright (C) 2014 Red Hat, Inc.
> + *
> + * 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/>.
> + *
> + * Authors:
> + * Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#ifndef __VIR_FIREWALL_H__
> +# define __VIR_FIREWALL_H__
> +
> +# include "internal.h"
> +
> +typedef struct _virFirewall virFirewall;
> +typedef virFirewall *virFirewallPtr;
> +
> +typedef struct _virFirewallRule virFirewallRule;
> +typedef virFirewallRule *virFirewallRulePtr;
> +
> +typedef enum {
> + VIR_FIREWALL_LAYER_ETHERNET,
> + VIR_FIREWALL_LAYER_IPV4,
> + VIR_FIREWALL_LAYER_IPV6,
> +
> + VIR_FIREWALL_LAYER_LAST,
> +} virFirewallLayer;
> +
> +virFirewallPtr virFirewallNew(void);
> +
> +void virFirewallFree(virFirewallPtr firewall);
> +
> +virFirewallRulePtr virFirewallAddRule(virFirewallPtr firewall,
> + virFirewallLayer layer,
> + ...)
> + ATTRIBUTE_SENTINEL;
> +
> +typedef int (*virFirewallQueryCallback)(virFirewallPtr firewall,
> + const char *const *lines,
> + void *opaque);
> +
> +virFirewallRulePtr virFirewallAddRuleFull(virFirewallPtr firewall,
> + virFirewallLayer layer,
> + bool ignoreErrors,
> + virFirewallQueryCallback cb,
> + void *opaque,
> + ...)
> + ATTRIBUTE_NONNULL(3) ATTRIBUTE_SENTINEL;
The Coverity build wasn't happy (it failed) since argument(3) is a bool:
In file included from util/virebtables.c:34:0:
util/virfirewall.h:62:5: error: nonnull argument references non-pointer
operand (argument 1, operand 3)
ATTRIBUTE_NONNULL(3) ATTRIBUTE_SENTINEL;
^
The only pointer argument is (1); however, virFirewallAddRuleFullV()
will check for it being NULL. Perhaps this was a cut-n-paste from
"virFirewallRuleAddArg()" below?
I wasn't sure which way to go in order to repair. Since it's only the
Coverity build that's broken - I figure it can wait for your feedback to
handle it.
If I changed it to (1), then my coverity build completes and there's no
new errors.
John
> +
> +void virFirewallRemoveRule(virFirewallPtr firewall,
> + virFirewallRulePtr rule);
> +
> +void virFirewallRuleAddArg(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *arg)
> + ATTRIBUTE_NONNULL(3);
> +
> +void virFirewallRuleAddArgFormat(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *fmt, ...)
> + ATTRIBUTE_NONNULL(3) ATTRIBUTE_FMT_PRINTF(3, 4);
> +
> +void virFirewallRuleAddArgSet(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + const char *const *args)
> + ATTRIBUTE_NONNULL(3);
> +
> +void virFirewallRuleAddArgList(virFirewallPtr firewall,
> + virFirewallRulePtr rule,
> + ...)
> + ATTRIBUTE_SENTINEL;
> +
> +size_t virFirewallRuleGetArgCount(virFirewallRulePtr rule);
> +
> +typedef enum {
> + /* Ignore all errors when applying rules, so no
> + * rollback block will be required */
> + VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS = (1 << 0),
> +} virFirewallTransactionFlags;
> +
> +void virFirewallStartTransaction(virFirewallPtr firewall,
> + unsigned int flags);
> +
> +typedef enum {
> + /* Execute previous rollback block before this
> + * one, to chain cleanup */
> + VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS = (1 << 0),
> +} virFirewallRollbackFlags;
> +
> +void virFirewallStartRollback(virFirewallPtr firewall,
> + unsigned int flags);
> +
> +int virFirewallApply(virFirewallPtr firewall);
> +
> +#endif /* __VIR_FIREWALL_H__ */
> diff --git a/src/util/virfirewallpriv.h b/src/util/virfirewallpriv.h
> new file mode 100644
> index 0000000..130aaa1
> --- /dev/null
> +++ b/src/util/virfirewallpriv.h
> @@ -0,0 +1,45 @@
> +/*
> + * virfirewallpriv.h: integration with firewalls private APIs
> + *
> + * Copyright (C) 2013 Red Hat, Inc.
> + *
> + * 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/>.
> + *
> + * Authors:
> + * Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#ifndef __VIR_FIREWALL_PRIV_H_ALLOW__
> +# error "virfirewallpriv.h may only be included by virfirewall.c or test suites"
> +#endif
> +
> +#ifndef __VIR_FIREWALL_PRIV_H__
> +# define __VIR_FIREWALL_PRIV_H__
> +
> +# include "virfirewall.h"
> +
> +# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1"
> +
> +typedef enum {
> + VIR_FIREWALL_BACKEND_AUTOMATIC,
> + VIR_FIREWALL_BACKEND_DIRECT,
> + VIR_FIREWALL_BACKEND_FIREWALLD,
> +
> + VIR_FIREWALL_BACKEND_LAST,
> +} virFirewallBackend;
> +
> +int virFirewallSetBackend(virFirewallBackend backend);
> +
> +#endif /* __VIR_FIREWALL_PRIV_H__ */
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index c13cc15..a10919d 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -148,6 +148,7 @@ test_programs = virshtest sockettest \
> virpcitest \
> virendiantest \
> virfiletest \
> + virfirewalltest \
> viriscsitest \
> virkeycodetest \
> virlockspacetest \
> @@ -998,6 +999,12 @@ virfiletest_SOURCES = \
> virfiletest.c testutils.h testutils.c
> virfiletest_LDADD = $(LDADDS)
>
> +virfirewalltest_SOURCES = \
> + virfirewalltest.c testutils.h testutils.c
> +virfirewalltest_LDADD = $(LDADDS)
> +virfirewalltest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
> +virfirewalltest_LDFLAGS = $(DRIVER_MODULE_LDFLAGS)
> +
> jsontest_SOURCES = \
> jsontest.c testutils.h testutils.c
> jsontest_LDADD = $(LDADDS)
> diff --git a/tests/testutils.c b/tests/testutils.c
> index 9767a78..022d543 100644
> --- a/tests/testutils.c
> +++ b/tests/testutils.c
> @@ -459,10 +459,20 @@ int virtTestDifference(FILE *stream,
> const char *expect,
> const char *actual)
> {
> - const char *expectStart = expect;
> - const char *expectEnd = expect + (strlen(expect)-1);
> - const char *actualStart = actual;
> - const char *actualEnd = actual + (strlen(actual)-1);
> + const char *expectStart;
> + const char *expectEnd;
> + const char *actualStart;
> + const char *actualEnd;
> +
> + if (!expect)
> + expect = "";
> + if (!actual)
> + actual = "";
> +
> + expectStart = expect;
> + expectEnd = expect + (strlen(expect)-1);
> + actualStart = actual;
> + actualEnd = actual + (strlen(actual)-1);
>
> if (!virTestGetDebug())
> return 0;
> diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c
> new file mode 100644
> index 0000000..805fa44
> --- /dev/null
> +++ b/tests/virfirewalltest.c
> @@ -0,0 +1,1186 @@
> +/*
> + * Copyright (C) 2013-2014 Red Hat, Inc.
> + *
> + * 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/>.
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#define __VIR_FIREWALL_PRIV_H_ALLOW__
> +#define __VIR_COMMAND_PRIV_H_ALLOW__
> +
> +#include "testutils.h"
> +#include "virbuffer.h"
> +#include "vircommandpriv.h"
> +#include "virfirewallpriv.h"
> +#include "virmock.h"
> +#include "virdbuspriv.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_FIREWALL
> +
> +#if WITH_DBUS
> +# include <dbus/dbus.h>
> +#endif
> +
> +static bool fwDisabled = true;
> +static virBufferPtr fwBuf;
> +static bool fwError;
> +
> +#define TEST_FILTER_TABLE_LIST \
> + "Chain INPUT (policy ACCEPT)\n" \
> + "target prot opt source destination\n" \
> + "\n" \
> + "Chain FORWARD (policy ACCEPT)\n" \
> + "target prot opt source destination\n" \
> + "\n" \
> + "Chain OUTPUT (policy ACCEPT)\n" \
> + "target prot opt source destination\n"
> +
> +#define TEST_NAT_TABLE_LIST \
> + "Chain PREROUTING (policy ACCEPT)\n" \
> + "target prot opt source destination\n" \
> + "\n" \
> + "Chain INPUT (policy ACCEPT)\n" \
> + "target prot opt source destination\n" \
> + "\n" \
> + "Chain OUTPUT (policy ACCEPT)\n" \
> + "target prot opt source destination\n" \
> + "\n" \
> + "Chain POSTROUTING (policy ACCEPT)\n" \
> + "target prot opt source destination\n"
> +
> +#if WITH_DBUS
> +VIR_MOCK_IMPL_RET_ARGS(dbus_connection_send_with_reply_and_block,
> + DBusMessage *,
> + DBusConnection *, connection,
> + DBusMessage *, message,
> + int, timeout_milliseconds,
> + DBusError *, error)
> +{
> + DBusMessage *reply = NULL;
> + const char *service = dbus_message_get_destination(message);
> + const char *member = dbus_message_get_member(message);
> + size_t i;
> + size_t nargs = 0;
> + char **args = NULL;
> + char *type = NULL;
> +
> + VIR_MOCK_IMPL_INIT_REAL(dbus_connection_send_with_reply_and_block);
> +
> + if (STREQ(service, "org.freedesktop.DBus") &&
> + STREQ(member, "ListNames")) {
> + const char *svc1 = "org.foo.bar.wizz";
> + const char *svc2 = VIR_FIREWALL_FIREWALLD_SERVICE;
> + DBusMessageIter iter;
> + DBusMessageIter sub;
> + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
> + dbus_message_iter_init_append(reply, &iter);
> + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
> + "s", &sub);
> +
> + if (!dbus_message_iter_append_basic(&sub,
> + DBUS_TYPE_STRING,
> + &svc1))
> + goto error;
> + if (!fwDisabled &&
> + !dbus_message_iter_append_basic(&sub,
> + DBUS_TYPE_STRING,
> + &svc2))
> + goto error;
> + dbus_message_iter_close_container(&iter, &sub);
> + } else if (STREQ(service, VIR_FIREWALL_FIREWALLD_SERVICE) &&
> + STREQ(member, "passthrough")) {
> + bool isAdd = false;
> + bool doError = false;
> +
> + if (virDBusMessageDecode(message,
> + "sa&s",
> + &type,
> + &nargs,
> + &args) < 0)
> + goto error;
> +
> + for (i = 0; i < nargs; i++) {
> + /* Fake failure on the command with this IP addr */
> + if (STREQ(args[i], "-A")) {
> + isAdd = true;
> + } else if (isAdd && STREQ(args[i], "192.168.122.255")) {
> + doError = true;
> + }
> + }
> +
> + if (fwBuf) {
> + if (STREQ(type, "ipv4"))
> + virBufferAddLit(fwBuf, IPTABLES_PATH);
> + else if (STREQ(type, "ipv4"))
> + virBufferAddLit(fwBuf, IP6TABLES_PATH);
> + else
> + virBufferAddLit(fwBuf, EBTABLES_PATH);
> + }
> + for (i = 0; i < nargs; i++) {
> + if (fwBuf) {
> + virBufferAddLit(fwBuf, " ");
> + virBufferEscapeShell(fwBuf, args[i]);
> + }
> + }
> + if (fwBuf)
> + virBufferAddLit(fwBuf, "\n");
> + if (doError) {
> + dbus_set_error_const(error,
> + "org.firewalld.error",
> + "something bad happened");
> + } else {
> + if (nargs == 1 &&
> + STREQ(type, "ipv4") &&
> + STREQ(args[0], "-L")) {
> + if (virDBusCreateReply(&reply,
> + "s", TEST_FILTER_TABLE_LIST) < 0)
> + goto error;
> + } else if (nargs == 3 &&
> + STREQ(type, "ipv4") &&
> + STREQ(args[0], "-t") &&
> + STREQ(args[1], "nat") &&
> + STREQ(args[2], "-L")) {
> + if (virDBusCreateReply(&reply,
> + "s", TEST_NAT_TABLE_LIST) < 0)
> + goto error;
> + } else {
> + if (virDBusCreateReply(&reply,
> + "s", "success") < 0)
> + goto error;
> + }
> + }
> + } else {
> + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
> + }
> +
> + cleanup:
> + VIR_FREE(type);
> + for (i = 0; i < nargs; i++)
> + VIR_FREE(args[i]);
> + VIR_FREE(args);
> + return reply;
> +
> + error:
> + if (reply)
> + dbus_message_unref(reply);
> + reply = NULL;
> + if (error && !dbus_error_is_set(error))
> + dbus_set_error_const(error,
> + "org.firewalld.error",
> + "something unexpected happened");
> +
> + goto cleanup;
> +}
> +#endif
> +
> +struct testFirewallData {
> + virFirewallBackend tryBackend;
> + virFirewallBackend expectBackend;
> + bool fwDisabled;
> +};
> +
> +static int
> +testFirewallSingleGroup(const void *opaque)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
> + virCommandSetDryRun(&cmdbuf, NULL, NULL);
> + else
> + fwBuf = &cmdbuf;
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +
> +static int
> +testFirewallRemoveRule(const void *opaque)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> + virFirewallRulePtr fwrule;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
> + virCommandSetDryRun(&cmdbuf, NULL, NULL);
> + else
> + fwBuf = &cmdbuf;
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT", NULL);
> + virFirewallRuleAddArg(fw, fwrule, "--source-host");
> + virFirewallRemoveRule(fw, fwrule);
> +
> + fwrule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT", NULL);
> + virFirewallRuleAddArg(fw, fwrule, "--source-host");
> + virFirewallRuleAddArgFormat(fw, fwrule, "%s", "!192.168.122.1");
> + virFirewallRuleAddArgList(fw, fwrule, "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +
> +static int
> +testFirewallManyGroups(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n"
> + IPTABLES_PATH " -A OUTPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A OUTPUT --jump DROP\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT)
> + virCommandSetDryRun(&cmdbuf, NULL, NULL);
> + else
> + fwBuf = &cmdbuf;
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--jump", "DROP", NULL);
> +
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +static void
> +testFirewallRollbackHook(const char *const*args,
> + const char *const*env ATTRIBUTE_UNUSED,
> + const char *input ATTRIBUTE_UNUSED,
> + char **output ATTRIBUTE_UNUSED,
> + char **error ATTRIBUTE_UNUSED,
> + int *status,
> + void *opaque ATTRIBUTE_UNUSED)
> +{
> + bool isAdd = false;
> + while (*args) {
> + /* Fake failure on the command with this IP addr */
> + if (STREQ(*args, "-A")) {
> + isAdd = true;
> + } else if (isAdd && STREQ(*args, "192.168.122.255")) {
> + *status = 127;
> + break;
> + }
> + args++;
> + }
> +}
> +
> +static int
> +testFirewallIgnoreFailGroup(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -A OUTPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A OUTPUT --jump DROP\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--jump", "DROP", NULL);
> +
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +
> +static int
> +testFirewallIgnoreFailRule(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -A OUTPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A OUTPUT --jump DROP\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_IPV4,
> + true, NULL, NULL,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "OUTPUT",
> + "--jump", "DROP", NULL);
> +
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +
> +static int
> +testFirewallNoRollback(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) == 0) {
> + fprintf(stderr, "Firewall apply unexpectedly worked\n");
> + goto cleanup;
> + }
> +
> + if (virtTestOOMActive())
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +static int
> +testFirewallSingleRollback(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwError = true;
> + fwBuf = &cmdbuf;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartRollback(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) == 0) {
> + fprintf(stderr, "Firewall apply unexpectedly worked\n");
> + goto cleanup;
> + }
> +
> + if (virtTestOOMActive())
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +static int
> +testFirewallManyRollback(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallStartRollback(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartRollback(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) == 0) {
> + fprintf(stderr, "Firewall apply unexpectedly worked\n");
> + goto cleanup;
> + }
> +
> + if (virtTestOOMActive())
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +static int
> +testFirewallChainedRollback(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.127 --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host 192.168.122.127 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n"
> + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallRollbackHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallStartRollback(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.127",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartRollback(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.127",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallStartRollback(fw, VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "192.168.122.255",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-D", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) == 0) {
> + fprintf(stderr, "Firewall apply unexpectedly worked\n");
> + goto cleanup;
> + }
> +
> + if (virtTestOOMActive())
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +
> +static const char *expectedLines[] = {
> + "Chain INPUT (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain FORWARD (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain OUTPUT (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain PREROUTING (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain INPUT (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain OUTPUT (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> + "Chain POSTROUTING (policy ACCEPT)",
> + "target prot opt source destination",
> + "",
> +};
> +static size_t expectedLineNum;
> +static bool expectedLineError;
> +
> +static void
> +testFirewallQueryHook(const char *const*args,
> + const char *const*env ATTRIBUTE_UNUSED,
> + const char *input ATTRIBUTE_UNUSED,
> + char **output,
> + char **error ATTRIBUTE_UNUSED,
> + int *status,
> + void *opaque ATTRIBUTE_UNUSED)
> +{
> + if (STREQ(args[0], IPTABLES_PATH) &&
> + STREQ(args[1], "-L")) {
> + if (VIR_STRDUP(*output, TEST_FILTER_TABLE_LIST) < 0)
> + *status = 127;
> + } else if (STREQ(args[0], IPTABLES_PATH) &&
> + STREQ(args[1], "-t") &&
> + STREQ(args[2], "nat") &&
> + STREQ(args[3], "-L")) {
> + if (VIR_STRDUP(*output, TEST_NAT_TABLE_LIST) < 0)
> + *status = 127;
> + }
> +}
> +
> +
> +static int
> +testFirewallQueryCallback(virFirewallPtr fw,
> + const char *const *lines,
> + void *opaque ATTRIBUTE_UNUSED)
> +{
> + size_t i;
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.129",
> + "--jump", "REJECT", NULL);
> +
> + for (i = 0; lines[i] != NULL; i++) {
> + if (expectedLineNum >= ARRAY_CARDINALITY(expectedLines)) {
> + expectedLineError = true;
> + break;
> + }
> + if (STRNEQ(expectedLines[expectedLineNum], lines[i])) {
> + fprintf(stderr, "Mismatch '%s' vs '%s' at %zu, %zu\n",
> + expectedLines[expectedLineNum], lines[i],
> + expectedLineNum, i);
> + expectedLineError = true;
> + break;
> + }
> + expectedLineNum++;
> + }
> + return 0;
> +}
> +
> +static int
> +testFirewallQuery(const void *opaque ATTRIBUTE_UNUSED)
> +{
> + virBuffer cmdbuf = VIR_BUFFER_INITIALIZER;
> + virFirewallPtr fw = NULL;
> + int ret = -1;
> + const char *actual = NULL;
> + const char *expected =
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.127 --jump REJECT\n"
> + IPTABLES_PATH " -L\n"
> + IPTABLES_PATH " -t nat -L\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.130 --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.129' --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.129' --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host 192.168.122.128 --jump REJECT\n"
> + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n";
> + const struct testFirewallData *data = opaque;
> +
> + expectedLineNum = 0;
> + expectedLineError = false;
> + fwDisabled = data->fwDisabled;
> + if (virFirewallSetBackend(data->tryBackend) < 0)
> + goto cleanup;
> +
> + if (data->expectBackend == VIR_FIREWALL_BACKEND_DIRECT) {
> + virCommandSetDryRun(&cmdbuf, testFirewallQueryHook, NULL);
> + } else {
> + fwBuf = &cmdbuf;
> + fwError = true;
> + }
> +
> + fw = virFirewallNew();
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.1",
> + "--jump", "ACCEPT", NULL);
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.127",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_IPV4,
> + false,
> + testFirewallQueryCallback,
> + NULL,
> + "-L", NULL);
> + virFirewallAddRuleFull(fw, VIR_FIREWALL_LAYER_IPV4,
> + false,
> + testFirewallQueryCallback,
> + NULL,
> + "-t", "nat", "-L", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.130",
> + "--jump", "REJECT", NULL);
> +
> +
> + virFirewallStartTransaction(fw, 0);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "192.168.122.128",
> + "--jump", "REJECT", NULL);
> +
> + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
> + "-A", "INPUT",
> + "--source-host", "!192.168.122.1",
> + "--jump", "REJECT", NULL);
> +
> + if (virFirewallApply(fw) < 0)
> + goto cleanup;
> +
> + if (virBufferError(&cmdbuf))
> + goto cleanup;
> +
> + actual = virBufferCurrentContent(&cmdbuf);
> +
> + if (expectedLineError) {
> + fprintf(stderr, "Got some unexpected query data\n");
> + goto cleanup;
> + }
> +
> + if (STRNEQ_NULLABLE(expected, actual)) {
> + fprintf(stderr, "Unexected command execution\n");
> + virtTestDifference(stderr, expected, actual);
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + virBufferFreeAndReset(&cmdbuf);
> + fwBuf = NULL;
> + virCommandSetDryRun(NULL, NULL, NULL);
> + virFirewallFree(fw);
> + return ret;
> +}
> +
> +static int
> +mymain(void)
> +{
> + int ret = 0;
> +
> +#define RUN_TEST_DIRECT(name, method) \
> + do { \
> + struct testFirewallData data; \
> + data.tryBackend = VIR_FIREWALL_BACKEND_AUTOMATIC; \
> + data.expectBackend = VIR_FIREWALL_BACKEND_DIRECT; \
> + data.fwDisabled = true; \
> + if (virtTestRun(name " auto direct", method, &data) < 0) \
> + ret = -1; \
> + data.tryBackend = VIR_FIREWALL_BACKEND_DIRECT; \
> + data.expectBackend = VIR_FIREWALL_BACKEND_DIRECT; \
> + data.fwDisabled = true; \
> + if (virtTestRun(name " manual direct", method, &data) < 0) \
> + ret = -1; \
> + } while (0)
> +
> +#if WITH_DBUS
> +# define RUN_TEST_FIREWALLD(name, method) \
> + do { \
> + struct testFirewallData data; \
> + data.tryBackend = VIR_FIREWALL_BACKEND_AUTOMATIC; \
> + data.expectBackend = VIR_FIREWALL_BACKEND_FIREWALLD; \
> + data.fwDisabled = false; \
> + if (virtTestRun(name " auto firewalld", method, &data) < 0) \
> + ret = -1; \
> + data.tryBackend = VIR_FIREWALL_BACKEND_FIREWALLD; \
> + data.expectBackend = VIR_FIREWALL_BACKEND_FIREWALLD; \
> + data.fwDisabled = false; \
> + if (virtTestRun(name " manual firewalld", method, &data) < 0) \
> + ret = -1; \
> + } while (0)
> +
> +# define RUN_TEST(name, method) \
> + RUN_TEST_DIRECT(name, method); \
> + RUN_TEST_FIREWALLD(name, method)
> +#else /* ! WITH_DBUS */
> +# define RUN_TEST(name, method) \
> + RUN_TEST_DIRECT(name, method)
> +#endif /* ! WITH_DBUS */
> +
> + RUN_TEST("single group", testFirewallSingleGroup);
> + RUN_TEST("remove rule", testFirewallRemoveRule);
> + RUN_TEST("many groups", testFirewallManyGroups);
> + RUN_TEST("ignore fail group", testFirewallIgnoreFailGroup);
> + RUN_TEST("ignore fail rule", testFirewallIgnoreFailRule);
> + RUN_TEST("no rollback", testFirewallNoRollback);
> + RUN_TEST("single rollback", testFirewallSingleRollback);
> + RUN_TEST("many rollback", testFirewallManyRollback);
> + RUN_TEST("chained rollback", testFirewallChainedRollback);
> + RUN_TEST("query transaction", testFirewallQuery);
> +
> + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> +}
> +
> +#if WITH_DBUS
> +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmockdbus.so")
> +#else
> +VIRT_TEST_MAIN(mymain)
> +#endif
>
More information about the libvir-list
mailing list