summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/nm-atm-manager.c38
-rw-r--r--src/devices/adsl/nm-device-adsl.c383
-rw-r--r--src/devices/adsl/nm-device-adsl.h6
-rw-r--r--src/devices/nm-device-ethernet.c2
-rw-r--r--src/devices/nm-device-vlan.c12
-rw-r--r--src/devices/nm-device.c4
-rw-r--r--src/devices/wifi/nm-device-wifi.c40
7 files changed, 297 insertions, 188 deletions
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index 0830f85d..aeb5af6c 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -30,6 +30,8 @@
 #include "nm-device-factory.h"
 #include "nm-logging.h"
 #include "nm-glib-compat.h"
+#include "nm-platform.h"
+#include "gsystem-local-alloc.h"
 
 typedef struct {
 	GUdevClient *client;
@@ -103,6 +105,8 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
 	const char *ifname, *sysfs_path = NULL;
 	char *driver = NULL;
+	gs_free char *atm_index_path = NULL;
+	int atm_index;
 	NMDevice *device;
 
 	g_return_if_fail (udev_device != NULL);
@@ -115,20 +119,34 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
 
 	nm_log_dbg (LOGD_HW, "(%s): found ATM device", ifname);
 
-	if (dev_get_attrs (udev_device, &sysfs_path, &driver)) {
-		g_assert (sysfs_path);
+	atm_index_path = g_strdup_printf ("/sys/class/atm/%s/atmindex",
+	                                  ASSERT_VALID_PATH_COMPONENT (ifname));
+	atm_index = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET,
+	                                                      atm_index_path,
+	                                                      10, 0, G_MAXINT,
+	                                                      -1);
+	if (atm_index < 0) {
+		nm_log_warn (LOGD_HW, "(%s): failed to get ATM index", ifname);
+		return;
+	}
 
-		device = nm_device_adsl_new (sysfs_path, ifname, driver);
-		g_assert (device);
+	if (!dev_get_attrs (udev_device, &sysfs_path, &driver)) {
+		nm_log_warn (LOGD_HW, "(%s): failed to get ATM attributes", ifname);
+		return;
+	}
 
-		priv->devices = g_slist_prepend (priv->devices, device);
-		g_object_weak_ref (G_OBJECT (device), device_destroyed, self);
+	g_assert (sysfs_path);
 
-		g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
-		g_object_unref (device);
+	device = nm_device_adsl_new (sysfs_path, ifname, driver, atm_index);
+	g_assert (device);
 
-		g_free (driver);
-	}
+	priv->devices = g_slist_prepend (priv->devices, device);
+	g_object_weak_ref (G_OBJECT (device), device_destroyed, self);
+
+	g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
+	g_object_unref (device);
+
+	g_free (driver);
 }
 
 static void
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 4d2cac5b..bcef39a2 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -55,10 +55,16 @@ G_DEFINE_TYPE (NMDeviceAdsl, nm_device_adsl, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ADSL, NMDeviceAdslPrivate))
 
