diff --git a/TODO b/TODO index 22138301ee..97466c81b8 100644 --- a/TODO +++ b/TODO @@ -1409,8 +1409,6 @@ Features: - libpam (only when called from PID 1) - bzip2, xz, lz4 (always — gzip and zstd should probably stay static deps the way they are, since they are so basic and our defaults) - o move into separate libsystemd-shared-iptables.so .so - - iptables-libs (only used by nspawn + networkd) * seccomp: maybe use seccomp_merge() to merge our filters per-arch if we can. Apparently kernel performance is much better with fewer larger seccomp diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 3a42774212..a884ed86ac 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -573,3 +573,9 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \ * `$SYSTEMD_REPART_OVERRIDE_FSTYPE` – if set the value will override the file system type specified in Format= lines in partition definition files. + +`systemd-nspawn`, `systemd-networkd`: + +* `$SYSTEMD_FIREWALL_BACKEND` – takes a string, either `iptables` or + `nftables`. Selects the firewall backend to use. If not specified tries to + use `nftables` and falls back to `iptables` if that's not available. diff --git a/meson.build b/meson.build index 43a87407b0..e33af4c6c7 100644 --- a/meson.build +++ b/meson.build @@ -1288,6 +1288,7 @@ conf.set10('HAVE_LIBIDN2', have) libiptc = dependency('libiptc', required : get_option('libiptc')) conf.set10('HAVE_LIBIPTC', libiptc.found()) +libiptc_cflags = libiptc.partial_dependency(includes: true, compile_args: true) libqrencode = dependency('libqrencode', version : '>= 3', diff --git a/src/shared/dlfcn-util.h b/src/shared/dlfcn-util.h index ca632f4e1f..7d8cb4c9ab 100644 --- a/src/shared/dlfcn-util.h +++ b/src/shared/dlfcn-util.h @@ -15,6 +15,11 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l #define dlopen_many_sym_or_warn(dlp, filename, log_level, ...) \ dlopen_many_sym_or_warn_sentinel(dlp, filename, log_level, __VA_ARGS__, NULL) +#define DLSYM_PROTOTYPE(symbol) \ + extern typeof(symbol)* sym_##symbol +#define DLSYM_FUNCTION(symbol) \ + typeof(symbol)* sym_##symbol = NULL + /* Macro useful for putting together variable/symbol name pairs when calling dlsym_many_or_warn(). Assumes * that each library symbol to resolve will be placed in a variable with the "sym_" prefix, i.e. a symbol * "foobar" is loaded into a variable "sym_foobar". */ diff --git a/src/shared/firewall-util-iptables.c b/src/shared/firewall-util-iptables.c index 55bb427e7b..b70b74074b 100644 --- a/src/shared/firewall-util-iptables.c +++ b/src/shared/firewall-util-iptables.c @@ -21,13 +21,24 @@ #include #include "alloc-util.h" +#include "dlfcn-util.h" #include "firewall-util.h" #include "firewall-util-private.h" #include "in-addr-util.h" #include "macro.h" #include "socket-util.h" -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle*, iptc_free, NULL); +static DLSYM_FUNCTION(iptc_check_entry); +static DLSYM_FUNCTION(iptc_commit); +static DLSYM_FUNCTION(iptc_delete_entry); +static DLSYM_FUNCTION(iptc_free); +static DLSYM_FUNCTION(iptc_init); +static DLSYM_FUNCTION(iptc_insert_entry); +static DLSYM_FUNCTION(iptc_strerror); + +static void *iptc_dl = NULL; + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct xtc_handle*, sym_iptc_free, NULL); static int entry_fill_basics( struct ipt_entry *entry, @@ -86,7 +97,7 @@ int fw_iptables_add_masquerade( unsigned source_prefixlen) { static const xt_chainlabel chain = "POSTROUTING"; - _cleanup_(iptc_freep) struct xtc_handle *h = NULL; + _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL; struct ipt_entry *entry, *mask; struct ipt_entry_target *t; size_t sz; @@ -132,15 +143,15 @@ int fw_iptables_add_masquerade( memset(mask, 0xFF, sz); if (add) { - if (iptc_check_entry(chain, entry, (unsigned char*) mask, h)) + if (sym_iptc_check_entry(chain, entry, (unsigned char*) mask, h)) return 0; if (errno != ENOENT) /* if other error than not existing yet, fail */ return -errno; - if (!iptc_insert_entry(chain, entry, 0, h)) + if (!sym_iptc_insert_entry(chain, entry, 0, h)) return -errno; } else { - if (!iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) { if (errno == ENOENT) /* if it's already gone, all is good! */ return 0; @@ -148,7 +159,7 @@ int fw_iptables_add_masquerade( } } - if (!iptc_commit(h)) + if (!sym_iptc_commit(h)) return -errno; return 0; @@ -164,7 +175,7 @@ int fw_iptables_add_local_dnat( const union in_addr_union *previous_remote) { static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT"; - _cleanup_(iptc_freep) struct xtc_handle *h = NULL; + _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL; struct ipt_entry *entry, *mask; struct ipt_entry_target *t; struct ipt_entry_match *m; @@ -275,11 +286,11 @@ int fw_iptables_add_local_dnat( if (add) { /* Add the PREROUTING rule, if it is missing so far */ - if (!iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -EINVAL; - if (!iptc_insert_entry(chain_pre, entry, 0, h)) + if (!sym_iptc_insert_entry(chain_pre, entry, 0, h)) return -errno; } @@ -287,7 +298,7 @@ int fw_iptables_add_local_dnat( if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; - if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -errno; } @@ -305,11 +316,11 @@ int fw_iptables_add_local_dnat( entry->ip.invflags = IPT_INV_DSTIP; } - if (!iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -errno; - if (!iptc_insert_entry(chain_output, entry, 0, h)) + if (!sym_iptc_insert_entry(chain_output, entry, 0, h)) return -errno; } @@ -317,14 +328,14 @@ int fw_iptables_add_local_dnat( if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; - if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -errno; } } } } else { - if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -errno; } @@ -336,25 +347,43 @@ int fw_iptables_add_local_dnat( entry->ip.invflags = IPT_INV_DSTIP; } - if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) { + if (!sym_iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) { if (errno != ENOENT) return -errno; } } } - if (!iptc_commit(h)) + if (!sym_iptc_commit(h)) return -errno; return 0; } -int fw_iptables_init_nat(struct xtc_handle **ret) { - _cleanup_(iptc_freep) struct xtc_handle *h = NULL; +static int dlopen_iptc(void) { + return dlopen_many_sym_or_warn( + &iptc_dl, + "libip4tc.so.2", LOG_DEBUG, + DLSYM_ARG(iptc_check_entry), + DLSYM_ARG(iptc_commit), + DLSYM_ARG(iptc_delete_entry), + DLSYM_ARG(iptc_free), + DLSYM_ARG(iptc_init), + DLSYM_ARG(iptc_insert_entry), + DLSYM_ARG(iptc_strerror)); +} - h = iptc_init("nat"); +int fw_iptables_init_nat(struct xtc_handle **ret) { + _cleanup_(sym_iptc_freep) struct xtc_handle *h = NULL; + int r; + + r = dlopen_iptc(); + if (r < 0) + return r; + + h = sym_iptc_init("nat"); if (!h) - return log_debug_errno(errno, "Failed to init \"nat\" table: %s", iptc_strerror(errno)); + return log_debug_errno(errno, "Failed to init \"nat\" table: %s", sym_iptc_strerror(errno)); if (ret) *ret = TAKE_PTR(h); diff --git a/src/shared/firewall-util-private.h b/src/shared/firewall-util-private.h index 97f8fe124e..38c8dfced5 100644 --- a/src/shared/firewall-util-private.h +++ b/src/shared/firewall-util-private.h @@ -6,6 +6,7 @@ #include "sd-netlink.h" +#include "firewall-util.h" #include "in-addr-util.h" typedef enum FirewallBackend { diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index ba3e9cbc5e..764ef5a018 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -21,19 +21,38 @@ static const char * const firewall_backend_table[_FW_BACKEND_MAX] = { DEFINE_STRING_TABLE_LOOKUP_TO_STRING(firewall_backend, FirewallBackend); static void firewall_backend_probe(FirewallContext *ctx, bool init_tables) { + const char *e; + assert(ctx); if (ctx->backend != _FW_BACKEND_INVALID) return; - if (fw_nftables_init_full(ctx, init_tables) >= 0) - ctx->backend = FW_BACKEND_NFTABLES; - else + e = secure_getenv("SYSTEMD_FIREWALL_BACKEND"); + if (e) { + if (streq(e, "nftables")) + ctx->backend = FW_BACKEND_NFTABLES; + else if (streq(e, "iptables")) #if HAVE_LIBIPTC - ctx->backend = FW_BACKEND_IPTABLES; + ctx->backend = FW_BACKEND_IPTABLES; #else - ctx->backend = FW_BACKEND_NONE; + log_debug("Unsupported firewall backend requested, ignoring: %s", e); #endif + else + log_debug("Unrecognized $SYSTEMD_FIREWALL_BACKEND value, ignoring: %s", e); + } + + if (ctx->backend == _FW_BACKEND_INVALID) { + + if (fw_nftables_init_full(ctx, init_tables) >= 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)); diff --git a/src/shared/meson.build b/src/shared/meson.build index 08441de0ad..f918c254fc 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -316,7 +316,7 @@ libshared_deps = [threads, libcrypt, libdl, libgcrypt, - libiptc, + libiptc_cflags, libkmod, liblz4, libmount,