[libvirt] [PATCH 0/7] POC: Saner firewall integration

We currently have three areas of code which deal with firewall changes. The bridge driver's iptables usage, the QEMU driver's ebtables usage for mac filters and the nwfilter code. These all directly invoke the iptables/ebtables commands or in the case of nwfilter invoke horrible generated shell scripts. The latter in particular has always been an unpleasant design choice, but it has been made much worse by support for firewalld. We are now invoking firewall-cmd just in order to make a DBus method call to firewalld which then invokes the real *tables commands. This has a notable performance impact. This proof of concept series introduces a new virFirewallPtr object for encapsulating all firewall changes. It provides a transactional API for making firewall changes, so the caller can define a set of rules which must either all succeed or all fail, along with a set of rules to perform rollback upon fail. It will either execute *tables commands directly or will call the DBus method for firewalld directly. The upshot is that it will become possible to unit test all the firewall code much more easily, instead of having to rely on integration testing that we currently have for nwfilter. We will also have much improved performance by avoiding the firewall-cmd tool and easier to understand code too. In this series I've only done the core infrastructure and the conversion of viriptables + virebtables source files. The work on nwfilter is a bigger job that I'm still working on. Daniel P. Berrange (7): Introduce a new set of helper macros for mocking symbols Create a re-usable DBus LD_PRELOAD mock library Switch systemd test to use generic dbus mock Add ability to register callback for virCommand dry run Introduce an object for managing firewall rulesets Convert bridge driver over to use new firewall APIs Convert ebtables code over to use firewall APIs include/libvirt/virterror.h | 1 + po/POTFILES.in | 1 + src/Makefile.am | 2 + src/libvirt_private.syms | 13 + src/network/bridge_driver_linux.c | 669 ++++++++++++++++---------------------- src/util/vircommand.c | 47 ++- src/util/vircommand.h | 12 +- src/util/virebtables.c | 187 +++-------- src/util/virerror.c | 1 + src/util/virfirewall.c | 653 +++++++++++++++++++++++++++++++++++++ src/util/virfirewall.h | 93 ++++++ src/util/virfirewallpriv.h | 45 +++ src/util/viriptables.c | 634 ++++++++++++++++-------------------- src/util/viriptables.h | 114 ++++--- tests/Makefile.am | 30 +- tests/testutils.c | 18 +- tests/virfirewalltest.c | 619 +++++++++++++++++++++++++++++++++++ tests/virkmodtest.c | 8 +- tests/virmock.h | 298 +++++++++++++++++ tests/virmockdbus.c | 64 ++++ tests/virnetdevbandwidthtest.c | 3 +- tests/virsystemdmock.c | 139 -------- tests/virsystemdtest.c | 89 ++++- 23 files changed, 2626 insertions(+), 1114 deletions(-) create mode 100644 src/util/virfirewall.c create mode 100644 src/util/virfirewall.h create mode 100644 src/util/virfirewallpriv.h create mode 100644 tests/virfirewalltest.c create mode 100644 tests/virmock.h create mode 100644 tests/virmockdbus.c delete mode 100644 tests/virsystemdmock.c -- 1.8.5.3

Introduce virmock.h which provides some macros to assist in creation of LD_PRELOAD overrides. When these are used, the LD_PRELOAD code simply has some stubs which forward to a wrapper function inside the main test case. This means that logic for the test no longer has to be split between the virXXXtest.c and virXXXmock.c files. It will also make it possible to provide some common reusable modules for mocking code like DBus. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 3 +- tests/virmock.h | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 tests/virmock.h diff --git a/tests/Makefile.am b/tests/Makefile.am index 3267ad3..459e104 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -123,7 +123,8 @@ EXTRA_DIST = \ xml2sexprdata \ xml2vmxdata \ vmwareverdata \ - .valgrind.supp + .valgrind.supp \ + virmock.h test_helpers = commandhelper ssh test_conf test_programs = virshtest sockettest \ diff --git a/tests/virmock.h b/tests/virmock.h new file mode 100644 index 0000000..762b8d6 --- /dev/null +++ b/tests/virmock.h @@ -0,0 +1,298 @@ +/* + * virmock.h: helper for mocking C functions + * + * 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/>. + * + */ + +#ifndef __VIR_MOCK_H__ +# define __VIR_MOCK_H__ + +# include <dlfcn.h> +# include <stdlib.h> +# include <stdio.h> + +# include "internal.h" + +# define VIR_MOCK_COUNT_ARGS(...) VIR_MOCK_ARG21(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +# define VIR_MOCK_ARG21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) _21 +# define VIR_MOCK_ARG_PASTE(a, b, ...) a##b(__VA_ARGS__) + +# define VIR_MOCK_ARGNAME(a, b) b +# define VIR_MOCK_ARGTYPE(a, b) a +# define VIR_MOCK_ARGTYPENAME(a, b) a b +# define VIR_MOCK_ARGTYPENAME_UNUSED(a, b) a b ATTRIBUTE_UNUSED + +# define VIR_MOCK_GET_ARG2(z, a, b) z(a, b) +# define VIR_MOCK_GET_ARG3(z, a, b, c) z(a, b) +# define VIR_MOCK_GET_ARG4(z, a, b, c, d) z(a, b), z(c, d) +# define VIR_MOCK_GET_ARG5(z, a, b, c, d, e) z(a, b), z(c, d) +# define VIR_MOCK_GET_ARG6(z, a, b, c, d, e, f) z(a, b), z(c, d), z(e, f) +# define VIR_MOCK_GET_ARG7(z, a, b, c, d, e, f, g) z(a, b), z(c, d), z(e, f) +# define VIR_MOCK_GET_ARG8(z, a, b, c, d, e, f, g, h) z(a, b), z(c, d), z(e, f), z(g, h) +# define VIR_MOCK_GET_ARG9(z, a, b, c, d, e, f, g, h, i) z(a, b), z(c, d), z(e, f), z(g, h) +# define VIR_MOCK_GET_ARG10(z, a, b, c, d, e, f, g, h, i, j) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j) +# define VIR_MOCK_GET_ARG11(z, a, b, c, d, e, f, g, h, i, j, k) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j) +# define VIR_MOCK_GET_ARG12(z, a, b, c, d, e, f, g, h, i, j, k, l) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l) +# define VIR_MOCK_GET_ARG13(z, a, b, c, d, e, f, g, h, i, j, k, l, m) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l) +# define VIR_MOCK_GET_ARG14(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n) +# define VIR_MOCK_GET_ARG15(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n) +# define VIR_MOCK_GET_ARG16(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p) +# define VIR_MOCK_GET_ARG17(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p) +# define VIR_MOCK_GET_ARG18(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r) +# define VIR_MOCK_GET_ARG19(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r) +# define VIR_MOCK_GET_ARG20(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r), z(s, t) +# define VIR_MOCK_GET_ARG21(z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) z(a, b), z(c, d), z(e, f), z(g, h), z(i, j), z(k, l), z(m, n), z(o, p), z(q, r), z(s, t) + +# define VIR_MOCK_LAST_ARG3(a, b, c) c +# define VIR_MOCK_LAST_ARG5(a, b, c, d, e) e +# define VIR_MOCK_LAST_ARG7(a, b, c, d, e, f, g) g +# define VIR_MOCK_LAST_ARG9(a, b, c, d, e, f, g, h, i) i +# define VIR_MOCK_LAST_ARG11(a, b, c, d, e, f, g, h, i, j, k) k +# define VIR_MOCK_LAST_ARG13(a, b, c, d, e, f, g, h, i, j, k, l, m) m +# define VIR_MOCK_LAST_ARG15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) o +# define VIR_MOCK_LAST_ARG17(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) q +# define VIR_MOCK_LAST_ARG19(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) s +# define VIR_MOCK_LAST_ARG21(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) u + + +# define VIR_MOCK_ARGNAMES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__) +# define VIR_MOCK_ARGNAMES(...) \ + VIR_MOCK_ARGNAMES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGNAME, __VA_ARGS__) + +# define VIR_MOCK_ARGTYPES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__) +# define VIR_MOCK_ARGTYPES(...) \ + VIR_MOCK_ARGTYPES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPE, __VA_ARGS__) + +# define VIR_MOCK_ARGTYPENAMES_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__) +# define VIR_MOCK_ARGTYPENAMES(...) \ + VIR_MOCK_ARGTYPENAMES_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPENAME, __VA_ARGS__) + +# define VIR_MOCK_ARGTYPENAMES_UNUSED_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__) +# define VIR_MOCK_ARGTYPENAMES_UNUSED(...) \ + VIR_MOCK_ARGTYPENAMES_UNUSED_EXPAND(VIR_MOCK_GET_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), VIR_MOCK_ARGTYPENAME_UNUSED, __VA_ARGS__) + +# define VIR_MOCK_LAST_ARG_EXPAND(a, b, ...) VIR_MOCK_ARG_PASTE(a, b, __VA_ARGS__) +# define VIR_MOCK_LAST_ARG(...) \ + VIR_MOCK_LAST_ARG_EXPAND(VIR_MOCK_LAST_ARG, VIR_MOCK_COUNT_ARGS(__VA_ARGS__), __VA_ARGS__) + + +/* + * The VIR_MOCK_LINK_NNN_MMM() macros are intended for use in + * LD_PRELOAD based wrappers. They provide a replacement for + * for an existing shared library symbol export. They will + * then lookup the same symbol name but with 'wrap_' prefixed + * on it, and call that. + * + * The actual test suite should provide the implemention of + * the wrap_XXXX symbol, using the VIR_MOCK_WRAP_NNN_MMM + * macros. + */ + + +/** + * VIR_MOCK_LINK_RET_ARGS: + * @name: the symbol name to replace + * @rettype: the return type + * @...: pairs of parameter type and parameter name + * + * Define a replacement for @name which invokes wrap_@name + * forwarding on all args, and passing back the return value. + */ +# define VIR_MOCK_LINK_RET_ARGS(name, rettype, ...) \ + rettype name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)) { \ + static rettype (*wrap_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \ + if (wrap_##name == NULL && \ + !(wrap_##name = dlsym(RTLD_DEFAULT, \ + "wrap_" #name))) { \ + fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \ + abort(); \ + } \ + \ + return wrap_##name(VIR_MOCK_ARGNAMES(__VA_ARGS__)); \ + } + +/** + * VIR_MOCK_LINK_RET_VOID: + * @name: the symbol name to replace + * @rettype: the return type + * + * Define a replacement for @name which invokes wrap_@name + * with no arguments, and passing back the return value. + */ +# define VIR_MOCK_LINK_RET_VOID(name, rettype) \ + rettype name(void) { \ + static rettype (*wrap_##name)(void); \ + if (wrap_##name == NULL && \ + !(wrap_##name = dlsym(RTLD_DEFAULT, \ + "wrap_" #name))) { \ + fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \ + abort(); \ + } \ + \ + return wrap_##name(); \ + } + +/** + * VIR_MOCK_LINK_VOID_ARGS: + * @name: the symbol name to replace + * @...: pairs of parameter type and parameter name + * + * Define a replacement for @name which invokes wrap_@name + * forwarding on all args, but with no return value. + */ +# define VIR_MOCK_LINK_VOID_ARGS(name, ...) \ + void name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)) { \ + static void (*wrap_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \ + if (wrap_##name == NULL && \ + !(wrap_##name = dlsym(RTLD_DEFAULT, \ + "wrap_" #name))) { \ + fprintf(stderr, "Missing symbol 'wrap_" #name "'\n"); \ + abort(); \ + } \ + \ + wrap_##name(VIR_MOCK_ARGNAMES(__VA_ARGS__)); \ + } + + + +/* + * The VIR_MOCK_STUB_NNN_MMM() macros are intended for use in + * LD_PRELOAD based wrappers. They provide a replacement for + * for an existing shared library symbol export. They will + * be a pure no-op, optionally returning a dummy value. + */ + + +/** + * VIR_MOCK_STUB_RET_ARGS: + * @name: the symbol name to replace + * @rettype: the return type + * @retval: the return value + * @...: pairs of parameter type and parameter name + * + * Define a replacement for @name which invokes wrap_@name + * forwarding on all args, and passing back the return value. + */ +# define VIR_MOCK_STUB_RET_ARGS(name, rettype, retval, ...) \ + rettype name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) { \ + return retval; \ + } + +/** + * VIR_MOCK_STUB_RET_VOID: + * @name: the symbol name to replace + * @rettype: the return type + * + * Define a replacement for @name which invokes wrap_@name + * with no arguments, and passing back the return value. + */ +# define VIR_MOCK_STUB_RET_VOID(name, rettype, retval) \ + rettype name(void) { \ + return retval; \ + } + +/** + * VIR_MOCK_STUB_VOID_ARGS: + * @name: the symbol name to replace + * @...: pairs of parameter type and parameter name + * + * Define a replacement for @name which invokes wrap_@name + * forwarding on all args, but with no return value. + */ +# define VIR_MOCK_STUB_VOID_ARGS(name, ...) \ + void name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) { \ + } + + + +/** + * VIR_MOCK_STUB_VOID_VOID: + * @name: the symbol name to replace + * + * Define a replacement for @name which invokes wrap_@name + * with no arguments and with no return value + */ +# define VIR_MOCK_STUB_VOID_VOID(name) \ + void name(void) { \ + } + + +/* + * The VIR_MOCK_IMPL_NNN_MMM() macros are intended for use in the + * individual test suites. The define a stub implementation of + * the wrapped method and insert the caller provided code snippet + * as the body of the method. + */ + +# define VIR_MOCK_IMPL_RET_ARGS(name, rettype, ...) \ + rettype wrap_##name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \ + rettype wrap_##name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) { \ + static rettype (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \ + if (real_##name == NULL && \ + !(real_##name = dlsym(RTLD_NEXT, \ + #name))) { \ + fprintf(stderr, "Missing symbol '" #name "'\n"); \ + abort(); \ + } \ + \ + VIR_MOCK_LAST_ARG(__VA_ARGS__) \ + } + +# define VIR_MOCK_IMPL_RET_VOID(name, rettype, _C_) \ + rettype wrap_##name(void); \ + rettype wrap_##name(void) { \ + static rettype (*real_##name)(void); \ + if (real_##name == NULL && \ + !(real_##name = dlsym(RTLD_NEXT, \ + #name))) { \ + fprintf(stderr, "Missing symbol '" #name "'\n"); \ + abort(); \ + } \ + \ + _C_ \ + } + +# define VIR_MOCK_IMPL_VOID_ARGS(name, ...) \ + void wrap_##name(VIR_MOCK_ARGTYPENAMES(__VA_ARGS__)); \ + void wrap_##name(VIR_MOCK_ARGTYPENAMES_UNUSED(__VA_ARGS__)) { \ + static void (*real_##name)(VIR_MOCK_ARGTYPES(__VA_ARGS__)); \ + if (real_##name == NULL && \ + !(real_##name = dlsym(RTLD_NEXT, \ + #name))) { \ + fprintf(stderr, "Missing symbol '" #name "'\n"); \ + abort(); \ + } \ + \ + VIR_MOCK_LAST_ARG(__VA_ARGS__) \ + } + +# define VIR_MOCK_IMPL_VOID_VOID(name, _C_) \ + void wrap_##name(void); \ + void wrap_##name(void) { \ + static void (*real_##name)(void); \ + if (real_##name == NULL && \ + !(real_##name = dlsym(RTLD_NEXT, \ + #name))) { \ + fprintf(stderr, "Missing symbol '" #name "'\n"); \ + abort(); \ + } \ + \ + _C_ \ + } + +#endif /* __VIR_MOCK_H__ */ -- 1.8.5.3