+enum {
+	PROP_0,
+	PROP_ATM_INDEX,
+
+	LAST_PROP
+};
+
 /**********************************************/
 
 typedef struct {
-	gboolean      disposed;
 	guint         carrier_poll_id;
 	int           atm_index;
 
@@ -69,6 +75,8 @@ typedef struct {
 	int           brfd;
 	int           nas_ifindex;
 	char *        nas_ifname;
+	guint         nas_update_id;
+	guint         nas_update_count;
 } NMDeviceAdslPrivate;
 
 /**************************************************************/
@@ -136,76 +144,6 @@ complete_connection (NMDevice *device,
 
 /**************************************************************/
 
-static void
-set_nas_iface (NMDeviceAdsl *self, int idx, const char *name)
-{
-	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
-
-	g_return_if_fail (name != NULL);
-
-	g_warn_if_fail (priv->nas_ifindex <= 0);
-	priv->nas_ifindex = idx > 0 ? idx : nm_platform_link_get_ifindex (NM_PLATFORM_GET, name);
-	g_warn_if_fail (priv->nas_ifindex > 0);
-
-	g_warn_if_fail (priv->nas_ifname == NULL);
-	priv->nas_ifname = g_strdup (name);
-}
-
-static gboolean
-br2684_create_iface (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
-{
-	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
-	struct atm_newif_br2684 ni;
-	int err, fd, errsv;
-	gboolean success = FALSE;
-	guint num = 0;
-
-	g_return_val_if_fail (s_adsl != NULL, FALSE);
-
-	fd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
-	if (fd < 0) {
-		errsv = errno;
-		_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv);
-		return FALSE;
-	}
-
-	memset (&ni, 0, sizeof (ni));
-	ni.backend_num = ATM_BACKEND_BR2684;
-	ni.media = BR2684_MEDIA_ETHERNET;
-	ni.mtu = 1500;
-
-	/* Loop attempting to create an interface that doesn't exist yet.  The
-	 * kernel can create one for us automatically, but due to API issues it
-	 * cannot return that name to us.  Since we want to know the name right
-	 * away, just brute-force it.
-	 */
-	while (num < 10000) {
-		memset (&ni.ifname, 0, sizeof (ni.ifname));
-		g_snprintf (ni.ifname, sizeof (ni.ifname), "nas%d", num);
-
-		err = ioctl (fd, ATM_NEWBACKENDIF, &ni);
-		if (err == 0) {
-			set_nas_iface (self, -1, ni.ifname);
-			_LOGI (LOGD_ADSL, "using NAS interface %s (%d)",
-			       priv->nas_ifname, priv->nas_ifindex);
-			success = TRUE;
-			break;
-		} else {
-			errsv = errno;
-			if (errsv == -EEXIST) {
-				/* Try again */
-				num++;
-			} else {
-				_LOGW (LOGD_ADSL, "failed to create br2684 interface (%d)", errsv);
-				break;
-			}
-		}
-	}
-
-	close (fd);
-	return success;
-}
-
 static gboolean
 br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 {
@@ -295,7 +233,13 @@ error:
 }
 
 static void
-link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self)
+link_changed_cb (NMPlatform *platform,
+                 NMPObjectType obj_type,
+                 int ifindex,
+                 NMPlatformLink *info,
+                 NMPlatformSignalChangeType change_type,
+                 NMPlatformReason reason,
+                 NMDeviceAdsl *self)
 {
 	if (change_type == NM_PLATFORM_SIGNAL_REMOVED) {
 		NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
@@ -303,9 +247,9 @@ link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPl
 
 		/* This only gets called for PPPoE connections and "nas" interfaces */
 
-		if (priv->nas_ifindex >= 0 && ifindex == priv->nas_ifindex) {
+		if (priv->nas_ifindex > 0 && ifindex == priv->nas_ifindex) {
 				/* NAS device went away for some reason; kill the connection */
-				_LOGD (LOGD_ADSL, "NAS interface disappeared");
+				_LOGD (LOGD_ADSL, "br2684 interface disappeared");
 				nm_device_state_changed (device,
 				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_BR2684_FAILED);
@@ -313,11 +257,140 @@ link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPl
 	}
 }
 
+static gboolean
+pppoe_vcc_config (NMDeviceAdsl *self)
+{
+	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	NMSettingAdsl *s_adsl;
+
+	s_adsl = nm_connection_get_setting_adsl (nm_device_get_connection (device));
+	g_assert (s_adsl);
+
+	/* Set up the VCC */
+	if (!br2684_assign_vcc (self, s_adsl))
+		return FALSE;
+
+	/* Watch for the 'nas' interface going away */
+	g_signal_connect (nm_platform_get (), NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                  G_CALLBACK (link_changed_cb),
+	                  self);
+
+	_LOGD (LOGD_ADSL, "ATM setup successful");
+
+	/* otherwise we're good for stage3 */
+	nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+nas_update_cb (gpointer user_data)
+{
+	NMDeviceAdsl *self = NM_DEVICE_ADSL (user_data);
+	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+
+	g_assert (priv->nas_ifname);
+
+	priv->nas_update_count++;
+
+	if (priv->nas_update_count > 10) {
+		priv->nas_update_id = 0;
+		_LOGW (LOGD_ADSL, "failed to find br2684 interface %s ifindex after timeout", priv->nas_ifname);
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BR2684_FAILED);
+		return G_SOURCE_REMOVE;
+	}
+
+	g_warn_if_fail (priv->nas_ifindex < 0);
+	priv->nas_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->nas_ifname);
+	if (priv->nas_ifindex < 0) {
+		/* Keep waiting for it to appear */
+		return G_SOURCE_CONTINUE;
+	}
+
+	priv->nas_update_id = 0;
+	_LOGD (LOGD_ADSL, "using br2684 iface '%s' index %d", priv->nas_ifname, priv->nas_ifindex);
+
+	if (pppoe_vcc_config (self)) {
+		nm_device_activate_schedule_stage3_ip_config_start (device);
+	} else {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_BR2684_FAILED);
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static NMActStageReturn
+br2684_create_iface (NMDeviceAdsl *self,
+                     NMSettingAdsl *s_adsl,
+                     NMDeviceStateReason *out_reason)
+{
+	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
+	struct atm_newif_br2684 ni;
+	int err, fd, errsv;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	guint num = 0;
+
+	g_return_val_if_fail (s_adsl != NULL, FALSE);
+
+	if (priv->nas_update_id) {
+		g_warn_if_fail (priv->nas_update_id == 0);
+		nm_clear_g_source (&priv->nas_update_id);
+	}
+
+	fd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
+	if (fd < 0) {
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv);
+		*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	memset (&ni, 0, sizeof (ni));
+	ni.backend_num = ATM_BACKEND_BR2684;
+	ni.media = BR2684_MEDIA_ETHERNET;
+	ni.mtu = 1500;
+
+	/* Loop attempting to create an interface that doesn't exist yet.  The
+	 * kernel can create one for us automatically, but due to API issues it
+	 * cannot return that name to us.  Since we want to know the name right
+	 * away, just brute-force it.
+	 */
+	while (num < 10000) {
+		memset (&ni.ifname, 0, sizeof (ni.ifname));
+		g_snprintf (ni.ifname, sizeof (ni.ifname), "nas%d", num++);
+
+		err = ioctl (fd, ATM_NEWBACKENDIF, &ni);
+		if (err == 0) {
+			g_free (priv->nas_ifname);
+			priv->nas_ifname = g_strdup (ni.ifname);
+			_LOGD (LOGD_ADSL, "waiting for br2684 iface '%s' to appear", priv->nas_ifname);
+
+			priv->nas_update_count = 0;
+			priv->nas_update_id = g_timeout_add (100, nas_update_cb, self);
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+			break;
+		} else if (errno != EEXIST) {
+			errsv = errno;
+			_LOGW (LOGD_ADSL, "failed to create br2684 interface (%d)", errsv);
+			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
+			break;
+		}
+	}
+
+	close (fd);
+	return ret;
+}
+
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 {
 	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
-	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMSettingAdsl *s_adsl;
 	const char *protocol;
@@ -331,37 +404,14 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 	_LOGD (LOGD_ADSL, "using ADSL protocol '%s'", protocol);
 
 	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {
-
 		/* PPPoE needs RFC2684 bridging before we can do PPP over it */
-		if (!br2684_create_iface (self, s_adsl)) {
-			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
-			goto done;
-		}
-
-		/* Set up the VCC */
-		if (!br2684_assign_vcc (self, s_adsl)) {
-			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
-			goto done;
-		}
-
-		/* Watch for the 'nas' interface going away */
-		g_signal_connect (nm_platform_get (), NM_PLATFORM_SIGNAL_LINK_CHANGED,
-		                  G_CALLBACK (link_changed_cb),
-		                  self);
-
-		_LOGD (LOGD_ADSL, "ATM setup successful");
-
-		/* otherwise we're good for stage3 */
-		nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);
-		ret = NM_ACT_STAGE_RETURN_SUCCESS;
-
+		ret = br2684_create_iface (self, s_adsl, out_reason);
 	} else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) {
 		/* PPPoA doesn't need anything special */
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	} else
 		_LOGW (LOGD_ADSL, "unhandled ADSL protocol '%s'", protocol);
 
-done:
 	return ret;
 }
 
@@ -425,7 +475,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 		g_assert (priv->nas_ifname);
 		ppp_iface = priv->nas_ifname;
 
-		_LOGD (LOGD_ADSL, "starting PPPoE on NAS interface %s", priv->nas_ifname);
+		_LOGD (LOGD_ADSL, "starting PPPoE on br2684 interface %s", priv->nas_ifname);
 	} else {
 		ppp_iface = nm_device_get_iface (device);
 		_LOGD (LOGD_ADSL, "starting PPPoA");
@@ -454,31 +504,37 @@ act_stage3_ip4_config_start (NMDevice *device,
 }
 
 static void
-deactivate (NMDevice *device)
+adsl_cleanup (NMDeviceAdsl *self)
 {
-	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
 	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
 
 	if (priv->ppp_manager) {
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
+		g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_state_changed), self);
+		g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_ip4_config), self);
+		g_clear_object (&priv->ppp_manager);
 	}
 
