libvirt/tests/virfirewalltest.c
Laine Stump 9293644d8a util/network: new virFirewallBackend enum
(This paragraph is for historical reference only, described only to
avoid confusion of past use of the name with its new use) In a past
life, virFirewallBackend had been a private static in virfirewall.c
that was set at daemon init time, and used to globally (i.e. for all
drivers in the daemon) determine whether to directly execute iptables
commands, or to run them indirectly via the firewalld passthrough
API. This was removed in commit d566cc55, since we decided that using
the firewalld passthrough API is never appropriate.

Now the same enum, virFirewallBackend, is being reintroduced, with a
different meaning and usage pattern. It will be used to pick between
using nftables commands or iptables commands (in either case directly
handled by libvirt, *not* via firewalld). Additionally, rather than
being a static known only within virfirewall.c and applying to all
firewall commands for all drivers, each virFirewall object will have
its own backend setting, which will be set during virFirewallNew() by
the driver who wants to add a firewall rule.

This will allow the nwfilter and network drivers to each have their
own backend setting, even when they coexist in a single unified
daemon. At least as important as that, it will also allow an instance
of the network driver to remove iptables rules that had been added by
a previous instance, and then add nftables rules for the new instance
(in the case that an admin, or possibly an update, switches the driver
backend from iptables to nftable)

Initially, the enum will only have one usable value -
VIR_FIREWALL_BACKEND_IPTABLES, and that will be hardcoded into all
calls to virFirewallNew(). The other enum value (along with a method
of setting it for each driver) will be added later, when it can be
used (when the nftables backend is in the code).

Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2024-05-22 23:19:18 -04:00

801 lines
26 KiB
C

