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