/* * 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; struct _virNWFilterInst { virNWFilterDef **filters; size_t nfilters; virNWFilterRuleInst **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 \\\n--concurrent \\\n-t nat \\\n-D PREROUTING \\\n-i vnet0 \\\n-j libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-D POSTROUTING \\\n-o vnet0 \\\n-j libvirt-P-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-L libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-L libvirt-P-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-F libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-X libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-F libvirt-P-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-X libvirt-P-vnet0\n", /* Creating ebtables chains */ "ebtables \\\n--concurrent \\\n-t nat \\\n-N libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-N libvirt-P-vnet0\n", /* Dropping iptables rules */ "iptables \\\n-w \\\n-D libvirt-out \\\n-m physdev \\\n--physdev-is-bridged \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "iptables \\\n-w \\\n-D libvirt-out \\\n-m physdev \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "iptables \\\n-w \\\n-D libvirt-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g FJ-vnet0\n" "iptables \\\n-w \\\n-D libvirt-host-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g HJ-vnet0\n" "iptables \\\n-w \\\n-F FP-vnet0\n" "iptables \\\n-w \\\n-X FP-vnet0\n" "iptables \\\n-w \\\n-F FJ-vnet0\n" "iptables \\\n-w \\\n-X FJ-vnet0\n" "iptables \\\n-w \\\n-F HJ-vnet0\n" "iptables \\\n-w \\\n-X HJ-vnet0\n", /* Creating iptables chains */ "iptables \\\n-w \\\n-N libvirt-in\n" "iptables \\\n-w \\\n-N libvirt-out\n" "iptables \\\n-w \\\n-N libvirt-in-post\n" "iptables \\\n-w \\\n-N libvirt-host-in\n" "iptables \\\n-w \\\n-D FORWARD \\\n-j libvirt-in\n" "iptables \\\n-w \\\n-D FORWARD \\\n-j libvirt-out\n" "iptables \\\n-w \\\n-D FORWARD \\\n-j libvirt-in-post\n" "iptables \\\n-w \\\n-D INPUT \\\n-j libvirt-host-in\n" "iptables \\\n-w \\\n-I FORWARD 1 \\\n-j libvirt-in\n" "iptables \\\n-w \\\n-I FORWARD 2 \\\n-j libvirt-out\n" "iptables \\\n-w \\\n-I FORWARD 3 \\\n-j libvirt-in-post\n" "iptables \\\n-w \\\n-I INPUT 1 \\\n-j libvirt-host-in\n" "iptables \\\n-w \\\n-N FP-vnet0\n" "iptables \\\n-w \\\n-N FJ-vnet0\n" "iptables \\\n-w \\\n-N HJ-vnet0\n" "iptables \\\n-w \\\n-A libvirt-out \\\n-m physdev \\\n--physdev-is-bridged \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "iptables \\\n-w \\\n-A libvirt-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g FJ-vnet0\n" "iptables \\\n-w \\\n-A libvirt-host-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g HJ-vnet0\n" "iptables \\\n-w \\\n-D libvirt-in-post \\\n-m physdev \\\n--physdev-in vnet0 \\\n-j ACCEPT\n" "iptables \\\n-w \\\n-A libvirt-in-post \\\n-m physdev \\\n--physdev-in vnet0 \\\n-j ACCEPT\n", /* Dropping ip6tables rules */ "ip6tables \\\n-w \\\n-D libvirt-out \\\n-m physdev \\\n--physdev-is-bridged \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "ip6tables \\\n-w \\\n-D libvirt-out \\\n-m physdev \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "ip6tables \\\n-w \\\n-D libvirt-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g FJ-vnet0\n" "ip6tables \\\n-w \\\n-D libvirt-host-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g HJ-vnet0\n" "ip6tables \\\n-w \\\n-F FP-vnet0\n" "ip6tables \\\n-w \\\n-X FP-vnet0\n" "ip6tables \\\n-w \\\n-F FJ-vnet0\n" "ip6tables \\\n-w \\\n-X FJ-vnet0\n" "ip6tables \\\n-w \\\n-F HJ-vnet0\n" "ip6tables \\\n-w \\\n-X HJ-vnet0\n", /* Creating ip6tables chains */ "ip6tables \\\n-w \\\n-N libvirt-in\n" "ip6tables \\\n-w \\\n-N libvirt-out\n" "ip6tables \\\n-w \\\n-N libvirt-in-post\n" "ip6tables \\\n-w \\\n-N libvirt-host-in\n" "ip6tables \\\n-w \\\n-D FORWARD \\\n-j libvirt-in\n" "ip6tables \\\n-w \\\n-D FORWARD \\\n-j libvirt-out\n" "ip6tables \\\n-w \\\n-D FORWARD \\\n-j libvirt-in-post\n" "ip6tables \\\n-w \\\n-D INPUT \\\n-j libvirt-host-in\n" "ip6tables \\\n-w \\\n-I FORWARD 1 \\\n-j libvirt-in\n" "ip6tables \\\n-w \\\n-I FORWARD 2 \\\n-j libvirt-out\n" "ip6tables \\\n-w \\\n-I FORWARD 3 \\\n-j libvirt-in-post\n" "ip6tables \\\n-w \\\n-I INPUT 1 \\\n-j libvirt-host-in\n" "ip6tables \\\n-w \\\n-N FP-vnet0\n" "ip6tables \\\n-w \\\n-N FJ-vnet0\n" "ip6tables \\\n-w \\\n-N HJ-vnet0\n" "ip6tables \\\n-w \\\n-A libvirt-out \\\n-m physdev \\\n--physdev-is-bridged \\\n--physdev-out vnet0 \\\n-g FP-vnet0\n" "ip6tables \\\n-w \\\n-A libvirt-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g FJ-vnet0\n" "ip6tables \\\n-w \\\n-A libvirt-host-in \\\n-m physdev \\\n--physdev-in vnet0 \\\n-g HJ-vnet0\n" "ip6tables \\\n-w \\\n-D libvirt-in-post \\\n-m physdev \\\n--physdev-in vnet0 \\\n-j ACCEPT\n" "ip6tables \\\n-w \\\n-A libvirt-in-post \\\n-m physdev \\\n--physdev-in vnet0 \\\n-j ACCEPT\n", /* Inserting ebtables rules */ "ebtables \\\n--concurrent \\\n-t nat \\\n-A PREROUTING \\\n-i vnet0 \\\n-j libvirt-J-vnet0\n" "ebtables \\\n--concurrent \\\n-t nat \\\n-A POSTROUTING \\\n-o vnet0 \\\n-j libvirt-P-vnet0\n", }; static GHashTable * virNWFilterCreateVarsFrom(GHashTable *vars1, GHashTable *vars2) { g_autoptr(GHashTable) res = virHashNew(virNWFilterVarValueHashFree); if (virNWFilterHashTablePutAll(vars1, res) < 0) return NULL; if (virNWFilterHashTablePutAll(vars2, res) < 0) return NULL; return g_steal_pointer(&res); } static void virNWFilterRuleInstFree(virNWFilterRuleInst *inst) { if (!inst) return; virHashFree(inst->vars); g_free(inst); } static void virNWFilterInstReset(virNWFilterInst *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, virNWFilterInst *inst); static int virNWFilterRuleDefToRuleInst(virNWFilterDef *def, virNWFilterRuleDef *rule, GHashTable *vars, virNWFilterInst *inst) { virNWFilterRuleInst *ruleinst; int ret = -1; ruleinst = g_new0(virNWFilterRuleInst, 1); ruleinst->chainSuffix = def->chainsuffix; ruleinst->chainPriority = def->chainPriority; ruleinst->def = rule; ruleinst->priority = rule->priority; ruleinst->vars = virHashNew(virNWFilterVarValueHashFree); if (virNWFilterHashTablePutAll(vars, ruleinst->vars) < 0) goto cleanup; VIR_APPEND_ELEMENT(inst->rules, inst->nrules, ruleinst); ret = 0; cleanup: virNWFilterRuleInstFree(ruleinst); return ret; } static int virNWFilterIncludeDefToRuleInst(virNWFilterIncludeDef *inc, GHashTable *vars, virNWFilterInst *inst) { GHashTable *tmpvars = NULL; int ret = -1; g_autofree char *xml = NULL; 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); return ret; } static int virNWFilterDefToInst(const char *xml, GHashTable *vars, virNWFilterInst *inst) { size_t i; int ret = -1; virNWFilterDef *def = virNWFilterDefParseFile(xml); if (!def) return -1; VIR_APPEND_ELEMENT_COPY(inst->filters, inst->nfilters, def); 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) { virNWFilterVarValue *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) { g_autofree char *actualargv = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; GHashTable *vars = virHashNew(virNWFilterVarValueHashFree); virNWFilterInst inst; int ret = -1; g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew(); memset(&inst, 0, sizeof(inst)); virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL); 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); testRemoveCommonRules(actualargv); if (virTestCompareToFileFull(actualargv, cmdline, false) < 0) goto cleanup; ret = 0; cleanup: 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; g_autofree char *xml = NULL; g_autofree 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); return result; } 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) { 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_PRELOAD(mymain, VIR_TEST_MOCK("virfirewall")) #else /* ! defined (__linux__) */ int main(void) { return EXIT_AM_SKIP; } #endif /* ! defined (__linux__) */