From f2a2e49d0701025e9856bade75cfcc8cd2980294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Wed, 30 Apr 2025 12:21:23 +0200 Subject: [PATCH 1/5] Revert "policy: refresh IPv4 forwarding after connection activation and disconnection" This reverts commit 2ad5fbf0253d6f915a0e8815ca70ba4d9ec41648. It is actually a partial revert. The changes to documentation don't need to be reverted. Fixes: 2ad5fbf0253d ('policy: refresh IPv4 forwarding after connection activation and disconnection') --- src/core/devices/nm-device.c | 51 +++++++------------ src/core/devices/nm-device.h | 10 ---- src/core/nm-policy.c | 95 ++---------------------------------- 3 files changed, 21 insertions(+), 135 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 21ba692855..b9254516bd 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -792,7 +792,6 @@ typedef struct _NMDevicePrivate { char *prop_ip_iface; /* IP interface D-Bus property */ GList *ping_operations; GSource *ping_timeout; - bool refresh_forwarding_done : 1; } NMDevicePrivate; G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) @@ -2166,8 +2165,8 @@ _prop_get_ipvx_dhcp_send_hostname(NMDevice *self, int addr_family) return send_hostname_v2; } -NMSettingIPConfigForwarding -nm_device_get_ipv4_forwarding(NMDevice *self) +static NMSettingIPConfigForwarding +_prop_get_ipv4_forwarding(NMDevice *self) { NMSettingIPConfig *s_ip; NMSettingIPConfigForwarding forwarding; @@ -3814,7 +3813,7 @@ nm_device_assume_state_reset(NMDevice *self) /*****************************************************************************/ -char * +static char * nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property) { const char *ifname; @@ -6681,7 +6680,7 @@ concheck_update_state(NMDevice *self, } } -const char * +static const char * nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family) { NMDeviceClass *klass; @@ -13199,7 +13198,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) goto out_devip; if (IS_IPv4) { - NMSettingIPConfigForwarding ipv4_forwarding = nm_device_get_ipv4_forwarding(self); + NMSettingIPConfigForwarding ipv4_forwarding = _prop_get_ipv4_forwarding(self); if (NM_IN_SET(ipv4_forwarding, NM_SETTING_IP_CONFIG_FORWARDING_NO, @@ -13639,6 +13638,19 @@ _dev_ipshared4_init(NMDevice *self) break; } + if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), + -1) + == 1) { + /* nothing to do. */ + } else if (!nm_platform_sysctl_set(nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), + "1")) { + errsv = errno; + _LOGW_ipshared(AF_INET, "error enabling IPv4 forwarding: %s", nm_strerror_native(errsv)); + return FALSE; + } + if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_dynaddr"), -1) @@ -17013,8 +17025,6 @@ _cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cl priv->v4_route_table_all_sync_before = FALSE; priv->v6_route_table_all_sync_before = FALSE; - priv->refresh_forwarding_done = FALSE; - priv->mtu_force_set_done = FALSE; priv->needs_ip6_subnet = FALSE; @@ -17060,7 +17070,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu NMDevicePrivate *priv; NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self); int ifindex; - gint32 default_forwarding_v4; g_return_if_fail(NM_IS_DEVICE(self)); @@ -17083,17 +17092,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu nm_device_sysctl_ip_conf_set(self, AF_INET6, "use_tempaddr", "0"); } - /* Restoring the device's forwarding to the sysctl default is necessary because - * `refresh_forwarding()` only updates forwarding on activated devices. */ - default_forwarding_v4 = nm_platform_sysctl_get_int32( - nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"), - 0); - nm_device_sysctl_ip_conf_set(self, - AF_INET, - "forwarding", - default_forwarding_v4 == 1 ? "1" : "0"); - /* Call device type-specific deactivation */ if (klass->deactivate) klass->deactivate(self); @@ -19047,19 +19045,6 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean return nm_assert_unreachable_val(NULL); } -gboolean -nm_device_get_refresh_forwarding_done(NMDevice *self) -{ - return NM_DEVICE_GET_PRIVATE(self)->refresh_forwarding_done; -} - -void -nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->refresh_forwarding_done = is_refresh_forwarding_done; -} - /*****************************************************************************/ static const char * diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index 820951ea6a..8632944a2d 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -853,14 +853,4 @@ void nm_routing_rules_sync(NMConnection *applied_connection, NMDevice *self, NMNetns *netns); -NMSettingIPConfigForwarding nm_device_get_ipv4_forwarding(NMDevice *self); - -const char *nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family); - -char *nm_device_sysctl_ip_conf_get(NMDevice *self, int addr_family, const char *property); - -gboolean nm_device_get_refresh_forwarding_done(NMDevice *self); - -void nm_device_set_refresh_forwarding_done(NMDevice *self, gboolean is_refresh_forwarding_done); - #endif /* __NETWORKMANAGER_DEVICE_H__ */ diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 3893620811..f7be1a9f87 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -18,7 +18,6 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device.h" #include "devices/nm-device-factory.h" -#include "devices/nm-device-private.h" #include "dns/nm-dns-manager.h" #include "nm-act-request.h" #include "nm-auth-utils.h" @@ -98,6 +97,7 @@ typedef struct { bool updating_dns : 1; GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */ + } NMPolicyPrivate; struct _NMPolicy { @@ -2083,65 +2083,6 @@ unblock_autoconnect_for_ports_for_sett_conn(NMPolicy *self, NMSettingsConnection unblock_autoconnect_for_ports(self, controller_device, controller_uuid_settings, NULL); } -static void -refresh_forwarding(NMPolicy *self, NMDevice *device, gboolean is_activated_shared_device) -{ - NMActiveConnection *ac; - NMDevice *tmp_device; - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); - const CList *tmp_lst; - gboolean any_shared_active = false; - gint32 default_forwarding_v4; - const char *new_value = NULL; - - /* FIXME: This implementation is still inefficient because refresh_forwarding() - * is called every time a device goes up or down, requiring a full scan of all - * active connections to determine if any shared connection is active. */ - nm_manager_for_each_active_connection (priv->manager, ac, tmp_lst) { - NMSettingIPConfig *s_ip; - NMDevice *to_device = nm_active_connection_get_device(ac); - - if (to_device) { - s_ip = nm_device_get_applied_setting(to_device, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip) { - if (nm_streq0(nm_device_get_effective_ip_config_method(to_device, AF_INET), - NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - any_shared_active = true; - break; - } - } - } - } - - default_forwarding_v4 = nm_platform_sysctl_get_int32( - NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding"), - 0); - - new_value = any_shared_active ? "1" : (default_forwarding_v4 ? "1" : "0"); - - nm_manager_for_each_device (priv->manager, tmp_device, tmp_lst) { - NMDeviceState state; - NMSettingIPConfigForwarding ipv4_forwarding; - - state = nm_device_get_state(tmp_device); - if (state != NM_DEVICE_STATE_ACTIVATED) - continue; - - ipv4_forwarding = nm_device_get_ipv4_forwarding(tmp_device); - - if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_AUTO - || (device == tmp_device && is_activated_shared_device)) { - gs_free char *sysctl_value = NULL; - - sysctl_value = nm_device_sysctl_ip_conf_get(tmp_device, AF_INET, "forwarding"); - - if (!nm_streq0(sysctl_value, new_value)) - nm_device_sysctl_ip_conf_set(tmp_device, AF_INET, "forwarding", new_value); - } - } -} - static void activate_port_or_children_connections(NMPolicy *self, NMDevice *device, @@ -2286,9 +2227,8 @@ device_state_changed(NMDevice *device, NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF(priv); NMActiveConnection *ac; - NMSettingsConnection *sett_conn = nm_device_get_settings_connection(device); - NMSettingConnection *s_con = NULL; - gboolean is_activated_shared_device = FALSE; + NMSettingsConnection *sett_conn = nm_device_get_settings_connection(device); + NMSettingConnection *s_con = NULL; switch (nm_device_state_reason_check(reason)) { case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: @@ -2404,10 +2344,6 @@ device_state_changed(NMDevice *device, } } } - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_ACTIVATED: if (nm_device_get_device_type(device) == NM_DEVICE_TYPE_OVS_INTERFACE) { @@ -2443,20 +2379,11 @@ device_state_changed(NMDevice *device, update_system_hostname(self, "routing and dns", TRUE); nm_dns_manager_end_updates(priv->dns_manager, __func__); - is_activated_shared_device = - nm_streq0(nm_device_get_effective_ip_config_method(device, AF_INET), - NM_SETTING_IP4_CONFIG_METHOD_SHARED); - refresh_forwarding(self, device, is_activated_shared_device); - nm_device_set_refresh_forwarding_done(device, FALSE); break; case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: if (old_state > NM_DEVICE_STATE_DISCONNECTED) update_routing_and_dns(self, FALSE, device); - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_DEACTIVATING: if (sett_conn) { @@ -2492,10 +2419,6 @@ device_state_changed(NMDevice *device, } } ip6_remove_device_prefix_delegations(self, device); - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_DISCONNECTED: g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self); @@ -2512,10 +2435,6 @@ device_state_changed(NMDevice *device, /* Device is now available for auto-activation */ nm_policy_device_recheck_auto_activate_schedule(self, device); - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_PREPARE: @@ -2531,10 +2450,6 @@ device_state_changed(NMDevice *device, g_object_weak_unref(G_OBJECT(ac), pending_ac_gone, self); g_object_unref(self); } - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_IP_CONFIG: /* We must have secrets if we got here. */ @@ -2545,10 +2460,6 @@ device_state_changed(NMDevice *device, sett_conn, NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED, FALSE); - if (!nm_device_get_refresh_forwarding_done(device)) { - refresh_forwarding(self, device, FALSE); - nm_device_set_refresh_forwarding_done(device, TRUE); - } break; case NM_DEVICE_STATE_SECONDARIES: if (sett_conn) From d58d0a793e41e8740898929647a95f292b8ad9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Fri, 2 May 2025 08:37:16 +0200 Subject: [PATCH 2/5] ip: restore IP forwarding on device deconfiguration With the ipv4.forwarding property we may modify the forwarding sysctl of the device on activation. In next commits, we will also modify it if the connection is shared, instead of modifying the global forwarding. Restore the forwarding value to the default one when the device is deconfigured for any reason. Fixes: a8a2e6d727f0 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option') --- src/core/devices/nm-device.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index b9254516bd..dd481180df 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -13845,6 +13845,32 @@ _dev_ipshared6_start(NMDevice *self) /*****************************************************************************/ +static void +_dev_ipforwarding_cleanup(NMDevice *self, int addr_family, CleanupType cleanup_type) +{ + gs_free const char *default_forwarding = NULL; + gs_free const char *current_forwarding = NULL; + + if (!NM_IS_IPv4(addr_family)) + return; + + if (!NM_IN_SET(cleanup_type, CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP_REAPPLY)) + return; + + default_forwarding = nm_platform_sysctl_get( + nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding")); + + if (!default_forwarding) + return; /* Non fatal */ + + current_forwarding = nm_device_sysctl_ip_conf_get(self, AF_INET, "forwarding"); + if (!nm_streq0(current_forwarding, default_forwarding)) + nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", default_forwarding); +} + +/*****************************************************************************/ + static void act_request_set(NMDevice *self, NMActRequest *act_request) { @@ -13957,6 +13983,8 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gboolean keep_reapply = (cleanup_type == CLEANUP_TYPE_KEEP_REAPPLY); + _dev_ipforwarding_cleanup(self, addr_family, cleanup_type); + _dev_ipsharedx_cleanup(self, addr_family); _dev_ipdev_cleanup(self, AF_UNSPEC); From 8faa33b9d4419938198110210f20480cd0d3158c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Fri, 2 May 2025 09:15:00 +0200 Subject: [PATCH 3/5] manager: add sharing-ipv4-changed signal This signal notifies about the "sharing state", that's it, when there is at least one shared connection active or not. Each device informs to nm_manager when a shared connection is activated or deactivated and nm_manager emits this signal when the first shared connection is activated or the last one is deactivated. For now we're only interested in IPv4 forwarding as it's the only one that we need to track from nm_device (in following commits). Fixes: a8a2e6d727f0 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option') --- src/core/devices/nm-device.c | 14 ++++++++--- src/core/nm-manager.c | 49 ++++++++++++++++++++++++++++++++++++ src/core/nm-manager.h | 4 +++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index dd481180df..4edec0c462 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -13528,15 +13528,21 @@ nm_device_activate_schedule_stage3_ip_config(NMDevice *self, gboolean do_sync) static void _dev_ipsharedx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMDeviceIPState old_state = priv->ipshared_data_x[IS_IPv4].state; - if (priv->ipshared_data_x[IS_IPv4].state != state) { + if (old_state != state) { _LOGD_ipshared(addr_family, "set state %s (was %s)", nm_device_ip_state_to_string(state), - nm_device_ip_state_to_string(priv->ipshared_data_x[IS_IPv4].state)); + nm_device_ip_state_to_string(old_state)); priv->ipshared_data_x[IS_IPv4].state = state; + + if (old_state == NM_DEVICE_IP_STATE_READY || state == NM_DEVICE_IP_STATE_READY) + nm_manager_update_shared_connection(NM_MANAGER_GET, + addr_family, + state == NM_DEVICE_IP_STATE_READY); } } diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 060e0c2c7c..87dde2c3af 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -136,6 +136,7 @@ enum { ACTIVE_CONNECTION_REMOVED, CONFIGURE_QUIT, DEVICE_IFINDEX_CHANGED, + SHARING_IPV4_CHANGED, LAST_SIGNAL }; @@ -238,6 +239,8 @@ typedef struct { guint8 device_state_prune_ratelimit_count; + guint shared_connections_ip4_count; + bool startup : 1; bool devices_inited : 1; @@ -8829,6 +8832,41 @@ nm_manager_emit_device_ifindex_changed(NMManager *self, NMDevice *device) g_signal_emit(self, signals[DEVICE_IFINDEX_CHANGED], 0, device); } +void +nm_manager_update_shared_connection(NMManager *self, int addr_family, gboolean enabled) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + gboolean state_changed, state; + + /* Only IPv4 supported for the moment */ + if (addr_family != AF_INET) + return; + + if (enabled) { + g_return_if_fail(priv->shared_connections_ip4_count < G_MAXUINT); + priv->shared_connections_ip4_count++; + state_changed = priv->shared_connections_ip4_count == 1; + } else { + g_return_if_fail(priv->shared_connections_ip4_count > 0); + priv->shared_connections_ip4_count--; + state_changed = priv->shared_connections_ip4_count == 0; + } + + if (state_changed) { + state = priv->shared_connections_ip4_count > 0; + _LOGD(LOGD_SHARING, "sharing-ipv4 state change %d -> %d", !state, state); + g_signal_emit(self, signals[SHARING_IPV4_CHANGED], 0, state); + } +} + +gboolean +nm_manager_get_sharing_ipv4(NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); + + return priv->shared_connections_ip4_count > 0; +} + /*****************************************************************************/ NM_DEFINE_SINGLETON_REGISTER(NMManager); @@ -9932,6 +9970,17 @@ nm_manager_class_init(NMManagerClass *manager_class) G_TYPE_NONE, 1, NM_TYPE_DEVICE); + + signals[SHARING_IPV4_CHANGED] = g_signal_new(NM_MANAGER_SHARING_IPV4_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); } NMConfig * diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h index 3c5213c4f2..e10ca0d110 100644 --- a/src/core/nm-manager.h +++ b/src/core/nm-manager.h @@ -61,6 +61,7 @@ #define NM_MANAGER_CONFIGURE_QUIT "configure-quit" #define NM_MANAGER_INTERNAL_DEVICE_ADDED "internal-device-added" #define NM_MANAGER_INTERNAL_DEVICE_REMOVED "internal-device-removed" +#define NM_MANAGER_SHARING_IPV4_CHANGED "sharing-ipv4-changed" GType nm_manager_get_type(void); @@ -212,6 +213,9 @@ struct _NMDnsManager; struct _NMDnsManager *nm_manager_get_dns_manager(NMManager *self); +void nm_manager_update_shared_connection(NMManager *self, int addr_family, gboolean enabled); +gboolean nm_manager_get_sharing_ipv4(NMManager *self); + /*****************************************************************************/ void nm_manager_notify_delete_settings_connections(NMManager *self, From 32cbf4c629a3f857c7fa2e77e73a7c63942a6383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Fri, 2 May 2025 10:02:23 +0200 Subject: [PATCH 4/5] ip: shared: stop using the global forwarding As we introduced the ipv4.forwarding property in a8a2e6d727f0 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option'), we must not enable or disable the global forwarding setting in the kernel, as it affects to all the devices, maybe forcing them to behave in a way different to what the user requested in ipv4.forwarding. Instead, we need to selectively enable or disable the per-device forwarding settings. Specifically, only devices activated with ipv4.forwarding=auto must have their forwarding enabled or disabled depending on shared connections. Devices with yes/no must not be affected by shared connections. Also, devices with ipv4.forwarding=auto must get the proper forwarding value on activation, but also change it when shared connections appear or disappear dynamically. Use the new sharing-ipv4-change signal from nm_manager to achieve it. Fixes: a8a2e6d727f0 ('ip-config: Support configuring per-device IPv4 sysctl forwarding option') --- src/core/devices/nm-device.c | 126 ++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 33 deletions(-) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 4edec0c462..86cef32f60 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -705,6 +705,8 @@ typedef struct _NMDevicePrivate { IPDevStateData ipdev_data_unspec; + gulong sharing_ipv4_changed_id; + struct { /* If we set the addrgenmode6, this records the previously set value. */ guint8 previous_mode_val; @@ -879,6 +881,8 @@ static void _dev_ipshared4_spawn_dnsmasq(NMDevice *self); static void _dev_ipshared6_start(NMDevice *self); +static void _dev_ipforwarding4_start(NMDevice *self, int addr_family); + static void _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean preserve_dhcp); @@ -13198,17 +13202,12 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family) goto out_devip; if (IS_IPv4) { - NMSettingIPConfigForwarding ipv4_forwarding = _prop_get_ipv4_forwarding(self); - - if (NM_IN_SET(ipv4_forwarding, - NM_SETTING_IP_CONFIG_FORWARDING_NO, - NM_SETTING_IP_CONFIG_FORWARDING_YES)) { - nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", ipv4_forwarding ? "1" : "0"); - } priv->ipll_data_4.v4.mode = _prop_get_ipv4_link_local(self); if (priv->ipll_data_4.v4.mode == NM_SETTING_IP4_LL_ENABLED) _dev_ipll4_start(self); + _dev_ipforwarding4_start(self, addr_family); + if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) _dev_ipdhcpx_start(self, AF_INET); else if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { @@ -13644,19 +13643,6 @@ _dev_ipshared4_init(NMDevice *self) break; } - if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), - -1) - == 1) { - /* nothing to do. */ - } else if (!nm_platform_sysctl_set(nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"), - "1")) { - errsv = errno; - _LOGW_ipshared(AF_INET, "error enabling IPv4 forwarding: %s", nm_strerror_native(errsv)); - return FALSE; - } - if (nm_platform_sysctl_get_int32(nm_device_get_platform(self), NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_dynaddr"), -1) @@ -13851,28 +13837,102 @@ _dev_ipshared6_start(NMDevice *self) /*****************************************************************************/ +/** + * Set the device's forwarding to the specified value. If %NM_TERNARY_DEFAULT is specified, + * it's set to the kernel's default, otherwise it's set to the specific value. + */ static void -_dev_ipforwarding_cleanup(NMDevice *self, int addr_family, CleanupType cleanup_type) +_dev_ipforwarding4_set(NMDevice *self, NMTernary val) { gs_free const char *default_forwarding = NULL; gs_free const char *current_forwarding = NULL; + const char *val_str; + + if (val != NM_TERNARY_DEFAULT) { + val_str = val ? "1" : "0"; + } else { + default_forwarding = nm_platform_sysctl_get( + nm_device_get_platform(self), + NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding")); + + if (!default_forwarding) { + _LOGW(LOGD_DEVICE, + "error setting IPv4 forwarding: can't read default forwarding value: %s", + nm_strerror_native(errno)); + return; /* Non fatal */ + } + + val_str = default_forwarding; + } + + current_forwarding = nm_device_sysctl_ip_conf_get(self, AF_INET, "forwarding"); + if (nm_streq0(current_forwarding, val_str)) + return; + + if (!nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", val_str)) + _LOGW(LOGD_DEVICE, + "error setting IPv4 forwarding to '%s': %s", + val_str, + nm_strerror_native(errno)); +} + +static void +_dev_ipforwarding4_auto_cb(NMManager *manager, gboolean sharing_ipv4, gpointer data) +{ + NMDevice *self = NM_DEVICE(data); + + _dev_ipforwarding4_set(self, sharing_ipv4 ? NM_TERNARY_TRUE : NM_TERNARY_DEFAULT); +} + +static void +_dev_ipforwarding4_start(NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMSettingIPConfigForwarding ipv4_forwarding = _prop_get_ipv4_forwarding(self); + NMTernary new_forwarding = NM_TERNARY_DEFAULT; + + /* IPv6 per-interface forwarding not supported yet */ + if (addr_family != AF_INET) + return; + + if (nm_streq(priv->ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + new_forwarding = NM_TERNARY_TRUE; + } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_YES) { + new_forwarding = NM_TERNARY_TRUE; + } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_NO) { + new_forwarding = NM_TERNARY_FALSE; + } else if (ipv4_forwarding == NM_SETTING_IP_CONFIG_FORWARDING_AUTO) { + if (nm_manager_get_sharing_ipv4(NM_MANAGER_GET)) + new_forwarding = NM_TERNARY_TRUE; + else + new_forwarding = NM_TERNARY_DEFAULT; + + if (!priv->sharing_ipv4_changed_id) + priv->sharing_ipv4_changed_id = g_signal_connect(NM_MANAGER_GET, + NM_MANAGER_SHARING_IPV4_CHANGED, + G_CALLBACK(_dev_ipforwarding4_auto_cb), + self); + } else { + nm_assert_not_reached(); + } + + _dev_ipforwarding4_set(self, new_forwarding); +} + +static void +_dev_ipforwarding_cleanup(NMDevice *self, int addr_family, CleanupType cleanup_type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); if (!NM_IS_IPv4(addr_family)) return; - if (!NM_IN_SET(cleanup_type, CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP_REAPPLY)) - return; + nm_clear_g_signal_handler(NM_MANAGER_GET, &priv->sharing_ipv4_changed_id); - default_forwarding = nm_platform_sysctl_get( - nm_device_get_platform(self), - NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/conf/default/forwarding")); - - if (!default_forwarding) - return; /* Non fatal */ - - current_forwarding = nm_device_sysctl_ip_conf_get(self, AF_INET, "forwarding"); - if (!nm_streq0(current_forwarding, default_forwarding)) - nm_device_sysctl_ip_conf_set(self, AF_INET, "forwarding", default_forwarding); + if (NM_IN_SET(cleanup_type, CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP_REAPPLY)) { + /* Deconfigure by restoring kernel's default */ + _dev_ipforwarding4_set(self, NM_TERNARY_DEFAULT); + } } /*****************************************************************************/ From e06aaba1ca36209480845fcb74fd80875a95d3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Wed, 22 Oct 2025 13:23:37 +0200 Subject: [PATCH 5/5] core: mark as managed-type=removed when unrealize This is needed to ensure that the right CleanupType is chosen when calling to nm_device_state_changed() a bit later. With this change CLEANUP_TYPE_REMOVED will be used instead of CLEANUP_TYPE_DECONFIGURE, which is wrong because the device has already disappeared. --- src/core/devices/nm-device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 86cef32f60..69f4ddb6c9 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -8678,6 +8678,8 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) g_object_thaw_notify(G_OBJECT(self)); + nm_device_managed_type_set(self, NM_DEVICE_MANAGED_TYPE_REMOVED); + nm_device_set_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT, TRUE); nm_device_set_unmanaged_flags(self,