core: change the rules for ignore-carrier

The previous ignore-carrier rules did not work well with dynamic IP
(dhcp/slaac) connections. Change the rule so that only static IP
connections can be activated when carrier is not present (but both
static and dynamic connections will remain up when carrier is lost).
This commit is contained in:
Dan Winship 2013-07-12 10:43:45 -04:00
parent 841ca9bb84
commit 17e91fd46a
6 changed files with 143 additions and 28 deletions

View file

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

View file

@ -147,27 +147,32 @@ Copyright (C) 2010 - 2013 Red Hat, Inc.
<term><varname>ignore-carrier</varname></term>
<listitem>
<para>
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
<literal>*</literal> 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.
</para>
<para>
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.
</para>
<para>
May have the special value <literal>*</literal> to apply
to all devices.
</para>
<para>
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.
</para>
<para>
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).
</para>
</listitem>
</varlistentry>

View file

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

View file

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

View file

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

View file

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