libvirt/tests/networkxml2firewalltest.c
Laine Stump 0a867cd895 util/tests: enable locking on iptables/ebtables commandlines by default
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 commit ba95426d6f (libvirt-1.2.0,
November 2013), and for ebtables in upstream commit dc33e6e4a5
(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>
2020-11-24 14:21:29 -05:00

217 lines
5.6 KiB
C

/*
* networkxml2firewalltest.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>
#include "testutils.h"
#include "viralloc.h"
#if defined (__linux__)
# include <gio/gio.h>
# include "network/bridge_driver_platform.h"
# include "virbuffer.h"
# include "virmock.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
VIR_MOCK_WRAP_RET_ARGS(g_dbus_connection_call_sync,
GVariant *,
GDBusConnection *, connection,
const gchar *, bus_name,
const gchar *, object_path,
const gchar *, interface_name,
const gchar *, method_name,
GVariant *, parameters,
const GVariantType *, reply_type,
GDBusCallFlags, flags,
gint, timeout_msec,
GCancellable *, cancellable,
GError **, error)
{
if (parameters) {
g_variant_ref_sink(parameters);
g_variant_unref(parameters);
}
VIR_MOCK_REAL_INIT(g_dbus_connection_call_sync);
*error = g_dbus_error_new_for_dbus_error("org.freedesktop.error",
"dbus is disabled");
return NULL;
}
static void
testCommandDryRun(const char *const*args G_GNUC_UNUSED,
const char *const*env G_GNUC_UNUSED,
const char *input G_GNUC_UNUSED,
char **output,
char **error,
int *status,
void *opaque G_GNUC_UNUSED)
{
*status = 0;
*output = g_strdup("");
*error = g_strdup("");
}
static int testCompareXMLToArgvFiles(const char *xml,
const char *cmdline,
const char *baseargs)
{
char *actualargv = NULL;
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
virNetworkDefPtr def = NULL;
int ret = -1;
char *actual;
virCommandSetDryRun(&buf, testCommandDryRun, NULL);
if (!(def = virNetworkDefParseFile(xml, NULL)))
goto cleanup;
if (networkAddFirewallRules(def) < 0)
goto cleanup;
actual = actualargv = virBufferContentAndReset(&buf);
virTestClearCommandPath(actualargv);
virCommandSetDryRun(NULL, NULL, NULL);
/* The first network to be created populates the
* libvirt global chains. We must skip args for
* that if present
*/
if (STRPREFIX(actual, baseargs))
actual += strlen(baseargs);
if (virTestCompareToFile(actual, cmdline) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(actualargv);
virNetworkDefFree(def);
return ret;
}
struct testInfo {
const char *name;
const char *baseargs;
};
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/networkxml2firewalldata/%s.xml",
abs_srcdir, info->name);
args = g_strdup_printf("%s/networkxml2firewalldata/%s-%s.args",
abs_srcdir, info->name, RULESTYPE);
result = testCompareXMLToArgvFiles(xml, args, info->baseargs);
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;
g_autofree char *basefile = NULL;
g_autofree char *baseargs = NULL;
# define DO_TEST(name) \
do { \
struct testInfo info = { \
name, baseargs, \
}; \
if (virTestRun("Network XML-2-iptables " 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;
}
basefile = g_strdup_printf("%s/networkxml2firewalldata/base.args", abs_srcdir);
if (virTestLoadFile(basefile, &baseargs) < 0)
return EXIT_FAILURE;
DO_TEST("nat-default");
DO_TEST("nat-tftp");
DO_TEST("nat-many-ips");
DO_TEST("nat-no-dhcp");
DO_TEST("nat-ipv6");
DO_TEST("nat-ipv6-masquerade");
DO_TEST("route-default");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virgdbus"))
#else /* ! defined (__linux__) */
int main(void)
{
return EXIT_AM_SKIP;
}
#endif /* ! defined (__linux__) */