-	g_signal_handlers_disconnect_by_func (nm_platform_get (), G_CALLBACK (link_changed_cb), device);
+	g_signal_handlers_disconnect_by_func (nm_platform_get (), G_CALLBACK (link_changed_cb), self);
 
 	if (priv->brfd >= 0) {
 		close (priv->brfd);
 		priv->brfd = -1;
 	}
 
+	nm_clear_g_source (&priv->nas_update_id);
+
 	/* FIXME: kernel has no way of explicitly deleting the 'nasX' interface yet,
 	 * so it gets leaked.  It does get destroyed when it's no longer in use,
 	 * but we have no control over that.
 	 */
-	if (priv->nas_ifindex >= 0)
-		priv->nas_ifindex = -1;
-	g_free (priv->nas_ifname);
-	priv->nas_ifname = NULL;
+	priv->nas_ifindex = -1;
+	g_clear_pointer (&priv->nas_ifname, g_free);
+}
+
+static void
+deactivate (NMDevice *device)
+{
+	adsl_cleanup (NM_DEVICE_ADSL (device));
 }
 
 /**************************************************************/
@@ -505,89 +561,74 @@ carrier_update_cb (gpointer user_data)
 NMDevice *
 nm_device_adsl_new (const char *udi,
                     const char *iface,
-                    const char *driver)
+                    const char *driver,
+                    int atm_index)
 {
 	g_return_val_if_fail (udi != NULL, NULL);
+	g_return_val_if_fail (atm_index >= 0, NULL);
 
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ADSL,
 	                                  NM_DEVICE_UDI, udi,
 	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_DRIVER, driver,
+	                                  NM_DEVICE_ADSL_ATM_INDEX, atm_index,
 	                                  NM_DEVICE_TYPE_DESC, "ADSL",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ADSL,
 	                                  NULL);
 }
 
