From e77a1df6e71e0a47a8bacb82e548ca0a7929fed2 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Thu, 27 Mar 2025 11:13:46 +0100 Subject: [PATCH 1/7] firewall/utils: fix ntf -> nft typo Fixes: 4badc1f33ace ('firewall: fix signalling timeout error reason from _fw_nft_call()') (cherry picked from commit e39e11963685ed589c4c837a9e1dce44d68145a0) --- src/core/nm-firewall-utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index b496061247..3b5b125dcf 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -466,13 +466,13 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user error->message); } else if (!call_data->timeout_source) { nm_log_dbg(LOGD_SHARING, - "firewall: ntf[%s]: communication timed out. Kill process", + "firewall: nft[%s]: communication timed out. Kill process", call_data->identifier); nm_clear_error(&error); nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "timeout communicating with nft"); } else { nm_log_dbg(LOGD_SHARING, - "firewall: ntf[%s]: communication cancelled. Kill process", + "firewall: nft[%s]: communication cancelled. Kill process", call_data->identifier); } From ff853203d9709b8319850ebf43cc63ffee8822b9 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Thu, 27 Mar 2025 11:14:51 +0100 Subject: [PATCH 2/7] firewall/utils: move logs from sharing to firewall domain (cherry picked from commit 10c2892d57e8b7c4e531e6f3a10ea6e3579cc3e6) --- src/core/nm-firewall-utils.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 3b5b125dcf..445dadb064 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -185,7 +185,7 @@ _share_iptables_call_v(const char *const *argv) gs_free char *argv_str = NULL; int status; - nm_log_dbg(LOGD_SHARING, "iptables: %s", (argv_str = g_strjoinv(" ", (char **) argv))); + nm_log_dbg(LOGD_FIREWALL, "iptables: %s", (argv_str = g_strjoinv(" ", (char **) argv))); if (!g_spawn_sync("/", (char **) argv, @@ -197,7 +197,7 @@ _share_iptables_call_v(const char *const *argv) NULL, &status, &error)) { - nm_log_warn(LOGD_SHARING, + nm_log_warn(LOGD_FIREWALL, "iptables: error executing command %s: %s", argv[0], error->message); @@ -205,7 +205,7 @@ _share_iptables_call_v(const char *const *argv) } if (!g_spawn_check_exit_status(status, &error)) { - nm_log_warn(LOGD_SHARING, "iptables: command %s failed: %s", argv[0], error->message); + nm_log_warn(LOGD_FIREWALL, "iptables: command %s failed: %s", argv[0], error->message); return FALSE; } @@ -460,18 +460,18 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user /* 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, + nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: communication failed: %s. Kill process", call_data->identifier, error->message); } else if (!call_data->timeout_source) { - nm_log_dbg(LOGD_SHARING, + nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: communication timed out. Kill process", call_data->identifier); nm_clear_error(&error); nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "timeout communicating with nft"); } else { - nm_log_dbg(LOGD_SHARING, + nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: communication cancelled. Kill process", call_data->identifier); } @@ -485,7 +485,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user 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); + nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: command successful", call_data->identifier); } else { char buf[NM_UTILS_GET_PROCESS_EXIT_STATUS_BUF_LEN]; gs_free char *ss_stdout = NULL; @@ -498,7 +498,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user nm_utils_get_process_exit_status_desc_buf(status, buf, sizeof(buf)); - nm_log_warn(LOGD_SHARING, + nm_log_warn(LOGD_FIREWALL, "firewall: nft[%s]: command %s:%s%s%s%s%s%s%s", call_data->identifier, buf, @@ -534,7 +534,7 @@ _fw_nft_call_cancelled_cb(GCancellable *cancellable, gpointer user_data) if (call_data->cancellable_id == 0) return; - nm_log_dbg(LOGD_SHARING, "firewall: nft[%s]: operation cancelled", call_data->identifier); + nm_log_dbg(LOGD_FIREWALL, "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); @@ -546,7 +546,7 @@ _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, + nm_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: cancel operation after timeout", call_data->identifier); @@ -573,7 +573,7 @@ nm_firewall_nft_call(GBytes *stdin_buf, .timeout_source = NULL, }; - nm_log_trace(LOGD_SHARING, + nm_log_trace(LOGD_FIREWALL, "firewall: nft: call command: [ '%s' ]", nm_utils_buf_utf8safe_escape_bytes(stdin_buf, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, @@ -585,7 +585,7 @@ nm_firewall_nft_call(GBytes *stdin_buf, call_data, NULL); if (call_data->cancellable_id == 0) { - nm_log_dbg(LOGD_SHARING, "firewall: nft: already cancelled"); + nm_log_dbg(LOGD_FIREWALL, "firewall: nft: already cancelled"); nm_utils_error_set_cancelled(&error, FALSE, NULL); _fw_nft_call_data_free(call_data, g_steal_pointer(&error)); return; @@ -602,14 +602,14 @@ nm_firewall_nft_call(GBytes *stdin_buf, &error); if (!call_data->subprocess) { - nm_log_dbg(LOGD_SHARING, "firewall: nft: spawning nft failed: %s", error->message); + nm_log_dbg(LOGD_FIREWALL, "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_log_dbg(LOGD_FIREWALL, "firewall: nft[%s]: communicate with nft", call_data->identifier); nm_shutdown_wait_obj_register_object(call_data->task, "nft-call"); @@ -1124,7 +1124,7 @@ again: if (!g_atomic_int_compare_and_exchange(&backend, NM_FIREWALL_BACKEND_UNKNOWN, b)) goto again; - nm_log_dbg(LOGD_SHARING, + nm_log_dbg(LOGD_FIREWALL, "firewall: use %s backend%s%s%s%s%s%s%s", FirewallBackends[b - 1].name, NM_PRINT_FMT_QUOTED(FirewallBackends[b - 1].path, From 57321f78c9aaad23099e80f7945ef996acbaa7cc Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 11 Mar 2025 11:18:59 +0100 Subject: [PATCH 3/7] build: add path definition for ip6tables (cherry picked from commit 0f469b30ad7d3b0daf47dfd9e868bc1b5cfd89b2) --- config.h.meson | 3 +++ contrib/fedora/rpm/NetworkManager.spec | 1 + contrib/fedora/rpm/build_clean.sh | 1 + contrib/fedora/rpm/configure-for-system.sh | 1 + meson.build | 2 ++ meson_options.txt | 1 + src/core/tests/test-core.c | 1 + 7 files changed, 10 insertions(+) diff --git a/config.h.meson b/config.h.meson index e59160dec5..9ae88b26bf 100644 --- a/config.h.meson +++ b/config.h.meson @@ -67,6 +67,9 @@ /* Define to path of iptables binary */ #mesondefine IPTABLES_PATH +/* Define to path of ip6tables binary */ +#mesondefine IP6TABLES_PATH + /* Define to path of nft binary */ #mesondefine NFT_PATH diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 2af0f44602..22a1a49cf8 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -597,6 +597,7 @@ Preferably use nmcli instead. %endif -Dnft=%{_sbindir}/nft \ -Diptables=%{_sbindir}/iptables \ + -Dip6tables=%{_sbindir}/ip6tables \ %if %{with dhclient} -Ddhclient=%{_sbindir}/dhclient \ %else diff --git a/contrib/fedora/rpm/build_clean.sh b/contrib/fedora/rpm/build_clean.sh index b1944d7780..2dbca0e7d2 100755 --- a/contrib/fedora/rpm/build_clean.sh +++ b/contrib/fedora/rpm/build_clean.sh @@ -222,6 +222,7 @@ if [[ $NO_DIST != 1 ]]; then -Dconfig_dhcp_default=internal \ -Dconfig_dns_rc_manager_default=auto \ -Diptables=/usr/sbin/iptables \ + -Dip6tables=/usr/sbin/ip6tables \ -Dnft=/usr/bin/nft \ || die "Error meson setup" diff --git a/contrib/fedora/rpm/configure-for-system.sh b/contrib/fedora/rpm/configure-for-system.sh index fff426bcad..f0638591bd 100755 --- a/contrib/fedora/rpm/configure-for-system.sh +++ b/contrib/fedora/rpm/configure-for-system.sh @@ -368,6 +368,7 @@ meson setup\ $(args_enable "$P_TEST" --werror) \ -Dnft="${D_SBINDIR}/nft" \ -Diptables="${D_SBINDIR}/iptables" \ + -Dip6tables="${D_SBINDIR}/ip6tables" \ -Ddhclient="${D_SBINDIR}/dhclient" \ -Ddhcpcd=no \ -Dconfig_dhcp_default="$P_DHCP_DEFAULT" \ diff --git a/meson.build b/meson.build index 52bd072b0c..e8d1431b19 100644 --- a/meson.build +++ b/meson.build @@ -723,6 +723,7 @@ default_paths = ['/sbin', '/usr/sbin'] # 0: cmdline option, 1: paths, 2: fallback progs = [['iptables', default_paths, '/usr/sbin/iptables'], + ['ip6tables', default_paths, '/usr/sbin/ip6tables'], ['nft', default_paths, '/usr/sbin/nft'], ['dnsmasq', default_paths, ''], ['modprobe', default_paths, '/sbin/modprobe'] @@ -1125,6 +1126,7 @@ endif output += '\n' output += ' jansson: ' + jansson_msg + '\n' output += ' iptables: ' + config_h.get('IPTABLES_PATH') + '\n' +output += ' ip6tables: ' + config_h.get('IP6TABLES_PATH') + '\n' output += ' nft: ' + config_h.get('NFT_PATH') + '\n' output += ' modprobe: ' + config_h.get('MODPROBE_PATH') + '\n' output += ' modemmanager-1: ' + enable_modem_manager.to_string() + '\n' diff --git a/meson_options.txt b/meson_options.txt index 4bc11aa08b..fe696aaf16 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,6 +6,7 @@ option('dbus_conf_dir', type: 'string', value: '', description: 'where D-Bus sys option('kernel_firmware_dir', type: 'string', value: '/lib/firmware', description: 'where kernel firmware directory is (default is /lib/firmware)') option('runtime_dir', type: 'string', value: '', description: 'Directory for transient runtime state [default: LOCALSTATEDIR/run or /run]') option('iptables', type: 'string', value: '', description: 'path to iptables') +option('ip6tables', type: 'string', value: '', description: 'path to ip6tables') option('nft', type: 'string', value: '', description: 'path to nft') option('dnsmasq', type: 'string', value: '', description: 'path to dnsmasq') option('modprobe', type: 'string', value: '', description: 'path to modprobe') diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index 2ff41f0067..e08296c20f 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -36,6 +36,7 @@ test_config_h(void) G_STMT_END ABSOLUTE_PATH(IPTABLES_PATH); + ABSOLUTE_PATH(IP6TABLES_PATH); ABSOLUTE_PATH(NFT_PATH); } From 2afcebe0c770c2d84faee3b9fb84a5f71f253e6d Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 11 Mar 2025 11:33:55 +0100 Subject: [PATCH 4/7] wireguard: add firewall rules to copy mark When a WG connection is connecting to an IPv6 endpoint, configures a default route, and firewalld is active with IPv6_rpfilter=yes, it never handshakes and doesn't pass traffic. This is because firewalld has a IPv6 reverse path filter which is discarding these packets. Thus, we add some firewall rules whenever a WG connection is brought up that ensure the conntrack mark and packet mark are copied over. These rules are largely inspired by wg-quick: https://git.zx2c4.com/wireguard-tools/tree/src/wg-quick/linux.bash?id=17c78d31c27a3c311a2ff42a881057753c6ef2a4#n221 (cherry picked from commit db557908a2b70ee05ba7d8afe4b388a595109f34) --- src/core/devices/nm-device-wireguard.c | 41 +++++++++ src/core/nm-firewall-utils.c | 114 ++++++++++++++++++++++++- src/core/nm-firewall-utils.h | 2 + 3 files changed, 155 insertions(+), 2 deletions(-) diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c index 4a08192e03..52a35655c0 100644 --- a/src/core/devices/nm-device-wireguard.c +++ b/src/core/devices/nm-device-wireguard.c @@ -23,6 +23,7 @@ #include "nm-active-connection.h" #include "nm-act-request.h" #include "dns/nm-dns-manager.h" +#include "nm-firewall-utils.h" #define _NMLOG_DEVICE_TYPE NMDeviceWireGuard #include "nm-device-logging.h" @@ -1207,6 +1208,25 @@ skip: *out_allowed_ips_data = g_steal_pointer(&allowed_ips); } +static void +_configure_firewall(NMDeviceWireGuard *self, NMConnection *connection, int addr_family, gboolean up) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); + const char *ip_iface; + + if (addr_family == AF_INET && !priv->auto_default_route_enabled_4) + return; + else if (addr_family == AF_INET6 && !priv->auto_default_route_enabled_6) + return; + + ip_iface = nm_device_get_ip_iface(NM_DEVICE(self)); + + nm_assert(priv->auto_default_route_fwmark); + nm_assert(ip_iface); + + nm_firewall_config_set_wg_rule(ip_iface, addr_family, priv->auto_default_route_fwmark, up); +} + /*****************************************************************************/ static void @@ -1300,6 +1320,18 @@ create_and_realize(NMDevice *device, return TRUE; } +static void +deactivate(NMDevice *device) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD(device); + NMConnection *connection = nm_device_get_applied_connection(NM_DEVICE(self)); + + if (connection) { + _configure_firewall(self, connection, AF_INET, FALSE); + _configure_firewall(self, connection, AF_INET6, FALSE); + } +} + /*****************************************************************************/ static void @@ -1768,6 +1800,10 @@ act_stage3_ip_config(NMDevice *device, int addr_family) nm_auto_unref_l3cd const NML3ConfigData *l3cd = NULL; l3cd = _get_dev2_ip_config(NM_DEVICE_WIREGUARD(device), addr_family); + _configure_firewall(NM_DEVICE_WIREGUARD(device), + nm_device_get_applied_connection(device), + addr_family, + TRUE); nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); } @@ -1866,6 +1902,10 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne if (state >= NM_DEVICE_STATE_CONFIG) { priv->auto_default_route_refresh = TRUE; + + _configure_firewall(self, con_old, AF_INET, FALSE); + _configure_firewall(self, con_old, AF_INET6, FALSE); + link_config(NM_DEVICE_WIREGUARD(device), "reapply", LINK_CONFIG_MODE_REAPPLY, NULL); } @@ -2018,6 +2058,7 @@ nm_device_wireguard_class_init(NMDeviceWireGuardClass *klass) device_class->state_changed = device_state_changed; device_class->create_and_realize = create_and_realize; + device_class->deactivate = deactivate; device_class->act_stage2_config = act_stage2_config; device_class->act_stage2_config_also_for_external_or_assume = TRUE; device_class->act_stage3_ip_config = act_stage3_ip_config; diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 445dadb064..7b3b4a1032 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -179,7 +179,7 @@ _share_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const c /*****************************************************************************/ static gboolean -_share_iptables_call_v(const char *const *argv) +_iptables_call_v(const char *const *argv) { gs_free_error GError *error = NULL; gs_free char *argv_str = NULL; @@ -213,7 +213,14 @@ _share_iptables_call_v(const char *const *argv) } #define _share_iptables_call(...) \ - _share_iptables_call_v(NM_MAKE_STRV("" IPTABLES_PATH "", "--wait", "2", __VA_ARGS__)) + _iptables_call_v(NM_MAKE_STRV("" IPTABLES_PATH "", "--wait", "2", __VA_ARGS__)) + +#define _ipxtables_call(family, ...) \ + _iptables_call_v( \ + NM_MAKE_STRV((family == AF_INET ? "" IPTABLES_PATH "" : "" IP6TABLES_PATH ""), \ + "--wait", \ + "2", \ + __VA_ARGS__)) static gboolean _share_iptables_chain_op(const char *table, const char *chain, const char *op) @@ -756,6 +763,85 @@ _fw_nft_set_shared_construct(gboolean up, const char *ip_iface, in_addr_t addr, return nm_str_buf_finalize_to_gbytes(&strbuf); } +static GBytes * +_fw_nft_wg_default_construct(const char *ip_iface, int family, int fwmark, gboolean up) +{ + nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); + gs_free char *table_name = NULL; + const char *family_str = family == AF_INET ? "ip" : "ip6"; + + table_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); + + _fw_nft_append_cmd_table(&strbuf, family_str, table_name, up); + + if (up) { + _append(&strbuf, + "add chain %s %s premangle {" + " type filter hook prerouting priority mangle; policy accept; " + " meta l4proto udp meta mark set ct mark; " + "};", + family_str, + table_name); + + _append(&strbuf, + "add chain %s %s postmangle {" + " type filter hook postrouting priority mangle; policy accept; " + " meta l4proto udp mark 0x%08x ct mark set meta mark; " + "};", + family_str, + table_name, + fwmark); + } + + return nm_str_buf_finalize_to_gbytes(&strbuf); +} + +static void +_fw_iptables_wg_configure(const char *ip_iface, int family, int fwmark, gboolean up) +{ + gs_free char *comment_name = NULL; + char fwmark_str[11]; + + comment_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); + g_snprintf(fwmark_str, sizeof(fwmark_str), "%" G_GUINT32_FORMAT, fwmark); + + nm_assert(strlen(fwmark_str) > 0); + + _ipxtables_call(family, + "--table", + "mangle", + up ? "--insert" : "--delete", + "POSTROUTING", + "--match", + "mark", + "--mark", + fwmark_str, + "--protocol", + "udp", + "--jump", + "CONNMARK", + "--save-mark", + "-m", + "comment", + "--comment", + comment_name); + + _ipxtables_call(family, + "--table", + "mangle", + up ? "--insert" : "--delete", + "PREROUTING", + "--protocol", + "udp", + "--jump", + "CONNMARK", + "--restore-mark", + "-m", + "comment", + "--comment", + comment_name); +} + /*****************************************************************************/ GBytes * @@ -1046,6 +1132,30 @@ nm_firewall_config_free(NMFirewallConfig *self) } /*****************************************************************************/ +void +nm_firewall_config_set_wg_rule(const char *ifname, int family, int fwmark, gboolean up) +{ + nm_assert(NM_IN_SET(family, AF_INET, AF_INET6)); + + switch (nm_firewall_utils_get_backend()) { + case NM_FIREWALL_BACKEND_NFTABLES: + { + gs_unref_bytes GBytes *stdin_buf = NULL; + + stdin_buf = _fw_nft_wg_default_construct(ifname, family, fwmark, up); + _fw_nft_call_sync(stdin_buf, NULL); + break; + } + case NM_FIREWALL_BACKEND_IPTABLES: + _fw_iptables_wg_configure(ifname, family, fwmark, up); + break; + case NM_FIREWALL_BACKEND_NONE: + break; + default: + nm_assert_not_reached(); + break; + } +} void nm_firewall_config_apply_sync(NMFirewallConfig *self, gboolean up) diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h index 9f13a5127e..f46b3666fa 100644 --- a/src/core/nm-firewall-utils.h +++ b/src/core/nm-firewall-utils.h @@ -24,6 +24,8 @@ NMFirewallConfig *nm_firewall_config_new_shared(const char *ip_iface, in_addr_t void nm_firewall_config_free(NMFirewallConfig *self); +void nm_firewall_config_set_wg_rule(const char *ifname, int family, int fwmark, gboolean up); + void nm_firewall_config_apply_sync(NMFirewallConfig *self, gboolean up); /*****************************************************************************/ From 4d0223f8a4cabc961bff6588adc7cbbfd3adad00 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Tue, 25 Mar 2025 14:00:53 +0100 Subject: [PATCH 5/7] firewall/wireguard: drop packets received to wrong interface If we receive a packet sent to the WG interface's address, but it does not come from the WG tunnel, let's assume something is broken and drop the packet. This is also inspired by wg-quick firewall rules: https://git.zx2c4.com/wireguard-tools/tree/src/wg-quick/linux.bash?id=17c78d31c27a3c311a2ff42a881057753c6ef2a4#n221 (cherry picked from commit a769c17af710ca10c0f4e66d798cc67296ba04da) --- src/core/devices/nm-device-wireguard.c | 29 +++++++--- src/core/nm-firewall-utils.c | 74 +++++++++++++++++++++++--- src/core/nm-firewall-utils.h | 5 +- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c index 52a35655c0..299e3b30e8 100644 --- a/src/core/devices/nm-device-wireguard.c +++ b/src/core/devices/nm-device-wireguard.c @@ -1213,18 +1213,33 @@ _configure_firewall(NMDeviceWireGuard *self, NMConnection *connection, int addr_ { NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(self); const char *ip_iface; - - if (addr_family == AF_INET && !priv->auto_default_route_enabled_4) - return; - else if (addr_family == AF_INET6 && !priv->auto_default_route_enabled_6) - return; + NMSettingIPConfig *ip_config; ip_iface = nm_device_get_ip_iface(NM_DEVICE(self)); - nm_assert(priv->auto_default_route_fwmark); nm_assert(ip_iface); - nm_firewall_config_set_wg_rule(ip_iface, addr_family, priv->auto_default_route_fwmark, up); + switch (addr_family) { + case AF_INET: + if (!priv->auto_default_route_enabled_4) + return; + + ip_config = nm_connection_get_setting_ip4_config(connection); + break; + case AF_INET6: + if (!priv->auto_default_route_enabled_6) + return; + + ip_config = nm_connection_get_setting_ip6_config(connection); + break; + default: + nm_assert_not_reached(); + } + + nm_assert(ip_config); + nm_assert(priv->auto_default_route_fwmark); + + nm_firewall_config_set_wg_rule(ip_iface, ip_config, priv->auto_default_route_fwmark, up); } /*****************************************************************************/ diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 7b3b4a1032..03d246593f 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -8,6 +8,7 @@ #include "nm-firewall-utils.h" +#include "libnm-core-aux-intern/nm-libnm-core-utils.h" #include "libnm-glib-aux/nm-str-buf.h" #include "libnm-glib-aux/nm-io-utils.h" #include "libnm-platform/nm-platform.h" @@ -764,17 +765,42 @@ _fw_nft_set_shared_construct(gboolean up, const char *ip_iface, in_addr_t addr, } static GBytes * -_fw_nft_wg_default_construct(const char *ip_iface, int family, int fwmark, gboolean up) +_fw_nft_wg_default_construct(const char *ip_iface, + NMSettingIPConfig *ip_config, + int fwmark, + gboolean up) { nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); gs_free char *table_name = NULL; - const char *family_str = family == AF_INET ? "ip" : "ip6"; + const char *family_str; table_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); + family_str = nm_setting_ip_config_get_addr_family(ip_config) == AF_INET ? "ip" : "ip6"; _fw_nft_append_cmd_table(&strbuf, family_str, table_name, up); if (up) { + guint n_addresses = nm_setting_ip_config_get_num_addresses(ip_config); + + if (n_addresses) { + _append(&strbuf, "add chain %s %s preraw {", family_str, table_name); + + for (guint i = 0; i < n_addresses; i++) { + NMIPAddress *addr = nm_setting_ip_config_get_address(ip_config, i); + + _append(&strbuf, + " iifname != \"%s\" " + " %s daddr %s " + " fib saddr type != local " + "drop;", + ip_iface, + family_str, + nm_ip_address_get_address(addr)); + } + + _append(&strbuf, "};"); + } + _append(&strbuf, "add chain %s %s premangle {" " type filter hook prerouting priority mangle; policy accept; " @@ -797,16 +823,47 @@ _fw_nft_wg_default_construct(const char *ip_iface, int family, int fwmark, gbool } static void -_fw_iptables_wg_configure(const char *ip_iface, int family, int fwmark, gboolean up) +_fw_iptables_wg_configure(const char *ip_iface, + NMSettingIPConfig *ip_config, + int fwmark, + gboolean up) { gs_free char *comment_name = NULL; char fwmark_str[11]; + int family = nm_setting_ip_config_get_addr_family(ip_config); + guint n_addresses = nm_setting_ip_config_get_num_addresses(ip_config); comment_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); g_snprintf(fwmark_str, sizeof(fwmark_str), "%" G_GUINT32_FORMAT, fwmark); nm_assert(strlen(fwmark_str) > 0); + for (guint i = 0; i < n_addresses; i++) { + NMIPAddress *addr = nm_setting_ip_config_get_address(ip_config, i); + + _ipxtables_call(family, + "--table", + "raw", + up ? "--insert" : "--delete", + "PREROUTING", + "!", + "--in-interface", + ip_iface, + "--destination", + nm_ip_address_get_address(addr), + "--match", + "addrtype", + "!", + "--src-type", + "LOCAL", + "-j", + "DROP", + "-m", + "comment", + "--comment", + comment_name); + } + _ipxtables_call(family, "--table", "mangle", @@ -1133,21 +1190,22 @@ nm_firewall_config_free(NMFirewallConfig *self) /*****************************************************************************/ void -nm_firewall_config_set_wg_rule(const char *ifname, int family, int fwmark, gboolean up) +nm_firewall_config_set_wg_rule(const char *ifname, + NMSettingIPConfig *ip_config, + int fwmark, + gboolean up) { - nm_assert(NM_IN_SET(family, AF_INET, AF_INET6)); - switch (nm_firewall_utils_get_backend()) { case NM_FIREWALL_BACKEND_NFTABLES: { gs_unref_bytes GBytes *stdin_buf = NULL; - stdin_buf = _fw_nft_wg_default_construct(ifname, family, fwmark, up); + stdin_buf = _fw_nft_wg_default_construct(ifname, ip_config, fwmark, up); _fw_nft_call_sync(stdin_buf, NULL); break; } case NM_FIREWALL_BACKEND_IPTABLES: - _fw_iptables_wg_configure(ifname, family, fwmark, up); + _fw_iptables_wg_configure(ifname, ip_config, fwmark, up); break; case NM_FIREWALL_BACKEND_NONE: break; diff --git a/src/core/nm-firewall-utils.h b/src/core/nm-firewall-utils.h index f46b3666fa..4df33d11db 100644 --- a/src/core/nm-firewall-utils.h +++ b/src/core/nm-firewall-utils.h @@ -24,7 +24,10 @@ NMFirewallConfig *nm_firewall_config_new_shared(const char *ip_iface, in_addr_t void nm_firewall_config_free(NMFirewallConfig *self); -void nm_firewall_config_set_wg_rule(const char *ifname, int family, int fwmark, gboolean up); +void nm_firewall_config_set_wg_rule(const char *ifname, + NMSettingIPConfig *ip_config, + int fwmark, + gboolean up); void nm_firewall_config_apply_sync(NMFirewallConfig *self, gboolean up); From 3f2c0869dca4b7098964e7e2d69bf6d2bd0db34d Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Thu, 27 Mar 2025 11:17:59 +0100 Subject: [PATCH 6/7] firewall/utils: remove _share prefix from iptables_get_name It's no longer used just for shared mode. (cherry picked from commit 18d5b7d641f52024c16a31009434ea86a59c27b0) --- src/core/nm-firewall-utils.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index 03d246593f..a312ebc98b 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -128,7 +128,7 @@ _share_iptables_subnet_to_str(char buf[static _SHARE_IPTABLES_SUBNET_TO_STR } static char * -_share_iptables_get_name(gboolean is_iptables_chain, const char *prefix, const char *ip_iface) +_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; @@ -252,7 +252,7 @@ _share_iptables_set_masquerade_sync(gboolean up, const char *ip_iface, in_addr_t char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN]; gs_free char *comment_name = NULL; - comment_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface); + comment_name = _iptables_get_name(FALSE, "nm-shared", ip_iface); _share_iptables_subnet_to_str(str_subnet, addr, plen); _share_iptables_call("--table", @@ -385,9 +385,9 @@ _share_iptables_set_shared_sync(gboolean up, const char *ip_iface, in_addr_t add gs_free char *chain_input = NULL; gs_free char *chain_forward = NULL; - 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); + comment_name = _iptables_get_name(FALSE, "nm-shared", ip_iface); + chain_input = _iptables_get_name(TRUE, "nm-sh-in", ip_iface); + chain_forward = _iptables_get_name(TRUE, "nm-sh-fw", ip_iface); if (up) _share_iptables_set_shared_chains_add(chain_input, chain_forward, ip_iface, addr, plen); @@ -699,7 +699,7 @@ _fw_nft_set_shared_construct(gboolean up, const char *ip_iface, in_addr_t addr, gs_free char *table_name = NULL; char str_subnet[_SHARE_IPTABLES_SUBNET_TO_STR_LEN]; - table_name = _share_iptables_get_name(FALSE, "nm-shared", ip_iface); + table_name = _iptables_get_name(FALSE, "nm-shared", ip_iface); _share_iptables_subnet_to_str(str_subnet, addr, plen); @@ -774,7 +774,7 @@ _fw_nft_wg_default_construct(const char *ip_iface, gs_free char *table_name = NULL; const char *family_str; - table_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); + table_name = _iptables_get_name(FALSE, "nm-wg", ip_iface); family_str = nm_setting_ip_config_get_addr_family(ip_config) == AF_INET ? "ip" : "ip6"; _fw_nft_append_cmd_table(&strbuf, family_str, table_name, up); @@ -833,7 +833,7 @@ _fw_iptables_wg_configure(const char *ip_iface, int family = nm_setting_ip_config_get_addr_family(ip_config); guint n_addresses = nm_setting_ip_config_get_num_addresses(ip_config); - comment_name = _share_iptables_get_name(FALSE, "nm-wg", ip_iface); + comment_name = _iptables_get_name(FALSE, "nm-wg", ip_iface); g_snprintf(fwmark_str, sizeof(fwmark_str), "%" G_GUINT32_FORMAT, fwmark); nm_assert(strlen(fwmark_str) > 0); From ae420a8dd633373373ce17a63b5ac926eee9b7b8 Mon Sep 17 00:00:00 2001 From: Jan Vaclav Date: Thu, 27 Mar 2025 11:21:45 +0100 Subject: [PATCH 7/7] firewall/utils: replace ipv4 iptables macro with ipxtables macro (cherry picked from commit 2106251e46397be029e43ceb27d0f28b990124b7) --- src/core/nm-firewall-utils.c | 208 ++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 101 deletions(-) diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c index a312ebc98b..60f40228db 100644 --- a/src/core/nm-firewall-utils.c +++ b/src/core/nm-firewall-utils.c @@ -213,9 +213,6 @@ _iptables_call_v(const char *const *argv) return TRUE; } -#define _share_iptables_call(...) \ - _iptables_call_v(NM_MAKE_STRV("" IPTABLES_PATH "", "--wait", "2", __VA_ARGS__)) - #define _ipxtables_call(family, ...) \ _iptables_call_v( \ NM_MAKE_STRV((family == AF_INET ? "" IPTABLES_PATH "" : "" IP6TABLES_PATH ""), \ @@ -226,7 +223,7 @@ _iptables_call_v(const char *const *argv) static gboolean _share_iptables_chain_op(const char *table, const char *chain, const char *op) { - return _share_iptables_call("--table", table, op, chain); + return _ipxtables_call(AF_INET, "--table", table, op, chain); } static gboolean @@ -255,21 +252,22 @@ _share_iptables_set_masquerade_sync(gboolean up, const char *ip_iface, in_addr_t comment_name = _iptables_get_name(FALSE, "nm-shared", ip_iface); _share_iptables_subnet_to_str(str_subnet, addr, plen); - _share_iptables_call("--table", - "nat", - up ? "--insert" : "--delete", - "POSTROUTING", - "--source", - str_subnet, - "!", - "--destination", - str_subnet, - "--jump", - "MASQUERADE", - "-m", - "comment", - "--comment", - comment_name); + _ipxtables_call(AF_INET, + "--table", + "nat", + up ? "--insert" : "--delete", + "POSTROUTING", + "--source", + str_subnet, + "!", + "--destination", + str_subnet, + "--jump", + "MASQUERADE", + "-m", + "comment", + "--comment", + comment_name); } static void @@ -305,70 +303,76 @@ _share_iptables_set_shared_chains_add(const char *chain_input, _share_iptables_chain_add("filter", chain_input); for (i = 0; i < (int) G_N_ELEMENTS(input_params); i++) { - _share_iptables_call("--table", - "filter", - "--append", - chain_input, - "--protocol", - input_params[i][0], - "--destination-port", - input_params[i][1], - "--jump", - "ACCEPT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_input, + "--protocol", + input_params[i][0], + "--destination-port", + input_params[i][1], + "--jump", + "ACCEPT"); } _share_iptables_chain_add("filter", chain_forward); - _share_iptables_call("--table", - "filter", - "--append", - chain_forward, - "--destination", - str_subnet, - "--out-interface", - ip_iface, - "--match", - "state", - "--state", - "ESTABLISHED,RELATED", - "--jump", - "ACCEPT"); - _share_iptables_call("--table", - "filter", - "--append", - chain_forward, - "--source", - str_subnet, - "--in-interface", - ip_iface, - "--jump", - "ACCEPT"); - _share_iptables_call("--table", - "filter", - "--append", - chain_forward, - "--in-interface", - ip_iface, - "--out-interface", - ip_iface, - "--jump", - "ACCEPT"); - _share_iptables_call("--table", - "filter", - "--append", - chain_forward, - "--out-interface", - ip_iface, - "--jump", - "REJECT"); - _share_iptables_call("--table", - "filter", - "--append", - chain_forward, - "--in-interface", - ip_iface, - "--jump", - "REJECT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_forward, + "--destination", + str_subnet, + "--out-interface", + ip_iface, + "--match", + "state", + "--state", + "ESTABLISHED,RELATED", + "--jump", + "ACCEPT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_forward, + "--source", + str_subnet, + "--in-interface", + ip_iface, + "--jump", + "ACCEPT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_forward, + "--in-interface", + ip_iface, + "--out-interface", + ip_iface, + "--jump", + "ACCEPT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_forward, + "--out-interface", + ip_iface, + "--jump", + "REJECT"); + _ipxtables_call(AF_INET, + "--table", + "filter", + "--append", + chain_forward, + "--in-interface", + ip_iface, + "--jump", + "REJECT"); } static void @@ -392,29 +396,31 @@ _share_iptables_set_shared_sync(gboolean up, const char *ip_iface, in_addr_t add if (up) _share_iptables_set_shared_chains_add(chain_input, chain_forward, ip_iface, addr, plen); - _share_iptables_call("--table", - "filter", - up ? "--insert" : "--delete", - "INPUT", - "--in-interface", - ip_iface, - "--jump", - chain_input, - "-m", - "comment", - "--comment", - comment_name); + _ipxtables_call(AF_INET, + "--table", + "filter", + up ? "--insert" : "--delete", + "INPUT", + "--in-interface", + ip_iface, + "--jump", + chain_input, + "-m", + "comment", + "--comment", + comment_name); - _share_iptables_call("--table", - "filter", - up ? "--insert" : "--delete", - "FORWARD", - "--jump", - chain_forward, - "-m", - "comment", - "--comment", - comment_name); + _ipxtables_call(AF_INET, + "--table", + "filter", + up ? "--insert" : "--delete", + "FORWARD", + "--jump", + chain_forward, + "-m", + "comment", + "--comment", + comment_name); if (!up) _share_iptables_set_shared_chains_delete(chain_input, chain_forward);