diff --git a/src/Makefile.am b/src/Makefile.am index 4bc7e2f1df..61b6e09ae4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 0b3ff91da3..cec566fed9 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -560,6 +560,7 @@ struct _virNWFilterDriverState { virNWFilterObjList nwfilters; char *configDir; + bool watchingFirewallD; }; diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c index 4fa73f8fd9..ae099606f2 100644 --- a/src/nwfilter/nwfilter_driver.c +++ b/src/nwfilter/nwfilter_driver.c @@ -27,6 +27,9 @@ #include +#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); diff --git a/src/nwfilter/nwfilter_driver.h b/src/nwfilter/nwfilter_driver.h index f1f22e815e..6bf61ac5c4 100644 --- a/src/nwfilter/nwfilter_driver.h +++ b/src/nwfilter/nwfilter_driver.h @@ -33,4 +33,6 @@ int nwfilterRegister(void); +bool virNWFilterDriverIsWatchingFirewallD(void); + #endif /* __VIR_NWFILTER_DRIVER_H__ */ diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index 1aa7091099..b008879525 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -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();