On 03/12/2014 07:21 AM, Daniel P. Berrange wrote:
Introduce virmock.h which provides some macros to assist in creation of LD_PRELOAD overrides. When these are used, the LD_PRELOAD code simply has some stubs which forward to a wrapper function inside the main test case. This means that logic for the test no longer has to be split between the virXXXtest.c and virXXXmock.c files. It will also make it possible to provide some common reusable modules for mocking code like DBus.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 3 +- tests/virmock.h | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 tests/virmock.h
diff --git a/tests/Makefile.am b/tests/Makefile.am index 3267ad3..459e104 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -123,7 +123,8 @@ EXTRA_DIST = \ xml2sexprdata \ xml2vmxdata \ vmwareverdata \ - .valgrind.supp + .valgrind.supp \ + virmock.h
Unusual sorting, and maybe a chance to add $(NULL) to this list.
+ +# define VIR_MOCK_COUNT_ARGS(...) VIR_MOCK_ARG21(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +# define VIR_MOCK_ARG21(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) _21 +# define VIR_MOCK_ARG_PASTE(a, b, ...) a##b(__VA_ARGS__)
Long lines - worth using \-newline breaks?
+/* + * The VIR_MOCK_LINK_NNN_MMM() macros are intended for use in + * LD_PRELOAD based wrappers. They provide a replacement for + * for an existing shared library symbol export. They will + * then lookup the same symbol name but with 'wrap_' prefixed + * on it, and call that. + * + * The actual test suite should provide the implemention of
s/implemention/implementation/
+/** + * VIR_MOCK_LINK_RET_ARGS:
Are you missing VIR_MOCK_LINK_VOID_VOID? ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

A number of test suites want to mock the DBus APIs. To avoid re-inventing the wheel create a re-usable virmockdbus.la library for LD_PRELOAD usage. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 11 +++++++-- tests/virmockdbus.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/virmockdbus.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 459e104..8ac200d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -349,7 +349,8 @@ test_libraries += libqemumonitortestutils.la \ endif WITH_QEMU if WITH_DBUS -test_libraries += virsystemdmock.la +test_libraries += virsystemdmock.la \ + virmockdbus.la endif WITH_DBUS if WITH_LINUX @@ -854,6 +855,12 @@ virdbustest_SOURCES = \ virdbustest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) virdbustest_LDADD = $(LDADDS) $(DBUS_LIBS) +virmockdbus_la_SOURCES = \ + virmockdbus.c +virmockdbus_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +virmockdbus_la_LDFLAGS = -module -avoid-version \ + -rpath /evil/libtool/hack/to/force/shared/lib/creation + virsystemdtest_SOURCES = \ virsystemdtest.c testutils.h testutils.c virsystemdtest_CFLAGS = $(AM_CFLAGS) @@ -866,7 +873,7 @@ virsystemdmock_la_LDFLAGS = -module -avoid-version \ -rpath /evil/libtool/hack/to/force/shared/lib/creation else ! WITH_DBUS -EXTRA_DIST += virdbustest.c virsystemdtest.c virsystemdmock.c +EXTRA_DIST += virdbustest.c virmockdbus.c virsystemdtest.c virsystemdmock.c endif ! WITH_DBUS viruritest_SOURCES = \ diff --git a/tests/virmockdbus.c b/tests/virmockdbus.c new file mode 100644 index 0000000..8a01d9d --- /dev/null +++ b/tests/virmockdbus.c @@ -0,0 +1,64 @@ +/* + * virmockdbus.c: mocking of dbus message send/reply + * + * Copyright (C) 2013 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/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#ifdef WITH_DBUS +# include "virmock.h" +# include <dbus/dbus.h> + +VIR_MOCK_STUB_VOID_ARGS(dbus_connection_set_change_sigpipe, + dbus_bool_t, will_modify_sigpipe) + + +VIR_MOCK_STUB_RET_ARGS(dbus_bus_get, + DBusConnection *, (DBusConnection *)0x1, + DBusBusType, type, + DBusError *, error) + +VIR_MOCK_STUB_VOID_ARGS(dbus_connection_set_exit_on_disconnect, + DBusConnection *, connection, + dbus_bool_t, exit_on_disconnect) + +VIR_MOCK_STUB_RET_ARGS(dbus_connection_set_watch_functions, + dbus_bool_t, 1, + DBusConnection *, connection, + DBusAddWatchFunction, add_function, + DBusRemoveWatchFunction, remove_function, + DBusWatchToggledFunction, toggled_function, + void *, data, + DBusFreeFunction, free_data_function) + +VIR_MOCK_STUB_RET_ARGS(dbus_message_set_reply_serial, + dbus_bool_t, 1, + DBusMessage *, message, + dbus_uint32_t, serial) + + +VIR_MOCK_LINK_RET_ARGS(dbus_connection_send_with_reply_and_block, + DBusMessage *, + DBusConnection *, connection, + DBusMessage *, message, + int, timeout_milliseconds, + DBusError *, error) + +#endif /* WITH_DBUS */ -- 1.8.5.3

On 03/12/2014 07:21 AM, Daniel P. Berrange wrote:
A number of test suites want to mock the DBus APIs. To avoid re-inventing the wheel create a re-usable virmockdbus.la library for LD_PRELOAD usage.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 11 +++++++-- tests/virmockdbus.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/virmockdbus.c
+++ b/tests/virmockdbus.c @@ -0,0 +1,64 @@ +/* + * virmockdbus.c: mocking of dbus message send/reply + * + * Copyright (C) 2013 Red Hat, Inc.
It's 2014; you've been sitting on this work for a while :) ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Remove the virsystemdmock.la library and instead make use of the shared virmockdbus.la library Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 13 ++--- tests/virsystemdmock.c | 139 ------------------------------------------------- tests/virsystemdtest.c | 89 +++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 153 deletions(-) delete mode 100644 tests/virsystemdmock.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ac200d..baf0483 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -349,7 +349,7 @@ test_libraries += libqemumonitortestutils.la \ endif WITH_QEMU if WITH_DBUS -test_libraries += virsystemdmock.la \ +test_libraries += \ virmockdbus.la endif WITH_DBUS @@ -863,17 +863,12 @@ virmockdbus_la_LDFLAGS = -module -avoid-version \ virsystemdtest_SOURCES = \ virsystemdtest.c testutils.h testutils.c -virsystemdtest_CFLAGS = $(AM_CFLAGS) +virsystemdtest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) virsystemdtest_LDADD = $(LDADDS) - -virsystemdmock_la_SOURCES = \ - virsystemdmock.c -virsystemdmock_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -virsystemdmock_la_LDFLAGS = -module -avoid-version \ - -rpath /evil/libtool/hack/to/force/shared/lib/creation +virsystemdtest_LDFLAGS = $(DRIVER_MODULE_LDFLAGS) else ! WITH_DBUS -EXTRA_DIST += virdbustest.c virmockdbus.c virsystemdtest.c virsystemdmock.c +EXTRA_DIST += virdbustest.c virmockdbus.c virsystemdtest.c endif ! WITH_DBUS viruritest_SOURCES = \ diff --git a/tests/virsystemdmock.c b/tests/virsystemdmock.c deleted file mode 100644 index b4fcf6e..0000000 --- a/tests/virsystemdmock.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2013 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/>. - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#ifdef __linux__ -# include "internal.h" - -# include <stdlib.h> - -# include <dbus/dbus.h> - -void dbus_connection_set_change_sigpipe(dbus_bool_t will_modify_sigpipe ATTRIBUTE_UNUSED) -{ -} - -DBusConnection *dbus_bus_get(DBusBusType type ATTRIBUTE_UNUSED, - DBusError *error ATTRIBUTE_UNUSED) -{ - return (DBusConnection *)0x1; -} - -void dbus_connection_set_exit_on_disconnect(DBusConnection *connection ATTRIBUTE_UNUSED, - dbus_bool_t exit_on_disconnect ATTRIBUTE_UNUSED) -{ -} - - -dbus_bool_t dbus_connection_set_watch_functions(DBusConnection *connection ATTRIBUTE_UNUSED, - DBusAddWatchFunction add_function ATTRIBUTE_UNUSED, - DBusRemoveWatchFunction remove_function ATTRIBUTE_UNUSED, - DBusWatchToggledFunction toggled_function ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED, - DBusFreeFunction free_data_function ATTRIBUTE_UNUSED) -{ - return 1; -} - -dbus_bool_t dbus_message_set_reply_serial(DBusMessage *message ATTRIBUTE_UNUSED, - dbus_uint32_t serial ATTRIBUTE_UNUSED) -{ - return 1; -} - -DBusMessage *dbus_connection_send_with_reply_and_block(DBusConnection *connection ATTRIBUTE_UNUSED, - DBusMessage *message, - int timeout_milliseconds ATTRIBUTE_UNUSED, - DBusError *error ATTRIBUTE_UNUSED) -{ - DBusMessage *reply = NULL; - const char *service = dbus_message_get_destination(message); - const char *member = dbus_message_get_member(message); - - if (STREQ(service, "org.freedesktop.machine1")) { - if (getenv("FAIL_BAD_SERVICE")) { - DBusMessageIter iter; - const char *error_message = "Something went wrong creating the machine"; - if (!(reply = dbus_message_new(DBUS_MESSAGE_TYPE_ERROR))) - return NULL; - dbus_message_set_error_name(reply, "org.freedesktop.systemd.badthing"); - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_append_basic(&iter, - DBUS_TYPE_STRING, - &error_message)) - goto error; - } else { - reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); - } - } else if (STREQ(service, "org.freedesktop.DBus") && - STREQ(member, "ListActivatableNames")) { - const char *svc1 = "org.foo.bar.wizz"; - const char *svc2 = "org.freedesktop.machine1"; - DBusMessageIter iter, sub; - reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "s", &sub); - - if (!dbus_message_iter_append_basic(&sub, - DBUS_TYPE_STRING, - &svc1)) - goto error; - if (!getenv("FAIL_NO_SERVICE") && - !dbus_message_iter_append_basic(&sub, - DBUS_TYPE_STRING, - &svc2)) - goto error; - dbus_message_iter_close_container(&iter, &sub); - } else if (STREQ(service, "org.freedesktop.DBus") && - STREQ(member, "ListNames")) { - const char *svc1 = "org.foo.bar.wizz"; - const char *svc2 = "org.freedesktop.systemd1"; - DBusMessageIter iter, sub; - reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "s", &sub); - - if (!dbus_message_iter_append_basic(&sub, - DBUS_TYPE_STRING, - &svc1)) - goto error; - if ((!getenv("FAIL_NO_SERVICE") && !getenv("FAIL_NOT_REGISTERED")) && - !dbus_message_iter_append_basic(&sub, - DBUS_TYPE_STRING, - &svc2)) - goto error; - dbus_message_iter_close_container(&iter, &sub); - } else { - reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); - } - - return reply; - - error: - dbus_message_unref(reply); - return NULL; -} - -#else -/* Nothing to override on non-__linux__ platforms */ -#endif diff --git a/tests/virsystemdtest.c b/tests/virsystemdtest.c index 9752d12..6d815d3 100644 --- a/tests/virsystemdtest.c +++ b/tests/virsystemdtest.c @@ -22,15 +22,94 @@ #include "testutils.h" -#ifdef __linux__ +#ifdef WITH_DBUS # include <stdlib.h> +# include <dbus/dbus.h> # include "virsystemd.h" # include "virlog.h" - +# include "virmock.h" # define VIR_FROM_THIS VIR_FROM_NONE +VIR_MOCK_IMPL_RET_ARGS(dbus_connection_send_with_reply_and_block, + DBusMessage *, + DBusConnection *, connection, + DBusMessage *, message, + int, timeout_milliseconds, + DBusError *, error, { + DBusMessage *reply = NULL; + const char *service = dbus_message_get_destination(message); + const char *member = dbus_message_get_member(message); + + if (STREQ(service, "org.freedesktop.machine1")) { + if (getenv("FAIL_BAD_SERVICE")) { + DBusMessageIter iter; + const char *error_message = "Something went wrong creating the machine"; + if (!(reply = dbus_message_new(DBUS_MESSAGE_TYPE_ERROR))) + return NULL; + dbus_message_set_error_name(reply, "org.freedesktop.systemd.badthing"); + dbus_message_iter_init_append(reply, &iter); + if (!dbus_message_iter_append_basic(&iter, + DBUS_TYPE_STRING, + &error_message)) + goto error; + } else { + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + } + } else if (STREQ(service, "org.freedesktop.DBus") && + STREQ(member, "ListActivatableNames")) { + const char *svc1 = "org.foo.bar.wizz"; + const char *svc2 = "org.freedesktop.machine1"; + DBusMessageIter iter; + DBusMessageIter sub; + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + "s", &sub); + + if (!dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc1)) + goto error; + if (!getenv("FAIL_NO_SERVICE") && + !dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc2)) + goto error; + dbus_message_iter_close_container(&iter, &sub); + } else if (STREQ(service, "org.freedesktop.DBus") && + STREQ(member, "ListNames")) { + const char *svc1 = "org.foo.bar.wizz"; + const char *svc2 = "org.freedesktop.systemd1"; + DBusMessageIter iter; + DBusMessageIter sub; + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + "s", &sub); + + if (!dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc1)) + goto error; + if ((!getenv("FAIL_NO_SERVICE") && !getenv("FAIL_NOT_REGISTERED")) && + !dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc2)) + goto error; + dbus_message_iter_close_container(&iter, &sub); + } else { + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + } + + return reply; + + error: + dbus_message_unref(reply); + return NULL; +}) + static int testCreateContainer(const void *opaque ATTRIBUTE_UNUSED) { unsigned char uuid[VIR_UUID_BUFLEN] = { @@ -276,12 +355,12 @@ mymain(void) return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; } -VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virsystemdmock.so") +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmockdbus.so") -#else +#else /* ! WITH_DBUS */ int main(void) { return EXIT_AM_SKIP; } -#endif +#endif /* ! WITH_DBUS */ -- 1.8.5.3

On 03/12/2014 07:21 AM, Daniel P. Berrange wrote:
Remove the virsystemdmock.la library and instead make use of the shared virmockdbus.la library
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tests/Makefile.am | 13 ++--- tests/virsystemdmock.c | 139 ------------------------------------------------- tests/virsystemdtest.c | 89 +++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 153 deletions(-) delete mode 100644 tests/virsystemdmock.c
diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ac200d..baf0483 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -349,7 +349,7 @@ test_libraries += libqemumonitortestutils.la \ endif WITH_QEMU
if WITH_DBUS -test_libraries += virsystemdmock.la \ +test_libraries += \ virmockdbus.la
Maybe worth collapsing to one line, or using $(NULL)? ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