-static int
-get_atm_index (const char *iface)
+static void
+constructed (GObject *object)
 {
-	char *path;
-	int idx;
-
-	path = g_strdup_printf ("/sys/class/atm/%s/atmindex",
-	                        ASSERT_VALID_PATH_COMPONENT (iface));
-	idx = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, G_MAXINT, -1);
-	g_free (path);
+	NMDeviceAdsl *self = NM_DEVICE_ADSL (object);
+	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
 
-	return idx;
-}
+	G_OBJECT_CLASS (nm_device_adsl_parent_class)->constructed (object);
 
-static GObject*
-constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
-{
-	GObject *object;
-	NMDeviceAdsl *self;
-	NMDeviceAdslPrivate *priv;
-
-	object = G_OBJECT_CLASS (nm_device_adsl_parent_class)->constructor (type,
-	                                                                    n_construct_params,
-	                                                                    construct_params);
-	if (!object)
-		return NULL;
-
-	self = NM_DEVICE_ADSL (object);
-	priv = NM_DEVICE_ADSL_GET_PRIVATE (object);
-
-	priv->atm_index = get_atm_index (nm_device_get_iface (NM_DEVICE (object)));
-	if (priv->atm_index < 0) {
-		_LOGE (LOGD_ADSL, "error reading ATM device index");
-		g_object_unref (object);
-		return NULL;
-	} else
-		_LOGD (LOGD_ADSL, "ATM device index %d", priv->atm_index);
+	priv->carrier_poll_id = g_timeout_add_seconds (5, carrier_update_cb, self);
 
-	/* Poll the carrier */
-	priv->carrier_poll_id = g_timeout_add_seconds (5, carrier_update_cb, object);
+	_LOGD (LOGD_ADSL, "ATM device index %d", priv->atm_index);
 
-	return object;
+	g_return_if_fail (priv->atm_index >= 0);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDeviceAdsl *self = NM_DEVICE_ADSL (object);
-	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
+	adsl_cleanup (NM_DEVICE_ADSL (object));
 
-	if (priv->disposed) {
-		G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
-		return;
-	}
+	nm_clear_g_source (&NM_DEVICE_ADSL_GET_PRIVATE (object)->carrier_poll_id);
 
-	priv->disposed = TRUE;
+	G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
+}
 
