Introduce an object for managing firewall rulesets

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@redhat.com>
This commit is contained in:
Daniel P. Berrange 2013-03-04 16:30:40 +00:00
parent 89f244ba7c
commit 3a0ca7de51
11 changed files with 2314 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -108,6 +108,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 \

View File

@ -1275,6 +1275,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;

View File

@ -129,6 +129,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Systemd",
"Bhyve",
"Crypto",
"Firewall",
)

932
src/util/virfirewall.c Normal file
View File

@ -0,0 +1,932 @@
/*
* 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@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("Firewalld 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) \
do { \
if (!firewall || firewall->err) \
return; \
} while (0)
#define VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule)\
do { \
if (!firewall || firewall->err || !rule) \
return; \
} while (0)
#define VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall) \
do { \
if (!firewall || firewall->err) \
return NULL; \
} while (0)
#define ADD_ARG(rule, str) \
do { \
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; \
} while (0)
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;
}

109
src/util/virfirewall.h Normal file
View File

@ -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@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;
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__ */

View File

@ -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@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__ */

View File

@ -152,6 +152,7 @@ test_programs = virshtest sockettest \
virpcitest \
virendiantest \
virfiletest \
virfirewalltest \
viriscsitest \
virkeycodetest \
virlockspacetest \
@ -1006,6 +1007,11 @@ 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)
jsontest_SOURCES = \
jsontest.c testutils.h testutils.c
jsontest_LDADD = $(LDADDS)

View File

@ -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;

1186
tests/virfirewalltest.c Normal file

File diff suppressed because it is too large Load Diff