mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-03 13:47:04 +03:00
Merge pull request #19076 from yuwata/firewall-util-modernizations
firewall-util: modernize code and improve test
This commit is contained in:
commit
a2c69debd9
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -4,22 +4,27 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -1,114 +1,110 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user