diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index ed1f67aded..fb38bbb553 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -6440,6 +6440,11 @@ carrier_changed(NMDevice *self, gboolean carrier) } if (carrier) { + NMSettingsConnection *const *connections; + gboolean recheck_auto_activate = FALSE; + guint n_connections; + guint i; + if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { nm_device_queue_state(self, NM_DEVICE_STATE_DISCONNECTED, @@ -6450,8 +6455,30 @@ carrier_changed(NMDevice *self, gboolean carrier) * when the carrier appears, auto connections are rechecked for * the device. */ - nm_device_recheck_auto_activate_schedule(self); + recheck_auto_activate = TRUE; } + + connections = nm_settings_get_connections(priv->settings, &n_connections); + for (i = 0; i < n_connections; i++) { + NMSettingsConnection *sett_conn = connections[i]; + + if (!NM_FLAGS_HAS(nm_settings_connection_autoconnect_blocked_reason_get(sett_conn), + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED)) + continue; + if (!nm_device_check_connection_compatible( + self, + nm_settings_connection_get_connection(sett_conn), + NULL)) + continue; + if (nm_settings_connection_autoconnect_blocked_reason_set( + sett_conn, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, + FALSE)) + recheck_auto_activate = TRUE; + } + + if (recheck_auto_activate) + nm_device_recheck_auto_activate_schedule(self); } else { if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { if (priv->queued_state.id && priv->queued_state.state >= NM_DEVICE_STATE_DISCONNECTED) diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index 55fca5ae27..989f904816 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -5112,6 +5112,62 @@ active_connection_parent_active(NMActiveConnection *active, unmanaged_to_disconnected(device); } +static gboolean +_check_autoconnect_port(NMActiveConnection *active, + NMSettingsConnection *master_connection, + NMDevice *master_device, + NMActiveConnection *master_ac) +{ + NMSettingConnection *s_con; + NMDevice *device; + + if (nm_active_connection_get_activation_reason(active) != NM_ACTIVATION_REASON_AUTOCONNECT) { + /* This is an explicit activation. Proceed. */ + return TRUE; + } + + if (!master_connection) { + /* This is not a port. Proceed. */ + return TRUE; + } + + device = nm_active_connection_get_device(active); + + if (!nm_device_is_real(device)) { + /* The device is not real. We don't know about the carrier. Proceed. */ + return TRUE; + } + + if (nm_device_get_ifindex(device) <= 0) { + /* The device has no ifindex. It has no concept of carrier. Proceed. */ + return TRUE; + } + + if (nm_device_has_carrier(device)) { + /* The device has carrier. Proceed. */ + return TRUE; + } + + s_con = nm_settings_connection_get_setting(master_connection, NM_META_SETTING_TYPE_CONNECTION); + + if (nm_setting_connection_get_autoconnect(s_con)) { + /* The controller profile has autoconnect enabled. Here we want to honor + * "ignore-carrier=no", which -- as configuration -- only makes sense for + * controllers that have autoconnect disable. Proceed. */ + return TRUE; + } + + if (nm_config_data_get_ignore_carrier_for_port( + NM_CONFIG_GET_DATA, + nm_setting_connection_get_interface_name(s_con), + nm_setting_connection_get_connection_type(s_con))) { + /* We ignore carrier on the master (as we would do by default). Proceed. */ + return TRUE; + } + + return FALSE; +} + static gboolean _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **error) { @@ -5192,6 +5248,18 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError ** return FALSE; } + if (!_check_autoconnect_port(active, master_connection, master_device, master_ac)) { + /* Usually, port and controller devices can (auto)connect without carrier. However, + * the controller has "ignore-carrier=no" configured. If the port autoconnects, + * has no carrier and the controller has ignore-carrier=no, then autoconnect + * is going to fail. */ + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "port has no carrier and controller does not ignore carrier"); + return FALSE; + } + /* Create any backing resources the device needs */ if (!nm_device_is_real(device)) { NMDevice *parent;