core: track autoconnect for removed software devices (rh #1005913)

When an interface is manually disconnected NM remembers that, and prevents
automatic activation of the device.
However, software devices are removed when they are disconnected, and thus
the state of the device is lost. We need to track autoconnect outside the
device - hash table of interface names not allowed to activate automatically.

Without that the device would be auto-activated again and again, even if
explicitly disconnected.
Test case:
$ nmcli con add type bond ifname bb con-name bb-con
$ nmcli con add type bond-slave ifname em1 con-name b1-con master bb
$ nmcli dev disconnect bb

https://bugzilla.redhat.com/show_bug.cgi?id=1005913
This commit is contained in:
Jiří Klimeš 2013-10-18 12:32:01 +02:00
parent 1a67f8df03
commit f4dbf27410
3 changed files with 68 additions and 4 deletions

View file

@ -42,6 +42,7 @@
#include "nm-device.h"
#include "nm-device-private.h"
#include "NetworkManagerUtils.h"
#include "nm-manager.h"
#include "nm-platform.h"
#include "nm-rdisc.h"
#include "nm-lndp-rdisc.h"
@ -4339,6 +4340,14 @@ disconnect_cb (NMDevice *device,
g_error_free (local);
} else {
priv->autoconnect = FALSE;
/* Software devices are removed when manually disconnected and thus
* we need to track the autoconnect flag outside the device.
*/
nm_manager_prevent_device_auto_connect (nm_manager_get (),
nm_device_get_ip_iface (device),
TRUE);
nm_device_state_changed (device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);

View file

@ -263,6 +263,9 @@ typedef struct {
GHashTable *nm_bridges;
/* Track auto-activation for software devices */
GHashTable *noauto_sw_devices;
gboolean startup;
gboolean disposed;
} NMManagerPrivate;
@ -3096,13 +3099,29 @@ nm_manager_activate_connection (NMManager *manager,
}
device = find_device_by_ip_iface (manager, iface);
g_free (iface);
if (!device) {
/* Create it */
/* Create the software device. Only exception is when:
* - this is an auto-activation *and* the device denies auto-activation
* at this time (the device was manually disconnected/deleted before)
*/
if (!nm_manager_can_device_auto_connect (manager, iface)) {
if (dbus_sender) {
/* Manual activation - allow device auto-activation again */
nm_manager_prevent_device_auto_connect (manager, iface, FALSE);
} else {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED,
"'%s' does not allow automatic connections at this time => software device '%s' not created for '%s'",
iface, nm_connection_get_id (connection), iface);
g_free (iface);
return NULL;
}
}
device = system_create_virtual_device (manager, connection);
if (!device) {
g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"Failed to create virtual interface");
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"Failed to create virtual interface '%s'", iface);
g_free (iface);
return NULL;
}
@ -3118,6 +3137,7 @@ nm_manager_activate_connection (NMManager *manager,
NM_DEVICE_STATE_REASON_NONE);
}
}
g_free (iface);
}
if (!nm_device_can_activate (device, connection)) {
@ -3498,6 +3518,30 @@ impl_manager_deactivate_connection (NMManager *self,
}
}
/*
* Track (software) devices that cannot auto activate.
* It is needed especially for software devices, that can be removed and added
* again. So we can't simply use a flag inside the device.
*/
void
nm_manager_prevent_device_auto_connect (NMManager *manager, const char *ifname, gboolean prevent)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (prevent)
g_hash_table_add (priv->noauto_sw_devices, g_strdup (ifname));
else
g_hash_table_remove (priv->noauto_sw_devices, ifname);
}
gboolean
nm_manager_can_device_auto_connect (NMManager *manager, const char *ifname)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
return !g_hash_table_contains (priv->noauto_sw_devices, ifname);
}
static void
do_sleep_wake (NMManager *self)
{
@ -4575,6 +4619,8 @@ dispose (GObject *object)
g_object_unref (priv->fw_monitor);
}
g_hash_table_unref (priv->noauto_sw_devices);
g_slist_free (priv->factories);
if (priv->timestamp_update_id) {
@ -4938,6 +4984,9 @@ nm_manager_init (NMManager *manager)
KERNEL_FIRMWARE_DIR);
}
/* Hash table storing software devices that should not auto activate */
priv->noauto_sw_devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
load_device_factories (manager);
/* Update timestamps in active connections */

View file

@ -125,6 +125,12 @@ gboolean nm_manager_deactivate_connection (NMManager *manager,
NMDeviceStateReason reason,
GError **error);
void nm_manager_prevent_device_auto_connect (NMManager *manager,
const char *ifname,
gboolean prevent);
gboolean nm_manager_can_device_auto_connect (NMManager *manager,
const char *ifname);
/* State handling */
NMState nm_manager_get_state (NMManager *manager);