merge: branch 'ih/forwarding'

core: ip forwarding: improve performance setting IPv4 forwarding for method=shared and forwarding=auto

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2201
This commit is contained in:
Íñigo Huguet 2025-10-24 05:27:36 +00:00
commit 384dd7d5f2
5 changed files with 180 additions and 145 deletions

View file

@ -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;
@ -792,7 +794,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)
@ -880,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);
@ -2166,8 +2169,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 +3817,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 +6684,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;
@ -8675,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,
@ -13199,17 +13204,12 @@ 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);
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)) {
@ -13529,15 +13529,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);
}
}
@ -13833,6 +13839,106 @@ _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_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;
nm_clear_g_signal_handler(NM_MANAGER_GET, &priv->sharing_ipv4_changed_id);
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);
}
}
/*****************************************************************************/
static void
act_request_set(NMDevice *self, NMActRequest *act_request)
{
@ -13945,6 +14051,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);
@ -17013,8 +17121,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 +17166,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 +17188,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 +19141,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 *

View file

@ -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__ */

View file

@ -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 *

View file

@ -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,

View file

@ -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)