nwfilter: provide basic support for firewalld

This patch provides basic support for using firewalld's firewall-cmd
rather than then plain eb/ip(6)tables commands.
This commit is contained in:
Stefan Berger 2012-08-08 12:00:23 -04:00 committed by Laine Stump
parent bf156385a0
commit 4efde75fab
5 changed files with 290 additions and 40 deletions

View File

@ -1156,9 +1156,9 @@ noinst_LTLIBRARIES += libvirt_driver_nwfilter.la
#libvirt_la_BUILT_LIBADD += libvirt_driver_nwfilter.la
endif
libvirt_driver_nwfilter_la_CFLAGS = $(LIBPCAP_CFLAGS) \
-I$(top_srcdir)/src/conf $(LIBNL_CFLAGS) $(AM_CFLAGS)
-I$(top_srcdir)/src/conf $(LIBNL_CFLAGS) $(AM_CFLAGS) $(DBUS_CFLAGS)
libvirt_driver_nwfilter_la_LDFLAGS = $(LD_AMFLAGS)
libvirt_driver_nwfilter_la_LIBADD = $(LIBPCAP_LIBS) $(LIBNL_LIBS)
libvirt_driver_nwfilter_la_LIBADD = $(LIBPCAP_LIBS) $(LIBNL_LIBS) $(DBUS_LIBS)
if WITH_DRIVER_MODULES
libvirt_driver_nwfilter_la_LIBADD += ../gnulib/lib/libgnu.la
libvirt_driver_nwfilter_la_LDFLAGS += -module -avoid-version

View File

@ -560,6 +560,7 @@ struct _virNWFilterDriverState {
virNWFilterObjList nwfilters;
char *configDir;
bool watchingFirewallD;
};

View File

