2009-11-03 22:11:01 +00:00
|
|
|
/*
|
2012-12-12 16:48:24 +00:00
|
|
|
* virebtables.c: Helper APIs for managing ebtables
|
|
|
|
*
|
2013-03-26 11:21:33 +00:00
|
|
|
* Copyright (C) 2007-2013 Red Hat, Inc.
|
2010-04-29 03:31:16 +00:00
|
|
|
* Copyright (C) 2009 IBM Corp.
|
2009-11-03 22:11:01 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-11-03 22:11:01 +00:00
|
|
|
*
|
|
|
|
* based on iptables.c
|
|
|
|
* Authors:
|
|
|
|
* Gerhard Stenzel <gerhard.stenzel@de.ibm.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2010-04-29 03:31:16 +00:00
|
|
|
#include <sys/wait.h>
|
2009-11-03 22:11:01 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_PATHS_H
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <paths.h>
|
2009-11-03 22:11:01 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "internal.h"
|
2012-12-12 16:48:24 +00:00
|
|
|
#include "virebtables.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
|
|
|
#include "virutil.h"
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
|
|
|
|
#if HAVE_FIREWALLD
|
|
|
|
static char *firewall_cmd_path = NULL;
|
|
|
|
|
|
|
|
static int
|
|
|
|
virEbTablesOnceInit(void)
|
|
|
|
{
|
|
|
|
firewall_cmd_path = virFindFileInPath("firewall-cmd");
|
|
|
|
if (!firewall_cmd_path) {
|
2012-08-22 20:00:28 +00:00
|
|
|
VIR_INFO("firewall-cmd not found on system. "
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
"firewalld support disabled for ebtables.");
|
|
|
|
} else {
|
|
|
|
virCommandPtr cmd = virCommandNew(firewall_cmd_path);
|
|
|
|
int status;
|
|
|
|
|
|
|
|
virCommandAddArgList(cmd, "--state", NULL);
|
|
|
|
if (virCommandRun(cmd, &status) < 0 || status != 0) {
|
2012-08-22 20:00:28 +00:00
|
|
|
VIR_INFO("firewall-cmd found but disabled for ebtables");
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
VIR_FREE(firewall_cmd_path);
|
|
|
|
firewall_cmd_path = NULL;
|
|
|
|
} else {
|
2012-08-22 20:00:28 +00:00
|
|
|
VIR_INFO("using firewalld for ebtables commands");
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
}
|
|
|
|
virCommandFree(cmd);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virEbTables)
|
|
|
|
|
|
|
|
#endif
|
2009-11-03 22:11:01 +00:00
|
|
|
|
|
|
|
struct _ebtablesContext
|
|
|
|
{
|
|
|
|
ebtRules *input_filter;
|
|
|
|
ebtRules *forward_filter;
|
|
|
|
ebtRules *nat_postrouting;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ADD = 0,
|
|
|
|
REMOVE,
|
|
|
|
CREATE,
|
|
|
|
POLICY,
|
|
|
|
INSERT
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
ebtRuleFree(ebtRule *rule)
|
|
|
|
{
|
|
|
|
VIR_FREE(rule->rule);
|
|
|
|
|
|
|
|
if (rule->argv) {
|
|
|
|
int i = 0;
|
|
|
|
while (rule->argv[i])
|
|
|
|
VIR_FREE(rule->argv[i++]);
|
|
|
|
VIR_FREE(rule->argv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ebtRulesAppend(ebtRules *rules,
|
|
|
|
char *rule,
|
|
|
|
const char **argv,
|
|
|
|
int command_idx)
|
|
|
|
{
|
|
|
|
if (VIR_REALLOC_N(rules->rules, rules->nrules+1) < 0) {
|
|
|
|
int i = 0;
|
|
|
|
while (argv[i])
|
|
|
|
VIR_FREE(argv[i++]);
|
|
|
|
VIR_FREE(argv);
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
rules->rules[rules->nrules].rule = rule;
|
|
|
|
rules->rules[rules->nrules].argv = argv;
|
|
|
|
rules->rules[rules->nrules].command_idx = command_idx;
|
|
|
|
|
|
|
|
rules->nrules++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ebtRulesRemove(ebtRules *rules,
|
|
|
|
char *rule)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < rules->nrules; i++)
|
|
|
|
if (STREQ(rules->rules[i].rule, rule))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i >= rules->nrules)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
ebtRuleFree(&rules->rules[i]);
|
|
|
|
|
|
|
|
memmove(&rules->rules[i],
|
|
|
|
&rules->rules[i+1],
|
2012-03-29 09:52:04 +00:00
|
|
|
(rules->nrules - i - 1) * sizeof(ebtRule));
|
2009-11-03 22:11:01 +00:00
|
|
|
|
|
|
|
rules->nrules--;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ebtRulesFree(ebtRules *rules)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
VIR_FREE(rules->table);
|
|
|
|
VIR_FREE(rules->chain);
|
|
|
|
|
|
|
|
if (rules->rules) {
|
|
|
|
for (i = 0; i < rules->nrules; i++)
|
|
|
|
ebtRuleFree(&rules->rules[i]);
|
|
|
|
|
|
|
|
VIR_FREE(rules->rules);
|
|
|
|
|
|
|
|
rules->nrules = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ebtRules *
|
|
|
|
ebtRulesNew(const char *table,
|
|
|
|
const char *chain)
|
|
|
|
{
|
|
|
|
ebtRules *rules;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(rules) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(rules->table = strdup(table)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!(rules->chain = strdup(chain)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
rules->rules = NULL;
|
|
|
|
rules->nrules = 0;
|
|
|
|
|
|
|
|
return rules;
|
|
|
|
|
|
|
|
error:
|
|
|
|
ebtRulesFree(rules);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-11-06 09:39:13 +00:00
|
|
|
static int ATTRIBUTE_SENTINEL
|
2009-11-03 22:11:01 +00:00
|
|
|
ebtablesAddRemoveRule(ebtRules *rules, int action, const char *arg, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int retval = ENOMEM;
|
|
|
|
const char **argv;
|
|
|
|
char *rule = NULL;
|
|
|
|
const char *s;
|
|
|
|
int n, command_idx;
|
|
|
|
|
|
|
|
n = 1 + /* /sbin/ebtables */
|
|
|
|
2 + /* --table foo */
|
|
|
|
2 + /* --insert bar */
|
|
|
|
1; /* arg */
|
|
|
|
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
#if HAVE_FIREWALLD
|
|
|
|
virEbTablesInitialize();
|
|
|
|
if (firewall_cmd_path)
|
|
|
|
n += 3; /* --direct --passthrough eb */
|
|
|
|
#endif
|
|
|
|
|
2009-11-03 22:11:01 +00:00
|
|
|
va_start(args, arg);
|
2010-03-05 16:36:40 +00:00
|
|
|
while (va_arg(args, const char *))
|
2009-11-03 22:11:01 +00:00
|
|
|
n++;
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(argv, n + 1) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
network: use firewalld instead of iptables, when available
* configure.ac, spec file: firewalld defaults to enabled if dbus is
available, otherwise is disabled. If --with_firewalld is explicitly
requested and dbus is not available, configure will fail.
* bridge_driver: add dbus filters to get the FirewallD1.Reloaded
signal and DBus.NameOwnerChanged on org.fedoraproject.FirewallD1.
When these are encountered, reload all the iptables reuls of all
libvirt's virtual networks (similar to what happens when libvirtd is
restarted).
* iptables, ebtables: use firewall-cmd's direct passthrough interface
when available, otherwise use iptables and ebtables commands. This
decision is made once the first time libvirt calls
iptables/ebtables, and that decision is maintained for the life of
libvirtd.
* Note that the nwfilter part of this patch was separated out into
another patch by Stefan in V2, so that needs to be revised and
re-reviewed as well.
================
All the configure.ac and specfile changes are unchanged from Thomas'
V3.
V3 re-ran "firewall-cmd --state" every time a new rule was added,
which was extremely inefficient. V4 uses VIR_ONCE_GLOBAL_INIT to set
up a one-time initialization function.
The VIR_ONCE_GLOBAL_INIT(x) macro references a static function called
vir(Ip|Eb)OnceInit(), which will then be called the first time that
the static function vir(Ip|Eb)TablesInitialize() is called (that
function is defined for you by the macro). This is
thread-safe, so there is no chance of any race.
IMPORTANT NOTE: I've left the VIR_DEBUG messages in these two init
functions (one for iptables, on for ebtables) as VIR_WARN so that I
don't have to turn on all the other debug message just to see
these. Even if this patch doesn't need any other modification, those
messages need to be changed to VIR_DEBUG before pushing.
This one-time initialization works well. However, I've encountered
problems with testing:
1) Whenever I have enabled the firewalld service, *all* attempts to
call firewall-cmd from within libvirtd end with firewall-cmd hanging
internally somewhere. This is *not* the case if firewall-cmd returns
non-0 in response to "firewall-cmd --state" (i.e. *that* command runs
and returns to libvirt successfully.)
2) If I start libvirtd while firewalld is stopped, then start
firewalld later, this triggers libvirtd to reload its iptables rules,
however it also spits out a *ton* of complaints about deletion failing
(I suppose because firewalld has nuked all of libvirt's rules). I
guess we need to suppress those messages (which is a more annoying
problem to fix than you might think, but that's another story).
3) I noticed a few times during this long line of errors that
firewalld made a complaint about "Resource Temporarily
unavailable. Having libvirtd access iptables commands directly at the
same time as firewalld is doing so is apparently problematic.
4) In general, I'm concerned about the "set it once and never change
it" method - if firewalld is disabled at libvirtd startup, causing
libvirtd to always use iptables/ebtables directly, this won't cause
*terrible* problems, but if libvirtd decides to use firewall-cmd and
firewalld is later disabled, libvirtd will not be able to recover.
2012-08-14 18:59:52 +00:00
|
|
|
#if HAVE_FIREWALLD
|
|
|
|
if (firewall_cmd_path) {
|
|
|
|
if (!(argv[n++] = strdup(firewall_cmd_path)))
|
|
|
|
goto error;
|
|
|
|
if (!(argv[n++] = strdup("--direct")))
|
|
|
|
goto error;
|
|
|
|
if (!(argv[n++] = strdup("--passthrough")))
|
|
|
|
goto error;
|
|
|
|
if (!(argv[n++] = strdup("eb")))
|
|
|
|
goto error;
|
|
|
|
} else
|
|
|
|
#endif
|
2009-11-03 22:11:01 +00:00
|
|
|
if (!(argv[n++] = strdup(EBTABLES_PATH)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
command_idx = n;
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
if (action == ADD || action == REMOVE) {
|
2009-11-03 22:11:01 +00:00
|
|
|
if (!(argv[n++] = strdup("--insert")))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!(argv[n++] = strdup(rules->chain)))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(argv[n++] = strdup(arg)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
va_start(args, arg);
|
|
|
|
|
2010-05-18 12:42:33 +00:00
|
|
|
while ((s = va_arg(args, const char *))) {
|
|
|
|
if (!(argv[n++] = strdup(s))) {
|
|
|
|
va_end(args);
|
2009-11-03 22:11:01 +00:00
|
|
|
goto error;
|
2010-05-18 12:42:33 +00:00
|
|
|
}
|
|
|
|
}
|
2009-11-03 22:11:01 +00:00
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (!(rule = virArgvToString(&argv[command_idx])))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (action == REMOVE) {
|
|
|
|
VIR_FREE(argv[command_idx]);
|
|
|
|
if (!(argv[command_idx] = strdup("--delete")))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
if (virRun(argv, NULL) < 0) {
|
2009-11-03 22:11:01 +00:00
|
|
|
retval = errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action == ADD || action == CREATE || action == POLICY ||
|
|
|
|
action == INSERT) {
|
|
|
|
retval = ebtRulesAppend(rules, rule, argv, command_idx);
|
|
|
|
rule = NULL;
|
|
|
|
argv = NULL;
|
|
|
|
} else {
|
|
|
|
retval = ebtRulesRemove(rules, rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FREE(rule);
|
|
|
|
|
|
|
|
if (argv) {
|
|
|
|
n = 0;
|
|
|
|
while (argv[n])
|
|
|
|
VIR_FREE(argv[n++]);
|
|
|
|
VIR_FREE(argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ebtablesContextNew:
|
|
|
|
*
|
|
|
|
* Create a new ebtable context
|
|
|
|
*
|
|
|
|
* Returns a pointer to the new structure or NULL in case of error
|
|
|
|
*/
|
|
|
|
ebtablesContext *
|
|
|
|
ebtablesContextNew(const char *driver)
|
|
|
|
{
|
2011-04-03 09:21:16 +00:00
|
|
|
bool success = false;
|
|
|
|
ebtablesContext *ctx = NULL;
|
|
|
|
char *input_chain = NULL;
|
|
|
|
char *forward_chain = NULL;
|
|
|
|
char *nat_chain = NULL;
|
2009-11-03 22:11:01 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(ctx) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2011-04-03 09:21:16 +00:00
|
|
|
if (virAsprintf(&input_chain, "libvirt_%s_INPUT", driver) < 0 ||
|
|
|
|
virAsprintf(&forward_chain, "libvirt_%s_FORWARD", driver) < 0 ||
|
|
|
|
virAsprintf(&nat_chain, "libvirt_%s_POSTROUTING", driver) < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-11-03 22:11:01 +00:00
|
|
|
|
2011-04-03 09:21:16 +00:00
|
|
|
if (!(ctx->input_filter = ebtRulesNew("filter", input_chain)))
|
|
|
|
goto cleanup;
|
2009-11-03 22:11:01 +00:00
|
|
|
|
2011-04-03 09:21:16 +00:00
|
|
|
if (!(ctx->forward_filter = ebtRulesNew("filter", forward_chain)))
|
|
|
|
goto cleanup;
|
2009-11-03 22:11:01 +00:00
|
|
|
|
2011-04-03 09:21:16 +00:00
|
|
|
if (!(ctx->nat_postrouting = ebtRulesNew("nat", nat_chain)))
|
|
|
|
goto cleanup;
|
2009-11-03 22:11:01 +00:00
|
|
|
|
2011-04-03 09:21:16 +00:00
|
|
|
success = true;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(input_chain);
|
|
|
|
VIR_FREE(forward_chain);
|
|
|
|
VIR_FREE(nat_chain);
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
ebtablesContextFree(ctx);
|
|
|
|
ctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx;
|
2009-11-03 22:11:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ebtablesContextFree:
|
|
|
|
* @ctx: pointer to the EB table context
|
|
|
|
*
|
|
|
|
* Free the resources associated with an EB table context
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ebtablesContextFree(ebtablesContext *ctx)
|
|
|
|
{
|
2010-11-30 16:00:30 +00:00
|
|
|
if (!ctx)
|
|
|
|
return;
|
2009-11-03 22:11:01 +00:00
|
|
|
if (ctx->input_filter)
|
|
|
|
ebtRulesFree(ctx->input_filter);
|
|
|
|
if (ctx->forward_filter)
|
|
|
|
ebtRulesFree(ctx->forward_filter);
|
|
|
|
if (ctx->nat_postrouting)
|
|
|
|
ebtRulesFree(ctx->nat_postrouting);
|
|
|
|
VIR_FREE(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ebtablesAddForwardPolicyReject(ebtablesContext *ctx)
|
|
|
|
{
|
|
|
|
return ebtablesForwardPolicyReject(ctx, ADD);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
ebtablesRemoveForwardPolicyReject(ebtablesContext *ctx)
|
|
|
|
{
|
|
|
|
return ebtablesForwardPolicyReject(ctx, REMOVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ebtablesForwardPolicyReject(ebtablesContext *ctx,
|
|
|
|
int action)
|
|
|
|
{
|
|
|
|
/* create it, if it does not exist */
|
|
|
|
if (action == ADD) {
|
|
|
|
ebtablesAddRemoveRule(ctx->forward_filter,
|
|
|
|
CREATE,
|
|
|
|
"--new-chain", ctx->forward_filter->chain, NULL,
|
|
|
|
NULL);
|
|
|
|
ebtablesAddRemoveRule(ctx->forward_filter,
|
|
|
|
INSERT,
|
|
|
|
"--insert", "FORWARD", "--jump",
|
|
|
|
ctx->forward_filter->chain, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ebtablesAddRemoveRule(ctx->forward_filter,
|
|
|
|
POLICY,
|
|
|
|
"-P", ctx->forward_filter->chain, "DROP",
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow all traffic destined to the bridge, with a valid network address
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ebtablesForwardAllowIn(ebtablesContext *ctx,
|
|
|
|
const char *iface,
|
|
|
|
const char *macaddr,
|
|
|
|
int action)
|
|
|
|
{
|
|
|
|
return ebtablesAddRemoveRule(ctx->forward_filter,
|
|
|
|
action,
|
|
|
|
"--in-interface", iface,
|
|
|
|
"--source", macaddr,
|
|
|
|
"--jump", "ACCEPT",
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ebtablesAddForwardAllowIn:
|
|
|
|
* @ctx: pointer to the EB table context
|
|
|
|
* @iface: the output interface name
|
|
|
|
* @physdev: the physical input device or NULL
|
|
|
|
*
|
|
|
|
* Add rules to the EB table context to allow the traffic on
|
|
|
|
* @physdev device to be forwarded to interface @iface. This allows
|
|
|
|
* the inbound traffic on a bridge.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or an error code otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ebtablesAddForwardAllowIn(ebtablesContext *ctx,
|
|
|
|
const char *iface,
|
2012-07-17 12:07:59 +00:00
|
|
|
const virMacAddrPtr mac)
|
2009-11-03 22:11:01 +00:00
|
|
|
{
|
2013-03-26 11:21:33 +00:00
|
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virMacAddrFormat(mac, macaddr);
|
2009-11-03 22:11:01 +00:00
|
|
|
return ebtablesForwardAllowIn(ctx, iface, macaddr, ADD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ebtablesRemoveForwardAllowIn:
|
|
|
|
* @ctx: pointer to the EB table context
|
|
|
|
* @iface: the output interface name
|
|
|
|
* @physdev: the physical input device or NULL
|
|
|
|
*
|
|
|
|
* Remove rules from the EB table context hence forbidding the traffic
|
|
|
|
* on the @physdev device to be forwarded to interface @iface. This
|
|
|
|
* stops the inbound traffic on a bridge.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or an error code otherwise
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ebtablesRemoveForwardAllowIn(ebtablesContext *ctx,
|
|
|
|
const char *iface,
|
2012-07-17 12:07:59 +00:00
|
|
|
const virMacAddrPtr mac)
|
2009-11-03 22:11:01 +00:00
|
|
|
{
|
2013-03-26 11:21:33 +00:00
|
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virMacAddrFormat(mac, macaddr);
|
2009-11-03 22:11:01 +00:00
|
|
|
return ebtablesForwardAllowIn(ctx, iface, macaddr, REMOVE);
|
|
|
|
}
|