-	if (priv->carrier_poll_id) {
-		g_source_remove (priv->carrier_poll_id);
-		priv->carrier_poll_id = 0;
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case PROP_ATM_INDEX:
+		g_value_set_int (value, NM_DEVICE_ADSL_GET_PRIVATE (object)->atm_index);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
 	}
+}
 
-	g_signal_handlers_disconnect_by_func (nm_platform_get (), G_CALLBACK (link_changed_cb), self);
-
-	g_free (priv->nas_ifname);
-	priv->nas_ifname = NULL;
-
-	G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case PROP_ATM_INDEX:
+		/* construct only */
+		NM_DEVICE_ADSL_GET_PRIVATE (object)->atm_index = g_value_get_int (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -603,8 +644,10 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceAdslPrivate));
 
-	object_class->constructor  = constructor;
+	object_class->constructed  = constructed;
 	object_class->dispose      = dispose;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 
@@ -615,6 +658,14 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	parent_class->deactivate = deactivate;
 
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_ATM_INDEX,
+		 g_param_spec_int (NM_DEVICE_ADSL_ATM_INDEX, "", "",
+		                   -1, G_MAXINT, -1,
+		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_adsl_object_info);
diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h
index f21381c2..bf41ed79 100644
--- a/src/devices/adsl/nm-device-adsl.h
+++ b/src/devices/adsl/nm-device-adsl.h
@@ -36,20 +36,22 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ADSL))
 #define NM_DEVICE_ADSL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass))
 
+#define NM_DEVICE_ADSL_ATM_INDEX "atm-index"
+
 typedef struct {
 	NMDevice parent;
 } NMDeviceAdsl;
 
 typedef struct {
 	NMDeviceClass parent;
-
 } NMDeviceAdslClass;
 
 GType nm_device_adsl_get_type (void);
 
 NMDevice *nm_device_adsl_new (const char *udi,
                               const char *iface,
-                              const char *driver);
+                              const char *driver,
+                              int atm_index);
 
 G_END_DECLS
 
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 8a023843..2dda84b3 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -883,7 +883,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 	}
 
 	if (!priv->supplicant.mgr)
-		priv->supplicant.mgr = nm_supplicant_manager_get ();
+		priv->supplicant.mgr = g_object_ref (nm_supplicant_manager_get ());
 
 	/* If we need secrets, get them */
 	setting_name = nm_connection_need_secrets (connection, NULL);
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index b330397f..7cce6593 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -96,7 +96,7 @@ parent_hwaddr_changed (NMDevice *parent,
 	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
 	NMConnection *connection;
 	NMSettingWired *s_wired;
-	const char *cloned_mac = NULL;
+	const char *cloned_mac = NULL, *new_mac;
 
 	/* Never touch assumed devices */
 	if (nm_device_uses_assumed_connection (self))
@@ -112,9 +112,13 @@ parent_hwaddr_changed (NMDevice *parent,
 		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
 
 	if (!cloned_mac) {
-		_LOGD (LOGD_VLAN, "parent hardware address changed");
-		nm_device_set_hw_addr (self, nm_device_get_hw_address (parent),
-		                       "set", LOGD_VLAN);
+		new_mac = nm_device_get_hw_address (parent);
+		_LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s",
+		       NM_PRINT_FMT_QUOTE_STRING (new_mac));
+		if (new_mac) {
+			nm_device_set_hw_addr (self, nm_device_get_hw_address (parent),
+			                       "set", LOGD_VLAN);
+		}
 	}
 }
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9b69d5ab..bb39ca55 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -7007,7 +7007,7 @@ nm_device_start_ip_check (NMDevice *self)
 		if (priv->ip4_config && priv->ip4_state == IP_DONE) {
 			guint gw = 0;
 
-			ping_binary = "/usr/bin/ping";
+			ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL);
 			log_domain = LOGD_IP4;
 
 			gw = nm_ip4_config_get_gateway (priv->ip4_config);