@ -27,6 +27,9 @@
#include <config.h>
#include "virdbus.h"
#include "logging.h"
#include "internal.h"
#include "virterror_internal.h"
@ -45,10 +48,24 @@
#define VIR_FROM_THIS VIR_FROM_NWFILTER
#define DBUS_RULE_FWD_NAMEOWNERCHANGED \
"type='signal'" \
",interface='"DBUS_INTERFACE_DBUS"'" \
",member='NameOwnerChanged'" \
",arg0='org.fedoraproject.FirewallD1'"
#define DBUS_RULE_FWD_RELOADED \
"type='signal'" \
",interface='org.fedoraproject.FirewallD1'" \
",member='Reloaded'"
static virNWFilterDriverStatePtr driverState;
static int nwfilterDriverShutdown(void);
static int nwfilterDriverReload(void);
static void nwfilterDriverLock(virNWFilterDriverStatePtr driver)
{
virMutexLock(&driver->lock);
@ -58,6 +75,89 @@ static void nwfilterDriverUnlock(virNWFilterDriverStatePtr driver)
virMutexUnlock(&driver->lock);
}
#if HAVE_FIREWALLD
static DBusHandlerResult
nwfilterFirewalldDBusFilter(DBusConnection *connection ATTRIBUTE_UNUSED,
DBusMessage *message,
void *user_data ATTRIBUTE_UNUSED)
{
if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
"NameOwnerChanged") ||
dbus_message_is_signal(message, "org.fedoraproject.FirewallD1",
"Reloaded")) {
VIR_DEBUG("Reload in nwfilter_driver because of firewalld.");
nwfilterDriverReload();
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void
nwfilterDriverRemoveDBusMatches(void)
{
DBusConnection *sysbus;
sysbus = virDBusGetSystemBus();
if (sysbus) {
dbus_bus_remove_match(sysbus,
DBUS_RULE_FWD_NAMEOWNERCHANGED,
NULL);
dbus_bus_remove_match(sysbus,
DBUS_RULE_FWD_RELOADED,
NULL);
dbus_connection_remove_filter(sysbus, nwfilterFirewalldDBusFilter, NULL);
}
}
/**
* virNWFilterDriverInstallDBusMatches
*
* Startup DBus matches for monitoring the state of firewalld
*/
static int
nwfilterDriverInstallDBusMatches(DBusConnection *sysbus)
{
int ret = 0;
if (!sysbus) {
ret = -1;
} else {
/* add matches for
* NameOwnerChanged on org.freedesktop.DBus for firewalld start/stop
* Reloaded on org.fedoraproject.FirewallD1 for firewalld reload
*/
dbus_bus_add_match(sysbus,
DBUS_RULE_FWD_NAMEOWNERCHANGED,
NULL);
dbus_bus_add_match(sysbus,
DBUS_RULE_FWD_RELOADED,
NULL);
if (!dbus_connection_add_filter(sysbus, nwfilterFirewalldDBusFilter,
NULL, NULL)) {
VIR_WARN(("Adding a filter to the DBus connection failed"));
nwfilterDriverRemoveDBusMatches();
ret = -1;
}
}
return ret;
}
#else /* HAVE_FIREWALLD */
static void
nwfilterDriverRemoveDBusMatches(void)
{
}
static int
nwfilterDriverInstallDBusMatches(DBusConnection *sysbus ATTRIBUTE_UNUSED)
{
return 0;
}
#endif /* HAVE_FIREWALLD */
/**
* virNWFilterStartup:
@ -65,14 +165,24 @@ static void nwfilterDriverUnlock(virNWFilterDriverStatePtr driver)
* Initialization function for the QEmu daemon
*/
static int
nwfilterDriverStartup(int privileged) {
nwfilterDriverStartup(int privileged)
{
char *base = NULL;
DBusConnection *sysbus = virDBusGetSystemBus();
if (VIR_ALLOC(driverState) < 0)
goto alloc_err_exit;
if (virMutexInit(&driverState->lock) < 0)
goto err_free_driverstate;
driverState->watchingFirewallD = (sysbus != NULL);
if (!privileged)
return 0;
if (virNWFilterIPAddrMapInit() < 0)
return -1;
goto err_free_driverstate;
if (virNWFilterLearnInit() < 0)
goto err_exit_ipaddrmapshutdown;
if (virNWFilterDHCPSnoopInit() < 0)
@ -81,16 +191,26 @@ nwfilterDriverStartup(int privileged) {
virNWFilterTechDriversInit(privileged);
if (virNWFilterConfLayerInit(virNWFilterDomainFWUpdateCB) < 0)
goto conf_init_err;
if (VIR_ALLOC(driverState) < 0)
goto alloc_err_exit;
if (virMutexInit(&driverState->lock) < 0)
goto alloc_err_exit;
goto err_techdrivers_shutdown;
nwfilterDriverLock(driverState);
/*
* startup the DBus late so we don't get a reload signal while
* initializing
*/
if (nwfilterDriverInstallDBusMatches(sysbus) < 0) {
VIR_ERROR(_("DBus matches could not be installed. Disabling nwfilter "
"driver"));
/*
* unfortunately this is fatal since virNWFilterTechDriversInit
* may have caused the ebiptables driver to use the firewall tool
* but now that the watches don't work, we just disable the nwfilter
* driver
*/
goto error;
}
if (privileged) {
if ((base = strdup (SYSCONFDIR "/libvirt")) == NULL)
goto out_of_memory;
@ -124,9 +244,11 @@ error:
nwfilterDriverShutdown();
alloc_err_exit:
virNWFilterConfLayerShutdown();
return -1;
conf_init_err:
nwfilterDriverUnlock(driverState);
err_techdrivers_shutdown:
virNWFilterTechDriversShutdown();
virNWFilterDHCPSnoopShutdown();
err_exit_learnshutdown:
@ -134,6 +256,9 @@ err_exit_learnshutdown:
err_exit_ipaddrmapshutdown:
virNWFilterIPAddrMapShutdown();
err_free_driverstate:
VIR_FREE(driverState);
return -1;
}
@ -192,6 +317,29 @@ nwfilterDriverActive(void) {
nwfilterDriverLock(driverState);
ret = driverState->nwfilters.count ? 1 : 0;
ret |= driverState->watchingFirewallD;
nwfilterDriverUnlock(driverState);
return ret;
}
/**
* virNWFilterIsWatchingFirewallD:
*
* Checks if the nwfilter has the DBus watches for FirewallD installed.
*
* Returns true if it is watching firewalld, false otherwise
*/
bool
virNWFilterDriverIsWatchingFirewallD(void)
{
bool ret;
if (!driverState)
return false;
nwfilterDriverLock(driverState);
ret = driverState->watchingFirewallD;
nwfilterDriverUnlock(driverState);
return ret;
@ -215,6 +363,8 @@ nwfilterDriverShutdown(void) {
nwfilterDriverLock(driverState);
nwfilterDriverRemoveDBusMatches();
/* free inactive nwfilters */
virNWFilterObjListFree(&driverState->nwfilters);

View File

@ -33,4 +33,6 @@
int nwfilterRegister(void);
bool virNWFilterDriverIsWatchingFirewallD(void);
#endif /* __VIR_NWFILTER_DRIVER_H__ */

View File

@ -1,9 +1,9 @@
/*
* nwfilter_ebiptables_driver.c: driver for ebtables/iptables on tap devices
*
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2010 IBM Corp.
* Copyright (C) 2010 Stefan Berger
* Copyright (C) 2011-2012 Red Hat, Inc.
* Copyright (C) 2010-2012 IBM Corp.
* Copyright (C) 2010-2012 Stefan Berger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -36,6 +36,7 @@
#include "virterror_internal.h"
#include "domain_conf.h"
#include "nwfilter_conf.h"
#include "nwfilter_driver.h"
#include "nwfilter_gentech_driver.h"
#include "nwfilter_ebiptables_driver.h"
#include "virfile.h"
@ -151,11 +152,11 @@ static const char ebiptables_script_set_ifs[] =
#define NWFILTER_FUNC_SET_IFS ebiptables_script_set_ifs
#define NWFILTER_SET_EBTABLES_SHELLVAR(BUFPTR) \
virBufferAsprintf(BUFPTR, "EBT=%s\n", ebtables_cmd_path);
virBufferAsprintf(BUFPTR, "EBT=\"%s\"\n", ebtables_cmd_path);
#define NWFILTER_SET_IPTABLES_SHELLVAR(BUFPTR) \
virBufferAsprintf(BUFPTR, "IPT=%s\n", iptables_cmd_path);
virBufferAsprintf(BUFPTR, "IPT=\"%s\"\n", iptables_cmd_path);
#define NWFILTER_SET_IP6TABLES_SHELLVAR(BUFPTR) \
virBufferAsprintf(BUFPTR, "IPT=%s\n", ip6tables_cmd_path);
virBufferAsprintf(BUFPTR, "IPT=\"%s\"\n", ip6tables_cmd_path);
#define VIRT_IN_CHAIN "libvirt-in"
#define VIRT_OUT_CHAIN "libvirt-out"
@ -4127,23 +4128,98 @@ virNWFilterTechDriver ebiptables_driver = {
.removeBasicRules = ebtablesRemoveBasicRules,
};
/*
* ebiptablesDriverInitWithFirewallD
*
* Try to use firewall-cmd by testing it once; if it works, have ebtables
* and ip6tables commands use firewall-cmd.
*/
static int
ebiptablesDriverInitWithFirewallD(void)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *firewall_cmd_path;
char *output = NULL;
int ret = -1;
if (!virNWFilterDriverIsWatchingFirewallD())
return -1;
firewall_cmd_path = virFindFileInPath("firewall-cmd");
if (firewall_cmd_path) {
virBufferAsprintf(&buf, "FWC=%s\n", firewall_cmd_path);
virBufferAsprintf(&buf,
CMD_DEF("$FWC --state") CMD_SEPARATOR
CMD_EXEC
"%s",
CMD_STOPONERR(1));
if (ebiptablesExecCLI(&buf, NULL, &output) == 0 &&
strlen(output) == 0) {
VIR_DEBUG("Using firewall-cmd in nwfilter_ebiptables_driver.");
ignore_value(virAsprintf(&ebtables_cmd_path,
"%s --direct --passthrough eb",
firewall_cmd_path));
ignore_value(virAsprintf(&iptables_cmd_path,
"%s --direct --passthrough ipv4",
firewall_cmd_path));
ignore_value(virAsprintf(&ip6tables_cmd_path,
"%s --direct --passthrough ipv6",
firewall_cmd_path));
if (!ebtables_cmd_path || !iptables_cmd_path ||
!ip6tables_cmd_path) {
virReportOOMError();
VIR_FREE(ebtables_cmd_path);
VIR_FREE(iptables_cmd_path);
VIR_FREE(ip6tables_cmd_path);
ret = -1;
goto err_exit;
}
ret = 0;
}
}
err_exit:
VIR_FREE(firewall_cmd_path);
VIR_FREE(output);
return ret;
}
static int
ebiptablesDriverInit(bool privileged)
ebiptablesDriverInitCLITools(void)
{
ebtables_cmd_path = virFindFileInPath("ebtables");
if (!ebtables_cmd_path)
VIR_WARN("Could not find 'ebtables' executable");
iptables_cmd_path = virFindFileInPath("iptables");
if (!iptables_cmd_path)
VIR_WARN("Could not find 'iptables' executable");
ip6tables_cmd_path = virFindFileInPath("ip6tables");
if (!ip6tables_cmd_path)
VIR_WARN("Could not find 'ip6tables' executable");
return 0;
}
/*
* ebiptablesDriverTestCLITools
*
* Test the CLI tools. If one is found not to be working, free the buffer
* holding its path as a sign that the tool cannot be used.
*/
static int
ebiptablesDriverTestCLITools(void)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *errmsg = NULL;
int ret = 0;
if (!privileged)
return 0;
if (virMutexInit(&execCLIMutex) < 0)
return -EINVAL;
gawk_cmd_path = virFindFileInPath("gawk");
grep_cmd_path = virFindFileInPath("grep");
ebtables_cmd_path = virFindFileInPath("ebtables");
if (ebtables_cmd_path) {
NWFILTER_SET_EBTABLES_SHELLVAR(&buf);
/* basic probing */
@ -4157,12 +4233,10 @@ ebiptablesDriverInit(bool privileged)
VIR_FREE(ebtables_cmd_path);
VIR_ERROR(_("Testing of ebtables command failed: %s"),
errmsg);
ret = -1;
}
} else {
VIR_WARN("Could not find 'ebtables' executable");
}
iptables_cmd_path = virFindFileInPath("iptables");
if (iptables_cmd_path) {
NWFILTER_SET_IPTABLES_SHELLVAR(&buf);
@ -4176,12 +4250,10 @@ ebiptablesDriverInit(bool privileged)
VIR_FREE(iptables_cmd_path);
VIR_ERROR(_("Testing of iptables command failed: %s"),
errmsg);
ret = -1;
}
} else {
VIR_WARN("Could not find 'iptables' executable");
}
ip6tables_cmd_path = virFindFileInPath("ip6tables");
if (ip6tables_cmd_path) {
NWFILTER_SET_IP6TABLES_SHELLVAR(&buf);
@ -4195,11 +4267,38 @@ ebiptablesDriverInit(bool privileged)
VIR_FREE(ip6tables_cmd_path);
VIR_ERROR(_("Testing of ip6tables command failed: %s"),
errmsg);
ret = -1;
}
} else {
VIR_WARN("Could not find 'ip6tables' executable");
}
VIR_FREE(errmsg);
return ret;
}
static int
ebiptablesDriverInit(bool privileged)
{
if (!privileged)
return 0;
if (virMutexInit(&execCLIMutex) < 0)
return -EINVAL;
gawk_cmd_path = virFindFileInPath("gawk");
grep_cmd_path = virFindFileInPath("grep");
/*
* check whether we can run with firewalld's tools --
* if not, we just fall back to eb/iptables command
* line tools.
*/
if (ebiptablesDriverInitWithFirewallD() < 0)
ebiptablesDriverInitCLITools();
/* make sure tools are available and work */
ebiptablesDriverTestCLITools();
/* ip(6)tables support needs gawk & grep, ebtables doesn't */
if ((iptables_cmd_path != NULL || ip6tables_cmd_path != NULL) &&
(!grep_cmd_path || !gawk_cmd_path)) {
@ -4209,8 +4308,6 @@ ebiptablesDriverInit(bool privileged)
VIR_FREE(ip6tables_cmd_path);
}
VIR_FREE(errmsg);
if (!ebtables_cmd_path && !iptables_cmd_path && !ip6tables_cmd_path) {
VIR_ERROR(_("firewall tools were not found or cannot be used"));
ebiptablesDriverShutdown();