From 9a95146b70c1be1607619650e357349eadfb9759 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 6 May 2021 16:50:20 +0200 Subject: [PATCH 1/9] core/format: drop /**/ comment from enums that lead to bad formatting Our clang-format style doesn't work well with these gtk-doc tags. For NetworkManager core, we don't use glib-mkenums. Thus, these comments serve no purpose. Drop them for better formatting. --- src/core/nm-config-data.h | 82 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index 835edd5d7e..d1b2c3744a 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -50,71 +50,71 @@ typedef enum { #define NM_CONFIG_DATA_NO_AUTO_DEFAULT "no-auto-default" #define NM_CONFIG_DATA_DNS_MODE "dns" -typedef enum { /*< flags >*/ - NM_CONFIG_GET_VALUE_NONE = 0, +typedef enum { + NM_CONFIG_GET_VALUE_NONE = 0, - /* use g_key_file_get_value() instead of g_key_file_get_string(). */ - NM_CONFIG_GET_VALUE_RAW = (1LL << 0), + /* use g_key_file_get_value() instead of g_key_file_get_string(). */ + NM_CONFIG_GET_VALUE_RAW = (1LL << 0), - /* strip whitespaces */ - NM_CONFIG_GET_VALUE_STRIP = (1LL << 1), + /* strip whitespaces */ + NM_CONFIG_GET_VALUE_STRIP = (1LL << 1), - /* if the returned string would be the empty word, return NULL. */ - NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2), + /* if the returned string would be the empty word, return NULL. */ + NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2), - /* special flag to read device spec. You want to use this before passing the + /* special flag to read device spec. You want to use this before passing the * value to nm_match_spec_split(). */ - NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW, + NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW, } NMConfigGetValueFlags; -typedef enum { /*< flags >*/ - NM_CONFIG_CHANGE_NONE = 0, +typedef enum { + NM_CONFIG_CHANGE_NONE = 0, - /************************************************************************** + /************************************************************************** * The external cause which triggered the reload/configuration-change *************************************************************************/ - NM_CONFIG_CHANGE_CAUSE_SIGHUP = (1L << 0), - NM_CONFIG_CHANGE_CAUSE_SIGUSR1 = (1L << 1), - NM_CONFIG_CHANGE_CAUSE_SIGUSR2 = (1L << 2), - NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT = (1L << 3), - NM_CONFIG_CHANGE_CAUSE_SET_VALUES = (1L << 4), - NM_CONFIG_CHANGE_CAUSE_CONF = (1L << 5), - NM_CONFIG_CHANGE_CAUSE_DNS_RC = (1L << 6), - NM_CONFIG_CHANGE_CAUSE_DNS_FULL = (1L << 7), + NM_CONFIG_CHANGE_CAUSE_SIGHUP = (1L << 0), + NM_CONFIG_CHANGE_CAUSE_SIGUSR1 = (1L << 1), + NM_CONFIG_CHANGE_CAUSE_SIGUSR2 = (1L << 2), + NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT = (1L << 3), + NM_CONFIG_CHANGE_CAUSE_SET_VALUES = (1L << 4), + NM_CONFIG_CHANGE_CAUSE_CONF = (1L << 5), + NM_CONFIG_CHANGE_CAUSE_DNS_RC = (1L << 6), + NM_CONFIG_CHANGE_CAUSE_DNS_FULL = (1L << 7), - NM_CONFIG_CHANGE_CAUSES = ((1L << 8) - 1), + NM_CONFIG_CHANGE_CAUSES = ((1L << 8) - 1), - /************************************************************************** + /************************************************************************** * Following flags describe which property of the configuration changed: *************************************************************************/ - /* main-file or config-description changed */ - NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 10), + /* main-file or config-description changed */ + NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 10), - /* any configuration on disk changed */ - NM_CONFIG_CHANGE_VALUES = (1L << 11), + /* any configuration on disk changed */ + NM_CONFIG_CHANGE_VALUES = (1L << 11), - /* any user configuration on disk changed (NetworkManager.conf) */ - NM_CONFIG_CHANGE_VALUES_USER = (1L << 12), + /* any user configuration on disk changed (NetworkManager.conf) */ + NM_CONFIG_CHANGE_VALUES_USER = (1L << 12), - /* any internal configuration on disk changed (NetworkManager-intern.conf) */ - NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 13), + /* any internal configuration on disk changed (NetworkManager-intern.conf) */ + NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 13), - /* configuration regarding connectivity changed */ - NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 14), + /* configuration regarding connectivity changed */ + NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 14), - /* configuration regarding no-auto-default changed */ - NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 15), + /* configuration regarding no-auto-default changed */ + NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 15), - /* configuration regarding dns-mode changed */ - NM_CONFIG_CHANGE_DNS_MODE = (1L << 16), + /* configuration regarding dns-mode changed */ + NM_CONFIG_CHANGE_DNS_MODE = (1L << 16), - /* configuration regarding rc-manager changed */ - NM_CONFIG_CHANGE_RC_MANAGER = (1L << 17), + /* configuration regarding rc-manager changed */ + NM_CONFIG_CHANGE_RC_MANAGER = (1L << 17), - /* configuration regarding global dns-config changed */ - NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG = (1L << 18), + /* configuration regarding global dns-config changed */ + NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG = (1L << 18), } NMConfigChangeFlags; From b5c5b2443d2e725e6c23c5931723ba888ee7586e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 7 May 2021 23:06:57 +0200 Subject: [PATCH 2/9] glib-aux: add nm_g_error_free() helper --- src/libnm-glib-aux/nm-macros-internal.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index a6910b2d3b..edaa6f3902 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -1082,6 +1082,13 @@ nm_clear_error(GError **err) } } +static inline void +nm_g_error_free(GError *err) +{ + if (err) + g_error_free(err); +} + /* Patch g_clear_error() to use nm_clear_error(), which is inlineable * and visible to the compiler. For example gs_free_error attribute only * frees the error after checking that it's not %NULL. So, in many cases From 62027350f7e29447ee7976785ccf56f918a635cc Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 7 May 2021 23:07:14 +0200 Subject: [PATCH 3/9] glib-aux: add NM_STRV_EMPTY() helper NM_PTRARRAY_EMPTY(char*) gives a "char *const*" pointer, which is often not useful where a strv array is needed. Add a helper macro. --- src/libnm-glib-aux/nm-shared-utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 699d4d52b2..4d0fa8522a 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -522,6 +522,7 @@ gboolean nm_utils_memeqzero(gconstpointer data, gsize length); extern const void *const _NM_PTRARRAY_EMPTY[1]; #define NM_PTRARRAY_EMPTY(type) ((type const *) _NM_PTRARRAY_EMPTY) +#define NM_STRV_EMPTY() ((char **) _NM_PTRARRAY_EMPTY) static inline void _nm_utils_strbuf_init(char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len) From 071ef784cfb05abbda6c811000ed4cd93026e6a9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 7 May 2021 23:08:27 +0200 Subject: [PATCH 4/9] glib-aux: add nm_g_subprocess_terminate_in_background() helper --- src/libnm-glib-aux/nm-io-utils.c | 73 ++++++++++++++++++++++++++++++++ src/libnm-glib-aux/nm-io-utils.h | 2 + 2 files changed, 75 insertions(+) diff --git a/src/libnm-glib-aux/nm-io-utils.c b/src/libnm-glib-aux/nm-io-utils.c index 0176abf7f8..894c8726c0 100644 --- a/src/libnm-glib-aux/nm-io-utils.c +++ b/src/libnm-glib-aux/nm-io-utils.c @@ -491,3 +491,76 @@ nm_utils_fd_read(int fd, NMStrBuf *out_string) return n_read; } + +/*****************************************************************************/ + +typedef struct { + GSubprocess *subprocess; + GSource * timeout_source; +} SubprocessTerminateData; + +static void +_subprocess_terminate_wait_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + SubprocessTerminateData *term_data = user_data; + + g_subprocess_wait_finish(G_SUBPROCESS(source), result, NULL); + + nm_clear_g_source_inst(&term_data->timeout_source); + g_object_unref(term_data->subprocess); + nm_g_slice_free(term_data); +} + +static gboolean +_subprocess_terminate_timeout_cb(gpointer user_data) +{ + SubprocessTerminateData *term_data = user_data; + + nm_clear_g_source_inst(&term_data->timeout_source); + g_subprocess_send_signal(term_data->subprocess, SIGKILL); + return G_SOURCE_REMOVE; +} + +void +nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill) +{ + SubprocessTerminateData *term_data; + GMainContext * main_context; + + nm_assert(timeout_msec_before_kill > 0); + + /* The GSubprocess stays alive until the child is reaped (an internal reference is held). + * + * This function first sends SIGTERM to the process right away, and after a + * timeout "timeout_msec_before_kill" send a SIGKILL. + * + * Otherwise, it does nothing, it does not log, there is no notification when the process + * completes and there is no way to abort the thing. + * + * It honors the current g_main_context_get_thread_default(). */ + + if (!subprocess) + return; + + g_return_if_fail(G_IS_SUBPROCESS(subprocess)); + + main_context = g_main_context_get_thread_default(); + + term_data = g_slice_new(SubprocessTerminateData); + *term_data = (SubprocessTerminateData){ + .subprocess = g_object_ref(subprocess), + .timeout_source = NULL, + }; + + g_subprocess_send_signal(subprocess, SIGTERM); + + g_subprocess_wait_async(subprocess, NULL, _subprocess_terminate_wait_cb, term_data); + + term_data->timeout_source = + nm_g_source_attach(nm_g_timeout_source_new(timeout_msec_before_kill, + G_PRIORITY_DEFAULT, + _subprocess_terminate_timeout_cb, + term_data, + NULL), + main_context); +} diff --git a/src/libnm-glib-aux/nm-io-utils.h b/src/libnm-glib-aux/nm-io-utils.h index 2b132ff07d..98e63ac01e 100644 --- a/src/libnm-glib-aux/nm-io-utils.h +++ b/src/libnm-glib-aux/nm-io-utils.h @@ -56,4 +56,6 @@ struct stat; int nm_utils_file_stat(const char *filename, struct stat *out_st); +void nm_g_subprocess_terminate_in_background(GSubprocess *subprocess, int timeout_msec_before_kill); + #endif /* __NM_IO_UTILS_H__ */ From 8a11380e80e27139f5949bc1e8fbffbdab75003a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 10 May 2021 20:43:51 +0200 Subject: [PATCH 5/9] glib-aux: add nm_auto_pop_and_unref_gmaincontext cleanup macro --- src/libnm-glib-aux/nm-shared-utils.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 4d0fa8522a..7bd3845a56 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -1564,6 +1564,18 @@ NM_AUTO_DEFINE_FCN0(GSource *, _nm_auto_destroy_and_unref_gsource, nm_g_source_d NM_AUTO_DEFINE_FCN0(GMainContext *, _nm_auto_pop_gmaincontext, g_main_context_pop_thread_default); #define nm_auto_pop_gmaincontext nm_auto(_nm_auto_pop_gmaincontext) +static inline void +nm_g_main_context_pop_and_unref(GMainContext *context) +{ + g_main_context_pop_thread_default(context); + g_main_context_unref(context); +} + +NM_AUTO_DEFINE_FCN0(GMainContext *, + _nm_auto_pop_and_unref_gmaincontext, + nm_g_main_context_pop_and_unref); +#define nm_auto_pop_and_unref_gmaincontext nm_auto(_nm_auto_pop_and_unref_gmaincontext) + static inline gboolean nm_source_func_unref_gobject(gpointer user_data) { From 2a1d42e77d5b35f654198078813e35e0bd189701 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 10 May 2021 18:45:03 +0200 Subject: [PATCH 6/9] firewall: refactor is_comment argument to _share_iptables_get_name() The new name makes it more generic, because the limitation exists for iptables chains. Everything else (iptables comments, nftables tables) has no such length limit. --- src/core/nm-firewall-utils.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index dc17e2c040..69aa3fdfb0 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -36,7 +36,7 @@ _share_iptables_subnet_to_str(char buf[static _SHARE_IPTABLES_SUBNET_TO_STR } static char * -_share_iptables_get_name(gboolean is_comment, const char *prefix, const char *ip_iface) +_share_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const char *ip_iface) { NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE); gsize ip_iface_len; @@ -58,7 +58,7 @@ _share_iptables_get_name(gboolean is_comment, const char *prefix, const char *ip * an plain name. * * That means, for chain names the prefix must be at most 8 chars long. */ - nm_assert(is_comment || (strlen(prefix) <= 8)); + nm_assert(!is_iptables_chain || (strlen(prefix) <= 8)); nm_str_buf_append(&strbuf, prefix); @@ -150,7 +150,7 @@ _share_iptables_set_masquerade(gboolean add, const char *ip_iface, in_addr_t add char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN]; gs_free char *comment_name = NULL; - comment_name = _share_iptables_get_name(TRUE, "nm-shared", ip_iface); + comment_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface); _share_iptables_subnet_to_str(str_subnet, addr, plen); _share_iptables_call("" IPTABLES_PATH "", @@ -290,9 +290,9 @@ _share_iptables_set_shared(gboolean add, const char *ip_iface, in_addr_t addr, g gs_free char *chain_input = NULL; gs_free char *chain_forward = NULL; - comment_name = _share_iptables_get_name(TRUE, "nm-shared", ip_iface); - chain_input = _share_iptables_get_name(FALSE, "nm-sh-in", ip_iface); - chain_forward = _share_iptables_get_name(FALSE, "nm-sh-fw", ip_iface); + comment_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface); + chain_input = _share_iptables_get_name(TRUE, "nm-sh-in", ip_iface); + chain_forward = _share_iptables_get_name(TRUE, "nm-sh-fw", ip_iface); if (add) _share_iptables_set_shared_chains_add(chain_input, chain_forward, ip_iface, addr, plen); From 1da1ad9c9995f6454ef39da3d8ba85b0a23e919f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 6 May 2021 16:50:25 +0200 Subject: [PATCH 7/9] firewall: make firewall-backend configurable via "NetworkManager.conf" "iptables" and "nftables" will be supported. Currently, the code is unused and only "iptables" is supported. --- config.h.meson | 5 +- configure.ac | 23 ++++++-- man/NetworkManager.conf.xml | 11 ++++ meson.build | 5 +- meson_options.txt | 1 + src/core/nm-firewall-utils.c | 97 +++++++++++++++++++++++++++++++++ src/core/nm-firewall-utils.h | 10 ++++ src/core/tests/test-core.c | 21 +++++++ src/libnm-base/nm-config-base.h | 3 +- 9 files changed, 168 insertions(+), 8 deletions(-) diff --git a/config.h.meson b/config.h.meson index a911dbe257..432402d5b4 100644 --- a/config.h.meson +++ b/config.h.meson @@ -67,7 +67,10 @@ /* Define to path of iptables binary */ #mesondefine IPTABLES_PATH -/* Define to path to the Jansson shared library */ +/* Define to path of nft binary */ +#mesondefine NFT_PATH + +//* Define to path to the Jansson shared library */ #mesondefine JANSSON_SONAME /* Define to path of the kernel firmware directory */ diff --git a/configure.ac b/configure.ac index e27ceeb7a1..d23d315cf8 100644 --- a/configure.ac +++ b/configure.ac @@ -943,13 +943,12 @@ fi AC_DEFINE_UNQUOTED(NM_CONFIG_DEFAULT_MAIN_RC_MANAGER, "$config_dns_rc_manager_default", [Default value for main.rc-manager setting (--with-config-dns-rc-manager-default)]) AC_SUBST(NM_CONFIG_DEFAULT_MAIN_RC_MANAGER, $config_dns_rc_manager_default) -# iptables path AC_ARG_WITH(iptables, - AS_HELP_STRING([--with-iptables=/path/to/iptables], [path to iptables])) + AS_HELP_STRING([--with-iptables=/usr/sbin/iptables], [path to iptables])) if test "x${with_iptables}" = x; then AC_PATH_PROG(IPTABLES_PATH, iptables, [], $PATH:/sbin:/usr/sbin) - if ! test -x "$IPTABLES_PATH"; then - AC_MSG_ERROR(iptables was not installed.) + if test "x$IPTABLES_PATH" = x; then + IPTABLES_PATH='/usr/sbin/iptables' fi else IPTABLES_PATH="$with_iptables" @@ -957,7 +956,19 @@ fi AC_DEFINE_UNQUOTED(IPTABLES_PATH, "$IPTABLES_PATH", [Define to path of iptables binary]) AC_SUBST(IPTABLES_PATH) -# dnsmasq path +AC_ARG_WITH(nft, + AS_HELP_STRING([--with-nft=/usr/sbin/nft], [path to nft])) +if test "x${with_nft}" = x; then + AC_PATH_PROG(NFT_PATH, nft, [], $PATH:/sbin:/usr/sbin) + if test "x$NFT_PATH" = x; then + NFT_PATH='/usr/sbin/nft' + fi +else + NFT_PATH="$with_nft" +fi +AC_DEFINE_UNQUOTED(NFT_PATH, "$NFT_PATH", [Define to path of nft binary]) +AC_SUBST(NFT_PATH) + AC_ARG_WITH(dnsmasq, AS_HELP_STRING([--with-dnsmasq=/path/to/dnsmasq], [path to dnsmasq])) if test "x${with_dnsmasq}" = x; then @@ -1372,6 +1383,8 @@ echo " nmtui: $build_nmtui" echo " nm-cloud-setup: $with_nm_cloud_setup" echo " iwd: $ac_with_iwd" echo " jansson: $have_jansson${JANSSON_SONAME:+ (soname: $JANSSON_SONAME)}" +echo " iptables: $IPTABLES_PATH" +echo " nft: $NFT_PATH" echo echo "Configuration plugins (main.plugins=${config_plugins_default})" diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 4320a9facd..f88688e94b 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -475,6 +475,17 @@ no-auto-default=* + + firewall-backend + + + The firewall backend for configuring masquerading. + Set to either iptables or nftables. + If unspecified, it will be auto detected. + + + + iwd-config-path diff --git a/meson.build b/meson.build index 33e840c173..de50c652a7 100644 --- a/meson.build +++ b/meson.build @@ -691,7 +691,8 @@ dnssec_ts_paths = ['/usr/local/libexec', '/usr/lib/dnssec-trigger'] # 0: cmdline option, 1: paths, 2: fallback -progs = [['iptables', default_paths, '/sbin/iptables'], +progs = [['iptables', default_paths, '/usr/sbin/iptables'], + ['nft', default_paths, '/usr/sbin/nft'], ['dnsmasq', default_paths, ''], ['dnssec_trigger', dnssec_ts_paths, join_paths(nm_libexecdir, 'dnssec-trigger-script') ], ] @@ -1044,6 +1045,8 @@ if enable_ppp endif output += '\n' output += ' jansson: ' + jansson_msg + '\n' +output += ' iptables: ' + config_h.get('IPTABLES_PATH') + '\n' +output += ' nft: ' + config_h.get('NFT_PATH') + '\n' output += ' modemmanager-1: ' + enable_modem_manager.to_string() + '\n' output += ' ofono: ' + enable_ofono.to_string() + '\n' output += ' concheck: ' + enable_concheck.to_string() + '\n' diff --git a/meson_options.txt b/meson_options.txt index 5100ed71f5..14ed4077a0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,6 +5,7 @@ option('udev_dir', type: 'string', value: '', description: 'Absolute path of the option('dbus_conf_dir', type: 'string', value: '', description: 'where D-Bus system.d directory is') option('kernel_firmware_dir', type: 'string', value: '/lib/firmware', description: 'where kernel firmware directory is (default is /lib/firmware)') option('iptables', type: 'string', value: '', description: 'path to iptables') +option('nft', type: 'string', value: '', description: 'path to nft') option('dnsmasq', type: 'string', value: '', description: 'path to dnsmasq') option('dnssec_trigger', type: 'string', value: '', description: 'path to unbound dnssec-trigger-script') diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 69aa3fdfb0..fdefbf5ea1 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -11,6 +11,26 @@ #include "libnm-glib-aux/nm-str-buf.h" #include "libnm-platform/nm-platform.h" +#include "nm-config.h" + +/*****************************************************************************/ + +static const struct { + const char *name; + const char *path; +} FirewallBackends[] = { + [NM_FIREWALL_BACKEND_NFTABLES - 1] = + { + .name = "nftables", + .path = NFT_PATH, + }, + [NM_FIREWALL_BACKEND_IPTABLES - 1] = + { + .name = "iptables", + .path = IPTABLES_PATH, + }, +}; + /*****************************************************************************/ #define _SHARE_IPTABLES_SUBNET_TO_STR_LEN (INET_ADDRSTRLEN + 1 + 2 + 1) @@ -327,6 +347,8 @@ _share_iptables_set_shared(gboolean add, const char *ip_iface, in_addr_t addr, g _share_iptables_set_shared_chains_delete(chain_input, chain_forward); } +/*****************************************************************************/ + struct _NMFirewallConfig { char * ip_iface; in_addr_t addr; @@ -367,3 +389,78 @@ nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared) _share_iptables_set_masquerade(shared, self->ip_iface, self->addr, self->plen); _share_iptables_set_shared(shared, self->ip_iface, self->addr, self->plen); } + +/*****************************************************************************/ + +static NMFirewallBackend +_firewall_backend_detect(void) +{ + if (g_file_test(NFT_PATH, G_FILE_TEST_IS_EXECUTABLE)) + return NM_FIREWALL_BACKEND_NFTABLES; + if (g_file_test(IPTABLES_PATH, G_FILE_TEST_IS_EXECUTABLE)) + return NM_FIREWALL_BACKEND_IPTABLES; + + return NM_FIREWALL_BACKEND_NFTABLES; +} + +NMFirewallBackend +nm_firewall_utils_get_backend(void) +{ + static int backend = NM_FIREWALL_BACKEND_UNKNOWN; + int b; + +again: + b = g_atomic_int_get(&backend); + if (b == NM_FIREWALL_BACKEND_UNKNOWN) { + gs_free char *conf_value = NULL; + gboolean detect; + int i; + + conf_value = + nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_FIREWALL_BACKEND, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + + if (conf_value) { + for (i = 0; i < (int) G_N_ELEMENTS(FirewallBackends); i++) { + if (!g_ascii_strcasecmp(conf_value, FirewallBackends[i].name)) { + b = (i + 1); + break; + } + } + } + + detect = (b == NM_FIREWALL_BACKEND_UNKNOWN); + if (detect) + b = _firewall_backend_detect(); + + nm_assert(NM_IN_SET(b, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES)); + + if (b == NM_FIREWALL_BACKEND_NFTABLES) { + if (!detect) + nm_log_warn(LOGD_SHARING, + "firewall: backend \"nftables\" is not yet implemented. Fallback to " + "\"iptables\""); + nm_clear_g_free(&conf_value); + b = NM_FIREWALL_BACKEND_IPTABLES; + } + + if (!g_atomic_int_compare_and_exchange(&backend, NM_FIREWALL_BACKEND_UNKNOWN, b)) + goto again; + + nm_log_dbg(LOGD_SHARING, + "firewall: use %s backend (%s)%s%s%s%s", + FirewallBackends[b - 1].name, + FirewallBackends[b - 1].path, + detect ? " (detected)" : "", + NM_PRINT_FMT_QUOTED(detect && conf_value, + " (invalid setting \"", + conf_value, + "\")", + "")); + } + + nm_assert(NM_IN_SET(b, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES)); + return b; +} diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h index 5ba1721c2e..f261aef384 100644 --- a/src/core/nm-firewall-utils.h +++ b/src/core/nm-firewall-utils.h @@ -7,6 +7,16 @@ #ifndef __NM_FIREWALL_UTILS_H__ #define __NM_FIREWALL_UTILS_H__ +typedef enum { + NM_FIREWALL_BACKEND_UNKNOWN, + NM_FIREWALL_BACKEND_IPTABLES, + NM_FIREWALL_BACKEND_NFTABLES, +} NMFirewallBackend; + +NMFirewallBackend nm_firewall_utils_get_backend(void); + +/*****************************************************************************/ + typedef struct _NMFirewallConfig NMFirewallConfig; NMFirewallConfig *nm_firewall_config_new(const char *ip_iface, in_addr_t addr, guint8 plen); diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index d7f733066a..15be7af1ed 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -22,6 +22,25 @@ #include "nm-test-utils-core.h" +/*****************************************************************************/ + +static void +test_config_h(void) +{ +#define ABSOLUTE_PATH(path) \ + G_STMT_START \ + { \ + g_assert_cmpstr("" path "", !=, ""); \ + g_assert("" path ""[0] == '/'); \ + } \ + G_STMT_END + + ABSOLUTE_PATH(IPTABLES_PATH); + ABSOLUTE_PATH(NFT_PATH); +} + +/*****************************************************************************/ + /* Reference implementation for nm_utils_ip6_address_clear_host_address. * Taken originally from set_address_masked(), src/ndisc/nm-lndp-ndisc.c **/ @@ -2570,6 +2589,8 @@ main(int argc, char **argv) { nmtst_init_with_logging(&argc, &argv, NULL, "ALL"); + g_test_add_func("/general/test_config_h", test_config_h); + g_test_add_func("/general/test_logging_domains", test_logging_domains); g_test_add_func("/general/test_logging_error", test_logging_error); diff --git a/src/libnm-base/nm-config-base.h b/src/libnm-base/nm-config-base.h index 6a2bab1548..7a23875a43 100644 --- a/src/libnm-base/nm-config-base.h +++ b/src/libnm-base/nm-config-base.h @@ -26,15 +26,16 @@ #define NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG "debug" #define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP "dhcp" #define NM_CONFIG_KEYFILE_KEY_MAIN_DNS "dns" +#define NM_CONFIG_KEYFILE_KEY_MAIN_FIREWALL_BACKEND "firewall-backend" #define NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE "hostname-mode" #define NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER "ignore-carrier" +#define NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH "iwd-config-path" #define NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES "monitor-connection-files" #define NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT "no-auto-default" #define NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS "plugins" #define NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER "rc-manager" #define NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER "slaves-order" #define NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED "systemd-resolved" -#define NM_CONFIG_KEYFILE_KEY_MAIN_IWD_CONFIG_PATH "iwd-config-path" #define NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT "audit" #define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend" From 9ebdb967ded7cdf53e5193129e24556447b8a1e0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 12 May 2021 12:01:12 +0200 Subject: [PATCH 8/9] firewall: implement masquerading for shared mode with nftables Add support for nftables, as a second backend beside iptables (firewalld still missing). Like iptables, choose to call the `nft` tool. The alternative would be to use libnftables or talk netlink. It's ugly to blocking wait for a process to complete. We already do that for iptables, but we better should not because we should not treat other processes as trusted and not allow untrusted code to block NetworkManager. Fixing that would require a central manager that serializes all requests. Especially with firewalld support, this will be interesting again, because we don't want to synchronously talk D-Bus either. For now, `nft` is still called synchronously. However, the internal implementation uses an asynchronous function. That currently serves no purpose except supporting a timeout. Otherwise, the only reason why this is asynchronous is that I implemented this first, and I think in the future we want this code to be non-blocking. So, instead of dropping the asynchronous code, I wrap it in a synchronous function for now. The configured nft table is: table inet nm-shared-eth0 { chain nat_postrouting { type nat hook postrouting priority srcnat; policy accept; ip saddr 192.168.42.0/24 ip daddr != 192.168.42.0/24 masquerade } chain filter_forward { type filter hook forward priority filter; policy accept; ip daddr 192.168.42.0/24 oifname "eth0" ct state { established, related } accept ip saddr 192.168.42.0/24 iifname "eth0" accept iifname "eth0" oifname "eth0" accept iifname "eth0" reject oifname "eth0" reject } } --- src/core/nm-firewall-utils.c | 350 +++++++++++++++++++++++++++++++++-- 1 file changed, 339 insertions(+), 11 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index fdefbf5ea1..22d52031a0 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -9,9 +9,11 @@ #include "nm-firewall-utils.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-glib-aux/nm-io-utils.h" #include "libnm-platform/nm-platform.h" #include "nm-config.h" +#include "NetworkManagerUtils.h" /*****************************************************************************/ @@ -349,6 +351,331 @@ _share_iptables_set_shared(gboolean add, const char *ip_iface, in_addr_t addr, g /*****************************************************************************/ +typedef struct { + GTask * task; + GSubprocess * subprocess; + GSource * timeout_source; + GCancellable *intern_cancellable; + char * identifier; + gulong cancellable_id; +} FwNftCallData; + +static void +_fw_nft_call_data_free(FwNftCallData *call_data, GError *error_take) +{ + nm_clear_g_signal_handler(g_task_get_cancellable(call_data->task), &call_data->cancellable_id); + nm_clear_g_cancellable(&call_data->intern_cancellable); + nm_clear_g_source_inst(&call_data->timeout_source); + + if (error_take) + g_task_return_error(call_data->task, g_steal_pointer(&error_take)); + else + g_task_return_boolean(call_data->task, TRUE); + + g_object_unref(call_data->task); + nm_g_object_unref(call_data->subprocess); + g_free(call_data->identifier); + + nm_g_slice_free(call_data); +} + +static void +_fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + FwNftCallData *call_data = user_data; + gs_free_error GError *error = NULL; + gs_unref_bytes GBytes *stdout_buf = NULL; + gs_unref_bytes GBytes *stderr_buf = NULL; + + nm_assert(source == (gpointer) call_data->subprocess); + + if (!g_subprocess_communicate_finish(G_SUBPROCESS(source), + result, + &stdout_buf, + &stderr_buf, + &error)) { + /* on any error, the process might still be running. We need to abort it in + * the background... */ + if (nm_utils_error_is_cancelled(error)) { + nm_log_dbg(LOGD_SHARING, + "firewall: ntf[%s]: communication cancelled. Kill process", + call_data->identifier); + } else { + nm_log_dbg(LOGD_SHARING, + "firewall: nft[%s]: communication failed: %s. Kill process", + call_data->identifier, + error->message); + } + + { + _nm_unused nm_auto_pop_gmaincontext GMainContext *main_context = + nm_g_main_context_push_thread_default(NULL); + + nm_shutdown_wait_obj_register_object(call_data->subprocess, "nft-terminate"); + G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG * 2 / 3); + nm_g_subprocess_terminate_in_background(call_data->subprocess, 200); + } + } else if (g_subprocess_get_successful(call_data->subprocess)) { + nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: command successful", call_data->identifier); + } else { + gs_free char *ss_stdout = NULL; + gs_free char *ss_stderr = NULL; + gboolean print_stdout = (stdout_buf && g_bytes_get_size(stdout_buf) > 0); + gboolean print_stderr = (stderr_buf && g_bytes_get_size(stderr_buf) > 0); + + nm_log_warn(LOGD_SHARING, + "firewall: nft[%s]: command failed:%s%s%s%s%s%s%s", + call_data->identifier, + print_stdout || print_stderr ? "" : " unknown reason", + NM_PRINT_FMT_QUOTED( + print_stdout, + " (stdout: \"", + nm_utils_buf_utf8safe_escape_bytes(stdout_buf, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &ss_stdout), + "\")", + ""), + NM_PRINT_FMT_QUOTED( + print_stderr, + " (stderr: \"", + nm_utils_buf_utf8safe_escape_bytes(stderr_buf, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &ss_stderr), + "\")", + "")); + } + + _fw_nft_call_data_free(call_data, error); +} + +static void +_fw_nft_call_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + FwNftCallData *call_data = user_data; + + if (call_data->cancellable_id == 0) + return; + + nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: operation cancelled", call_data->identifier); + + nm_clear_g_signal_handler(g_task_get_cancellable(call_data->task), &call_data->cancellable_id); + nm_clear_g_cancellable(&call_data->intern_cancellable); +} + +static gboolean +_fw_nft_call_timeout_cb(gpointer user_data) +{ + FwNftCallData *call_data = user_data; + + nm_clear_g_source_inst(&call_data->timeout_source); + nm_log_dbg(LOGD_SHARING, + "firewall: nft[%s]: cancel operation after timeout", + call_data->identifier); + + nm_clear_g_cancellable(&call_data->intern_cancellable); + return G_SOURCE_CONTINUE; +} + +static void +_fw_nft_call(GBytes * stdin_buf, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer callback_user_data) +{ + gs_unref_object GSubprocessLauncher *subprocess_launcher = NULL; + gs_free_error GError *error = NULL; + FwNftCallData * call_data; + + call_data = g_slice_new(FwNftCallData); + *call_data = (FwNftCallData){ + .task = nm_g_task_new(NULL, cancellable, _fw_nft_call, callback, callback_user_data), + .subprocess = NULL, + .timeout_source = NULL, + }; + + if (cancellable) { + call_data->cancellable_id = g_cancellable_connect(cancellable, + G_CALLBACK(_fw_nft_call_cancelled_cb), + call_data, + NULL); + if (call_data->cancellable_id == 0) { + nm_log_dbg(LOGD_SHARING, "firewall: nft: already cancelled"); + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _fw_nft_call_data_free(call_data, g_steal_pointer(&error)); + return; + } + } + + subprocess_launcher = + g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE + | G_SUBPROCESS_FLAGS_STDERR_PIPE); + g_subprocess_launcher_set_environ(subprocess_launcher, NM_STRV_EMPTY()); + + call_data->subprocess = g_subprocess_launcher_spawnv(subprocess_launcher, + NM_MAKE_STRV(NFT_PATH, "-f", "-"), + &error); + + if (!call_data->subprocess) { + nm_log_dbg(LOGD_SHARING, "firewall: nft: spawning nft failed: %s", error->message); + _fw_nft_call_data_free(call_data, g_steal_pointer(&error)); + return; + } + + call_data->identifier = g_strdup(g_subprocess_get_identifier(call_data->subprocess)); + + nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: communicate with nft", call_data->identifier); + + nm_shutdown_wait_obj_register_object(call_data->task, "nft-call"); + + call_data->intern_cancellable = g_cancellable_new(), + + g_subprocess_communicate_async(call_data->subprocess, + stdin_buf, + call_data->intern_cancellable, + _fw_nft_call_communicate_cb, + call_data); + + call_data->timeout_source = + nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_MS * 2) / 3, + G_PRIORITY_DEFAULT, + _fw_nft_call_timeout_cb, + call_data, + NULL), + g_task_get_context(call_data->task)); +} + +static gboolean +_fw_nft_call_finish(GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(nm_g_task_is_valid(result, NULL, _fw_nft_call), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *loop; + GError ** error; + gboolean success; +} FwNftCallSyncData; + +static void +_fw_nft_call_sync_done(GObject *source, GAsyncResult *result, gpointer user_data) +{ + FwNftCallSyncData *data = user_data; + + data->success = _fw_nft_call_finish(result, data->error); + g_main_loop_quit(data->loop); +} + +static gboolean +_fw_nft_call_sync(GBytes *stdin_buf, GError **error) +{ + nm_auto_pop_and_unref_gmaincontext GMainContext *main_context = + nm_g_main_context_push_thread_default(g_main_context_new()); + nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new(main_context, FALSE); + FwNftCallSyncData data = (FwNftCallSyncData){ + .loop = main_loop, + .error = error, + }; + + _fw_nft_call(stdin_buf, NULL, _fw_nft_call_sync_done, &data); + + g_main_loop_run(main_loop); + return data.success; +} + +/*****************************************************************************/ + +static void +_fw_nft_set(gboolean add, const char *ip_iface, in_addr_t addr, guint8 plen) +{ + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + gs_unref_bytes GBytes *stdin_buf = NULL; + gs_free char * table_name = NULL; + gs_free char * ss1 = NULL; + char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN]; + + table_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface); + + _share_iptables_subnet_to_str(str_subnet, addr, plen); + +#define _append(p_strbuf, fmt, ...) nm_str_buf_append_printf((p_strbuf), "" fmt "\n", ##__VA_ARGS__) + + _append(&strbuf, "add table inet %s", table_name); + _append(&strbuf, "%s table inet %s", add ? "flush" : "delete", table_name); + + if (add) { + _append(&strbuf, + "add chain inet %s nat_postrouting {" + " type nat hook postrouting priority 100; policy accept; " + "};", + table_name); + _append(&strbuf, + "add rule inet %s nat_postrouting ip saddr %s ip daddr != %s masquerade;", + table_name, + str_subnet, + str_subnet); + + /* This filter_input chain serves no real purpose, because "accept" only stops + * evaluation of the current rule. It cannot fully accept the packet. Since + * this chain has no other rules, it is useless in this form. + */ + /* + _append(&strbuf, + "add chain inet %s filter_input {" + " type filter hook input priority 0; policy accept; " + "};", + table_name); + _append(&strbuf, "add rule inet %s filter_input tcp dport { 67, 53 } accept;", table_name); + _append(&strbuf, "add rule inet %s filter_input udp dport { 67, 53 } accept;", table_name); + */ + + _append(&strbuf, + "add chain inet %s filter_forward {" + " type filter hook forward priority 0; policy accept; " + "};", + table_name); + _append(&strbuf, + "add rule inet %s filter_forward ip daddr %s oifname \"%s\" " + " ct state { established, related } accept;", + table_name, + str_subnet, + ip_iface); + _append(&strbuf, + "add rule inet %s filter_forward ip saddr %s iifname \"%s\" accept;", + table_name, + str_subnet, + ip_iface); + _append(&strbuf, + "add rule inet %s filter_forward iifname \"%s\" oifname \"%s\" accept;", + table_name, + ip_iface, + ip_iface); + _append(&strbuf, + "add rule inet %s filter_forward iifname \"%s\" reject;", + table_name, + ip_iface); + _append(&strbuf, + "add rule inet %s filter_forward oifname \"%s\" reject;", + table_name, + ip_iface); + } + + nm_log_trace(LOGD_SHARING, + "firewall: nft command: [ %s ]", + nm_utils_str_utf8safe_escape(nm_str_buf_get_str(&strbuf), + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &ss1)); + + stdin_buf = g_bytes_new_static(nm_str_buf_get_str(&strbuf), strbuf.len); + + _fw_nft_call_sync(stdin_buf, NULL); +} + +/*****************************************************************************/ + struct _NMFirewallConfig { char * ip_iface; in_addr_t addr; @@ -386,8 +713,18 @@ nm_firewall_config_free(NMFirewallConfig *self) void nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared) { - _share_iptables_set_masquerade(shared, self->ip_iface, self->addr, self->plen); - _share_iptables_set_shared(shared, self->ip_iface, self->addr, self->plen); + switch (nm_firewall_utils_get_backend()) { + case NM_FIREWALL_BACKEND_IPTABLES: + _share_iptables_set_masquerade(shared, self->ip_iface, self->addr, self->plen); + _share_iptables_set_shared(shared, self->ip_iface, self->addr, self->plen); + break; + case NM_FIREWALL_BACKEND_NFTABLES: + _fw_nft_set(shared, self->ip_iface, self->addr, self->plen); + break; + default: + nm_assert_not_reached(); + break; + } } /*****************************************************************************/ @@ -437,15 +774,6 @@ again: nm_assert(NM_IN_SET(b, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES)); - if (b == NM_FIREWALL_BACKEND_NFTABLES) { - if (!detect) - nm_log_warn(LOGD_SHARING, - "firewall: backend \"nftables\" is not yet implemented. Fallback to " - "\"iptables\""); - nm_clear_g_free(&conf_value); - b = NM_FIREWALL_BACKEND_IPTABLES; - } - if (!g_atomic_int_compare_and_exchange(&backend, NM_FIREWALL_BACKEND_UNKNOWN, b)) goto again; From a79d5e2218d4adc40cb8794b56845b06cb5a2478 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 12 May 2021 12:02:33 +0200 Subject: [PATCH 9/9] firewall: add special firewall-backend "none" --- man/NetworkManager.conf.xml | 11 +++++++++-- src/core/nm-firewall-utils.c | 24 ++++++++++++++++++++---- src/core/nm-firewall-utils.h | 1 + 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index f88688e94b..d8fce34d6e 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -479,8 +479,15 @@ no-auto-default=* firewall-backend - The firewall backend for configuring masquerading. - Set to either iptables or nftables. + The firewall backend for configuring masquerading + with shared mode. + Set to either iptables, nftables + or none. + iptables and nftables + require iptables and nft + application, respectively. + none means to skip firewall configuration if + the users wish to manage firewall themselves. If unspecified, it will be auto detected. diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 22d52031a0..cc342b3f22 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -21,6 +21,10 @@ static const struct { const char *name; const char *path; } FirewallBackends[] = { + [NM_FIREWALL_BACKEND_NONE - 1] = + { + .name = "none", + }, [NM_FIREWALL_BACKEND_NFTABLES - 1] = { .name = "nftables", @@ -721,6 +725,8 @@ nm_firewall_config_apply(NMFirewallConfig *self, gboolean shared) case NM_FIREWALL_BACKEND_NFTABLES: _fw_nft_set(shared, self->ip_iface, self->addr, self->plen); break; + case NM_FIREWALL_BACKEND_NONE: + break; default: nm_assert_not_reached(); break; @@ -772,15 +778,22 @@ again: if (detect) b = _firewall_backend_detect(); - nm_assert(NM_IN_SET(b, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES)); + nm_assert(NM_IN_SET(b, + NM_FIREWALL_BACKEND_NONE, + NM_FIREWALL_BACKEND_IPTABLES, + NM_FIREWALL_BACKEND_NFTABLES)); if (!g_atomic_int_compare_and_exchange(&backend, NM_FIREWALL_BACKEND_UNKNOWN, b)) goto again; nm_log_dbg(LOGD_SHARING, - "firewall: use %s backend (%s)%s%s%s%s", + "firewall: use %s backend%s%s%s%s%s%s%s", FirewallBackends[b - 1].name, - FirewallBackends[b - 1].path, + NM_PRINT_FMT_QUOTED(FirewallBackends[b - 1].path, + " (", + FirewallBackends[b - 1].path, + ")", + ""), detect ? " (detected)" : "", NM_PRINT_FMT_QUOTED(detect && conf_value, " (invalid setting \"", @@ -789,6 +802,9 @@ again: "")); } - nm_assert(NM_IN_SET(b, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES)); + nm_assert(NM_IN_SET(b, + NM_FIREWALL_BACKEND_NONE, + NM_FIREWALL_BACKEND_IPTABLES, + NM_FIREWALL_BACKEND_NFTABLES)); return b; } diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h index f261aef384..3d6c8a6962 100644 --- a/src/core/nm-firewall-utils.h +++ b/src/core/nm-firewall-utils.h @@ -9,6 +9,7 @@ typedef enum { NM_FIREWALL_BACKEND_UNKNOWN, + NM_FIREWALL_BACKEND_NONE, NM_FIREWALL_BACKEND_IPTABLES, NM_FIREWALL_BACKEND_NFTABLES, } NMFirewallBackend;