diff --git a/data/server.conf.in b/data/server.conf.in index b026fe4362..a63be5110e 100644 --- a/data/server.conf.in +++ b/data/server.conf.in @@ -12,5 +12,5 @@ no-auto-default=* # Ignore the carrier (cable plugged in) state when attempting to -# activate connections. +# activate static-IP connections. ignore-carrier=* diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index d735cfe330..035e2ae37e 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -147,27 +147,32 @@ Copyright (C) 2010 - 2013 Red Hat, Inc. ignore-carrier - Comma-separated list of devices for which - NetworkManager will ignore the carrier (cable plugged in) - state. Normally, for device types that support - carrier-detect, such as Ethernet and InfiniBand, - NetworkManager will only allow a connection to be activated on - the device if carrier is present (ie, a cable is plugged in). - List a device here to allow attempting to activate a - connection anyways. May have the special value - * to apply to all devices. + Comma-separated list of devices for which NetworkManager + will (partially) ignore the carrier state. Normally, for + device types that support carrier-detect, such as Ethernet + and InfiniBand, NetworkManager will only allow a + connection to be activated on the device if carrier is + present (ie, a cable is plugged in), and it will + deactivate the device if carrier drops for more than a few + seconds. + + + Listing a device here will allow activating connections on + that device even when it does not have carrier, provided + that the connection uses only statically-configured IP + addresses. Additionally, it will allow any active + connection (whether static or dynamic) to remain active on + the device when carrier is lost. + + + May have the special value * to apply + to all devices. Note that the "carrier" property of NMDevices and device D-Bus interfaces will still reflect the actual device state; it's just that NetworkManager will not make use of that information. - - You should probably not set this to apply to devices where you are - doing automatic IP config, since they will eventually fail if there - is no actual network connectivity, and NetworkManager won't retry - them right away when carrier comes back up (since it's ignoring it). - diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b779d8e656..45573422a0 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -983,14 +983,63 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean failed) return success; } +static gboolean +connection_is_static (NMConnection *connection) +{ + NMSettingIP4Config *ip4; + NMSettingIP6Config *ip6; + const char *method; + + ip4 = nm_connection_get_setting_ip4_config (connection); + if (ip4) { + method = nm_setting_ip4_config_get_method (ip4); + if ( g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 + && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0) + return FALSE; + } + + ip6 = nm_connection_get_setting_ip6_config (connection); + if (ip6) { + method = nm_setting_ip6_config_get_method (ip6); + if ( g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 + && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0) + return FALSE; + } + + return TRUE; +} + +static gboolean +has_static_connection (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const GSList *connections, *iter; + + connections = nm_connection_provider_get_connections (priv->con_provider); + for (iter = connections; iter; iter = iter->next) { + NMConnection *connection = iter->data; + + if ( nm_device_check_connection_compatible (self, connection, NULL) + && connection_is_static (connection)) + return TRUE; + } + return FALSE; +} + static void carrier_changed (NMDevice *device, gboolean carrier) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - if (priv->ignore_carrier || !nm_device_get_managed (device)) + if (!nm_device_get_managed (device)) return; + if (priv->ignore_carrier) { + /* Ignore all carrier-off, and ignore carrier-on on connected devices */ + if (!carrier || priv->state > NM_DEVICE_STATE_DISCONNECTED) + return; + } + if (nm_device_is_master (device)) { /* Bridge/bond carrier does not affect its own activation, but * when carrier comes on, if there are slaves waiting, it will @@ -1368,9 +1417,32 @@ is_available (NMDevice *device) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - return priv->carrier || priv->ignore_carrier; + if (!priv->carrier) { + if (priv->ignore_carrier && has_static_connection (device)) + return TRUE; + return FALSE; + } + + return TRUE; } +/** + * nm_device_is_available: + * @self: the #NMDevice + * + * Checks if @self would currently be capable of activating a + * connection. In particular, it checks that the device is ready (eg, + * is not missing firmware), that it has carrier (if necessary), and + * that any necessary external software (eg, ModemManager, + * wpa_supplicant) is available. + * + * @self can only be in a state higher than + * %NM_DEVICE_STATE_UNAVAILABLE when nm_device_is_available() returns + * %TRUE. (But note that it can still be %NM_DEVICE_STATE_UNMANAGED + * when it is available.) + * + * Returns: %TRUE or %FALSE + */ gboolean nm_device_is_available (NMDevice *self) { @@ -1382,17 +1454,54 @@ nm_device_is_available (NMDevice *self) return NM_DEVICE_GET_CLASS (self)->is_available (self); } +/** + * nm_device_can_activate: + * @self: the #NMDevice + * @connection: (allow-none) an #NMConnection, or %NULL + * + * Checks if @self can currently activate @connection. In particular, + * this requires that @self is available (per + * nm_device_is_available()); that it is either managed or able to + * become managed; and that it is able to activate @connection in its + * current state (eg, if @connection requires carrier, then @self has + * carrier). + * + * If @connection is %NULL, this just checks that @self could + * theoretically activate *some* connection. + * + * Returns: %TRUE or %FALSE + */ gboolean -nm_device_can_activate (NMDevice *self) +nm_device_can_activate (NMDevice *self, NMConnection *connection) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (!priv->manager_managed) return FALSE; - if (!priv->default_unmanaged && priv->state < NM_DEVICE_STATE_DISCONNECTED) + + if ( connection + && !nm_device_check_connection_compatible (self, connection, NULL)) return FALSE; - return nm_device_is_available (self); + if (priv->default_unmanaged) { + if (!nm_device_is_available (self)) + return FALSE; + } else if (priv->state < NM_DEVICE_STATE_DISCONNECTED) { + if (priv->state != NM_DEVICE_STATE_UNAVAILABLE || priv->carrier || !priv->ignore_carrier) + return FALSE; + + /* @self is UNAVAILABLE because it doesn't have carrier, but + * ignore-carrier is set, so we might be able to ignore that. + */ + if (connection && connection_is_static (connection)) + return TRUE; + else if (!connection && has_static_connection (self)) + return TRUE; + else + return FALSE; + } + + return TRUE; } gboolean @@ -1471,7 +1580,7 @@ can_auto_connect (NMDevice *device, if (!nm_setting_connection_get_autoconnect (s_con)) return FALSE; - return nm_device_check_connection_compatible (device, connection, NULL); + return nm_device_can_activate (device, connection); } /** @@ -4105,7 +4214,7 @@ nm_device_activate (NMDevice *self, NMActRequest *req) nm_connection_get_id (connection)); if (priv->state < NM_DEVICE_STATE_DISCONNECTED) { - g_return_if_fail (nm_device_can_activate (self)); + g_return_if_fail (nm_device_can_activate (self, connection)); if (priv->state == NM_DEVICE_STATE_UNMANAGED) { nm_device_state_changed (self, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index dafe4c6aa2..d6d12bfa71 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -235,8 +235,9 @@ void nm_device_slave_notify_enslaved (NMDevice *dev, NMActRequest * nm_device_get_act_request (NMDevice *dev); NMConnection * nm_device_get_connection (NMDevice *dev); -gboolean nm_device_is_available (NMDevice *dev); -gboolean nm_device_can_activate (NMDevice *dev); +gboolean nm_device_is_available (NMDevice *dev); +gboolean nm_device_can_activate (NMDevice *dev, + NMConnection *connection); gboolean nm_device_has_carrier (NMDevice *dev); gboolean nm_device_ignore_carrier (NMDevice *dev); diff --git a/src/nm-manager.c b/src/nm-manager.c index 06cab67128..7ffcb20a3d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -2008,7 +2008,7 @@ add_device (NMManager *self, NMDevice *device) system_create_virtual_devices (self); /* If the device has a connection it can assume, do that now */ - if (existing && nm_device_can_activate (device)) { + if (existing && nm_device_can_activate (device, existing)) { NMActiveConnection *ac; GError *error = NULL; @@ -2950,7 +2950,7 @@ nm_manager_activate_connection (NMManager *manager, } } - if (!nm_device_can_activate (device)) { + if (!nm_device_can_activate (device, connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, "Device not managed by NetworkManager or unavailable"); return NULL; diff --git a/src/nm-policy.c b/src/nm-policy.c index a7684e29fa..849a1dee16 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1119,7 +1119,7 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds if (nm_manager_get_state (policy->manager) == NM_STATE_ASLEEP) return; - if (!nm_device_can_activate (device)) + if (!nm_device_can_activate (device, NULL)) return; if (!nm_device_get_enabled (device))