core: change the rules for ignore-carrier
authorDan Winship <danw@gnome.org>
Fri, 12 Jul 2013 14:43:45 +0000 (10:43 -0400)
committerDan Winship <danw@gnome.org>
Mon, 22 Jul 2013 15:30:21 +0000 (11:30 -0400)
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).

data/server.conf.in
man/NetworkManager.conf.xml
src/devices/nm-device.c
src/devices/nm-device.h
src/nm-manager.c
src/nm-policy.c

index b026fe4..a63be51 100644 (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=*
index d735cfe..035e2ae 100644 (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>
 
index b779d8e..4557342 100644 (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,
index dafe4c6..d6d12bf 100644 (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);
index 06cab67..7ffcb20 100644 (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;
index a7684e2..849a1de 100644 (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))