To allow for fault injection of the virCommand dry run, add the ability to register a callback. The callback will be passed the argv, env and stdin buffer and is expected to return the exit status and optionally fill stdout and stderr buffers. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/util/vircommand.c | 47 +++++++++++++++++++++++++++++++----------- src/util/vircommand.h | 12 ++++++++++- tests/virkmodtest.c | 8 +++---- tests/virnetdevbandwidthtest.c | 3 ++- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/util/vircommand.c b/src/util/vircommand.c index db4166f..42d0182 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -132,6 +132,9 @@ struct _virCommand { /* See virCommandSetDryRun for description for this variable */ static virBufferPtr dryRunBuffer; +static virCommandDryRunCallback dryRunCallback; +static void *dryRunOpaque; +static int dryRunStatus; /* * virCommandFDIsSet: @@ -2264,16 +2267,25 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) } str = virCommandToString(cmd); - if (dryRunBuffer) { + if (dryRunBuffer || dryRunCallback) { + dryRunStatus = 0; if (!str) { /* error already reported by virCommandToString */ goto cleanup; } - VIR_DEBUG("Dry run requested, appending stringified " - "command to dryRunBuffer=%p", dryRunBuffer); - virBufferAdd(dryRunBuffer, str, -1); - virBufferAddChar(dryRunBuffer, '\n'); + if (dryRunBuffer) { + VIR_DEBUG("Dry run requested, appending stringified " + "command to dryRunBuffer=%p", dryRunBuffer); + virBufferAdd(dryRunBuffer, str, -1); + virBufferAddChar(dryRunBuffer, '\n'); + } + if (dryRunCallback) { + dryRunCallback((const char *const*)cmd->args, + (const char *const*)cmd->env, + cmd->inbuf, cmd->outbuf, cmd->errbuf, + &dryRunStatus, dryRunOpaque); + } ret = 0; goto cleanup; } @@ -2353,10 +2365,11 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) return -1; } - if (dryRunBuffer) { - VIR_DEBUG("Dry run requested, claiming success"); + if (dryRunBuffer || dryRunCallback) { + VIR_DEBUG("Dry run requested, returning status %d", + dryRunStatus); if (exitstatus) - *exitstatus = 0; + *exitstatus = dryRunStatus; return 0; } @@ -2701,6 +2714,7 @@ virCommandDoAsyncIO(virCommandPtr cmd) /** * virCommandSetDryRun: * @buf: buffer to store stringified commands + * @callback: callback to process input/output/args * * Sometimes it's desired to not actually run given command, but * see its string representation without having to change the @@ -2709,8 +2723,13 @@ virCommandDoAsyncIO(virCommandPtr cmd) * virCommandRun* API. The virCommandSetDryRun allows you to * modify this behavior: once called, every call to * virCommandRun* results in command string representation being - * appended to @buf instead of being executed. the strings are - * escaped for a shell and separated by a newline. For example: + * appended to @buf instead of being executed. If @callback is + * provided, then it is invoked with the argv, env and stdin + * data string for the command. It is expected to fill the stdout + * and stderr data strings and exit status variables. + * + * The strings stored in @buf are escaped for a shell and + * separated by a newline. For example: * * virBuffer buffer = VIR_BUFFER_INITIALIZER; * virCommandSetDryRun(&buffer); @@ -2722,10 +2741,14 @@ virCommandDoAsyncIO(virCommandPtr cmd) * * /bin/echo 'Hello world'\n * - * To cancel this effect pass NULL. + * To cancel this effect pass NULL for @buf and @callback. */ void -virCommandSetDryRun(virBufferPtr buf) +virCommandSetDryRun(virBufferPtr buf, + virCommandDryRunCallback cb, + void *opaque) { dryRunBuffer = buf; + dryRunCallback = cb; + dryRunOpaque = opaque; } diff --git a/src/util/vircommand.h b/src/util/vircommand.h index 7485edc..9405f5f 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -187,5 +187,15 @@ void virCommandFree(virCommandPtr cmd); void virCommandDoAsyncIO(virCommandPtr cmd); -void virCommandSetDryRun(virBufferPtr buf); +typedef void (*virCommandDryRunCallback)(const char *const*args, + const char *const*env, + const char *input, + char **output, + char **error, + int *status, + void *opaque); + +void virCommandSetDryRun(virBufferPtr buf, + virCommandDryRunCallback cb, + void *opaque); #endif /* __VIR_COMMAND_H__ */ diff --git a/tests/virkmodtest.c b/tests/virkmodtest.c index c6f5a72..f362d03 100644 --- a/tests/virkmodtest.c +++ b/tests/virkmodtest.c @@ -95,7 +95,7 @@ testKModLoad(const void *args) bool useBlacklist = info->useBlacklist; virBuffer buf = VIR_BUFFER_INITIALIZER; - virCommandSetDryRun(&buf); + virCommandSetDryRun(&buf, NULL, NULL); errbuf = virKModLoad(module, useBlacklist); if (errbuf) { @@ -109,7 +109,7 @@ testKModLoad(const void *args) ret = 0; cleanup: - virCommandSetDryRun(NULL); + virCommandSetDryRun(NULL, NULL, NULL); VIR_FREE(errbuf); return ret; } @@ -124,7 +124,7 @@ testKModUnload(const void *args) const char *module = info->module; virBuffer buf = VIR_BUFFER_INITIALIZER; - virCommandSetDryRun(&buf); + virCommandSetDryRun(&buf, NULL, NULL); errbuf = virKModUnload(module); if (errbuf) { @@ -138,7 +138,7 @@ testKModUnload(const void *args) ret = 0; cleanup: - virCommandSetDryRun(NULL); + virCommandSetDryRun(NULL, NULL, NULL); VIR_FREE(errbuf); return ret; } diff --git a/tests/virnetdevbandwidthtest.c b/tests/virnetdevbandwidthtest.c index 073fdf8..3aebd49 100644 --- a/tests/virnetdevbandwidthtest.c +++ b/tests/virnetdevbandwidthtest.c @@ -76,7 +76,7 @@ testVirNetDevBandwidthSet(const void *data) if (!iface) iface = "eth0"; - virCommandSetDryRun(&buf); + virCommandSetDryRun(&buf, NULL, NULL); if (virNetDevBandwidthSet(iface, band, info->hierarchical_class) < 0) goto cleanup; @@ -100,6 +100,7 @@ testVirNetDevBandwidthSet(const void *data) ret = 0; cleanup: + virCommandSetDryRun(NULL, NULL, NULL); virNetDevBandwidthFree(band); virBufferFreeAndReset(&buf); VIR_FREE(actual_cmd); -- 1.8.5.3

On 03/12/2014 02:21 PM, Daniel P. Berrange wrote:
To allow for fault injection of the virCommand dry run, add the ability to register a callback. The callback will be passed the argv, env and stdin buffer and is expected to return the exit status and optionally fill stdout and stderr buffers.
virCommandProcessIO should be skipped on a dry run, since it overwrites the buffers. Jan

The network and nwfilter drivers both have a need to update firewall rules. The currently share no code for interacting with iptables / firewalld. The nwfilter driver is fairly tied to the concept of creating shell scripts to execute which makes it very hard to port to talk to firewalld via DBus APIs. This patch introduces a virFirewallPtr object which is able to represent a complete sequence of rule changes, with the ability to have multiple transactional checkpoints with rollbacks. By formally separating the definition of the rules to be applied from the mechanism used to apply them, it is also possible to write a firewall engine that uses firewalld DBus APIs natively instead of via the slow firewalld-cmd. --- include/libvirt/virterror.h | 1 + po/POTFILES.in | 1 + src/Makefile.am | 2 + src/libvirt_private.syms | 13 + src/util/virerror.c | 1 + src/util/virfirewall.c | 653 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virfirewall.h | 93 +++++++ src/util/virfirewallpriv.h | 45 +++ tests/Makefile.am | 7 + tests/testutils.c | 18 +- tests/virfirewalltest.c | 619 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1449 insertions(+), 4 deletions(-) create mode 100644 src/util/virfirewall.c create mode 100644 src/util/virfirewall.h create mode 100644 src/util/virfirewallpriv.h create mode 100644 tests/virfirewalltest.c diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 495c121..be90797 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -122,6 +122,7 @@ typedef enum { VIR_FROM_SYSTEMD = 56, /* Error from systemd code */ VIR_FROM_BHYVE = 57, /* Error from bhyve driver */ VIR_FROM_CRYPTO = 58, /* Error from crypto code */ + VIR_FROM_FIREWALL = 59, /* Error from firewall */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/po/POTFILES.in b/po/POTFILES.in index efac7b2..17c93f5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -162,6 +162,7 @@ src/util/virdbus.c src/util/virdnsmasq.c src/util/vireventpoll.c src/util/virfile.c +src/util/virfirewall.c src/util/virhash.c src/util/virhook.c src/util/virhostdev.c diff --git a/src/Makefile.am b/src/Makefile.am index a88b258..299fcdb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,8 @@ UTIL_SOURCES = \ util/virevent.c util/virevent.h \ util/vireventpoll.c util/vireventpoll.h \ util/virfile.c util/virfile.h \ + util/virfirewall.c util/virfirewall.h \ + util/virfirewallpriv.h \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ util/virhook.c util/virhook.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f1607cd..0524569 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1272,6 +1272,19 @@ virFileWriteStr; virFindFileInPath; +# util/virfirewall.h +virFirewallAddRule; +virFirewallApply; +virFirewallFree; +virFirewallNew; +virFirewallRuleAddArg; +virFirewallRuleAddArgFormat; +virFirewallRuleAddArgList; +virFirewallRuleAddArgSet; +virFirewallStartRollback; +virFirewallStartTransaction; + + # util/virhash.h virHashAddEntry; virHashCreate; diff --git a/src/util/virerror.c b/src/util/virerror.c index 3eb4f5d..f64c4b2 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -126,6 +126,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Systemd", "Bhyve", "Crypto", + "Firewall", ) diff --git a/src/util/virfirewall.c b/src/util/virfirewall.c new file mode 100644 index 0000000..f1cf678 --- /dev/null +++ b/src/util/virfirewall.c @@ -0,0 +1,653 @@ +/* + * virfirewall.c: integration with firewalls + * + * Copyright (C) 2013 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/>. + * + * Authors: + * Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#define __VIR_FIREWALL_PRIV_H_ALLOW__ + +#include <stdarg.h> + +#include "viralloc.h" +#include "virfirewallpriv.h" +#include "virerror.h" +#include "virutil.h" +#include "virstring.h" +#include "vircommand.h" +#include "virlog.h" +#include "virdbus.h" +#include "virfile.h" +#include "virthread.h" + +#define VIR_FROM_THIS VIR_FROM_FIREWALL + +typedef struct _virFirewallGroup virFirewallGroup; +typedef virFirewallGroup *virFirewallGroupPtr; + +VIR_ENUM_DECL(virFirewallLayerCommand) +VIR_ENUM_IMPL(virFirewallLayerCommand, VIR_FIREWALL_LAYER_LAST, + EBTABLES_PATH, + IPTABLES_PATH, + IP6TABLES_PATH); + +VIR_ENUM_DECL(virFirewallLayerFirewallD) +VIR_ENUM_IMPL(virFirewallLayerFirewallD, VIR_FIREWALL_LAYER_LAST, + "eb", "ipv4", "ipv6") + + +struct _virFirewallRule { + virFirewallLayer layer; + + size_t argsAlloc; + size_t argsLen; + char **args; +}; + +struct _virFirewallGroup { + unsigned int actionFlags; + unsigned int rollbackFlags; + + size_t naction; + virFirewallRulePtr *action; + + size_t nrollback; + virFirewallRulePtr *rollback; + + bool addingRollback; +}; + + +struct _virFirewall { + int err; + + size_t ngroups; + virFirewallGroupPtr *groups; +}; + +static virFirewallBackend currentBackend; + +static int +virFirewallValidateBackend(virFirewallBackend backend); + +static int +virFirewallOnceInit(void) +{ + return virFirewallValidateBackend(VIR_FIREWALL_BACKEND_AUTOMATIC); +} + +VIR_ONCE_GLOBAL_INIT(virFirewall) + +static int +virFirewallValidateBackend(virFirewallBackend backend) +{ + VIR_DEBUG("Validating backend %d", backend); + if (backend == VIR_FIREWALL_BACKEND_AUTOMATIC || + backend == VIR_FIREWALL_BACKEND_FIREWALLD) { + int rv = virDBusIsServiceRegistered(VIR_FIREWALL_FIREWALLD_SERVICE); + VIR_DEBUG("Firewalled is registered ? %d", rv); + if (rv < 0) { + if (rv == -2) { + if (backend == VIR_FIREWALL_BACKEND_FIREWALLD) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("firewalld backend requested, but firewalld service is not running")); + return -1; + } else { + backend = VIR_FIREWALL_BACKEND_DIRECT; + } + } else { + return -1; + } + } + } + + if (backend == VIR_FIREWALL_BACKEND_DIRECT) { + if (!virFileIsExecutable(IPTABLES_PATH)) { + virReportSystemError(errno, + _("direct backend requested, but %s is not available"), + IPTABLES_PATH); + return -1; + } + if (!virFileIsExecutable(IP6TABLES_PATH)) { + virReportSystemError(errno, + _("direct backend requested, but %s is not available"), + IP6TABLES_PATH); + return -1; + } + if (!virFileIsExecutable(EBTABLES_PATH)) { + virReportSystemError(errno, + _("direct backend requested, but %s is not available"), + EBTABLES_PATH); + return -1; + } + } + + VIR_DEBUG("Set backend to %d", backend); + currentBackend = backend; + return 0; +} + +int +virFirewallSetBackend(virFirewallBackend backend) +{ + if (virFirewallInitialize() < 0) + return -1; + + return virFirewallValidateBackend(backend); +} + +static virFirewallGroupPtr +virFirewallGroupNew(void) +{ + virFirewallGroupPtr group; + + if (VIR_ALLOC(group) < 0) + return NULL; + + return group; +} + + +/** + * virFirewallNew: + * + * Creates a new firewall ruleset for changing rules + * of @layer. This should be followed by a call to + * virFirewallStartTransaction before adding + * any rules + * + * Returns the new firewall ruleset + */ +virFirewallPtr virFirewallNew(void) +{ + virFirewallPtr firewall; + + if (VIR_ALLOC(firewall) < 0) + return NULL; + + return firewall; +} + + +static void +virFirewallRuleFree(virFirewallRulePtr rule) +{ + size_t i; + + if (!rule) + return; + + for (i = 0; i < rule->argsLen; i++) + VIR_FREE(rule->args[i]); + VIR_FREE(rule->args); + VIR_FREE(rule); +} + + +static void +virFirewallGroupFree(virFirewallGroupPtr group) +{ + size_t i; + + if (!group) + return; + + for (i = 0; i < group->naction; i++) + virFirewallRuleFree(group->action[i]); + VIR_FREE(group->action); + + for (i = 0; i < group->nrollback; i++) + virFirewallRuleFree(group->rollback[i]); + VIR_FREE(group->rollback); + + VIR_FREE(group); +} + + +/** + * virFirewallFree: + * + * Release all memory associated with the firewall + * ruleset + */ +void virFirewallFree(virFirewallPtr firewall) +{ + size_t i; + + if (!firewall) + return; + + for (i = 0; i < firewall->ngroups; i++) + virFirewallGroupFree(firewall->groups[i]); + VIR_FREE(firewall->groups); + + VIR_FREE(firewall); +} + +#define VIR_FIREWALL_RETURN_IF_ERROR(firewall) \ + if (!firewall || firewall->err) \ + return; + +#define VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, ruel)\ + if (!firewall || firewall->err || !rule) \ + return; + +#define VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall) \ + if (!firewall || firewall->err) \ + return NULL; + +#define ADD_ARG(rule, str) \ + if (VIR_RESIZE_N(rule->args, \ + rule->argsAlloc, \ + rule->argsLen, 1) < 0) \ + goto no_memory; \ + \ + if (VIR_STRDUP(rule->args[rule->argsLen++], str) < 0)\ + goto no_memory; + +/** + * virFirewallRuleAny: + * @firewall: add any type of rule to the firewall ruleset + * @layer: the firewall layer to change + * @...: NULL terminated list of strings for the rule + * + * Add any type of rule to the firewall ruleset. + * + * Returns the new rule + */ +virFirewallRulePtr +virFirewallAddRule(virFirewallPtr firewall, + virFirewallLayer layer, + ...) +{ + virFirewallGroupPtr group; + virFirewallRulePtr rule; + va_list list; + char *str; + + VIR_FIREWALL_RETURN_NULL_IF_ERROR(firewall); + + if (firewall->ngroups == 0) { + firewall->err = ENODATA; + return NULL; + } + group = firewall->groups[firewall->ngroups - 1]; + + va_start(list, layer); + + if (VIR_ALLOC(rule) < 0) + goto no_memory; + + rule->layer = layer; + + while ((str = va_arg(list, char *)) != NULL) { + ADD_ARG(rule, str); + } + + if (group->addingRollback) { + if (VIR_EXPAND_N(group->rollback, + group->nrollback, 1) < 0) + goto no_memory; + + group->rollback[group->nrollback - 1] = rule; + } else { + if (VIR_EXPAND_N(group->action, + group->naction, 1) < 0) + goto no_memory; + + group->action[group->naction - 1] = rule; + } + + va_end(list); + + return rule; + +no_memory: + firewall->err = ENOMEM; + virFirewallRuleFree(rule); + va_end(list); + return NULL; +} + +void virFirewallRuleAddArg(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *arg) +{ + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule); + + ADD_ARG(rule, arg); + + return; + + no_memory: + firewall->err = ENOMEM; +} + + +void virFirewallRuleAddArgFormat(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *fmt, ...) +{ + char *arg; + va_list list; + + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule); + + va_start(list, fmt); + + if (virVasprintf(&arg, fmt, list) < 0) + goto no_memory; + + ADD_ARG(rule, arg); + + va_end(list); + + return; + + no_memory: + firewall->err = ENOMEM; + va_end(list); +} + + +void virFirewallRuleAddArgSet(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *const *args) +{ + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule); + + while (*args) { + ADD_ARG(rule, *args); + args++; + } + + return; + + no_memory: + firewall->err = ENOMEM; +} + + +void virFirewallRuleAddArgList(virFirewallPtr firewall, + virFirewallRulePtr rule, + ...) +{ + va_list list; + const char *str; + + VIR_FIREWALL_RULE_RETURN_IF_ERROR(firewall, rule); + + va_start(list, rule); + + while ((str = va_arg(list, char *)) != NULL) { + ADD_ARG(rule, str); + } + + va_end(list); + + return; + + no_memory: + firewall->err = ENOMEM; + va_end(list); +} + + +/** + * virFirewallStartTransaction: + * @firewall: the firewall ruleset + * @flags: bitset of virFirewallTransactionFlags + * + * Start a new transaction with associated rollback + * block. + * + * Should be followed by calls to add various rules to + * the transaction. Then virFirwallStartRollback should + * be used to provide rules to rollback upon transaction + * failure + */ +void virFirewallStartTransaction(virFirewallPtr firewall, + unsigned int flags) +{ + virFirewallGroupPtr group; + + VIR_FIREWALL_RETURN_IF_ERROR(firewall); + + if (!(group = virFirewallGroupNew())) { + firewall->err = ENOMEM; + return; + } + group->actionFlags = flags; + + if (VIR_EXPAND_N(firewall->groups, + firewall->ngroups, 1) < 0) { + firewall->err = ENOMEM; + virFirewallGroupFree(group); + return; + } + firewall->groups[firewall->ngroups - 1] = group; +} + +/** + * virFirewallBeginRollback: + * @firewall: the firewall ruleset + * @flags: bitset of virFirewallRollbackFlags + * + * Mark the beginning of a set of rules able to rollback + * changes in this and all earlier transactions. + * + * Should be followed by calls to add various rules needed + * to rollback state. Then virFirewallStartTransaction + * should be used to indicate the beginning of the next + * transactional ruleset. + */ +void virFirewallStartRollback(virFirewallPtr firewall, + unsigned int flags) +{ + virFirewallGroupPtr group; + + VIR_FIREWALL_RETURN_IF_ERROR(firewall); + + if (firewall->ngroups == 0) { + firewall->err = ENODATA; + return; + } + + group = firewall->groups[firewall->ngroups-1]; + group->rollbackFlags = flags; + group->addingRollback = true; +} + + +static int +virFirewallApplyRuleDirect(virFirewallRulePtr rule, + bool ignoreErrors) +{ + size_t i; + const char *bin = virFirewallLayerCommandTypeToString(rule->layer); + virCommandPtr cmd = NULL; + int status; + int ret = -1; + + if (!bin) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown firewall layer %d"), + rule->layer); + goto cleanup; + } + + cmd = virCommandNewArgList(bin, NULL); + + for (i = 0; i < rule->argsLen; i++) + virCommandAddArg(cmd, rule->args[i]); + + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + if (status != 0) { + if (ignoreErrors) { + VIR_DEBUG("Ignoring error running command"); + } else { + char *args = virCommandToString(cmd); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to apply firewall rules %s"), + NULLSTR(args)); + VIR_FREE(args); + goto cleanup; + } + } + + ret = 0; + cleanup: + virCommandFree(cmd); + return ret; +} + +static int +virFirewallApplyRuleFirewallD(virFirewallRulePtr rule, + bool ignoreErrors) +{ + const char *ipv = virFirewallLayerFirewallDTypeToString(rule->layer); + DBusConnection *sysbus = virDBusGetSystemBus(); + int ret = -1; + + if (!sysbus) + return -1; + + if (!ipv) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown firewall layer %d"), + rule->layer); + goto cleanup; + } + + if (virDBusCallMethod(sysbus, + NULL, + VIR_FIREWALL_FIREWALLD_SERVICE, + "/org/fedoraproject/FirewallD1", + "org.fedoraproject.FirewallD1.direct", + "passthrough", + "sas", + ipv, + (int)rule->argsLen, + rule->args) < 0) { + if (ignoreErrors) { + virResetLastError(); + VIR_DEBUG("Ignoring error running command"); + } else { + goto cleanup; + } + } + + ret = 0; + + cleanup: + return ret; +} + + +static int +virFirewallApplyRule(virFirewallRulePtr rule, + bool ignoreErrors) +{ + switch (currentBackend) { + case VIR_FIREWALL_BACKEND_DIRECT: + return virFirewallApplyRuleDirect(rule, ignoreErrors); + case VIR_FIREWALL_BACKEND_FIREWALLD: + return virFirewallApplyRuleFirewallD(rule, ignoreErrors); + default: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected firewall engine backend")); + return -1; + } +} + +static int +virFirewallApplyGroup(virFirewallGroupPtr group) +{ + size_t i; + bool ignoreErrors = (group->actionFlags & VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); + + VIR_DEBUG("Applying rules for group %p", group); + for (i = 0; i < group->naction; i++) { + if (virFirewallApplyRule(group->action[i], + ignoreErrors) < 0) + return -1; + } + return 0; +} + + +static void +virFirewallRollbackGroup(virFirewallGroupPtr group) +{ + size_t i; + + VIR_DEBUG("Rolling back rules for group %p", group); + for (i = 0; i < group->nrollback; i++) { + ignore_value(virFirewallApplyRule(group->rollback[i], + true)); + } +} + + +int +virFirewallApply(virFirewallPtr firewall) +{ + size_t i, j; + + if (virFirewallInitialize() < 0) + return -1; + + VIR_DEBUG("Applying groups for %p", firewall); + for (i = 0; i < firewall->ngroups; i++) { + if (virFirewallApplyGroup(firewall->groups[i]) < 0) { + VIR_DEBUG("Rolling back groups upto %zu for %p", i, firewall); + size_t first = i; + virErrorPtr saved_error = virSaveLastError(); + + /* + * Look at any inheritance markers to figure out + * what the first rollback group we need to apply is + */ + for (j = 0; j <= i; j++) { + VIR_DEBUG("Checking inheritance of group %zu", i - j); + if (firewall->groups[i - j]->rollbackFlags & + VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS) + first = (i - j) - 1; + } + /* + * Now apply all rollback groups in order + */ + for (j = first; j <= i; j++) { + VIR_DEBUG("Rolling back group %zu", j); + virFirewallRollbackGroup(firewall->groups[j]); + } + + virSetError(saved_error); + virFreeError(saved_error); + VIR_DEBUG("Done rolling back groups for %p", firewall); + return -1; + } + } + VIR_DEBUG("Done applying groups for %p", firewall); + return 0; +} diff --git a/src/util/virfirewall.h b/src/util/virfirewall.h new file mode 100644 index 0000000..6a1cd92 --- /dev/null +++ b/src/util/virfirewall.h @@ -0,0 +1,93 @@ +/* + * virfirewall.h: integration with firewalls + * + * 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/>. + * + * Authors: + * Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_FIREWALL_H__ +# define __VIR_FIREWALL_H__ + +# include "internal.h" + +typedef struct _virFirewall virFirewall; +typedef virFirewall *virFirewallPtr; + +typedef struct _virFirewallRule virFirewallRule; +typedef virFirewallRule *virFirewallRulePtr; + +typedef enum { + VIR_FIREWALL_LAYER_ETHERNET, + VIR_FIREWALL_LAYER_IPV4, + VIR_FIREWALL_LAYER_IPV6, + + VIR_FIREWALL_LAYER_LAST, +} virFirewallLayer; + +virFirewallPtr virFirewallNew(void); + +void virFirewallFree(virFirewallPtr firewall); + +virFirewallRulePtr virFirewallAddRule(virFirewallPtr firewall, + virFirewallLayer layer, + ...) + ATTRIBUTE_SENTINEL; + +void virFirewallRuleAddArg(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *arg) + ATTRIBUTE_NONNULL(3); + +void virFirewallRuleAddArgFormat(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *fmt, ...) + ATTRIBUTE_NONNULL(3) ATTRIBUTE_FMT_PRINTF(3, 4); + +void virFirewallRuleAddArgSet(virFirewallPtr firewall, + virFirewallRulePtr rule, + const char *const *args) + ATTRIBUTE_NONNULL(3); + +void virFirewallRuleAddArgList(virFirewallPtr firewall, + virFirewallRulePtr rule, + ...) + ATTRIBUTE_SENTINEL; + + +typedef enum { + /* Ignore all errors when applying rules, so no + * rollback block will be required */ + VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS = (1 << 0), +} virFirewallTransactionFlags; + +void virFirewallStartTransaction(virFirewallPtr firewall, + unsigned int flags); + +typedef enum { + /* Execute previous rollback block before this + * one, to chain cleanup */ + VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS = (1 << 0), +} virFirewallRollbackFlags; + +void virFirewallStartRollback(virFirewallPtr firewall, + unsigned int flags); + +int virFirewallApply(virFirewallPtr firewall); + +#endif /* __VIR_FIREWALL_H__ */ diff --git a/src/util/virfirewallpriv.h b/src/util/virfirewallpriv.h new file mode 100644 index 0000000..359d0e4 --- /dev/null +++ b/src/util/virfirewallpriv.h @@ -0,0 +1,45 @@ +/* + * virfirewall.h: integration with firewalls + * + * Copyright (C) 2013 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/>. + * + * Authors: + * Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_FIREWALL_PRIV_H_ALLOW__ +# error "virfirewallpriv.h may only be included by virfirewall.c or test suites" +#endif + +#ifndef __VIR_FIREWALL_PRIV_H__ +# define __VIR_FIREWALL_PRIV_H__ + +# include "virfirewall.h" + +# define VIR_FIREWALL_FIREWALLD_SERVICE "org.fedoraproject.FirewallD1" + +typedef enum { + VIR_FIREWALL_BACKEND_AUTOMATIC, + VIR_FIREWALL_BACKEND_DIRECT, + VIR_FIREWALL_BACKEND_FIREWALLD, + + VIR_FIREWALL_BACKEND_LAST, +} virFirewallBackend; + +int virFirewallSetBackend(virFirewallBackend backend); + +#endif /* __VIR_FIREWALL_PRIV_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index baf0483..63e869e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -141,6 +141,7 @@ test_programs = virshtest sockettest \ virpcitest \ virendiantest \ virfiletest \ + virfirewalltest \ viridentitytest \ virkeycodetest \ virlockspacetest \ @@ -943,6 +944,12 @@ virfiletest_SOURCES = \ virfiletest.c testutils.h testutils.c virfiletest_LDADD = $(LDADDS) +virfirewalltest_SOURCES = \ + virfirewalltest.c testutils.h testutils.c +virfirewalltest_LDADD = $(LDADDS) +virfirewalltest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +virfirewalltest_LDFLAGS = $(DRIVER_MODULE_LDFLAGS) + jsontest_SOURCES = \ jsontest.c testutils.h testutils.c jsontest_LDADD = $(LDADDS) diff --git a/tests/testutils.c b/tests/testutils.c index ede6239..a46823d 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -456,10 +456,20 @@ int virtTestDifference(FILE *stream, const char *expect, const char *actual) { - const char *expectStart = expect; - const char *expectEnd = expect + (strlen(expect)-1); - const char *actualStart = actual; - const char *actualEnd = actual + (strlen(actual)-1); + const char *expectStart; + const char *expectEnd; + const char *actualStart; + const char *actualEnd; + + if (!expect) + expect = ""; + if (!actual) + actual = ""; + + expectStart = expect; + expectEnd = expect + (strlen(expect)-1); + actualStart = actual; + actualEnd = actual + (strlen(actual)-1); if (!virTestGetDebug()) return 0; diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c new file mode 100644 index 0000000..144614c --- /dev/null +++ b/tests/virfirewalltest.c @@ -0,0 +1,619 @@ +/* + * 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/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#define __VIR_FIREWALL_PRIV_H_ALLOW__ + +#include "testutils.h" +#include "virbuffer.h" +#include "vircommand.h" +#include "virfirewallpriv.h" +#include "virmock.h" + +#define VIR_FROM_THIS VIR_FROM_FIREWALL + +#include <dbus/dbus.h> + +static bool noFirewalld = true; + +VIR_MOCK_IMPL_RET_ARGS(dbus_connection_send_with_reply_and_block, + DBusMessage *, + DBusConnection *, connection, + DBusMessage *, message, + int, timeout_milliseconds, + DBusError *, error, { + DBusMessage *reply = NULL; + const char *service = dbus_message_get_destination(message); + const char *member = dbus_message_get_member(message); + + if (STREQ(service, "org.freedesktop.DBus") && + STREQ(member, "ListNames")) { + const char *svc1 = "org.foo.bar.wizz"; + const char *svc2 = VIR_FIREWALL_FIREWALLD_SERVICE; + DBusMessageIter iter; + DBusMessageIter sub; + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + "s", &sub); + + if (!dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc1)) + goto error; + if (!noFirewalld && + !dbus_message_iter_append_basic(&sub, + DBUS_TYPE_STRING, + &svc2)) + goto error; + dbus_message_iter_close_container(&iter, &sub); + } else { + reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + } + + return reply; + + error: + dbus_message_unref(reply); + return NULL; +}) + + +static int +testFirewallSingleGroup(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n"; + + virCommandSetDryRun(&buf, NULL, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + if (virFirewallApply(fw) < 0) + goto cleanup; + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + + +static int +testFirewallManyGroups(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n" + IPTABLES_PATH " -A OUTPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A OUTPUT --jump DROP\n"; + + virCommandSetDryRun(&buf, NULL, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "OUTPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "OUTPUT", + "--jump", "DROP", NULL); + + + if (virFirewallApply(fw) < 0) + goto cleanup; + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + +static void +testFirewallRollbackHook(const char *const*args, + const char *const*env ATTRIBUTE_UNUSED, + const char *input ATTRIBUTE_UNUSED, + char **output ATTRIBUTE_UNUSED, + char **error ATTRIBUTE_UNUSED, + int *status, + void *opaque ATTRIBUTE_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 +testFirewallIgnoreFail(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -A OUTPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A OUTPUT --jump DROP\n"; + + virCommandSetDryRun(&buf, testFirewallRollbackHook, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "OUTPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "OUTPUT", + "--jump", "DROP", NULL); + + + if (virFirewallApply(fw) < 0) + goto cleanup; + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + + +static int +testFirewallNoRollback(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n"; + + virCommandSetDryRun(&buf, testFirewallRollbackHook, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + if (virFirewallApply(fw) == 0) { + fprintf(stderr, "Firewall apply unexpectedly worked\n"); + goto cleanup; + } + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + +static int +testFirewallSingleRollback(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n"; + + virCommandSetDryRun(&buf, testFirewallRollbackHook, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + virFirewallStartRollback(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + if (virFirewallApply(fw) == 0) { + fprintf(stderr, "Firewall apply unexpectedly worked\n"); + goto cleanup; + } + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + +static int +testFirewallManyRollback(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n"; + + virCommandSetDryRun(&buf, testFirewallRollbackHook, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallStartRollback(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + virFirewallStartRollback(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + if (virFirewallApply(fw) == 0) { + fprintf(stderr, "Firewall apply unexpectedly worked\n"); + goto cleanup; + } + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + +static int +testFirewallChainedRollback(const void *opaque ATTRIBUTE_UNUSED) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virFirewallPtr fw = NULL; + int ret = -1; + char *actual = NULL; + const char *expected = + IPTABLES_PATH " -A INPUT --source-host 192.168.122.1 --jump ACCEPT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.127 --jump REJECT\n" + IPTABLES_PATH " -A INPUT --source-host '!192.168.122.1' --jump REJECT\n" + IPTABLES_PATH " -A INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host 192.168.122.127 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host 192.168.122.255 --jump REJECT\n" + IPTABLES_PATH " -D INPUT --source-host '!192.168.122.1' --jump REJECT\n"; + + virCommandSetDryRun(&buf, testFirewallRollbackHook, NULL); + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + virFirewallStartRollback(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.1", + "--jump", "ACCEPT", NULL); + + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.127", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + virFirewallStartRollback(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.127", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + + virFirewallStartTransaction(fw, 0); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-A", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + virFirewallStartRollback(fw, VIR_FIREWALL_ROLLBACK_INHERIT_PREVIOUS); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "192.168.122.255", + "--jump", "REJECT", NULL); + + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "-D", "INPUT", + "--source-host", "!192.168.122.1", + "--jump", "REJECT", NULL); + + if (virFirewallApply(fw) == 0) { + fprintf(stderr, "Firewall apply unexpectedly worked\n"); + goto cleanup; + } + + if (virBufferError(&buf)) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (STRNEQ_NULLABLE(expected, actual)) { + fprintf(stderr, "Unexected command execution"); + virtTestDifference(stderr, expected, actual); + } + + ret = 0; + cleanup: + VIR_FREE(actual); + virCommandSetDryRun(NULL, NULL, NULL); + virFirewallFree(fw); + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + + if (virtTestRun("single group", testFirewallSingleGroup, NULL) < 0) + ret = -1; + if (virtTestRun("many groups", testFirewallManyGroups, NULL) < 0) + ret = -1; + if (virtTestRun("ignore fail", testFirewallIgnoreFail, NULL) < 0) + ret = -1; + if (virtTestRun("no rollback", testFirewallNoRollback, NULL) < 0) + ret = -1; + if (virtTestRun("single rollback", testFirewallSingleRollback, NULL) < 0) + ret = -1; + if (virtTestRun("many rollback", testFirewallManyRollback, NULL) < 0) + ret = -1; + if (virtTestRun("chained rollback", testFirewallChainedRollback, NULL) < 0) + ret = -1; + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#if WITH_DBUS +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virmockdbus.so") +#else +VIRT_TEST_MAIN(mymain) +#endif -- 1.8.5.3

Update the iptablesXXXX methods so that instead of directly executing iptables commands, they populate rules in an instance of virFirewallPtr. The bridge driver can thus construct the ruleset and then invoke it in one operation having rollback handled automatically. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/network/bridge_driver_linux.c | 669 ++++++++++++++++---------------------- src/util/viriptables.c | 634 ++++++++++++++++-------------------- src/util/viriptables.h | 114 ++++--- 3 files changed, 620 insertions(+), 797 deletions(-) diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c index ff62cb3..fea913a 100644 --- a/src/network/bridge_driver_linux.c +++ b/src/network/bridge_driver_linux.c @@ -27,6 +27,7 @@ #include "virfile.h" #include "viriptables.h" #include "virstring.h" +#include "virfirewall.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -131,7 +132,8 @@ static const char networkLocalMulticast[] = "224.0.0.0/24"; static const char networkLocalBroadcast[] = "255.255.255.255/32"; static int -networkAddMasqueradingFirewallRules(virNetworkObjPtr network, +networkAddMasqueradingFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); @@ -141,32 +143,26 @@ networkAddMasqueradingFirewallRules(virNetworkObjPtr network, virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid prefix or netmask for '%s'"), network->def->bridge); - goto masqerr1; + return -1; } /* allow forwarding packets from the bridge interface */ - if (iptablesAddForwardAllowOut(&ipdef->address, + if (iptablesAddForwardAllowOut(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow forwarding from '%s'"), - network->def->bridge); - goto masqerr1; - } + forwardIf) < 0) + return -1; /* allow forwarding packets to the bridge interface if they are * part of an existing connection */ - if (iptablesAddForwardAllowRelatedIn(&ipdef->address, + if (iptablesAddForwardAllowRelatedIn(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow forwarding to '%s'"), - network->def->bridge); - goto masqerr2; - } + forwardIf) < 0) + return -1; /* * Enable masquerading. @@ -201,176 +197,127 @@ networkAddMasqueradingFirewallRules(virNetworkObjPtr network, */ /* First the generic masquerade rule for other protocols */ - if (iptablesAddForwardMasquerade(&ipdef->address, + if (iptablesAddForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - NULL) < 0) { - if (forwardIf) - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable masquerading to %s"), - forwardIf); - else - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("failed to add iptables rule to enable masquerading")); - goto masqerr3; - } + NULL) < 0) + return -1; /* UDP with a source port restriction */ - if (iptablesAddForwardMasquerade(&ipdef->address, + if (iptablesAddForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - "udp") < 0) { - if (forwardIf) - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable UDP masquerading to %s"), - forwardIf); - else - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("failed to add iptables rule to enable UDP masquerading")); - goto masqerr4; - } + "udp") < 0) + return -1; /* TCP with a source port restriction */ - if (iptablesAddForwardMasquerade(&ipdef->address, + if (iptablesAddForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - "tcp") < 0) { - if (forwardIf) - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable TCP masquerading to %s"), - forwardIf); - else - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("failed to add iptables rule to enable TCP masquerading")); - goto masqerr5; - } + "tcp") < 0) + return -1; /* exempt local network broadcast address as destination */ - if (iptablesAddDontMasquerade(&ipdef->address, + if (iptablesAddDontMasquerade(fw, + &ipdef->address, prefix, forwardIf, - networkLocalBroadcast) < 0) { - if (forwardIf) - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to prevent local broadcast masquerading on %s"), - forwardIf); - else - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("failed to add iptables rule to prevent local broadcast masquerading")); - goto masqerr6; - } + networkLocalBroadcast) < 0) + return -1; /* exempt local multicast range as destination */ - if (iptablesAddDontMasquerade(&ipdef->address, + if (iptablesAddDontMasquerade(fw, + &ipdef->address, prefix, forwardIf, - networkLocalMulticast) < 0) { - if (forwardIf) - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to prevent local multicast masquerading on %s"), - forwardIf); - else - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("failed to add iptables rule to prevent local multicast masquerading")); - goto masqerr7; - } + networkLocalMulticast) < 0) + return -1; return 0; - - masqerr7: - iptablesRemoveDontMasquerade(&ipdef->address, - prefix, - forwardIf, - networkLocalBroadcast); - masqerr6: - iptablesRemoveForwardMasquerade(&ipdef->address, - prefix, - forwardIf, - &network->def->forward.addr, - &network->def->forward.port, - "tcp"); - masqerr5: - iptablesRemoveForwardMasquerade(&ipdef->address, - prefix, - forwardIf, - &network->def->forward.addr, - &network->def->forward.port, - "udp"); - masqerr4: - iptablesRemoveForwardMasquerade(&ipdef->address, - prefix, - forwardIf, - &network->def->forward.addr, - &network->def->forward.port, - NULL); - masqerr3: - iptablesRemoveForwardAllowRelatedIn(&ipdef->address, - prefix, - network->def->bridge, - forwardIf); - masqerr2: - iptablesRemoveForwardAllowOut(&ipdef->address, - prefix, - network->def->bridge, - forwardIf); - masqerr1: - return -1; } -static void -networkRemoveMasqueradingFirewallRules(virNetworkObjPtr network, +static int +networkRemoveMasqueradingFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(network->def, 0); - if (prefix >= 0) { - iptablesRemoveDontMasquerade(&ipdef->address, + if (prefix < 0) + return 0; + + if (iptablesRemoveDontMasquerade(fw, + &ipdef->address, prefix, forwardIf, - networkLocalMulticast); - iptablesRemoveDontMasquerade(&ipdef->address, + networkLocalMulticast) < 0) + return -1; + + if (iptablesRemoveDontMasquerade(fw, + &ipdef->address, prefix, forwardIf, - networkLocalBroadcast); - iptablesRemoveForwardMasquerade(&ipdef->address, + networkLocalBroadcast) < 0) + return -1; + + if (iptablesRemoveForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - "tcp"); - iptablesRemoveForwardMasquerade(&ipdef->address, + "tcp") < 0) + return -1; + + if (iptablesRemoveForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - "udp"); - iptablesRemoveForwardMasquerade(&ipdef->address, + "udp") < 0) + return -1; + + if (iptablesRemoveForwardMasquerade(fw, + &ipdef->address, prefix, forwardIf, &network->def->forward.addr, &network->def->forward.port, - NULL); + NULL) < 0) + return -1; - iptablesRemoveForwardAllowRelatedIn(&ipdef->address, + if (iptablesRemoveForwardAllowRelatedIn(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf); - iptablesRemoveForwardAllowOut(&ipdef->address, + forwardIf) < 0) + return -1; + + if (iptablesRemoveForwardAllowOut(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf); - } + forwardIf) < 0) + return -1; + + return 0; } + static int -networkAddRoutingFirewallRules(virNetworkObjPtr network, +networkAddRoutingFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); @@ -380,168 +327,199 @@ networkAddRoutingFirewallRules(virNetworkObjPtr network, virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid prefix or netmask for '%s'"), network->def->bridge); - goto routeerr1; + return -1; } /* allow routing packets from the bridge interface */ - if (iptablesAddForwardAllowOut(&ipdef->address, + if (iptablesAddForwardAllowOut(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow routing from '%s'"), - network->def->bridge); - goto routeerr1; - } + forwardIf) < 0) + return -1; /* allow routing packets to the bridge interface */ - if (iptablesAddForwardAllowIn(&ipdef->address, + if (iptablesAddForwardAllowIn(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow routing to '%s'"), - network->def->bridge); - goto routeerr2; - } + forwardIf) < 0) + return -1; return 0; - -routeerr2: - iptablesRemoveForwardAllowOut(&ipdef->address, - prefix, - network->def->bridge, - forwardIf); -routeerr1: - return -1; } -static void -networkRemoveRoutingFirewallRules(virNetworkObjPtr network, +static int +networkRemoveRoutingFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); const char *forwardIf = virNetworkDefForwardIf(network->def, 0); - if (prefix >= 0) { - iptablesRemoveForwardAllowIn(&ipdef->address, + if (prefix < 0) + return 0; + + if (iptablesRemoveForwardAllowIn(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf); + forwardIf) < 0) + return -1; - iptablesRemoveForwardAllowOut(&ipdef->address, + if (iptablesRemoveForwardAllowOut(fw, + &ipdef->address, prefix, network->def->bridge, - forwardIf); + forwardIf) < 0) + return -1; + + return 0; +} + + +static void +networkAddGeneralIPv4FirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) +{ + size_t i; + virNetworkIpDefPtr ipv4def; + + /* First look for first IPv4 address that has dhcp or tftpboot defined. */ + /* We support dhcp config on 1 IPv4 interface only. */ + for (i = 0; + (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, i)); + i++) { + if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot) + break; + } + + /* allow DHCP requests through to dnsmasq */ + iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 67); + iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 67); + iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 68); + + /* allow DNS requests through to dnsmasq */ + iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 53); + iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 53); + + /* allow TFTP requests through to dnsmasq if necessary */ + if (ipv4def && ipv4def->tftproot) + iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 69); + + /* Catch all rules to block forwarding to/from bridges */ + iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); + iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); + + /* Allow traffic between guests on the same bridge */ + iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); +} + +static void +networkRemoveGeneralIPv4FirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) +{ + size_t i; + virNetworkIpDefPtr ipv4def; + + for (i = 0; + (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, i)); + i++) { + if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot) + break; } + + iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); + iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); + iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge); + + if (ipv4def && ipv4def->tftproot) + iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 69); + + iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 53); + iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 53); + + iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 68); + iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 67); + iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, network->def->bridge, 67); } + /* Add all once/network rules required for IPv6. * If no IPv6 addresses are defined and <network ipv6='yes'> is * specified, then allow IPv6 commuinications between virtual systems. * If any IPv6 addresses are defined, then add the rules for regular operation. */ -static int -networkAddGeneralIp6tablesRules(virNetworkObjPtr network) +static void +networkAddGeneralIPv6FirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) { if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0) && !network->def->ipv6nogw) { - return 0; + return; } /* Catch all rules to block forwarding to/from bridges */ - - if (iptablesAddForwardRejectOut(AF_INET6, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to block outbound traffic from '%s'"), - network->def->bridge); - goto err1; - } - - if (iptablesAddForwardRejectIn(AF_INET6, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to block inbound traffic to '%s'"), - network->def->bridge); - goto err2; - } + iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); + iptablesAddForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); /* Allow traffic between guests on the same bridge */ - if (iptablesAddForwardAllowCross(AF_INET6, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to allow cross bridge traffic on '%s'"), - network->def->bridge); - goto err3; - } - - /* if no IPv6 addresses are defined, we are done. */ - if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) - return 0; - - /* allow DNS over IPv6 */ - if (iptablesAddTcpInput(AF_INET6, network->def->bridge, 53) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to allow DNS requests from '%s'"), - network->def->bridge); - goto err4; - } - - if (iptablesAddUdpInput(AF_INET6, network->def->bridge, 53) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to allow DNS requests from '%s'"), - network->def->bridge); - goto err5; - } + iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); - if (iptablesAddUdpInput(AF_INET6, network->def->bridge, 547) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add ip6tables rule to allow DHCP6 requests from '%s'"), - network->def->bridge); - goto err6; + if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { + /* allow DNS over IPv6 */ + iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 53); + iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 53); + iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 547); } - - return 0; - - /* unwind in reverse order from the point of failure */ -err6: - iptablesRemoveUdpInput(AF_INET6, network->def->bridge, 53); -err5: - iptablesRemoveTcpInput(AF_INET6, network->def->bridge, 53); -err4: - iptablesRemoveForwardAllowCross(AF_INET6, network->def->bridge); -err3: - iptablesRemoveForwardRejectIn(AF_INET6, network->def->bridge); -err2: - iptablesRemoveForwardRejectOut(AF_INET6, network->def->bridge); -err1: - return -1; } static void -networkRemoveGeneralIp6tablesRules(virNetworkObjPtr network) +networkRemoveGeneralIPv6FirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) { if (!virNetworkDefGetIpByIndex(network->def, AF_INET6, 0) && !network->def->ipv6nogw) { return; } + if (virNetworkDefGetIpByIndex(network->def, AF_INET6, 0)) { - iptablesRemoveUdpInput(AF_INET6, network->def->bridge, 547); - iptablesRemoveUdpInput(AF_INET6, network->def->bridge, 53); - iptablesRemoveTcpInput(AF_INET6, network->def->bridge, 53); + iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 547); + iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 53); + iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge, 53); } /* the following rules are there if no IPv6 address has been defined * but network->def->ipv6nogw == true */ - iptablesRemoveForwardAllowCross(AF_INET6, network->def->bridge); - iptablesRemoveForwardRejectIn(AF_INET6, network->def->bridge); - iptablesRemoveForwardRejectOut(AF_INET6, network->def->bridge); + iptablesRemoveForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); + iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); + iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV6, network->def->bridge); } -static int -networkAddGeneralFirewallRules(virNetworkObjPtr network) +static void +networkAddGeneralFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) +{ + networkAddGeneralIPv4FirewallRules(fw, network); + networkAddGeneralIPv6FirewallRules(fw, network); +} + + +static void +networkRemoveGeneralFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) +{ + networkRemoveGeneralIPv4FirewallRules(fw, network); + networkRemoveGeneralIPv6FirewallRules(fw, network); +} + +static void +networkAddChecksumFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) { size_t i; virNetworkIpDefPtr ipv4def; @@ -551,161 +529,44 @@ networkAddGeneralFirewallRules(virNetworkObjPtr network) for (i = 0; (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, i)); i++) { - if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot) + if (ipv4def->nranges || ipv4def->nhosts) break; } - /* allow DHCP requests through to dnsmasq */ - - if (iptablesAddTcpInput(AF_INET, network->def->bridge, 67) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s'"), - network->def->bridge); - goto err1; - } - - if (iptablesAddUdpInput(AF_INET, network->def->bridge, 67) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow DHCP requests from '%s'"), - network->def->bridge); - goto err2; - } - - if (iptablesAddUdpOutput(AF_INET, network->def->bridge, 68) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow DHCP replies to '%s'"), - network->def->bridge); - goto err3; - } - /* If we are doing local DHCP service on this network, attempt to * add a rule that will fixup the checksum of DHCP response * packets back to the guests (but report failure without * aborting, since not all iptables implementations support it). */ - - if (ipv4def && (ipv4def->nranges || ipv4def->nhosts) && - (iptablesAddOutputFixUdpChecksum(network->def->bridge, 68) < 0)) { - VIR_WARN("Could not add rule to fixup DHCP response checksums " - "on network '%s'.", network->def->name); - VIR_WARN("May need to update iptables package & kernel to support CHECKSUM rule."); - } - - /* allow DNS requests through to dnsmasq */ - if (iptablesAddTcpInput(AF_INET, network->def->bridge, 53) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s'"), - network->def->bridge); - goto err4; - } - - if (iptablesAddUdpInput(AF_INET, network->def->bridge, 53) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow DNS requests from '%s'"), - network->def->bridge); - goto err5; - } - - /* allow TFTP requests through to dnsmasq if necessary */ - if (ipv4def && ipv4def->tftproot && - iptablesAddUdpInput(AF_INET, network->def->bridge, 69) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow TFTP requests from '%s'"), - network->def->bridge); - goto err6; - } - - /* Catch all rules to block forwarding to/from bridges */ - - if (iptablesAddForwardRejectOut(AF_INET, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to block outbound traffic from '%s'"), - network->def->bridge); - goto err7; - } - - if (iptablesAddForwardRejectIn(AF_INET, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to block inbound traffic to '%s'"), - network->def->bridge); - goto err8; - } - - /* Allow traffic between guests on the same bridge */ - if (iptablesAddForwardAllowCross(AF_INET, network->def->bridge) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to allow cross bridge traffic on '%s'"), - network->def->bridge); - goto err9; - } - - /* add IPv6 general rules, if needed */ - if (networkAddGeneralIp6tablesRules(network) < 0) { - goto err10; - } - - return 0; - - /* unwind in reverse order from the point of failure */ -err10: - iptablesRemoveForwardAllowCross(AF_INET, network->def->bridge); -err9: - iptablesRemoveForwardRejectIn(AF_INET, network->def->bridge); -err8: - iptablesRemoveForwardRejectOut(AF_INET, network->def->bridge); -err7: - if (ipv4def && ipv4def->tftproot) { - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 69); - } -err6: - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 53); -err5: - iptablesRemoveTcpInput(AF_INET, network->def->bridge, 53); -err4: - iptablesRemoveUdpOutput(AF_INET, network->def->bridge, 68); -err3: - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 67); -err2: - iptablesRemoveTcpInput(AF_INET, network->def->bridge, 67); -err1: - return -1; + if (ipv4def) + iptablesAddOutputFixUdpChecksum(fw, network->def->bridge, 68); } static void -networkRemoveGeneralFirewallRules(virNetworkObjPtr network) +networkRemoveChecksumFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network) { size_t i; virNetworkIpDefPtr ipv4def; - networkRemoveGeneralIp6tablesRules(network); - + /* First look for first IPv4 address that has dhcp or tftpboot defined. */ + /* We support dhcp config on 1 IPv4 interface only. */ for (i = 0; (ipv4def = virNetworkDefGetIpByIndex(network->def, AF_INET, i)); i++) { - if (ipv4def->nranges || ipv4def->nhosts || ipv4def->tftproot) + if (ipv4def->nranges || ipv4def->nhosts) break; } - iptablesRemoveForwardAllowCross(AF_INET, network->def->bridge); - iptablesRemoveForwardRejectIn(AF_INET, network->def->bridge); - iptablesRemoveForwardRejectOut(AF_INET, network->def->bridge); - if (ipv4def && ipv4def->tftproot) { - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 69); - } - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 53); - iptablesRemoveTcpInput(AF_INET, network->def->bridge, 53); - if (ipv4def && (ipv4def->nranges || ipv4def->nhosts)) { - iptablesRemoveOutputFixUdpChecksum(network->def->bridge, 68); - } - iptablesRemoveUdpOutput(AF_INET, network->def->bridge, 68); - iptablesRemoveUdpInput(AF_INET, network->def->bridge, 67); - iptablesRemoveTcpInput(AF_INET, network->def->bridge, 67); + if (ipv4def) + iptablesRemoveOutputFixUdpChecksum(fw, network->def->bridge, 68); } static int -networkAddIpSpecificFirewallRules(virNetworkObjPtr network, +networkAddIpSpecificFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { /* NB: in the case of IPv6, routing rules are added when the @@ -714,70 +575,74 @@ networkAddIpSpecificFirewallRules(virNetworkObjPtr network, if (network->def->forward.type == VIR_NETWORK_FORWARD_NAT) { if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) - return networkAddMasqueradingFirewallRules(network, ipdef); + return networkAddMasqueradingFirewallRules(fw, network, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) - return networkAddRoutingFirewallRules(network, ipdef); + return networkAddRoutingFirewallRules(fw, network, ipdef); } else if (network->def->forward.type == VIR_NETWORK_FORWARD_ROUTE) { - return networkAddRoutingFirewallRules(network, ipdef); + return networkAddRoutingFirewallRules(fw, network, ipdef); } return 0; } -static void -networkRemoveIpSpecificFirewallRules(virNetworkObjPtr network, +static int +networkRemoveIpSpecificFirewallRules(virFirewallPtr fw, + virNetworkObjPtr network, virNetworkIpDefPtr ipdef) { if (network->def->forward.type == VIR_NETWORK_FORWARD_NAT) { if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) - networkRemoveMasqueradingFirewallRules(network, ipdef); + return networkRemoveMasqueradingFirewallRules(fw, network, ipdef); else if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6)) - networkRemoveRoutingFirewallRules(network, ipdef); + return networkRemoveRoutingFirewallRules(fw, network, ipdef); } else if (network->def->forward.type == VIR_NETWORK_FORWARD_ROUTE) { - networkRemoveRoutingFirewallRules(network, ipdef); + return networkRemoveRoutingFirewallRules(fw, network, ipdef); } + return 0; } /* Add all rules for all ip addresses (and general rules) on a network */ int networkAddFirewallRules(virNetworkObjPtr network) { - size_t i, j; + size_t i; virNetworkIpDefPtr ipdef; - virErrorPtr orig_error; + virFirewallPtr fw = NULL; + int ret = -1; - /* Add "once per network" rules */ - if (networkAddGeneralFirewallRules(network) < 0) - return -1; + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, 0); + + networkAddGeneralFirewallRules(fw, network); for (i = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); i++) { - /* Add address-specific iptables rules */ - if (networkAddIpSpecificFirewallRules(network, ipdef) < 0) { - goto err; - } + if (networkAddIpSpecificFirewallRules(fw, network, ipdef) < 0) + goto cleanup; } - return 0; -err: - /* store the previous error message before attempting removal of rules */ - orig_error = virSaveLastError(); + virFirewallStartRollback(fw, 0); - /* The final failed call to networkAddIpSpecificFirewallRules will - * have removed any rules it created, but we need to remove those - * added for previous IP addresses. - */ - for (j = 0; j < i; j++) { - if ((ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, j))) - networkRemoveIpSpecificFirewallRules(network, ipdef); + for (i = 0; + (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); + i++) { + if (networkRemoveIpSpecificFirewallRules(fw, network, ipdef) < 0) + goto cleanup; } - networkRemoveGeneralFirewallRules(network); + networkRemoveGeneralFirewallRules(fw, network); + + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); + networkAddChecksumFirewallRules(fw, network); + + if (virFirewallApply(fw) < 0) + goto cleanup; - /* return the original error */ - virSetError(orig_error); - virFreeError(orig_error); - return -1; + ret = 0; + cleanup: + virFirewallFree(fw); + return ret; } /* Remove all rules for all ip addresses (and general rules) on a network */ @@ -785,11 +650,25 @@ void networkRemoveFirewallRules(virNetworkObjPtr network) { size_t i; virNetworkIpDefPtr ipdef; + virFirewallPtr fw = NULL; + + fw = virFirewallNew(); + + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); + networkRemoveChecksumFirewallRules(fw, network); + + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); for (i = 0; (ipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, i)); i++) { - networkRemoveIpSpecificFirewallRules(network, ipdef); + if (networkRemoveIpSpecificFirewallRules(fw, network, ipdef) < 0) + goto cleanup; } - networkRemoveGeneralFirewallRules(network); + networkRemoveGeneralFirewallRules(fw, network); + + virFirewallApply(fw); + + cleanup: + virFirewallFree(fw); } diff --git a/src/util/viriptables.c b/src/util/viriptables.c index 45f7789..bd34176 100644 --- a/src/util/viriptables.c +++ b/src/util/viriptables.c @@ -52,56 +52,6 @@ bool iptables_supports_xlock = false; -#if HAVE_FIREWALLD -static char *firewall_cmd_path = NULL; -#endif - -static int -virIpTablesOnceInit(void) -{ - virCommandPtr cmd; - int status; - -#if HAVE_FIREWALLD - firewall_cmd_path = virFindFileInPath("firewall-cmd"); - if (!firewall_cmd_path) { - VIR_INFO("firewall-cmd not found on system. " - "firewalld support disabled for iptables."); - } else { - cmd = virCommandNew(firewall_cmd_path); - - virCommandAddArgList(cmd, "--state", NULL); - /* don't log non-zero status */ - if (virCommandRun(cmd, &status) < 0 || status != 0) { - VIR_INFO("firewall-cmd found but disabled for iptables"); - VIR_FREE(firewall_cmd_path); - firewall_cmd_path = NULL; - } else { - VIR_INFO("using firewalld for iptables commands"); - } - virCommandFree(cmd); - } - - if (firewall_cmd_path) - return 0; - -#endif - - cmd = virCommandNew(IPTABLES_PATH); - virCommandAddArgList(cmd, "-w", "-L", "-n", NULL); - /* don't log non-zero status */ - if (virCommandRun(cmd, &status) < 0 || status != 0) { - VIR_INFO("xtables locking not supported by your iptables"); - } else { - VIR_INFO("using xtables locking for iptables"); - iptables_supports_xlock = true; - } - virCommandFree(cmd); - return 0; -} - -VIR_ONCE_GLOBAL_INIT(virIpTables) - #define VIR_FROM_THIS VIR_FROM_NONE enum { @@ -109,63 +59,10 @@ enum { REMOVE }; -static virCommandPtr -iptablesCommandNew(const char *table, const char *chain, int family, int action) -{ - virCommandPtr cmd = NULL; - virIpTablesInitialize(); -#if HAVE_FIREWALLD - if (firewall_cmd_path) { - cmd = virCommandNew(firewall_cmd_path); - virCommandAddArgList(cmd, "--direct", "--passthrough", - (family == AF_INET6) ? "ipv6" : "ipv4", NULL); - } -#endif - - if (cmd == NULL) { - cmd = virCommandNew((family == AF_INET6) - ? IP6TABLES_PATH : IPTABLES_PATH); - if (iptables_supports_xlock) - virCommandAddArgList(cmd, "-w", NULL); - } - - virCommandAddArgList(cmd, "--table", table, - action == ADD ? "--insert" : "--delete", - chain, NULL); - return cmd; -} - -static int -iptablesCommandRunAndFree(virCommandPtr cmd) -{ - int ret; - ret = virCommandRun(cmd, NULL); - virCommandFree(cmd); - return ret; -} - -static int ATTRIBUTE_SENTINEL -iptablesAddRemoveRule(const char *table, const char *chain, int family, int action, - const char *arg, ...) -{ - va_list args; - virCommandPtr cmd = NULL; - const char *s; - - cmd = iptablesCommandNew(table, chain, family, action); - virCommandAddArg(cmd, arg); - - va_start(args, arg); - while ((s = va_arg(args, const char *))) - virCommandAddArg(cmd, s); - va_end(args); - - return iptablesCommandRunAndFree(cmd); -} - -static int -iptablesInput(int family, +static void +iptablesInput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port, int action, @@ -176,18 +73,19 @@ iptablesInput(int family, snprintf(portstr, sizeof(portstr), "%d", port); portstr[sizeof(portstr) - 1] = '\0'; - return iptablesAddRemoveRule("filter", "INPUT", - family, - action, - "--in-interface", iface, - "--protocol", tcp ? "tcp" : "udp", - "--destination-port", portstr, - "--jump", "ACCEPT", - NULL); + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "INPUT", + "--in-interface", iface, + "--protocol", tcp ? "tcp" : "udp", + "--destination-port", portstr, + "--jump", "ACCEPT", + NULL); } -static int -iptablesOutput(int family, +static void +iptablesOutput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port, int action, @@ -198,14 +96,14 @@ iptablesOutput(int family, snprintf(portstr, sizeof(portstr), "%d", port); portstr[sizeof(portstr) - 1] = '\0'; - return iptablesAddRemoveRule("filter", "OUTPUT", - family, - action, - "--out-interface", iface, - "--protocol", tcp ? "tcp" : "udp", - "--destination-port", portstr, - "--jump", "ACCEPT", - NULL); + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "OUTPUT", + "--out-interface", iface, + "--protocol", tcp ? "tcp" : "udp", + "--destination-port", portstr, + "--jump", "ACCEPT", + NULL); } /** @@ -216,16 +114,14 @@ iptablesOutput(int family, * * Add an input to the IP table allowing access to the given @port on * the given @iface interface for TCP packets - * - * Returns 0 in case of success or an error code in case of error */ - -int -iptablesAddTcpInput(int family, +void +iptablesAddTcpInput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesInput(family, iface, port, ADD, 1); + iptablesInput(fw, layer, iface, port, ADD, 1); } /** @@ -236,15 +132,14 @@ iptablesAddTcpInput(int family, * * Removes an input from the IP table, hence forbidding access to the given * @port on the given @iface interface for TCP packets - * - * Returns 0 in case of success or an error code in case of error */ -int -iptablesRemoveTcpInput(int family, +void +iptablesRemoveTcpInput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesInput(family, iface, port, REMOVE, 1); + iptablesInput(fw, layer, iface, port, REMOVE, 1); } /** @@ -255,16 +150,14 @@ iptablesRemoveTcpInput(int family, * * Add an input to the IP table allowing access to the given @port on * the given @iface interface for UDP packets - * - * Returns 0 in case of success or an error code in case of error */ - -int -iptablesAddUdpInput(int family, +void +iptablesAddUdpInput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesInput(family, iface, port, ADD, 0); + iptablesInput(fw, layer, iface, port, ADD, 0); } /** @@ -275,15 +168,14 @@ iptablesAddUdpInput(int family, * * Removes an input from the IP table, hence forbidding access to the given * @port on the given @iface interface for UDP packets - * - * Returns 0 in case of success or an error code in case of error */ -int -iptablesRemoveUdpInput(int family, +void +iptablesRemoveUdpInput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesInput(family, iface, port, REMOVE, 0); + return iptablesInput(fw, layer, iface, port, REMOVE, 0); } /** @@ -294,16 +186,14 @@ iptablesRemoveUdpInput(int family, * * Add an output to the IP table allowing access to the given @port from * the given @iface interface for UDP packets - * - * Returns 0 in case of success or an error code in case of error */ - -int -iptablesAddUdpOutput(int family, +void +iptablesAddUdpOutput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesOutput(family, iface, port, ADD, 0); + iptablesOutput(fw, layer, iface, port, ADD, 0); } /** @@ -314,15 +204,14 @@ iptablesAddUdpOutput(int family, * * Removes an output from the IP table, hence forbidding access to the given * @port from the given @iface interface for UDP packets - * - * Returns 0 in case of success or an error code in case of error */ -int -iptablesRemoveUdpOutput(int family, +void +iptablesRemoveUdpOutput(virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port) { - return iptablesOutput(family, iface, port, REMOVE, 0); + iptablesOutput(fw, layer, iface, port, REMOVE, 0); } @@ -362,34 +251,40 @@ static char *iptablesFormatNetwork(virSocketAddr *netaddr, * to proceed to WAN */ static int -iptablesForwardAllowOut(virSocketAddr *netaddr, +iptablesForwardAllowOut(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { - int ret; char *networkstr; - virCommandPtr cmd = NULL; + virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; - cmd = iptablesCommandNew("filter", "FORWARD", - VIR_SOCKET_ADDR_FAMILY(netaddr), - action); - virCommandAddArgList(cmd, - "--source", networkstr, - "--in-interface", iface, NULL); - if (physdev && physdev[0]) - virCommandAddArgList(cmd, "--out-interface", physdev, NULL); - - virCommandAddArgList(cmd, "--jump", "ACCEPT", NULL); + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--source", networkstr, + "--in-interface", iface, + "--out-interface", physdev, + "--jump", "ACCEPT", + NULL); + else + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--source", networkstr, + "--in-interface", iface, + "--jump", "ACCEPT", + NULL); - ret = iptablesCommandRunAndFree(cmd); VIR_FREE(networkstr); - return ret; + return 0; } /** @@ -406,12 +301,13 @@ iptablesForwardAllowOut(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesAddForwardAllowOut(virSocketAddr *netaddr, +iptablesAddForwardAllowOut(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, ADD); + return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, ADD); } /** @@ -428,12 +324,13 @@ iptablesAddForwardAllowOut(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesRemoveForwardAllowOut(virSocketAddr *netaddr, +iptablesRemoveForwardAllowOut(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, REMOVE); + return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, REMOVE); } @@ -441,42 +338,44 @@ iptablesRemoveForwardAllowOut(virSocketAddr *netaddr, * and associated with an existing connection */ static int -iptablesForwardAllowRelatedIn(virSocketAddr *netaddr, +iptablesForwardAllowRelatedIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { - int ret; + virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; char *networkstr; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; - if (physdev && physdev[0]) { - ret = iptablesAddRemoveRule("filter", "FORWARD", - VIR_SOCKET_ADDR_FAMILY(netaddr), - action, - "--destination", networkstr, - "--in-interface", physdev, - "--out-interface", iface, - "--match", "conntrack", - "--ctstate", "ESTABLISHED,RELATED", - "--jump", "ACCEPT", - NULL); - } else { - ret = iptablesAddRemoveRule("filter", "FORWARD", - VIR_SOCKET_ADDR_FAMILY(netaddr), - action, - "--destination", networkstr, - "--out-interface", iface, - "--match", "conntrack", - "--ctstate", "ESTABLISHED,RELATED", - "--jump", "ACCEPT", - NULL); - } + if (physdev && physdev[0]) + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--destination", networkstr, + "--in-interface", physdev, + "--out-interface", iface, + "--match", "conntrack", + "--ctstate", "ESTABLISHED,RELATED", + "--jump", "ACCEPT", + NULL); + else + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--destination", networkstr, + "--out-interface", iface, + "--match", "conntrack", + "--ctstate", "ESTABLISHED,RELATED", + "--jump", "ACCEPT", + NULL); + VIR_FREE(networkstr); - return ret; + return 0; } /** @@ -493,12 +392,13 @@ iptablesForwardAllowRelatedIn(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesAddForwardAllowRelatedIn(virSocketAddr *netaddr, +iptablesAddForwardAllowRelatedIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, ADD); + return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, ADD); } /** @@ -515,49 +415,51 @@ iptablesAddForwardAllowRelatedIn(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesRemoveForwardAllowRelatedIn(virSocketAddr *netaddr, +iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, REMOVE); + return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, REMOVE); } /* Allow all traffic destined to the bridge, with a valid network address */ static int -iptablesForwardAllowIn(virSocketAddr *netaddr, +iptablesForwardAllowIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { - int ret; + virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? + VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; char *networkstr; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; - if (physdev && physdev[0]) { - ret = iptablesAddRemoveRule("filter", "FORWARD", - VIR_SOCKET_ADDR_FAMILY(netaddr), - action, - "--destination", networkstr, - "--in-interface", physdev, - "--out-interface", iface, - "--jump", "ACCEPT", - NULL); - } else { - ret = iptablesAddRemoveRule("filter", "FORWARD", - VIR_SOCKET_ADDR_FAMILY(netaddr), - action, - "--destination", networkstr, - "--out-interface", iface, - "--jump", "ACCEPT", - NULL); - } + if (physdev && physdev[0]) + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--destination", networkstr, + "--in-interface", physdev, + "--out-interface", iface, + "--jump", "ACCEPT", + NULL); + else + virFirewallAddRule(fw, layer, + "--table", "filter", + action == ADD ? "--insert" : "--delete", "FORWARD", + "--destination", networkstr, + "--out-interface", iface, + "--jump", "ACCEPT", + NULL); VIR_FREE(networkstr); - return ret; + return 0; } /** @@ -574,12 +476,13 @@ iptablesForwardAllowIn(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesAddForwardAllowIn(virSocketAddr *netaddr, +iptablesAddForwardAllowIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, ADD); + return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, ADD); } /** @@ -596,30 +499,13 @@ iptablesAddForwardAllowIn(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesRemoveForwardAllowIn(virSocketAddr *netaddr, +iptablesRemoveForwardAllowIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev) { - return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, REMOVE); -} - - -/* Allow all traffic between guests on the same bridge, - * with a valid network address - */ -static int -iptablesForwardAllowCross(int family, - const char *iface, - int action) -{ - return iptablesAddRemoveRule("filter", "FORWARD", - family, - action, - "--in-interface", iface, - "--out-interface", iface, - "--jump", "ACCEPT", - NULL); + return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, REMOVE); } /** @@ -633,11 +519,18 @@ iptablesForwardAllowCross(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesAddForwardAllowCross(int family, +void +iptablesAddForwardAllowCross(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardAllowCross(family, iface, ADD); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--insert", "FORWARD", + "--in-interface", iface, + "--out-interface", iface, + "--jump", "ACCEPT", + NULL); } /** @@ -651,28 +544,18 @@ iptablesAddForwardAllowCross(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesRemoveForwardAllowCross(int family, +void +iptablesRemoveForwardAllowCross(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardAllowCross(family, iface, REMOVE); -} - - -/* Drop all traffic trying to forward from the bridge. - * ie the bridge is the in interface - */ -static int -iptablesForwardRejectOut(int family, - const char *iface, - int action) -{ - return iptablesAddRemoveRule("filter", "FORWARD", - family, - action, - "--in-interface", iface, - "--jump", "REJECT", - NULL); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--delete", "FORWARD", + "--in-interface", iface, + "--out-interface", iface, + "--jump", "ACCEPT", + NULL); } /** @@ -685,11 +568,17 @@ iptablesForwardRejectOut(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesAddForwardRejectOut(int family, +void +iptablesAddForwardRejectOut(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardRejectOut(family, iface, ADD); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--insert", "FORWARD", + "--in-interface", iface, + "--jump", "REJECT", + NULL); } /** @@ -702,32 +591,20 @@ iptablesAddForwardRejectOut(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesRemoveForwardRejectOut(int family, +void +iptablesRemoveForwardRejectOut(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardRejectOut(family, iface, REMOVE); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--delete", "FORWARD", + "--in-interface", iface, + "--jump", "REJECT", + NULL); } - - -/* Drop all traffic trying to forward to the bridge. - * ie the bridge is the out interface - */ -static int -iptablesForwardRejectIn(int family, - const char *iface, - int action) -{ - return iptablesAddRemoveRule("filter", "FORWARD", - family, - action, - "--out-interface", iface, - "--jump", "REJECT", - NULL); -} - /** * iptablesAddForwardRejectIn: * @ctx: pointer to the IP table context @@ -738,11 +615,17 @@ iptablesForwardRejectIn(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesAddForwardRejectIn(int family, +void +iptablesAddForwardRejectIn(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardRejectIn(family, iface, ADD); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--insert", "FORWARD", + "--out-interface", iface, + "--jump", "REJECT", + NULL); } /** @@ -755,11 +638,17 @@ iptablesAddForwardRejectIn(int family, * * Returns 0 in case of success or an error code otherwise */ -int -iptablesRemoveForwardRejectIn(int family, +void +iptablesRemoveForwardRejectIn(virFirewallPtr fw, + virFirewallLayer layer, const char *iface) { - return iptablesForwardRejectIn(family, iface, REMOVE); + virFirewallAddRule(fw, layer, + "--table", "filter", + "--delete", "FORWARD", + "--out-interface", iface, + "--jump", "REJECT", + NULL); } @@ -767,7 +656,8 @@ iptablesRemoveForwardRejectIn(int family, * with the bridge */ static int -iptablesForwardMasquerade(virSocketAddr *netaddr, +iptablesForwardMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, virSocketAddrRangePtr addr, @@ -781,7 +671,7 @@ iptablesForwardMasquerade(virSocketAddr *netaddr, char *addrEndStr = NULL; char *portRangeStr = NULL; char *natRangeStr = NULL; - virCommandPtr cmd = NULL; + virFirewallRulePtr rule; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; @@ -803,16 +693,25 @@ iptablesForwardMasquerade(virSocketAddr *netaddr, } } - cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action); - virCommandAddArgList(cmd, "--source", networkstr, NULL); - - if (protocol && protocol[0]) - virCommandAddArgList(cmd, "-p", protocol, NULL); - - virCommandAddArgList(cmd, "!", "--destination", networkstr, NULL); + if (protocol && protocol[0]) { + rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "--table", "nat", + action == ADD ? "--insert" : "--delete", "POSTROUTING", + "--source", networkstr, + "-p", protocol, + "!", "--destination", networkstr, + NULL); + } else { + rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "--table", "nat", + action == ADD ? "--insert" : "--delete", "POSTROUTING", + "--source", networkstr, + "!", "--destination", networkstr, + NULL); + } if (physdev && physdev[0]) - virCommandAddArgList(cmd, "--out-interface", physdev, NULL); + virFirewallRuleAddArgList(fw, rule, "--out-interface", physdev, NULL); if (protocol && protocol[0]) { if (port->start == 0 && port->end == 0) { @@ -846,18 +745,20 @@ iptablesForwardMasquerade(virSocketAddr *netaddr, if (r < 0) goto cleanup; - virCommandAddArgList(cmd, "--jump", "SNAT", + virFirewallRuleAddArgList(fw, rule, + "--jump", "SNAT", "--to-source", natRangeStr, NULL); - } else { - virCommandAddArgList(cmd, "--jump", "MASQUERADE", NULL); + } else { + virFirewallRuleAddArgList(fw, rule, + "--jump", "MASQUERADE", NULL); - if (portRangeStr && portRangeStr[0]) - virCommandAddArgList(cmd, "--to-ports", &portRangeStr[1], NULL); - } + if (portRangeStr && portRangeStr[0]) + virFirewallRuleAddArgList(fw, rule, + "--to-ports", &portRangeStr[1], NULL); + } - ret = virCommandRun(cmd, NULL); + ret = 0; cleanup: - virCommandFree(cmd); VIR_FREE(networkstr); VIR_FREE(addrStartStr); VIR_FREE(addrEndStr); @@ -880,14 +781,15 @@ cleanup: * Returns 0 in case of success or an error code otherwise */ int -iptablesAddForwardMasquerade(virSocketAddr *netaddr, +iptablesAddForwardMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, virSocketAddrRangePtr addr, virPortRangePtr port, const char *protocol) { - return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port, + return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port, protocol, ADD); } @@ -905,14 +807,15 @@ iptablesAddForwardMasquerade(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise */ int -iptablesRemoveForwardMasquerade(virSocketAddr *netaddr, +iptablesRemoveForwardMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, virSocketAddrRangePtr addr, virPortRangePtr port, const char *protocol) { - return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port, + return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port, protocol, REMOVE); } @@ -921,7 +824,8 @@ iptablesRemoveForwardMasquerade(virSocketAddr *netaddr, * if said traffic targets @destaddr. */ static int -iptablesForwardDontMasquerade(virSocketAddr *netaddr, +iptablesForwardDontMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, const char *destaddr, @@ -929,7 +833,6 @@ iptablesForwardDontMasquerade(virSocketAddr *netaddr, { int ret = -1; char *networkstr = NULL; - virCommandPtr cmd = NULL; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; @@ -942,16 +845,26 @@ iptablesForwardDontMasquerade(virSocketAddr *netaddr, goto cleanup; } - cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action); - if (physdev && physdev[0]) - virCommandAddArgList(cmd, "--out-interface", physdev, NULL); - - virCommandAddArgList(cmd, "--source", networkstr, - "--destination", destaddr, "--jump", "RETURN", NULL); - ret = virCommandRun(cmd, NULL); -cleanup: - virCommandFree(cmd); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "--table", "nat", + action == ADD ? "--insert" : "--delete", "POSTROUTING", + "--out-interface", physdev, + "--source", networkstr, + "--destination", destaddr, + "--jump", "RETURN", + NULL); + else + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "--table", "nat", + action == ADD ? "--insert" : "--delete", "POSTROUTING", + "--source", networkstr, + "--destination", destaddr, + "--jump", "RETURN", + NULL); + + ret = 0; + cleanup: VIR_FREE(networkstr); return ret; } @@ -971,12 +884,13 @@ cleanup: * Returns 0 in case of success or an error code otherwise. */ int -iptablesAddDontMasquerade(virSocketAddr *netaddr, +iptablesAddDontMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, const char *destaddr) { - return iptablesForwardDontMasquerade(netaddr, prefix, physdev, destaddr, + return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr, ADD); } @@ -995,18 +909,20 @@ iptablesAddDontMasquerade(virSocketAddr *netaddr, * Returns 0 in case of success or an error code otherwise. */ int -iptablesRemoveDontMasquerade(virSocketAddr *netaddr, +iptablesRemoveDontMasquerade(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, const char *destaddr) { - return iptablesForwardDontMasquerade(netaddr, prefix, physdev, destaddr, + return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr, REMOVE); } -static int -iptablesOutputFixUdpChecksum(const char *iface, +static void +iptablesOutputFixUdpChecksum(virFirewallPtr fw, + const char *iface, int port, int action) { @@ -1015,14 +931,14 @@ iptablesOutputFixUdpChecksum(const char *iface, snprintf(portstr, sizeof(portstr), "%d", port); portstr[sizeof(portstr) - 1] = '\0'; - return iptablesAddRemoveRule("mangle", "POSTROUTING", - AF_INET, - action, - "--out-interface", iface, - "--protocol", "udp", - "--destination-port", portstr, - "--jump", "CHECKSUM", "--checksum-fill", - NULL); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4, + "--table", "mangle", + action == ADD ? "--insert" : "--delete", "POSTROUTING", + "--out-interface", iface, + "--protocol", "udp", + "--destination-port", portstr, + "--jump", "CHECKSUM", "--checksum-fill", + NULL); } /** @@ -1035,16 +951,13 @@ iptablesOutputFixUdpChecksum(const char *iface, * checksum of packets with the given destination @port. * the given @iface interface for TCP packets. * - * Returns 0 in case of success or an error code in case of error. - * (NB: if the system's iptables does not support checksum mangling, - * this will return an error, which should be ignored.) */ - -int -iptablesAddOutputFixUdpChecksum(const char *iface, +void +iptablesAddOutputFixUdpChecksum(virFirewallPtr fw, + const char *iface, int port) { - return iptablesOutputFixUdpChecksum(iface, port, ADD); + iptablesOutputFixUdpChecksum(fw, iface, port, ADD); } /** @@ -1055,14 +968,11 @@ iptablesAddOutputFixUdpChecksum(const char *iface, * * Removes the checksum fixup rule that was previous added with * iptablesAddOutputFixUdpChecksum. - * - * Returns 0 in case of success or an error code in case of error - * (again, if iptables doesn't support checksum fixup, this will - * return an error, which should be ignored) */ -int -iptablesRemoveOutputFixUdpChecksum(const char *iface, +void +iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw, + const char *iface, int port) { - return iptablesOutputFixUdpChecksum(iface, port, REMOVE); + iptablesOutputFixUdpChecksum(fw, iface, port, REMOVE); } diff --git a/src/util/viriptables.h b/src/util/viriptables.h index 2f9a212..9ea25fc 100644 --- a/src/util/viriptables.h +++ b/src/util/viriptables.h @@ -21,97 +21,131 @@ * Mark McLoughlin <markmc@redhat.com> */ -#ifndef __QEMUD_IPTABLES_H__ -# define __QEMUD_IPTABLES_H__ +#ifndef __VIR_IPTABLES_H__ +# define __VIR_IPTABLES_H__ # include "virsocketaddr.h" +# include "virfirewall.h" -int iptablesAddTcpInput (int family, +void iptablesAddTcpInput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesRemoveTcpInput (int family, +void iptablesRemoveTcpInput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesAddUdpInput (int family, +void iptablesAddUdpInput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesRemoveUdpInput (int family, +void iptablesRemoveUdpInput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesAddUdpOutput (int family, +void iptablesAddUdpOutput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesRemoveUdpOutput (int family, +void iptablesRemoveUdpOutput (virFirewallPtr fw, + virFirewallLayer layer, const char *iface, int port); -int iptablesAddForwardAllowOut (virSocketAddr *netaddr, +int iptablesAddForwardAllowOut (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, - const char *physdev); -int iptablesRemoveForwardAllowOut (virSocketAddr *netaddr, + const char *physdev) + ATTRIBUTE_RETURN_CHECK; +int iptablesRemoveForwardAllowOut (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, - const char *physdev); - -int iptablesAddForwardAllowRelatedIn(virSocketAddr *netaddr, - unsigned int prefix, - const char *iface, - const char *physdev); -int iptablesRemoveForwardAllowRelatedIn(virSocketAddr *netaddr, + const char *physdev) + ATTRIBUTE_RETURN_CHECK; +int iptablesAddForwardAllowRelatedIn(virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, - const char *physdev); + const char *physdev) + ATTRIBUTE_RETURN_CHECK; +int iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw, + virSocketAddr *netaddr, + unsigned int prefix, + const char *iface, + const char *physdev) + ATTRIBUTE_RETURN_CHECK; -int iptablesAddForwardAllowIn (virSocketAddr *netaddr, +int iptablesAddForwardAllowIn (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, - const char *physdev); -int iptablesRemoveForwardAllowIn (virSocketAddr *netaddr, + const char *physdev) + ATTRIBUTE_RETURN_CHECK; +int iptablesRemoveForwardAllowIn (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *iface, - const char *physdev); + const char *physdev) + ATTRIBUTE_RETURN_CHECK; -int iptablesAddForwardAllowCross (int family, +void iptablesAddForwardAllowCross (virFirewallPtr fw, + virFirewallLayer layer, const char *iface); -int iptablesRemoveForwardAllowCross (int family, +void iptablesRemoveForwardAllowCross (virFirewallPtr fw, + virFirewallLayer layer, const char *iface); -int iptablesAddForwardRejectOut (int family, +void iptablesAddForwardRejectOut (virFirewallPtr fw, + virFirewallLayer layer, const char *iface); -int iptablesRemoveForwardRejectOut (int family, +void iptablesRemoveForwardRejectOut (virFirewallPtr fw, + virFirewallLayer layer, const char *iface); -int iptablesAddForwardRejectIn (int family, +void iptablesAddForwardRejectIn (virFirewallPtr fw, + virFirewallLayer layer, const char *iface); -int iptablesRemoveForwardRejectIn (int family, +void iptablesRemoveForwardRejectIn (virFirewallPtr fw, + virFirewallLayer layery, const char *iface); -int iptablesAddForwardMasquerade (virSocketAddr *netaddr, +int iptablesAddForwardMasquerade (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, virSocketAddrRangePtr addr, virPortRangePtr port, - const char *protocol); -int iptablesRemoveForwardMasquerade (virSocketAddr *netaddr, + const char *protocol) + ATTRIBUTE_RETURN_CHECK; +int iptablesRemoveForwardMasquerade (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, virSocketAddrRangePtr addr, virPortRangePtr port, - const char *protocol); -int iptablesAddDontMasquerade (virSocketAddr *netaddr, + const char *protocol) + ATTRIBUTE_RETURN_CHECK; +int iptablesAddDontMasquerade (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, - const char *destaddr); -int iptablesRemoveDontMasquerade (virSocketAddr *netaddr, + const char *destaddr) + ATTRIBUTE_RETURN_CHECK; +int iptablesRemoveDontMasquerade (virFirewallPtr fw, + virSocketAddr *netaddr, unsigned int prefix, const char *physdev, - const char *destaddr); -int iptablesAddOutputFixUdpChecksum (const char *iface, + const char *destaddr) + ATTRIBUTE_RETURN_CHECK; +void iptablesAddOutputFixUdpChecksum (virFirewallPtr fw, + const char *iface, int port); -int iptablesRemoveOutputFixUdpChecksum (const char *iface, +void iptablesRemoveOutputFixUdpChecksum (virFirewallPtr fw, + const char *iface, int port); -#endif /* __QEMUD_IPTABLES_H__ */ +#endif /* __VIR_IPTABLES_H__ */ -- 1.8.5.3

Convert the virebtables.{c,h} files to use the new virFirewall APIs for changing ebtables rules. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/util/virebtables.c | 187 ++++++++++++------------------------------------- 1 file changed, 46 insertions(+), 141 deletions(-) diff --git a/src/util/virebtables.c b/src/util/virebtables.c index 01fb15e..8fbae98 100644 --- a/src/util/virebtables.c +++ b/src/util/virebtables.c @@ -25,65 +25,16 @@ #include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <limits.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#ifdef HAVE_PATHS_H -# include <paths.h> -#endif - #include "internal.h" #include "virebtables.h" -#include "vircommand.h" #include "viralloc.h" #include "virerror.h" -#include "virfile.h" #include "virlog.h" -#include "virthread.h" #include "virstring.h" -#include "virutil.h" +#include "virfirewall.h" #define VIR_FROM_THIS VIR_FROM_NONE -#if HAVE_FIREWALLD -static char *firewall_cmd_path = NULL; - -static int -virEbTablesOnceInit(void) -{ - firewall_cmd_path = virFindFileInPath("firewall-cmd"); - if (!firewall_cmd_path) { - VIR_INFO("firewall-cmd not found on system. " - "firewalld support disabled for ebtables."); - } else { - virCommandPtr cmd = virCommandNew(firewall_cmd_path); - - virCommandAddArgList(cmd, "--state", NULL); - if (virCommandRun(cmd, NULL) < 0) { - VIR_INFO("firewall-cmd found but disabled for ebtables"); - VIR_FREE(firewall_cmd_path); - firewall_cmd_path = NULL; - } else { - VIR_INFO("using firewalld for ebtables commands"); - } - virCommandFree(cmd); - } - return 0; -} - -VIR_ONCE_GLOBAL_INIT(virEbTables) - -#endif - struct _ebtablesContext { char *chain; @@ -94,84 +45,6 @@ enum { REMOVE, }; - -static int ATTRIBUTE_SENTINEL -ebtablesAddRemoveRule(const char *arg, ...) -{ - va_list args; - int retval = ENOMEM; - char **argv; - const char *s; - int n; - - n = 1 + /* /sbin/ebtables */ - 2 + /* --table foo */ - 2 + /* --insert bar */ - 1; /* arg */ - -#if HAVE_FIREWALLD - virEbTablesInitialize(); - if (firewall_cmd_path) - n += 3; /* --direct --passthrough eb */ -#endif - - va_start(args, arg); - while (va_arg(args, const char *)) - n++; - - va_end(args); - - if (VIR_ALLOC_N(argv, n + 1) < 0) - goto error; - - n = 0; - -#if HAVE_FIREWALLD - if (firewall_cmd_path) { - if (VIR_STRDUP(argv[n++], firewall_cmd_path) < 0) - goto error; - if (VIR_STRDUP(argv[n++], "--direct") < 0) - goto error; - if (VIR_STRDUP(argv[n++], "--passthrough") < 0) - goto error; - if (VIR_STRDUP(argv[n++], "eb") < 0) - goto error; - } else -#endif - if (VIR_STRDUP(argv[n++], EBTABLES_PATH) < 0) - goto error; - - if (VIR_STRDUP(argv[n++], arg) < 0) - goto error; - - va_start(args, arg); - - while ((s = va_arg(args, const char *))) { - if (VIR_STRDUP(argv[n++], s) < 0) { - va_end(args); - goto error; - } - } - - va_end(args); - - if (virRun((const char **)argv, NULL) < 0) { - retval = errno; - goto error; - } - - error: - if (argv) { - n = 0; - while (argv[n]) - VIR_FREE(argv[n++]); - VIR_FREE(argv); - } - - return retval; -} - - /** * ebtablesContextNew: * @@ -214,12 +87,30 @@ ebtablesContextFree(ebtablesContext *ctx) int ebtablesAddForwardPolicyReject(ebtablesContext *ctx) { - ebtablesAddRemoveRule("--new-chain", ctx->chain, NULL, - NULL); - ebtablesAddRemoveRule("--insert", "FORWARD", "--jump", - ctx->chain, NULL); - return ebtablesAddRemoveRule("-P", ctx->chain, "DROP", - NULL); + virFirewallPtr fw = NULL; + int ret = -1; + + fw = virFirewallNew(); + virFirewallStartTransaction(fw, VIR_FIREWALL_TRANSACTION_IGNORE_ERRORS); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET, + "--new-chain", ctx->chain, + NULL); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET, + "--insert", "FORWARD", + "--jump", ctx->chain, NULL); + + virFirewallStartTransaction(fw, 0); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET, + "-P", ctx->chain, "DROP", + NULL); + + if (virFirewallApply(fw) < 0) + goto cleanup; + + ret = 0; + cleanup: + virFirewallFree(fw); + return ret; } @@ -232,12 +123,26 @@ ebtablesForwardAllowIn(ebtablesContext *ctx, const char *macaddr, int action) { - return ebtablesAddRemoveRule(action == ADD ? "--insert" : "--delete", - ctx->chain, - "--in-interface", iface, - "--source", macaddr, - "--jump", "ACCEPT", - NULL); + virFirewallPtr fw = NULL; + int ret = -1; + + fw = virFirewallNew(); + virFirewallStartTransaction(fw, 0); + virFirewallAddRule(fw, VIR_FIREWALL_LAYER_ETHERNET, + action == ADD ? "--insert" : "--delete", + ctx->chain, + "--in-interface", iface, + "--source", macaddr, + "--jump", "ACCEPT", + NULL); + + if (virFirewallApply(fw) < 0) + goto cleanup; + + ret = 0; + cleanup: + virFirewallFree(fw); + return ret; } /** @@ -259,7 +164,7 @@ ebtablesAddForwardAllowIn(ebtablesContext *ctx, { char macaddr[VIR_MAC_STRING_BUFLEN]; - virMacAddrFormat(mac, macaddr); + virMacAddrFormat(mac, macaddr); return ebtablesForwardAllowIn(ctx, iface, macaddr, ADD); } -- 1.8.5.3

On 03/12/2014 07:21 AM, Daniel P. Berrange wrote:
We currently have three areas of code which deal with firewall changes. The bridge driver's iptables usage, the QEMU driver's ebtables usage for mac filters and the nwfilter code.
These all directly invoke the iptables/ebtables commands or in the case of nwfilter invoke horrible generated shell scripts. The latter in particular has always been an unpleasant design choice, but it has been made much worse by support for firewalld. We are now invoking firewall-cmd just in order to make a DBus method call to firewalld which then invokes the real *tables commands. This has a notable performance impact.
This proof of concept series introduces a new virFirewallPtr object for encapsulating all firewall changes. It provides a transactional API for making firewall changes, so the caller can define a set of rules which must either all succeed or all fail, along with a set of rules to perform rollback upon fail. It will either execute *tables commands directly or will call the DBus method for firewalld directly.
Aside from the reported huge decrease in network startup time, I love the fact that this eliminates all of the multi-layered error exit labels in the bridge driver functions that add multiple rules. It looks *much* cleaner. I was a bit confused to see IPv4 vs. IPv6 status put into a variable called "layer", since they are two different protocols at the same layer, but it made more sense when I looked at the enum definition and saw that the other choice is "ethernet"; I can't really think of a better name (unless it was made more generic, e.g. "type", but that is probably a bad idea, since "type" is used for so many different things. This seems like a reasonable initial implementation, but I think it would be very good in the longer term to replace the freeform-and-yet-platform-specific "list of strings to add to an iptables command" interface of virFirewallAddRule() with something more structured/abstract, so that the calls would be less tied to the iptables backend implementation. This might make it possible to replace the backend with something that used ipfw (or whatever *BSD is using these days), or maybe even the packet filtering in Open vSwitch (This would allow us to use Open vSwitch for virtual networks without any loss of functionality, and to support nwfilter rules for guests connected to an Open vSwitch bridge). That's likely better done after what you have here is all in and tested, though. Another thing that would be nice to consider for the future is some method of storing the list of rules that were applied (so that they could be saved as part of the domain/network status). This would permit us to delete exactly the rules that we had previously added. right now we have to assume that the libvirtd that wrote the rules we want to delete was running the same code as the current libvirtd; instead, when we add rules we should be saving all the information needed to delete those exact same rules, meaning that if the code is changed somehow, we end up trying to delete rules that don't exist, or not deleting rules that we should delete. Again, that is new functionality, so out of the scope of this series. Since this is just Proof of Concept code, I won't bother with a detailed enough look to ACK (unless you're thinking of pushing this, in which case I'll go back and look for more details), but definitely "thumbs up" :-)
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Ján Tomko
-
Laine Stump