@@ -7016,7 +7016,7 @@ nm_device_start_ip_check (NMDevice *self)
 		} else if (priv->ip6_config && priv->ip6_state == IP_DONE) {
 			const struct in6_addr *gw = NULL;
 
-			ping_binary = "/usr/bin/ping6";
+			ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL);
 			log_domain = LOGD_IP6;
 
 			gw = nm_ip6_config_get_gateway (priv->ip6_config);
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 15d41206..1bf64bcc 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -136,6 +136,8 @@ struct _NMDeviceWifiPrivate {
 	guint32           failed_link_count;
 	guint             periodic_source_id;
 	guint             link_timeout_id;
+	guint32           failed_iface_count;
+	guint             reacquire_iface_id;
 
 	NMDeviceWifiCapabilities capabilities;
 };
@@ -217,8 +219,7 @@ constructor (GType type,
 		_LOGI (LOGD_HW | LOGD_WIFI, "driver supports Access Point (AP) mode");
 
 	/* Connect to the supplicant manager */
-	priv->sup_mgr = nm_supplicant_manager_get ();
-	g_assert (priv->sup_mgr);
+	priv->sup_mgr = g_object_ref (nm_supplicant_manager_get ());
 
 	return object;
 }
@@ -1959,6 +1960,15 @@ cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
 }
 
 static void
+cleanup_supplicant_failures (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_clear_g_source (&priv->reacquire_iface_id);
+	priv->failed_iface_count = 0;
+}
+
+static void
 wifi_secrets_cb (NMActRequest *req,
                  guint32 call_id,
                  NMConnection *connection,
@@ -2158,6 +2168,24 @@ handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
 	return handled;
 }
 
+static gboolean
+reacquire_interface_cb (gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	priv->reacquire_iface_id = 0;
+	priv->failed_iface_count++;
+
+	_LOGW (LOGD_WIFI, "re-acquiring supplicant interface (#%d).", priv->failed_iface_count);
+
+	if (!priv->sup_iface)
+		supplicant_interface_acquire (self);
+
+	return G_SOURCE_REMOVE;
+}
+
 static void
 supplicant_iface_state_cb (NMSupplicantInterface *iface,
                            guint32 new_state,
@@ -2264,7 +2292,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		 * ready if the supplicant comes back.
 		 */
 		supplicant_interface_release (self);
-		supplicant_interface_acquire (self);
+		if (priv->failed_iface_count < 5)
+			priv->reacquire_iface_id = g_timeout_add_seconds (10, reacquire_interface_cb, self);
+		else
+			_LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up");
 		break;
 	default:
 		break;
@@ -3061,6 +3092,7 @@ device_state_changed (NMDevice *device,
 		}
 
 		cleanup_association_attempt (self, TRUE);
+		cleanup_supplicant_failures (self);
 		remove_all_aps (self);
 	}
 
@@ -3147,6 +3179,7 @@ set_enabled (NMDevice *device, gboolean enabled)
 		}
 
 		/* Re-initialize the supplicant interface and wait for it to be ready */
+		cleanup_supplicant_failures (self);
 		if (priv->sup_iface)
 			supplicant_interface_release (self);
 		supplicant_interface_acquire (self);
@@ -3201,6 +3234,7 @@ dispose (GObject *object)
 
 	cleanup_association_attempt (self, TRUE);
 	supplicant_interface_release (self);
+	cleanup_supplicant_failures (self);
 
 	g_clear_object (&priv->sup_mgr);