diff --git a/src/network/networkd.c b/src/network/networkd.c index 24b3cb57af..48f6061b1f 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -93,10 +93,6 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - r = fw_ctx_new(&m->fw_ctx); - if (r < 0) - log_warning_errno(r, "Could not initialize firewall, IPMasquerade= option not available: %m"); - r = manager_start(m); if (r < 0) return log_error_errno(r, "Could not start manager: %m"); diff --git a/src/shared/firewall-util-nft.c b/src/shared/firewall-util-nft.c index 05c322d353..ecabc5fc40 100644 --- a/src/shared/firewall-util-nft.c +++ b/src/shared/firewall-util-nft.c @@ -756,9 +756,11 @@ int fw_nftables_init(FirewallContext *ctx) { if (r < 0) return r; - r = fw_nftables_init_family(nfnl, AF_INET6); - if (r < 0) - log_debug_errno(r, "Failed to init ipv6 NAT: %m"); + if (socket_ipv6_is_supported()) { + r = fw_nftables_init_family(nfnl, AF_INET6); + if (r < 0) + log_debug_errno(r, "Failed to init ipv6 NAT: %m"); + } ctx->nfnl = TAKE_PTR(nfnl); return 0; @@ -810,40 +812,6 @@ static int nft_message_add_setelem_iprange(sd_netlink_message *m, return 0; } -/* When someone runs 'nft flush ruleset' in the same net namespace - * this will also tear down the systemd nat table. - * - * Unlike iptables -t nat -F (which will remove all rules added by the - * systemd iptables backend, iptables has builtin chains that cannot be - * deleted -- the next add operation will 'just work'. - * - * In the nftables case, everything gets removed. The next add operation - * will yield -ENOENT. - * - * If we see -ENOENT on add, replay the initial table setup. - * If that works, re-do the add operation. - * - * Note that this doesn't protect against external sabotage such as a - * 'while true; nft flush ruleset;done'. There is nothing that could be - * done about that short of extending the kernel to allow tables to be - * owned by stystemd-networkd and making them non-deleteable except by - * the 'owning process'. - */ -static int fw_nftables_recreate_table(sd_netlink *nfnl, int af, sd_netlink_message **old, size_t size) { - int r = fw_nftables_init_family(nfnl, af); - - if (r != 0) - return r; - - while (size > 0) { - size_t i = --size; - - old[i] = sd_netlink_message_unref(old[i]); - } - - return 0; -} - static int nft_message_add_setelem_ip6range( sd_netlink_message *m, const union in_addr_union *source, @@ -877,14 +845,14 @@ static int nft_message_add_setelem_ip6range( #define NFT_MASQ_MSGS 3 -int fw_nftables_add_masquerade( +static int fw_nftables_add_masquerade_internal( FirewallContext *ctx, bool add, int af, const union in_addr_union *source, unsigned int source_prefixlen) { + sd_netlink_message *transaction[NFT_MASQ_MSGS] = {}; - bool retry = true; size_t tsize; int r; @@ -893,7 +861,7 @@ int fw_nftables_add_masquerade( if (af == AF_INET6 && source_prefixlen < 8) return -EINVAL; -again: + r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]); if (r < 0) return r; @@ -902,7 +870,6 @@ again: r = sd_nfnl_nft_message_new_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME); else r = sd_nfnl_nft_message_del_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME); - if (r < 0) goto out_unref; @@ -910,7 +877,6 @@ again: r = nft_message_add_setelem_iprange(transaction[tsize], source, source_prefixlen); else r = nft_message_add_setelem_ip6range(transaction[tsize], source, source_prefixlen); - if (r < 0) goto out_unref; @@ -919,26 +885,59 @@ again: r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]); if (r < 0) return r; + ++tsize; r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize); - if (retry && r == -ENOENT) { - int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize); - if (tmp == 0) { - retry = false; - goto again; - } - } - out_unref: while (tsize > 0) sd_netlink_message_unref(transaction[--tsize]); return r < 0 ? r : 0; } +int fw_nftables_add_masquerade( + FirewallContext *ctx, + bool add, + int af, + const union in_addr_union *source, + unsigned int source_prefixlen) { + + int r; + + if (!socket_ipv6_is_supported() && af == AF_INET6) + return -EOPNOTSUPP; + + r = fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen); + if (r != -ENOENT) + return r; + + /* When someone runs 'nft flush ruleset' in the same net namespace this will also tear down the + * systemd nat table. + * + * Unlike iptables -t nat -F (which will remove all rules added by the systemd iptables + * backend, iptables has builtin chains that cannot be deleted -- the next add operation will + * 'just work'. + * + * In the nftables case, everything gets removed. The next add operation will yield -ENOENT. + * + * If we see -ENOENT on add, replay the initial table setup. If that works, re-do the add + * operation. + * + * Note that this doesn't protect against external sabotage such as a + * 'while true; nft flush ruleset; done'. There is nothing that could be done about that short + * of extending the kernel to allow tables to be owned by stystemd-networkd and making them + * non-deleteable except by the 'owning process'. */ + + r = fw_nftables_init_family(ctx->nfnl, af); + if (r < 0) + return r; + + return fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen); +} + #define NFT_DNAT_MSGS 4 -int fw_nftables_add_local_dnat( +static int fw_nftables_add_local_dnat_internal( FirewallContext *ctx, bool add, int af, @@ -947,21 +946,24 @@ int fw_nftables_add_local_dnat( const union in_addr_union *remote, uint16_t remote_port, const union in_addr_union *previous_remote) { - uint32_t data[5], key[2], dlen; + sd_netlink_message *transaction[NFT_DNAT_MSGS] = {}; - bool retry = true; + static bool ipv6_supported = true; + uint32_t data[5], key[2], dlen; size_t tsize; int r; assert(add || !previous_remote); + if (!ipv6_supported && af == AF_INET6) + return -EOPNOTSUPP; + if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP)) return -EPROTONOSUPPORT; if (local_port <= 0) return -EINVAL; -again: key[0] = protocol; key[1] = htobe16(local_port); @@ -1006,9 +1008,11 @@ again: assert(tsize < NFT_DNAT_MSGS); if (add) - nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + r = nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); else - nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen); + if (r < 0) + goto out_unref; tsize++; assert(tsize < NFT_DNAT_MSGS); @@ -1019,21 +1023,48 @@ again: tsize++; assert(tsize <= NFT_DNAT_MSGS); + r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize); - - if (retry && r == -ENOENT) { - int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize); - - if (tmp == 0) { - /* table created anew; previous address already gone */ - previous_remote = NULL; - retry = false; - goto again; - } + if (r == -EOVERFLOW && af == AF_INET6) { + /* The current implementation of DNAT in systemd requires kernel's + * fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns + * -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */ + log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m"); + ipv6_supported = false; + r = -EOPNOTSUPP; } out_unref: while (tsize > 0) sd_netlink_message_unref(transaction[--tsize]); + return r < 0 ? r : 0; } + +int fw_nftables_add_local_dnat( + FirewallContext *ctx, + bool add, + int af, + int protocol, + uint16_t local_port, + const union in_addr_union *remote, + uint16_t remote_port, + const union in_addr_union *previous_remote) { + + int r; + + if (!socket_ipv6_is_supported() && af == AF_INET6) + return -EOPNOTSUPP; + + r = fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote); + if (r != -ENOENT) + return r; + + /* See comment in fw_nftables_add_masquerade(). */ + r = fw_nftables_init_family(ctx->nfnl, af); + if (r < 0) + return r; + + /* table created anew; previous address already gone */ + return fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, NULL); +} diff --git a/src/shared/firewall-util-private.h b/src/shared/firewall-util-private.h index 59e1e502fd..07e2d0bbd3 100644 --- a/src/shared/firewall-util-private.h +++ b/src/shared/firewall-util-private.h @@ -4,22 +4,27 @@ #include #include -#include "in-addr-util.h" #include "sd-netlink.h" -enum FirewallBackend { +#include "in-addr-util.h" + +typedef enum FirewallBackend { FW_BACKEND_NONE, #if HAVE_LIBIPTC FW_BACKEND_IPTABLES, #endif FW_BACKEND_NFTABLES, -}; + _FW_BACKEND_MAX, + _FW_BACKEND_INVALID = -EINVAL, +} FirewallBackend; struct FirewallContext { - enum FirewallBackend firewall_backend; + FirewallBackend backend; sd_netlink *nfnl; }; +const char *firewall_backend_to_string(FirewallBackend b) _const_; + int fw_nftables_init(FirewallContext *ctx); void fw_nftables_exit(FirewallContext *ctx); diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 3bed941127..afa3e02b45 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -7,31 +7,53 @@ #include "alloc-util.h" #include "firewall-util.h" #include "firewall-util-private.h" +#include "log.h" +#include "string-table.h" -static enum FirewallBackend firewall_backend_probe(FirewallContext *ctx) { - if (fw_nftables_init(ctx) == 0) - return FW_BACKEND_NFTABLES; +static const char * const firewall_backend_table[_FW_BACKEND_MAX] = { + [FW_BACKEND_NONE] = "none", #if HAVE_LIBIPTC - return FW_BACKEND_IPTABLES; -#else - return FW_BACKEND_NONE; + [FW_BACKEND_IPTABLES] = "iptables", #endif + [FW_BACKEND_NFTABLES] = "nftables", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(firewall_backend, FirewallBackend); + +static void firewall_backend_probe(FirewallContext *ctx) { + assert(ctx); + + if (ctx->backend != _FW_BACKEND_INVALID) + return; + + if (fw_nftables_init(ctx) >= 0) + ctx->backend = FW_BACKEND_NFTABLES; + else +#if HAVE_LIBIPTC + ctx->backend = FW_BACKEND_IPTABLES; +#else + ctx->backend = FW_BACKEND_NONE; +#endif + + if (ctx->backend != FW_BACKEND_NONE) + log_debug("Using %s as firewall backend.", firewall_backend_to_string(ctx->backend)); + else + log_debug("No firewall backend found."); } int fw_ctx_new(FirewallContext **ret) { _cleanup_free_ FirewallContext *ctx = NULL; - ctx = new0(FirewallContext, 1); + ctx = new(FirewallContext, 1); if (!ctx) return -ENOMEM; - /* could probe here. However, this means that we will load - * iptable_nat or nf_tables, both will enable connection tracking. - * - * Alternative would be to probe here but only call - * fw_ctx_new when nspawn/networkd know they will call - * fw_add_masquerade/local_dnat later anyway. - */ + *ctx = (FirewallContext) { + .backend = _FW_BACKEND_INVALID, + }; + + firewall_backend_probe(ctx); + *ret = TAKE_PTR(ctx); return 0; } @@ -40,47 +62,42 @@ FirewallContext *fw_ctx_free(FirewallContext *ctx) { if (!ctx) return NULL; - if (ctx->firewall_backend == FW_BACKEND_NFTABLES) - fw_nftables_exit(ctx); + fw_nftables_exit(ctx); return mfree(ctx); } int fw_add_masquerade( - FirewallContext **fw_ctx, + FirewallContext **ctx, bool add, int af, const union in_addr_union *source, unsigned source_prefixlen) { - FirewallContext *ctx; + int r; - if (!*fw_ctx) { - r = fw_ctx_new(fw_ctx); + assert(ctx); + + if (!*ctx) { + r = fw_ctx_new(ctx); if (r < 0) return r; } - ctx = *fw_ctx; - if (ctx->firewall_backend == FW_BACKEND_NONE) - ctx->firewall_backend = firewall_backend_probe(ctx); - - switch (ctx->firewall_backend) { - case FW_BACKEND_NONE: - return -EOPNOTSUPP; + switch ((*ctx)->backend) { #if HAVE_LIBIPTC case FW_BACKEND_IPTABLES: return fw_iptables_add_masquerade(add, af, source, source_prefixlen); #endif case FW_BACKEND_NFTABLES: - return fw_nftables_add_masquerade(ctx, add, af, source, source_prefixlen); + return fw_nftables_add_masquerade(*ctx, add, af, source, source_prefixlen); + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } int fw_add_local_dnat( - FirewallContext **fw_ctx, + FirewallContext **ctx, bool add, int af, int protocol, @@ -88,28 +105,25 @@ int fw_add_local_dnat( const union in_addr_union *remote, uint16_t remote_port, const union in_addr_union *previous_remote) { - FirewallContext *ctx; - if (!*fw_ctx) { - int ret = fw_ctx_new(fw_ctx); - if (ret < 0) - return ret; + int r; + + assert(ctx); + + if (!*ctx) { + r = fw_ctx_new(ctx); + if (r < 0) + return r; } - ctx = *fw_ctx; - if (ctx->firewall_backend == FW_BACKEND_NONE) - ctx->firewall_backend = firewall_backend_probe(ctx); - - switch (ctx->firewall_backend) { - case FW_BACKEND_NONE: - return -EOPNOTSUPP; - case FW_BACKEND_NFTABLES: - return fw_nftables_add_local_dnat(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote); + switch ((*ctx)->backend) { #if HAVE_LIBIPTC case FW_BACKEND_IPTABLES: return fw_iptables_add_local_dnat(add, af, protocol, local_port, remote, remote_port, previous_remote); #endif + case FW_BACKEND_NFTABLES: + return fw_nftables_add_local_dnat(*ctx, add, af, protocol, local_port, remote, remote_port, previous_remote); + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h index 5180b429d3..7725a5e58d 100644 --- a/src/shared/firewall-util.h +++ b/src/shared/firewall-util.h @@ -9,19 +9,19 @@ typedef struct FirewallContext FirewallContext; int fw_ctx_new(FirewallContext **ret); -FirewallContext *fw_ctx_free(FirewallContext *fw_ctx); +FirewallContext *fw_ctx_free(FirewallContext *ctx); DEFINE_TRIVIAL_CLEANUP_FUNC(FirewallContext *, fw_ctx_free); int fw_add_masquerade( - FirewallContext **fw_ctx, + FirewallContext **ctx, bool add, int af, const union in_addr_union *source, unsigned source_prefixlen); int fw_add_local_dnat( - FirewallContext **fw_ctx, + FirewallContext **ctx, bool add, int af, int protocol, diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c index 8d69f192d8..abc28d500d 100644 --- a/src/test/test-firewall-util.c +++ b/src/test/test-firewall-util.c @@ -1,114 +1,110 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "firewall-util.h" +#include "firewall-util-private.h" #include "log.h" #include "random-util.h" +#include "socket-util.h" #include "tests.h" -#define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))} -#define MAKE_IN6_ADDR_UNION(str, u) assert_se(in_addr_from_string(AF_INET6, str, u) >= 0) - -static void test_v6(FirewallContext **ctx) { - union in_addr_union u = {}, u2 = {}; +static void test_v6(FirewallContext *ctx) { + union in_addr_union u1, u2, u3; uint8_t prefixlen; int r; - MAKE_IN6_ADDR_UNION("dead::beef", &u); + log_info("/* %s(backend=%s) */", __func__, firewall_backend_to_string(ctx->backend)); - r = fw_add_masquerade(ctx, true, AF_INET6, &u, 128); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); + if (!socket_ipv6_is_supported()) + return log_info("IPv6 is not supported by kernel, skipping tests."); - r = fw_add_masquerade(ctx, false, AF_INET6, &u, 128); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); + assert_se(in_addr_from_string(AF_INET6, "dead::beef", &u1) >= 0); + assert_se(in_addr_from_string(AF_INET6, "1c3::c01d", &u2) >= 0); - r = fw_add_masquerade(ctx, true, AF_INET6, &u, 64); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); + prefixlen = random_u64_range(128 + 1 - 8) + 8; + pseudo_random_bytes(&u3, sizeof(u3)); - r = fw_add_masquerade(ctx, false, AF_INET6, &u, 64); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); + assert_se(fw_add_masquerade(&ctx, true, AF_INET6, &u1, 128) >= 0); + assert_se(fw_add_masquerade(&ctx, false, AF_INET6, &u1, 128) >= 0); + assert_se(fw_add_masquerade(&ctx, true, AF_INET6, &u1, 64) >= 0); + assert_se(fw_add_masquerade(&ctx, false, AF_INET6, &u1, 64) >= 0); + assert_se(fw_add_masquerade(&ctx, true, AF_INET6, &u3, prefixlen) >= 0); + assert_se(fw_add_masquerade(&ctx, false, AF_INET6, &u3, prefixlen) >= 0); - r = fw_add_local_dnat(ctx, true, AF_INET6, IPPROTO_TCP, 4711, &u, 815, NULL); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); + r = fw_add_local_dnat(&ctx, true, AF_INET6, IPPROTO_TCP, 4711, &u1, 815, NULL); + if (r == -EOPNOTSUPP) { + log_info("IPv6 DNAT seems not supported, skipping the following tests."); + return; + } + assert_se(r >= 0); - MAKE_IN6_ADDR_UNION("1c3::c01d", &u2); - r = fw_add_local_dnat(ctx, true, AF_INET6, IPPROTO_TCP, 4711, &u2, 815, &u); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); + assert_se(fw_add_local_dnat(&ctx, true, AF_INET6, IPPROTO_TCP, 4711, &u2, 815, &u1) >= 0); + assert_se(fw_add_local_dnat(&ctx, false, AF_INET6, IPPROTO_TCP, 4711, &u2, 815, NULL) >= 0); - r = fw_add_local_dnat(ctx, false, AF_INET6, IPPROTO_TCP, 4711, &u2, 815, NULL); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); +} - prefixlen = random_u32() % (128 + 1 - 8); - prefixlen += 8; - pseudo_random_bytes(&u, sizeof(u)); +static union in_addr_union *parse_addr(const char *str, union in_addr_union *u) { + assert(str); + assert_se(in_addr_from_string(AF_INET, str, u) >= 0); + return u; +} - r = fw_add_masquerade(ctx, true, AF_INET6, &u, prefixlen); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); +static bool test_v4(FirewallContext *ctx) { + union in_addr_union u, v; + int r; - r = fw_add_masquerade(ctx, false, AF_INET6, &u, prefixlen); - if (r < 0) - log_error_errno(r, "Failed to modify ipv6 firewall: %m"); + log_info("/* %s(backend=%s) */", __func__, firewall_backend_to_string(ctx->backend)); + + assert_se(fw_add_masquerade(&ctx, true, AF_INET, NULL, 0) == -EINVAL); + assert_se(fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.1.2.0", &u), 0) == -EINVAL); + + r = fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.1.2.3", &u), 32); + if (r < 0) { + bool ignore = IN_SET(r, -EPERM, -EOPNOTSUPP, -ENOPROTOOPT); + + log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r, + "Failed to add IPv4 masquerade%s: %m", + ignore ? ", skipping following tests" : ""); + + if (ignore) + return false; + } + assert(r >= 0); + + assert_se(fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.0.2.0", &u), 28) >= 0); + assert_se(fw_add_masquerade(&ctx, false, AF_INET, parse_addr("10.0.2.0", &u), 28) >= 0); + assert_se(fw_add_masquerade(&ctx, false, AF_INET, parse_addr("10.1.2.3", &u), 32) >= 0); + assert_se(fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, parse_addr("1.2.3.4", &u), 815, NULL) >= 0); + assert_se(fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, parse_addr("1.2.3.4", &u), 815, NULL) >= 0); + assert_se(fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, parse_addr("1.2.3.5", &u), 815, parse_addr("1.2.3.4", &v)) >= 0); + assert_se(fw_add_local_dnat(&ctx, false, AF_INET, IPPROTO_TCP, 4711, parse_addr("1.2.3.5", &u), 815, NULL) >= 0); + + return true; } int main(int argc, char *argv[]) { - _cleanup_(fw_ctx_freep) FirewallContext *ctx; - int r; + _cleanup_(fw_ctx_freep) FirewallContext *ctx = NULL; + test_setup_logging(LOG_DEBUG); - uint8_t prefixlen = 32; - r = fw_ctx_new(&ctx); - if (r < 0) - return log_error_errno(r, "Failed to init firewall: %m"); + if (getuid() != 0) + return log_tests_skipped("not root"); - r = fw_add_masquerade(&ctx, true, AF_INET, NULL, 0); - if (r == 0) - log_error("Expected failure: NULL source"); + assert_se(fw_ctx_new(&ctx) >= 0); - r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,0), 0); - if (r == 0) - log_error("Expected failure: 0 prefixlen"); + if (ctx->backend == FW_BACKEND_NONE) + return EXIT_TEST_SKIP; - r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,3), prefixlen); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); + if (test_v4(ctx) && ctx->backend == FW_BACKEND_NFTABLES) + test_v6(ctx); - prefixlen = 28; - r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,0,2,0), prefixlen); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_masquerade(&ctx, false, AF_INET, &MAKE_IN_ADDR_UNION(10,0,2,0), prefixlen); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_masquerade(&ctx, false, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,3), 32); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, &MAKE_IN_ADDR_UNION(1, 2, 3, 4)); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - r = fw_add_local_dnat(&ctx, false, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, NULL); - if (r < 0) - log_error_errno(r, "Failed to modify firewall: %m"); - - test_v6(&ctx); +#if HAVE_LIBIPTC + if (ctx->backend != FW_BACKEND_IPTABLES) { + ctx->backend = FW_BACKEND_IPTABLES; + test_v4(ctx); + } +#endif return 0; }