mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-03 20:01:16 +00:00
0a867cd895
iptables and ip6tables have had a "-w" commandline option to grab a systemwide lock that prevents two iptables invocations from modifying the iptables chains since 2013 (upstream commit 93587a04 in iptables-1.4.20). Similarly, ebtables has had a "--concurrent" commandline option for the same purpose since 2011 (in the upstream ebtables commit f9b4bcb93, which was present in ebtables-2.0.10.4). Libvirt added code to conditionally use the commandline option for iptables/ip6tables in upstream commitba95426d6f
(libvirt-1.2.0, November 2013), and for ebtables in upstream commitdc33e6e4a5
(libvirt-1.2.11, November 2014) (the latter actually *re*-added the locking for iptables/ip6tables, as it had accidentally been removed during a refactor of firewall code in the interim). I say "conditionally" because a check was made during firewall module initialization that tried executing a test command with the -w/--concurrent option, and only continued using it for actual commands if that test command completed successfully. At the time the code was added this was a reasonable thing to do, as it had been less than a year since introduction of -w to iptables, so many distros supported by libvirt were still using iptables (and possibly even ebtables) versions too old to have the new commandline options. It is now 2020, and as far as I can discern from repology.org (and manually examining a RHEL7.9 system), every version of every distro that is supported by libvirt now uses new enough versions of both iptables and ebtables that they all have support for -w/--concurrent. That means we can finally remove the conditional code and simply always use them. Signed-off-by: Laine Stump <laine@redhat.com> Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
521 lines
15 KiB
C
521 lines
15 KiB
C
/*
|
|
* nwfilterxml2firewalltest.c: Test iptables rule generation
|
|
*
|
|
* 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/>.
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#if defined (__linux__)
|
|
|
|
# include "testutils.h"
|
|
# include "nwfilter/nwfilter_ebiptables_driver.h"
|
|
# include "virbuffer.h"
|
|
|
|
# define LIBVIRT_VIRFIREWALLPRIV_H_ALLOW
|
|
# include "virfirewallpriv.h"
|
|
|
|
# define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
|
|
# include "vircommandpriv.h"
|
|
|
|
# define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
# ifdef __linux__
|
|
# define RULESTYPE "linux"
|
|
# else
|
|
# error "test case not ported to this platform"
|
|
# endif
|
|
|
|
typedef struct _virNWFilterInst virNWFilterInst;
|
|
typedef virNWFilterInst *virNWFilterInstPtr;
|
|
struct _virNWFilterInst {
|
|
virNWFilterDefPtr *filters;
|
|
size_t nfilters;
|
|
virNWFilterRuleInstPtr *rules;
|
|
size_t nrules;
|
|
};
|
|
|
|
/*
|
|
* Some sets of rules that will be common to all test files,
|
|
* so we don't bother including them in the test data files
|
|
* as that would just bloat them
|
|
*/
|
|
|
|
static const char *commonRules[] = {
|
|
/* Dropping ebtables rules */
|
|
"ebtables --concurrent -t nat -D PREROUTING -i vnet0 -j libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -D POSTROUTING -o vnet0 -j libvirt-P-vnet0\n"
|
|
"ebtables --concurrent -t nat -L libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -L libvirt-P-vnet0\n"
|
|
"ebtables --concurrent -t nat -F libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -X libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -F libvirt-P-vnet0\n"
|
|
"ebtables --concurrent -t nat -X libvirt-P-vnet0\n",
|
|
|
|
/* Creating ebtables chains */
|
|
"ebtables --concurrent -t nat -N libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -N libvirt-P-vnet0\n",
|
|
|
|
/* Dropping iptables rules */
|
|
"iptables -w -D libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
|
|
"iptables -w -D libvirt-out -m physdev --physdev-out vnet0 -g FP-vnet0\n"
|
|
"iptables -w -D libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
|
|
"iptables -w -D libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
|
|
"iptables -w -F FP-vnet0\n"
|
|
"iptables -w -X FP-vnet0\n"
|
|
"iptables -w -F FJ-vnet0\n"
|
|
"iptables -w -X FJ-vnet0\n"
|
|
"iptables -w -F HJ-vnet0\n"
|
|
"iptables -w -X HJ-vnet0\n",
|
|
|
|
/* Creating iptables chains */
|
|
"iptables -w -N libvirt-in\n"
|
|
"iptables -w -N libvirt-out\n"
|
|
"iptables -w -N libvirt-in-post\n"
|
|
"iptables -w -N libvirt-host-in\n"
|
|
"iptables -w -D FORWARD -j libvirt-in\n"
|
|
"iptables -w -D FORWARD -j libvirt-out\n"
|
|
"iptables -w -D FORWARD -j libvirt-in-post\n"
|
|
"iptables -w -D INPUT -j libvirt-host-in\n"
|
|
"iptables -w -I FORWARD 1 -j libvirt-in\n"
|
|
"iptables -w -I FORWARD 2 -j libvirt-out\n"
|
|
"iptables -w -I FORWARD 3 -j libvirt-in-post\n"
|
|
"iptables -w -I INPUT 1 -j libvirt-host-in\n"
|
|
"iptables -w -N FP-vnet0\n"
|
|
"iptables -w -N FJ-vnet0\n"
|
|
"iptables -w -N HJ-vnet0\n"
|
|
"iptables -w -A libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
|
|
"iptables -w -A libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
|
|
"iptables -w -A libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
|
|
"iptables -w -D libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n"
|
|
"iptables -w -A libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n",
|
|
|
|
/* Dropping ip6tables rules */
|
|
"ip6tables -w -D libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
|
|
"ip6tables -w -D libvirt-out -m physdev --physdev-out vnet0 -g FP-vnet0\n"
|
|
"ip6tables -w -D libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
|
|
"ip6tables -w -D libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
|
|
"ip6tables -w -F FP-vnet0\n"
|
|
"ip6tables -w -X FP-vnet0\n"
|
|
"ip6tables -w -F FJ-vnet0\n"
|
|
"ip6tables -w -X FJ-vnet0\n"
|
|
"ip6tables -w -F HJ-vnet0\n"
|
|
"ip6tables -w -X HJ-vnet0\n",
|
|
|
|
/* Creating ip6tables chains */
|
|
"ip6tables -w -N libvirt-in\n"
|
|
"ip6tables -w -N libvirt-out\n"
|
|
"ip6tables -w -N libvirt-in-post\n"
|
|
"ip6tables -w -N libvirt-host-in\n"
|
|
"ip6tables -w -D FORWARD -j libvirt-in\n"
|
|
"ip6tables -w -D FORWARD -j libvirt-out\n"
|
|
"ip6tables -w -D FORWARD -j libvirt-in-post\n"
|
|
"ip6tables -w -D INPUT -j libvirt-host-in\n"
|
|
"ip6tables -w -I FORWARD 1 -j libvirt-in\n"
|
|
"ip6tables -w -I FORWARD 2 -j libvirt-out\n"
|
|
"ip6tables -w -I FORWARD 3 -j libvirt-in-post\n"
|
|
"ip6tables -w -I INPUT 1 -j libvirt-host-in\n"
|
|
"ip6tables -w -N FP-vnet0\n"
|
|
"ip6tables -w -N FJ-vnet0\n"
|
|
"ip6tables -w -N HJ-vnet0\n"
|
|
"ip6tables -w -A libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
|
|
"ip6tables -w -A libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
|
|
"ip6tables -w -A libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
|
|
"ip6tables -w -D libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n"
|
|
"ip6tables -w -A libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n",
|
|
|
|
/* Inserting ebtables rules */
|
|
"ebtables --concurrent -t nat -A PREROUTING -i vnet0 -j libvirt-J-vnet0\n"
|
|
"ebtables --concurrent -t nat -A POSTROUTING -o vnet0 -j libvirt-P-vnet0\n",
|
|
};
|
|
|
|
|
|
static GHashTable *
|
|
virNWFilterCreateVarsFrom(GHashTable *vars1,
|
|
GHashTable *vars2)
|
|
{
|
|
GHashTable *res = virHashNew(virNWFilterVarValueHashFree);
|
|
if (!res)
|
|
return NULL;
|
|
|
|
if (virNWFilterHashTablePutAll(vars1, res) < 0)
|
|
goto err_exit;
|
|
|
|
if (virNWFilterHashTablePutAll(vars2, res) < 0)
|
|
goto err_exit;
|
|
|
|
return res;
|
|
|
|
err_exit:
|
|
virHashFree(res);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
virNWFilterRuleInstFree(virNWFilterRuleInstPtr inst)
|
|
{
|
|
if (!inst)
|
|
return;
|
|
|
|
virHashFree(inst->vars);
|
|
VIR_FREE(inst);
|
|
}
|
|
|
|
|
|
static void
|
|
virNWFilterInstReset(virNWFilterInstPtr inst)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < inst->nfilters; i++)
|
|
virNWFilterDefFree(inst->filters[i]);
|
|
VIR_FREE(inst->filters);
|
|
inst->nfilters = 0;
|
|
|
|
for (i = 0; i < inst->nrules; i++)
|
|
virNWFilterRuleInstFree(inst->rules[i]);
|
|
VIR_FREE(inst->rules);
|
|
inst->nrules = 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virNWFilterDefToInst(const char *xml,
|
|
GHashTable *vars,
|
|
virNWFilterInstPtr inst);
|
|
|
|
static int
|
|
virNWFilterRuleDefToRuleInst(virNWFilterDefPtr def,
|
|
virNWFilterRuleDefPtr rule,
|
|
GHashTable *vars,
|
|
virNWFilterInstPtr inst)
|
|
{
|
|
virNWFilterRuleInstPtr ruleinst;
|
|
int ret = -1;
|
|
|
|
ruleinst = g_new0(virNWFilterRuleInst, 1);
|
|
|
|
ruleinst->chainSuffix = def->chainsuffix;
|
|
ruleinst->chainPriority = def->chainPriority;
|
|
ruleinst->def = rule;
|
|
ruleinst->priority = rule->priority;
|
|
if (!(ruleinst->vars = virHashNew(virNWFilterVarValueHashFree)))
|
|
goto cleanup;
|
|
if (virNWFilterHashTablePutAll(vars, ruleinst->vars) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_APPEND_ELEMENT(inst->rules,
|
|
inst->nrules,
|
|
ruleinst) < 0)
|
|
goto cleanup;
|
|
ruleinst = NULL;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virNWFilterRuleInstFree(ruleinst);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNWFilterIncludeDefToRuleInst(virNWFilterIncludeDefPtr inc,
|
|
GHashTable *vars,
|
|
virNWFilterInstPtr inst)
|
|
{
|
|
GHashTable *tmpvars = NULL;
|
|
int ret = -1;
|
|
char *xml;
|
|
|
|
xml = g_strdup_printf("%s/nwfilterxml2firewalldata/%s.xml", abs_srcdir,
|
|
inc->filterref);
|
|
|
|
/* create a temporary hashmap for depth-first tree traversal */
|
|
if (!(tmpvars = virNWFilterCreateVarsFrom(inc->params,
|
|
vars)))
|
|
goto cleanup;
|
|
|
|
if (virNWFilterDefToInst(xml,
|
|
tmpvars,
|
|
inst) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
if (ret < 0)
|
|
virNWFilterInstReset(inst);
|
|
virHashFree(tmpvars);
|
|
VIR_FREE(xml);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNWFilterDefToInst(const char *xml,
|
|
GHashTable *vars,
|
|
virNWFilterInstPtr inst)
|
|
{
|
|
size_t i;
|
|
int ret = -1;
|
|
virNWFilterDefPtr def = virNWFilterDefParseFile(xml);
|
|
|
|
if (!def)
|
|
return -1;
|
|
|
|
if (VIR_APPEND_ELEMENT_COPY(inst->filters,
|
|
inst->nfilters,
|
|
def) < 0) {
|
|
virNWFilterDefFree(def);
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < def->nentries; i++) {
|
|
if (def->filterEntries[i]->rule) {
|
|
if (virNWFilterRuleDefToRuleInst(def,
|
|
def->filterEntries[i]->rule,
|
|
vars,
|
|
inst) < 0)
|
|
goto cleanup;
|
|
} else if (def->filterEntries[i]->include) {
|
|
if (virNWFilterIncludeDefToRuleInst(def->filterEntries[i]->include,
|
|
vars,
|
|
inst) < 0)
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
if (ret < 0)
|
|
virNWFilterInstReset(inst);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void testRemoveCommonRules(char *rules)
|
|
{
|
|
size_t i;
|
|
char *offset = rules;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(commonRules); i++) {
|
|
char *tmp = strstr(offset, commonRules[i]);
|
|
size_t len = strlen(commonRules[i]);
|
|
if (tmp) {
|
|
memmove(tmp, tmp + len, (strlen(tmp) + 1) - len);
|
|
offset = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int testSetOneParameter(GHashTable *vars,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
virNWFilterVarValuePtr val;
|
|
|
|
if ((val = virHashLookup(vars, name)) == NULL) {
|
|
val = virNWFilterVarValueCreateSimpleCopyValue(value);
|
|
if (!val)
|
|
return -1;
|
|
if (virHashUpdateEntry(vars, name, val) < 0) {
|
|
virNWFilterVarValueFree(val);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (virNWFilterVarValueAddValueCopy(val, value) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int testSetDefaultParameters(GHashTable *vars)
|
|
{
|
|
if (testSetOneParameter(vars, "IPSETNAME", "tck_test") < 0 ||
|
|
testSetOneParameter(vars, "A", "1.1.1.1") ||
|
|
testSetOneParameter(vars, "A", "2.2.2.2") ||
|
|
testSetOneParameter(vars, "A", "3.3.3.3") ||
|
|
testSetOneParameter(vars, "A", "3.3.3.3") ||
|
|
testSetOneParameter(vars, "B", "80") ||
|
|
testSetOneParameter(vars, "B", "90") ||
|
|
testSetOneParameter(vars, "B", "80") ||
|
|
testSetOneParameter(vars, "B", "80") ||
|
|
testSetOneParameter(vars, "C", "1080") ||
|
|
testSetOneParameter(vars, "C", "1090") ||
|
|
testSetOneParameter(vars, "C", "1100") ||
|
|
testSetOneParameter(vars, "C", "1110"))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int testCompareXMLToArgvFiles(const char *xml,
|
|
const char *cmdline)
|
|
{
|
|
char *actualargv = NULL;
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
GHashTable *vars = virHashNew(virNWFilterVarValueHashFree);
|
|
virNWFilterInst inst;
|
|
int ret = -1;
|
|
|
|
memset(&inst, 0, sizeof(inst));
|
|
|
|
virCommandSetDryRun(&buf, NULL, NULL);
|
|
|
|
if (!vars)
|
|
goto cleanup;
|
|
|
|
if (testSetDefaultParameters(vars) < 0)
|
|
goto cleanup;
|
|
|
|
if (virNWFilterDefToInst(xml,
|
|
vars,
|
|
&inst) < 0)
|
|
goto cleanup;
|
|
|
|
if (ebiptables_driver.applyNewRules("vnet0", inst.rules, inst.nrules) < 0)
|
|
goto cleanup;
|
|
|
|
actualargv = virBufferContentAndReset(&buf);
|
|
virTestClearCommandPath(actualargv);
|
|
virCommandSetDryRun(NULL, NULL, NULL);
|
|
|
|
testRemoveCommonRules(actualargv);
|
|
|
|
if (virTestCompareToFile(actualargv, cmdline) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(actualargv);
|
|
virNWFilterInstReset(&inst);
|
|
virHashFree(vars);
|
|
return ret;
|
|
}
|
|
|
|
struct testInfo {
|
|
const char *name;
|
|
};
|
|
|
|
|
|
static int
|
|
testCompareXMLToIPTablesHelper(const void *data)
|
|
{
|
|
int result = -1;
|
|
const struct testInfo *info = data;
|
|
char *xml = NULL;
|
|
char *args = NULL;
|
|
|
|
xml = g_strdup_printf("%s/nwfilterxml2firewalldata/%s.xml",
|
|
abs_srcdir, info->name);
|
|
args = g_strdup_printf("%s/nwfilterxml2firewalldata/%s-%s.args",
|
|
abs_srcdir, info->name, RULESTYPE);
|
|
|
|
result = testCompareXMLToArgvFiles(xml, args);
|
|
|
|
VIR_FREE(xml);
|
|
VIR_FREE(args);
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
hasNetfilterTools(void)
|
|
{
|
|
return virFileIsExecutable(IPTABLES_PATH) &&
|
|
virFileIsExecutable(IP6TABLES_PATH) &&
|
|
virFileIsExecutable(EBTABLES_PATH);
|
|
}
|
|
|
|
|
|
static int
|
|
mymain(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
# define DO_TEST(name) \
|
|
do { \
|
|
static struct testInfo info = { \
|
|
name, \
|
|
}; \
|
|
if (virTestRun("NWFilter XML-2-firewall " name, \
|
|
testCompareXMLToIPTablesHelper, &info) < 0) \
|
|
ret = -1; \
|
|
} while (0)
|
|
|
|
if (virFirewallSetBackend(VIR_FIREWALL_BACKEND_DIRECT) < 0) {
|
|
if (!hasNetfilterTools()) {
|
|
fprintf(stderr, "iptables/ip6tables/ebtables tools not present");
|
|
return EXIT_AM_SKIP;
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
DO_TEST("ah");
|
|
DO_TEST("ah-ipv6");
|
|
DO_TEST("all");
|
|
DO_TEST("all-ipv6");
|
|
DO_TEST("arp");
|
|
DO_TEST("comment");
|
|
DO_TEST("conntrack");
|
|
DO_TEST("esp");
|
|
DO_TEST("esp-ipv6");
|
|
DO_TEST("example-1");
|
|
DO_TEST("example-2");
|
|
DO_TEST("hex-data");
|
|
DO_TEST("icmp-direction2");
|
|
DO_TEST("icmp-direction3");
|
|
DO_TEST("icmp-direction");
|
|
DO_TEST("icmp");
|
|
DO_TEST("icmpv6");
|
|
DO_TEST("igmp");
|
|
DO_TEST("ip");
|
|
DO_TEST("ipset");
|
|
DO_TEST("ipt-no-macspoof");
|
|
DO_TEST("ipv6");
|
|
DO_TEST("iter1");
|
|
DO_TEST("iter2");
|
|
DO_TEST("iter3");
|
|
DO_TEST("mac");
|
|
DO_TEST("rarp");
|
|
DO_TEST("sctp");
|
|
DO_TEST("sctp-ipv6");
|
|
DO_TEST("stp");
|
|
DO_TEST("target2");
|
|
DO_TEST("target");
|
|
DO_TEST("tcp");
|
|
DO_TEST("tcp-ipv6");
|
|
DO_TEST("udp");
|
|
DO_TEST("udp-ipv6");
|
|
DO_TEST("udplite");
|
|
DO_TEST("udplite-ipv6");
|
|
DO_TEST("vlan");
|
|
|
|
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
VIR_TEST_MAIN(mymain)
|
|
|
|
#else /* ! defined (__linux__) */
|
|
|
|
int main(void)
|
|
{
|
|
return EXIT_AM_SKIP;
|
|
}
|
|
|
|
#endif /* ! defined (__linux__) */
|