diff options
| author | Michael Biebl <biebl@debian.org> | 2025-08-01 19:15:13 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2025-08-01 19:15:13 +0200 |
| commit | 7e9091a5960f67a84787c03153324915220e4816 (patch) | |
| tree | dc743de962532932ebc3b4ed3a36ddd093d97d68 /src/core/nm-policy.c | |
| parent | 582eb4472a0f3375042afb9026cbeb50e058a27d (diff) | |
New upstream version 1.54.0 upstream/1.54.0
Diffstat (limited to 'src/core/nm-policy.c')
| -rw-r--r-- | src/core/nm-policy.c | 272 |
1 files changed, 218 insertions, 54 deletions
diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index 33073e4a..fbcee40d 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -18,6 +18,7 @@ #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" @@ -97,7 +98,6 @@ typedef struct { bool updating_dns : 1; GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */ - } NMPolicyPrivate; struct _NMPolicy { @@ -155,17 +155,15 @@ static gboolean hostname_retry_cb(gpointer user_data); typedef struct { NMPlatformIP6Address prefix; - NMDevice *device; /* The requesting ("uplink") device */ - guint64 next_subnet; /* Cache of the next subnet number to be - * assigned from this prefix */ - GHashTable *subnets; /* ifindex -> NMPlatformIP6Address */ + NMDevice *device; /* The requesting ("uplink") device */ + GHashTable *map_subnet_id_to_ifindex; /* (guint64 *) subnet_id -> int ifindex */ + GHashTable *map_ifindex_to_subnet; /* int ifindex -> (NMPlatformIP6Address *) prefix */ } IP6PrefixDelegation; static void -_clear_ip6_subnet(gpointer key, gpointer value, gpointer user_data) +clear_ip6_subnet(int ifindex, NMPlatformIP6Address *subnet) { - NMPlatformIP6Address *subnet = value; - NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(key)); + NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, ifindex); if (device) { /* We can not remove a subnet we already started announcing. @@ -177,6 +175,12 @@ _clear_ip6_subnet(gpointer key, gpointer value, gpointer user_data) } static void +clear_ip6_subnet_entry(gpointer key, gpointer value, gpointer user_data) +{ + clear_ip6_subnet(GPOINTER_TO_INT(key), value); +} + +static void clear_ip6_prefix_delegation(gpointer data) { IP6PrefixDelegation *delegation = data; @@ -187,8 +191,9 @@ clear_ip6_prefix_delegation(gpointer data) nm_inet6_ntop(&delegation->prefix.address, sbuf), delegation->prefix.plen); - g_hash_table_foreach(delegation->subnets, _clear_ip6_subnet, NULL); - g_hash_table_destroy(delegation->subnets); + g_hash_table_foreach(delegation->map_ifindex_to_subnet, clear_ip6_subnet_entry, NULL); + g_hash_table_destroy(delegation->map_ifindex_to_subnet); + g_hash_table_destroy(delegation->map_subnet_id_to_ifindex); } static void @@ -215,46 +220,112 @@ expire_ip6_delegations(NMPolicy *self) static gboolean ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device) { - NMPlatformIP6Address *subnet; - int ifindex = nm_device_get_ifindex(device); - char sbuf[NM_INET_ADDRSTRLEN]; - - subnet = g_hash_table_lookup(delegation->subnets, GINT_TO_POINTER(ifindex)); - if (!subnet) { - /* Check for out-of-prefixes condition. */ - if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) { - _LOGD(LOGD_IP6, - "ipv6-pd: no more prefixes in %s/%d", - nm_inet6_ntop(&delegation->prefix.address, sbuf), - delegation->prefix.plen); - return FALSE; + NMPlatformIP6Address *subnet; + int ifindex = nm_device_get_ifindex(device); + char sbuf[NM_INET_ADDRSTRLEN]; + NMSettingPrefixDelegation *s_pd; + gint64 wanted_subnet_id = -1; + guint64 num_subnets; + guint64 old_subnet_id; + + nm_assert(delegation->prefix.plen > 0 && delegation->prefix.plen <= 64); + + s_pd = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PREFIX_DELEGATION); + if (s_pd) { + wanted_subnet_id = nm_setting_prefix_delegation_get_subnet_id(s_pd); + } + + /* Try to use the cached subnet assigned to the interface */ + subnet = g_hash_table_lookup(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex)); + if (subnet) { + old_subnet_id = nm_ip6_addr_get_subnet_id(&subnet->address, delegation->prefix.plen); + if (wanted_subnet_id != -1 && wanted_subnet_id != old_subnet_id) { + /* The device had a subnet assigned before, but now wants a + * different subnet-id. Release the old subnet and continue below + * to get a new one. */ + clear_ip6_subnet(ifindex, subnet); + subnet = NULL; + g_hash_table_remove(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex)); + g_hash_table_remove(delegation->map_subnet_id_to_ifindex, &old_subnet_id); + } else { + goto subnet_found; } + } - /* Allocate a new subnet. */ - subnet = g_slice_new0(NMPlatformIP6Address); - g_hash_table_insert(delegation->subnets, GINT_TO_POINTER(ifindex), subnet); + /* Check for out-of-prefixes condition */ + num_subnets = 1 << (64 - delegation->prefix.plen); + if (nm_g_hash_table_size(delegation->map_subnet_id_to_ifindex) >= num_subnets) { + _LOGD(LOGD_IP6, + "ipv6-pd: no more prefixes in %s/%u", + nm_inet6_ntop(&delegation->prefix.address, sbuf), + delegation->prefix.plen); + return FALSE; + } + + /* Try to honor the "prefix-delegation.subnet-id" property */ + if (wanted_subnet_id >= 0) { + gpointer value; + NMDevice *other_device; + + if (g_hash_table_lookup_extended(delegation->map_subnet_id_to_ifindex, + &wanted_subnet_id, + NULL, + &value)) { + other_device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(value)); + _LOGW(LOGD_IP6, + "ipv6-pd: subnet-id 0x%" G_GINT64_MODIFIER + "x wanted by device %s is already in use by " + "device %s (ifindex %d)", + (guint64) wanted_subnet_id, + nm_device_get_iface(device), + other_device ? nm_device_get_ip_iface(other_device) : NULL, + GPOINTER_TO_INT(value)); + wanted_subnet_id = -1; + } + } + + /* If we don't have a subnet-id yet, find the first one available */ + if (wanted_subnet_id < 0) { + guint64 i; - subnet->plen = 64; - subnet->address.s6_addr32[0] = - delegation->prefix.address.s6_addr32[0] | htonl(delegation->next_subnet >> 32); - subnet->address.s6_addr32[1] = - delegation->prefix.address.s6_addr32[1] | htonl(delegation->next_subnet); + for (i = 0; i < num_subnets; i++) { + if (!g_hash_table_lookup_extended(delegation->map_subnet_id_to_ifindex, + &i, + NULL, + NULL)) { + wanted_subnet_id = (gint64) i; + break; + } + } - /* Out subnet pool management is pretty unsophisticated. We only add - * the subnets and index them by ifindex. That keeps the implementation - * simple and the dead entries make it easy to reuse the same subnet on - * subsequent activations. On the other hand they may waste the subnet - * space. */ - delegation->next_subnet++; + if (wanted_subnet_id < 0) { + /* We already verified that there are available subnets, this should not happen */ + return nm_assert_unreachable_val(FALSE); + } } + /* Allocate a new subnet */ + subnet = g_slice_new0(NMPlatformIP6Address); + g_hash_table_insert(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex), subnet); + g_hash_table_insert(delegation->map_subnet_id_to_ifindex, + nm_memdup(&wanted_subnet_id, sizeof(guint64)), + GINT_TO_POINTER(ifindex)); + + subnet->plen = 64; + subnet->address.s6_addr32[0] = + delegation->prefix.address.s6_addr32[0] | htonl(wanted_subnet_id >> 32); + subnet->address.s6_addr32[1] = + delegation->prefix.address.s6_addr32[1] | htonl(wanted_subnet_id); + +subnet_found: subnet->timestamp = delegation->prefix.timestamp; subnet->lifetime = delegation->prefix.lifetime; subnet->preferred = delegation->prefix.preferred; _LOGD(LOGD_IP6, - "ipv6-pd: %s allocated from a /%d prefix on %s", + "ipv6-pd: %s/64 (subnet-id 0x%" G_GINT64_MODIFIER "x) allocated from a /%d prefix on %s", nm_inet6_ntop(&subnet->address, sbuf), + (guint64) wanted_subnet_id, delegation->prefix.plen, nm_device_get_iface(device)); @@ -345,8 +416,9 @@ device_ip6_prefix_delegated(NMDevice *device, if (i == priv->ip6_prefix_delegations->len) { /* Allocate a delegation for new prefix. */ delegation = nm_g_array_append_new(priv->ip6_prefix_delegations, IP6PrefixDelegation); - delegation->subnets = g_hash_table_new(nm_direct_hash, NULL); - delegation->next_subnet = 0; + delegation->map_subnet_id_to_ifindex = + g_hash_table_new_full(nm_puint64_hash, nm_puint64_equal, g_free, NULL); + delegation->map_ifindex_to_subnet = g_hash_table_new(nm_direct_hash, NULL); } delegation->device = device; @@ -960,7 +1032,7 @@ update_system_hostname(NMPolicy *self, const char *msg, gboolean reset_retry_int /* Try a persistent hostname first */ configured_hostname = nm_hostname_manager_get_static_hostname(priv->hostname_manager); - if (configured_hostname && nm_utils_is_specific_hostname(configured_hostname)) { + if (configured_hostname && nm_utils_is_not_empty_hostname(configured_hostname)) { _set_hostname(self, configured_hostname, "from system configuration", FALSE); priv->dhcp_hostname = FALSE; return; @@ -2012,6 +2084,65 @@ unblock_autoconnect_for_ports_for_sett_conn(NMPolicy *self, NMSettingsConnection } 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, gboolean activate_children_connections_only) @@ -2155,8 +2286,9 @@ 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; + NMSettingsConnection *sett_conn = nm_device_get_settings_connection(device); + NMSettingConnection *s_con = NULL; + gboolean is_activated_shared_device = FALSE; switch (nm_device_state_reason_check(reason)) { case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: @@ -2211,8 +2343,10 @@ device_state_changed(NMDevice *device, con_v = nm_settings_connection_get_last_secret_agent_version_id(sett_conn); if (con_v == 0 || con_v == nm_agent_manager_get_agent_version_id(priv->agent_mgr)) { _LOGD(LOGD_DEVICE, - "block-autoconnect: connection '%s' now blocked from autoconnect due to " - "no secrets", + "block-autoconnect: connection[" NM_HASH_OBFUSCATE_PTR_FMT + "] (%s) now blocked from " + "autoconnect due to no secrets", + NM_HASH_OBFUSCATE_PTR(sett_conn), nm_settings_connection_get_id(sett_conn)); nm_settings_connection_autoconnect_blocked_reason_set( sett_conn, @@ -2230,10 +2364,10 @@ device_state_changed(NMDevice *device, * be marked as blocked when it fails with dependency-failed. */ _LOGD(LOGD_DEVICE, - "block-autoconnect: connection[%p] (%s) now blocked from autoconnect due to " - "failed " - "dependency", - sett_conn, + "block-autoconnect: connection[" NM_HASH_OBFUSCATE_PTR_FMT + "] (%s) now blocked " + "from autoconnect due to failed dependency", + NM_HASH_OBFUSCATE_PTR(sett_conn), nm_settings_connection_get_id(sett_conn)); nm_manager_devcon_autoconnect_blocked_reason_set( priv->manager, @@ -2255,20 +2389,25 @@ device_state_changed(NMDevice *device, /* blocked */ } else if (tries != NM_AUTOCONNECT_RETRIES_FOREVER) { _LOGD(LOGD_DEVICE, - "autoconnect: connection[%p] (%s): failed to autoconnect; %u tries left", - sett_conn, + "autoconnect: connection[" NM_HASH_OBFUSCATE_PTR_FMT "] (%s): failed to " + "autoconnect; %u tries left", + NM_HASH_OBFUSCATE_PTR(sett_conn), nm_settings_connection_get_id(sett_conn), tries - 1u); _connection_autoconnect_retries_set(self, device, sett_conn, tries - 1u); } else { _LOGD(LOGD_DEVICE, - "autoconnect: connection[%p] (%s) failed to autoconnect; infinite tries " - "left", - sett_conn, + "autoconnect: connection[" NM_HASH_OBFUSCATE_PTR_FMT "] (%s) failed to " + "autoconnect; infinite tries left", + NM_HASH_OBFUSCATE_PTR(sett_conn), nm_settings_connection_get_id(sett_conn)); } } } + 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) { @@ -2301,11 +2440,20 @@ 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) { @@ -2341,6 +2489,10 @@ 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); @@ -2357,6 +2509,10 @@ 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: @@ -2372,6 +2528,10 @@ 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. */ @@ -2382,6 +2542,10 @@ 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) |