/*
* Copyright (C) 2013-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>
#include "testutils.h"
#if defined(__linux__)
# include <gio/gio.h>
# include "virbuffer.h"
# include "virfirewall.h"
# define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
# include "vircommandpriv.h"
# define LIBVIRT_VIRFIREWALLDPRIV_H_ALLOW
# include "virfirewalldpriv.h"
# define VIR_FROM_THIS VIR_FROM_FIREWALL
# define TEST_FILTER_TABLE_LIST \
"Chain INPUT (policy ACCEPT)\n" \
"target prot opt source destination\n" \
"\n" \
"Chain FORWARD (policy ACCEPT)\n" \
"target prot opt source destination\n" \
"\n" \
"Chain OUTPUT (policy ACCEPT)\n" \
"target prot opt source destination\n"
# define TEST_NAT_TABLE_LIST \
"Chain PREROUTING (policy ACCEPT)\n" \
"target prot opt source destination\n" \
"\n" \
"Chain INPUT (policy ACCEPT)\n" \
"target prot opt source destination\n" \
"\n" \
"Chain OUTPUT (policy ACCEPT)\n" \
"target prot opt source destination\n" \
"\n" \
"Chain POSTROUTING (policy ACCEPT)\n" \
"target prot opt source destination\n"
static int
testFirewallSingleGroup(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.1' --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, NULL, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallRemoveRule(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.1' --jump REJECT\n";
virFirewallCmd *fwrule;
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, NULL, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
fwrule = virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT", NULL);
virFirewallCmdAddArg(fw, fwrule, "--source");
virFirewallRemoveCmd(fw, fwrule);
fwrule = virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT", NULL);
virFirewallCmdAddArg(fw, fwrule, "--source");
virFirewallCmdAddArgFormat(fw, fwrule, "%s", "!192.168.122.1");
virFirewallCmdAddArgList(fw, fwrule, "--jump", "REJECT", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallManyGroups(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.1' --jump REJECT\n"
IPTABLES " -w -A OUTPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A OUTPUT --jump DROP\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, NULL, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--jump", "DROP", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static void
testFirewallRollbackHook(const char *const*args,
const char *const*env G_GNUC_UNUSED,
const char *input G_GNUC_UNUSED,
char **output G_GNUC_UNUSED,
char **error G_GNUC_UNUSED,
int *status,
void *opaque G_GNUC_UNUSED)
{
bool isAdd = false;
while (*args) {
/* Fake failure on the command with this IP addr */
if (STREQ(*args, "-A")) {
isAdd = true;
} else if (isAdd && STREQ(*args, "192.168.122.255")) {
*status = 127;
break;
}
args++;
}
}
static int
testFirewallIgnoreFailGroup(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -A OUTPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A OUTPUT --jump DROP\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--jump", "DROP", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallIgnoreFailRule(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -A OUTPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A OUTPUT --jump DROP\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmdFull(fw, VIR_FIREWALL_LAYER_IPV4,
true, NULL, NULL,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "OUTPUT",
"--jump", "DROP", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallNoRollback(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) == 0) {
fprintf(stderr, "Firewall apply unexpectedly worked\n");
return -1;
}
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallSingleRollback(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -D INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source '!192.168.122.1' --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartRollback(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) == 0) {
fprintf(stderr, "Firewall apply unexpectedly worked\n");
return -1;
}
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallManyRollback(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source '!192.168.122.1' --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallStartRollback(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartRollback(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) == 0) {
fprintf(stderr, "Firewall apply unexpectedly worked\n");
return -1;
}
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
testFirewallChainedRollback(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.127 --jump REJECT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.1' --jump REJECT\n"
IPTABLES " -w -A INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source 192.168.122.127 --jump REJECT\n"
IPTABLES " -w -D INPUT --source '!192.168.122.1' --jump REJECT\n"
IPTABLES " -w -D INPUT --source 192.168.122.255 --jump REJECT\n"
IPTABLES " -w -D INPUT --source '!192.168.122.1' --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallRollbackHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallStartRollback(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.127",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartRollback(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.127",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
virFirewallStartRollback(fw, VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "192.168.122.255",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-D", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) == 0) {
fprintf(stderr, "Firewall apply unexpectedly worked\n");
return -1;
}
actual = virBufferCurrentContent(&cmdbuf);
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static const char *expectedLines[] = {
"Chain INPUT (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain FORWARD (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain OUTPUT (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain PREROUTING (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain INPUT (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain OUTPUT (policy ACCEPT)",
"target prot opt source destination",
"",
"Chain POSTROUTING (policy ACCEPT)",
"target prot opt source destination",
"",
};
static size_t expectedLineNum;
static bool expectedLineError;
static void
testFirewallQueryHook(const char *const*args,
const char *const*env G_GNUC_UNUSED,
const char *input G_GNUC_UNUSED,
char **output,
char **error G_GNUC_UNUSED,
int *status G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
if (STREQ(args[0], IPTABLES) &&
STREQ(args[1], "-w") &&
STREQ(args[2], "-L")) {
*output = g_strdup(TEST_FILTER_TABLE_LIST);
} else if (STREQ(args[0], IPTABLES) &&
STREQ(args[1], "-w") &&
STREQ(args[2], "-t") &&
STREQ(args[3], "nat") &&
STREQ(args[4], "-L")) {
*output = g_strdup(TEST_NAT_TABLE_LIST);
}
}
static int
testFirewallQueryCallback(virFirewall *fw,
virFirewallLayer layer,
const char *const *lines,
void *opaque G_GNUC_UNUSED)
{
size_t i;
virFirewallAddCmd(fw, layer,
"-A", "INPUT",
"--source", "!192.168.122.129",
"--jump", "REJECT", NULL);
for (i = 0; lines[i] != NULL; i++) {
if (expectedLineNum >= G_N_ELEMENTS(expectedLines)) {
expectedLineError = true;
break;
}
if (STRNEQ(expectedLines[expectedLineNum], lines[i])) {
fprintf(stderr, "Mismatch '%s' vs '%s' at %zu, %zu\n",
expectedLines[expectedLineNum], lines[i],
expectedLineNum, i);
expectedLineError = true;
break;
}
expectedLineNum++;
}
return 0;
}
static int
testFirewallQuery(const void *opaque G_GNUC_UNUSED)
{
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
g_autoptr(virFirewall) fw = virFirewallNew(VIR_FIREWALL_BACKEND_IPTABLES);
const char *actual = NULL;
const char *expected =
IPTABLES " -w -A INPUT --source 192.168.122.1 --jump ACCEPT\n"
IPTABLES " -w -A INPUT --source 192.168.122.127 --jump REJECT\n"
IPTABLES " -w -L\n"
IPTABLES " -w -t nat -L\n"
IPTABLES " -w -A INPUT --source 192.168.122.130 --jump REJECT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.129' --jump REJECT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.129' --jump REJECT\n"
IPTABLES " -w -A INPUT --source 192.168.122.128 --jump REJECT\n"
IPTABLES " -w -A INPUT --source '!192.168.122.1' --jump REJECT\n";
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
expectedLineNum = 0;
expectedLineError = false;
virCommandSetDryRun(dryRunToken, &cmdbuf, false, false, testFirewallQueryHook, NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.1",
"--jump", "ACCEPT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.127",
"--jump", "REJECT", NULL);
virFirewallAddCmdFull(fw, VIR_FIREWALL_LAYER_IPV4,
false,
testFirewallQueryCallback,
NULL,
"-L", NULL);
virFirewallAddCmdFull(fw, VIR_FIREWALL_LAYER_IPV4,
false,
testFirewallQueryCallback,
NULL,
"-t", "nat", "-L", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.130",
"--jump", "REJECT", NULL);
virFirewallStartTransaction(fw, 0);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "192.168.122.128",
"--jump", "REJECT", NULL);
virFirewallAddCmd(fw, VIR_FIREWALL_LAYER_IPV4,
"-A", "INPUT",
"--source", "!192.168.122.1",
"--jump", "REJECT", NULL);
if (virFirewallApply(fw) < 0)
return -1;
actual = virBufferCurrentContent(&cmdbuf);
if (expectedLineError) {
fprintf(stderr, "Got some unexpected query data\n");
return -1;
}
if (virTestCompareToString(expected, actual) < 0) {
fprintf(stderr, "Unexpected command execution\n");
return -1;
}
return 0;
}
static int
mymain(void)
{
int ret = 0;
# define RUN_TEST(name, method) \
do { \
if (virTestRun(name, method, NULL) < 0) \
ret = -1; \
} while (0)
RUN_TEST("single group", testFirewallSingleGroup);
RUN_TEST("remove rule", testFirewallRemoveRule);
RUN_TEST("many groups", testFirewallManyGroups);
RUN_TEST("ignore fail group", testFirewallIgnoreFailGroup);
RUN_TEST("ignore fail rule", testFirewallIgnoreFailRule);
RUN_TEST("no rollback", testFirewallNoRollback);
RUN_TEST("single rollback", testFirewallSingleRollback);
RUN_TEST("many rollback", testFirewallManyRollback);
RUN_TEST("chained rollback", testFirewallChainedRollback);
RUN_TEST("query transaction", testFirewallQuery);
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__) */