/*
* 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
* .
*
*/
#include
#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 -t nat -D PREROUTING -i vnet0 -j libvirt-J-vnet0\n"
"ebtables -t nat -D POSTROUTING -o vnet0 -j libvirt-P-vnet0\n"
"ebtables -t nat -L libvirt-J-vnet0\n"
"ebtables -t nat -L libvirt-P-vnet0\n"
"ebtables -t nat -F libvirt-J-vnet0\n"
"ebtables -t nat -X libvirt-J-vnet0\n"
"ebtables -t nat -F libvirt-P-vnet0\n"
"ebtables -t nat -X libvirt-P-vnet0\n",
/* Creating ebtables chains */
"ebtables -t nat -N libvirt-J-vnet0\n"
"ebtables -t nat -N libvirt-P-vnet0\n",
/* Dropping iptables rules */
"iptables -D libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
"iptables -D libvirt-out -m physdev --physdev-out vnet0 -g FP-vnet0\n"
"iptables -D libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
"iptables -D libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
"iptables -F FP-vnet0\n"
"iptables -X FP-vnet0\n"
"iptables -F FJ-vnet0\n"
"iptables -X FJ-vnet0\n"
"iptables -F HJ-vnet0\n"
"iptables -X HJ-vnet0\n",
/* Creating iptables chains */
"iptables -N libvirt-in\n"
"iptables -N libvirt-out\n"
"iptables -N libvirt-in-post\n"
"iptables -N libvirt-host-in\n"
"iptables -D FORWARD -j libvirt-in\n"
"iptables -D FORWARD -j libvirt-out\n"
"iptables -D FORWARD -j libvirt-in-post\n"
"iptables -D INPUT -j libvirt-host-in\n"
"iptables -I FORWARD 1 -j libvirt-in\n"
"iptables -I FORWARD 2 -j libvirt-out\n"
"iptables -I FORWARD 3 -j libvirt-in-post\n"
"iptables -I INPUT 1 -j libvirt-host-in\n"
"iptables -N FP-vnet0\n"
"iptables -N FJ-vnet0\n"
"iptables -N HJ-vnet0\n"
"iptables -A libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
"iptables -A libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
"iptables -A libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
"iptables -D libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n"
"iptables -A libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n",
/* Dropping ip6tables rules */
"ip6tables -D libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
"ip6tables -D libvirt-out -m physdev --physdev-out vnet0 -g FP-vnet0\n"
"ip6tables -D libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
"ip6tables -D libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
"ip6tables -F FP-vnet0\n"
"ip6tables -X FP-vnet0\n"
"ip6tables -F FJ-vnet0\n"
"ip6tables -X FJ-vnet0\n"
"ip6tables -F HJ-vnet0\n"
"ip6tables -X HJ-vnet0\n",
/* Creating ip6tables chains */
"ip6tables -N libvirt-in\n"
"ip6tables -N libvirt-out\n"
"ip6tables -N libvirt-in-post\n"
"ip6tables -N libvirt-host-in\n"
"ip6tables -D FORWARD -j libvirt-in\n"
"ip6tables -D FORWARD -j libvirt-out\n"
"ip6tables -D FORWARD -j libvirt-in-post\n"
"ip6tables -D INPUT -j libvirt-host-in\n"
"ip6tables -I FORWARD 1 -j libvirt-in\n"
"ip6tables -I FORWARD 2 -j libvirt-out\n"
"ip6tables -I FORWARD 3 -j libvirt-in-post\n"
"ip6tables -I INPUT 1 -j libvirt-host-in\n"
"ip6tables -N FP-vnet0\n"
"ip6tables -N FJ-vnet0\n"
"ip6tables -N HJ-vnet0\n"
"ip6tables -A libvirt-out -m physdev --physdev-is-bridged --physdev-out vnet0 -g FP-vnet0\n"
"ip6tables -A libvirt-in -m physdev --physdev-in vnet0 -g FJ-vnet0\n"
"ip6tables -A libvirt-host-in -m physdev --physdev-in vnet0 -g HJ-vnet0\n"
"ip6tables -D libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n"
"ip6tables -A libvirt-in-post -m physdev --physdev-in vnet0 -j ACCEPT\n",
/* Inserting ebtables rules */
"ebtables -t nat -A PREROUTING -i vnet0 -j libvirt-J-vnet0\n"
"ebtables -t nat -A POSTROUTING -o vnet0 -j libvirt-P-vnet0\n",
};
static virHashTablePtr
virNWFilterCreateVarsFrom(virHashTablePtr vars1,
virHashTablePtr vars2)
{
virHashTablePtr res = virNWFilterHashTableCreate(0);
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,
virHashTablePtr vars,
virNWFilterInstPtr inst);
static int
virNWFilterRuleDefToRuleInst(virNWFilterDefPtr def,
virNWFilterRuleDefPtr rule,
virHashTablePtr vars,
virNWFilterInstPtr inst)
{
virNWFilterRuleInstPtr ruleinst;
int ret = -1;
if (VIR_ALLOC(ruleinst) < 0)
goto cleanup;
ruleinst->chainSuffix = def->chainsuffix;
ruleinst->chainPriority = def->chainPriority;
ruleinst->def = rule;
ruleinst->priority = rule->priority;
if (!(ruleinst->vars = virNWFilterHashTableCreate(0)))
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,
virHashTablePtr vars,
virNWFilterInstPtr inst)
{
virHashTablePtr tmpvars = NULL;
int ret = -1;
char *xml;
if (virAsprintf(&xml, "%s/nwfilterxml2firewalldata/%s.xml",
abs_srcdir, inc->filterref) < 0)
return -1;
/* 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,
virHashTablePtr 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(virHashTablePtr vars,
const char *name,
const char *value)
{
int ret = -1;
virNWFilterVarValuePtr val;
if ((val = virHashLookup(vars, name)) == NULL) {
val = virNWFilterVarValueCreateSimpleCopyValue(value);
if (!val)
goto cleanup;
if (virHashUpdateEntry(vars, name, val) < 0) {
virNWFilterVarValueFree(val);
goto cleanup;
}
} else {
if (virNWFilterVarValueAddValueCopy(val, value) < 0)
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int testSetDefaultParameters(virHashTablePtr 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;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virHashTablePtr vars = virNWFilterHashTableCreate(0);
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:
virBufferFreeAndReset(&buf);
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;
if (virAsprintf(&xml, "%s/nwfilterxml2firewalldata/%s.xml",
abs_srcdir, info->name) < 0 ||
virAsprintf(&args, "%s/nwfilterxml2firewalldata/%s-%s.args",
abs_srcdir, info->name, RULESTYPE) < 0)
goto cleanup;
result = testCompareXMLToArgvFiles(xml, args);
cleanup:
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)
virFirewallSetLockOverride(true);
if (virFirewallSetBackend(VIR_FIREWALL_BACKEND_DIRECT) < 0) {
if (!hasNetfilterTools()) {
fprintf(stderr, "iptables/ip6tables/ebtables tools not present");
return EXIT_AM_SKIP;
}
ret = -1;
goto cleanup;
}
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");
cleanup:
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN(mymain)
#else /* ! defined (__linux__) */
int main(void)
{
return EXIT_AM_SKIP;
}
#endif /* ! defined (__linux__) */