summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
committerMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
commit2c032d8f1c6292c1338a615e6ec40252889ba85c (patch)
tree1f77182220b2b0264288ba4a476ab47e5bc48716 /src/devices
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/Makefile.am11
-rw-r--r--src/devices/adsl/Makefile.in17
-rw-r--r--src/devices/adsl/exports.ver1
-rw-r--r--src/devices/adsl/nm-atm-manager.c34
-rw-r--r--src/devices/adsl/nm-atm-manager.h6
-rw-r--r--src/devices/adsl/nm-device-adsl.c129
-rw-r--r--src/devices/adsl/nm-device-adsl.h4
-rw-r--r--src/devices/bluetooth/Makefile.am26
-rw-r--r--src/devices/bluetooth/Makefile.in92
-rw-r--r--src/devices/bluetooth/exports.ver1
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h4
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c285
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h11
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c35
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h6
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c20
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h6
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c2
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.h6
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c410
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h46
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c4
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h6
-rw-r--r--src/devices/bluetooth/nm-bt-enum-types.c3
-rw-r--r--src/devices/bluetooth/nm-bt-error.c35
-rw-r--r--src/devices/bluetooth/nm-bt-error.h35
-rw-r--r--src/devices/bluetooth/nm-device-bt.c262
-rw-r--r--src/devices/bluetooth/nm-device-bt.h12
-rw-r--r--src/devices/nm-device-bond.c227
-rw-r--r--src/devices/nm-device-bond.h24
-rw-r--r--src/devices/nm-device-bridge.c288
-rw-r--r--src/devices/nm-device-bridge.h26
-rw-r--r--src/devices/nm-device-ethernet-utils.c55
-rw-r--r--src/devices/nm-device-ethernet-utils.h26
-rw-r--r--src/devices/nm-device-ethernet.c523
-rw-r--r--src/devices/nm-device-ethernet.h25
-rw-r--r--src/devices/nm-device-factory.c52
-rw-r--r--src/devices/nm-device-factory.h125
-rw-r--r--src/devices/nm-device-generic.c24
-rw-r--r--src/devices/nm-device-generic.h11
-rw-r--r--src/devices/nm-device-gre.c117
-rw-r--r--src/devices/nm-device-gre.h16
-rw-r--r--src/devices/nm-device-infiniband.c231
-rw-r--r--src/devices/nm-device-infiniband.h25
-rw-r--r--src/devices/nm-device-logging.h46
-rw-r--r--src/devices/nm-device-macvlan.c64
-rw-r--r--src/devices/nm-device-macvlan.h16
-rw-r--r--src/devices/nm-device-private.h23
-rw-r--r--src/devices/nm-device-tun.c109
-rw-r--r--src/devices/nm-device-tun.h16
-rw-r--r--src/devices/nm-device-veth.c49
-rw-r--r--src/devices/nm-device-veth.h16
-rw-r--r--src/devices/nm-device-vlan.c333
-rw-r--r--src/devices/nm-device-vlan.h22
-rw-r--r--src/devices/nm-device-vxlan.c155
-rw-r--r--src/devices/nm-device-vxlan.h16
-rw-r--r--src/devices/nm-device.c4392
-rw-r--r--src/devices/nm-device.h92
-rw-r--r--src/devices/team/Makefile.am68
-rw-r--r--src/devices/team/Makefile.in845
-rw-r--r--src/devices/team/exports.ver6
-rw-r--r--src/devices/team/nm-device-team-glue.h73
-rw-r--r--src/devices/team/nm-device-team.c (renamed from src/devices/nm-device-team.c)435
-rw-r--r--src/devices/team/nm-device-team.h (renamed from src/devices/nm-device-team.h)14
-rw-r--r--src/devices/team/nm-team-enum-types.c12
-rw-r--r--src/devices/team/nm-team-enum-types.h17
-rw-r--r--src/devices/team/nm-team-factory.c93
-rw-r--r--src/devices/team/nm-team-factory.h37
-rw-r--r--src/devices/wifi/Makefile.am12
-rw-r--r--src/devices/wifi/Makefile.in18
-rw-r--r--src/devices/wifi/exports.ver1
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c127
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.h12
-rw-r--r--src/devices/wifi/nm-device-wifi.c931
-rw-r--r--src/devices/wifi/nm-device-wifi.h16
-rw-r--r--src/devices/wifi/nm-wifi-ap-utils.c316
-rw-r--r--src/devices/wifi/nm-wifi-ap-utils.h9
-rw-r--r--src/devices/wifi/nm-wifi-ap.c211
-rw-r--r--src/devices/wifi/nm-wifi-ap.h76
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.c44
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.h4
-rw-r--r--src/devices/wifi/nm-wifi-factory.c18
-rw-r--r--src/devices/wifi/tests/Makefile.am9
-rw-r--r--src/devices/wifi/tests/Makefile.in15
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c231
-rw-r--r--src/devices/wimax/Makefile.am12
-rw-r--r--src/devices/wimax/Makefile.in18
-rw-r--r--src/devices/wimax/exports.ver1
-rw-r--r--src/devices/wimax/iwmxsdk.c10
-rw-r--r--src/devices/wimax/iwmxsdk.h9
-rw-r--r--src/devices/wimax/nm-device-wimax.c132
-rw-r--r--src/devices/wimax/nm-device-wimax.h13
-rw-r--r--src/devices/wimax/nm-wimax-factory.c18
-rw-r--r--src/devices/wimax/nm-wimax-nsp.c33
-rw-r--r--src/devices/wimax/nm-wimax-nsp.h4
-rw-r--r--src/devices/wimax/nm-wimax-types.h4
-rw-r--r--src/devices/wimax/nm-wimax-util.c3
-rw-r--r--src/devices/wimax/nm-wimax-util.h9
-rw-r--r--src/devices/wwan/Makefile.am19
-rw-r--r--src/devices/wwan/Makefile.in46
-rw-r--r--src/devices/wwan/README45
-rw-r--r--src/devices/wwan/exports.ver1
-rw-r--r--src/devices/wwan/nm-device-modem.c207
-rw-r--r--src/devices/wwan/nm-device-modem.h6
-rw-r--r--src/devices/wwan/nm-modem-broadband.c283
-rw-r--r--src/devices/wwan/nm-modem-broadband.h7
-rw-r--r--src/devices/wwan/nm-modem-enum-types.c34
-rw-r--r--src/devices/wwan/nm-modem-enum-types.h6
-rw-r--r--src/devices/wwan/nm-modem-manager.c460
-rw-r--r--src/devices/wwan/nm-modem-manager.h6
-rw-r--r--src/devices/wwan/nm-modem-old-types.h70
-rw-r--r--src/devices/wwan/nm-modem-old.c1139
-rw-r--r--src/devices/wwan/nm-modem-old.h53
-rw-r--r--src/devices/wwan/nm-modem.c482
-rw-r--r--src/devices/wwan/nm-modem.h74
-rw-r--r--src/devices/wwan/nm-wwan-factory.c28
-rw-r--r--src/devices/wwan/nm-wwan-factory.h6
-rw-r--r--src/devices/wwan/wwan-exports.ver2
118 files changed, 8439 insertions, 7104 deletions
diff --git a/src/devices/adsl/Makefile.am b/src/devices/adsl/Makefile.am
index 0430f47d..07a8f9ee 100644
--- a/src/devices/adsl/Makefile.am
+++ b/src/devices/adsl/Makefile.am
@@ -5,20 +5,15 @@ include $(GLIB_MAKEFILE)
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
-	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/src/ppp-manager \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-adsl"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
 	$(GUDEV_CFLAGS)
 
 GLIB_GENERATED = nm-adsl-enum-types.h nm-adsl-enum-types.c
diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in
index 708a45d2..7942c559 100644
--- a/src/devices/adsl/Makefile.in
+++ b/src/devices/adsl/Makefile.in
@@ -217,6 +217,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -340,6 +342,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -359,6 +362,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -385,7 +389,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -467,20 +471,15 @@ with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
-	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/src/ppp-manager \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-adsl"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
 	$(GUDEV_CFLAGS)
 
 GLIB_GENERATED = nm-adsl-enum-types.h nm-adsl-enum-types.c
diff --git a/src/devices/adsl/exports.ver b/src/devices/adsl/exports.ver
index d2c45124..24cd848c 100644
--- a/src/devices/adsl/exports.ver
+++ b/src/devices/adsl/exports.ver
@@ -1,7 +1,6 @@
 {
 global:
 	nm_device_factory_create;
-	nm_device_factory_get_device_type;
 local:
 	*;
 };
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index e8db3596..6f34c02b 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -18,7 +18,7 @@
  * Copyright (C) 2009 - 2013 Red Hat, Inc.
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <string.h>
 #include <gudev/gudev.h>
@@ -28,11 +28,11 @@
 #include "nm-device-adsl.h"
 #include "nm-device-factory.h"
 #include "nm-logging.h"
+#include "nm-glib-compat.h"
 
 typedef struct {
 	GUdevClient *client;
 	GSList *devices;
-	guint start_id;
 } NMAtmManagerPrivate;
 
 #define NM_ATM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ATM_MANAGER, NMAtmManagerPrivate))
@@ -46,20 +46,12 @@ G_DEFINE_TYPE_EXTENDED (NMAtmManager, nm_atm_manager, G_TYPE_OBJECT, 0,
 
 /**************************************************************************/
 
-#define PLUGIN_TYPE NM_DEVICE_TYPE_ADSL
-
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_ATM_MANAGER, NULL);
 }
 
-G_MODULE_EXPORT NMDeviceType
-nm_device_factory_get_device_type (void)
-{
-	return PLUGIN_TYPE;
-}
-
 /************************************************************************/
 
 static gboolean
@@ -163,9 +155,10 @@ adsl_remove (NMAtmManager *self, GUdevDevice *udev_device)
 	}
 }
 
-static gboolean
-query_devices (NMAtmManager *self)
+static void
+start (NMDeviceFactory *factory)
 {
+	NMAtmManager *self = NM_ATM_MANAGER (factory);
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
 	GUdevEnumerator *enumerator;
 	GList *devices, *iter;
@@ -180,8 +173,6 @@ query_devices (NMAtmManager *self)
 	}
 	g_list_free (devices);
 	g_object_unref (enumerator);
-
-	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -212,6 +203,12 @@ handle_uevent (GUdevClient *client,
 		adsl_remove (self, device);
 }
 
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_ADSL;
+}
+
 /*********************************************************************/
 
 static void
@@ -222,13 +219,13 @@ nm_atm_manager_init (NMAtmManager *self)
 
 	priv->client = g_udev_client_new (subsys);
 	g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
-
-	priv->start_id = g_idle_add ((GSourceFunc) query_devices, self);
 }
 
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
+	factory_iface->get_device_type = get_device_type;
+	factory_iface->start = start;
 }
 
 static void
@@ -242,11 +239,6 @@ dispose (GObject *object)
 		g_signal_handlers_disconnect_by_func (priv->client, handle_uevent, self);
 	g_clear_object (&priv->client);
 
-	if (priv->start_id) {
-		g_source_remove (priv->start_id);
-		priv->start_id = 0;
-	}
-
 	for (iter = priv->devices; iter; iter = iter->next)
 		g_object_weak_unref (G_OBJECT (iter->data), device_destroyed, self);
 	g_clear_pointer (&priv->devices, g_slist_free);
diff --git a/src/devices/adsl/nm-atm-manager.h b/src/devices/adsl/nm-atm-manager.h
index 00525220..e4ec3e6a 100644
--- a/src/devices/adsl/nm-atm-manager.h
+++ b/src/devices/adsl/nm-atm-manager.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2014 Red Hat, Inc.
  */
 
-#ifndef NM_ATM_MANAGER_H
-#define NM_ATM_MANAGER_H
+#ifndef __NETWORKMANAGER_ATM_MANAGER_H__
+#define __NETWORKMANAGER_ATM_MANAGER_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -38,5 +38,5 @@ typedef struct {
 	GObjectClass parent;
 } NMAtmManagerClass;
 
-#endif /* NM_ATM_MANAGER_H */
+#endif /* __NETWORKMANAGER_ATM_MANAGER_H__ */
 
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 0c35bb7a..e664ff48 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -18,7 +18,7 @@
  * Pantelis Koukousoulas <pktoss@gmail.com>
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <sys/socket.h>
 #include <linux/atmdev.h>
@@ -44,9 +44,13 @@
 
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-setting-adsl.h"
+#include "nm-utils.h"
 
 #include "nm-device-adsl-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF (NMDeviceAdsl);
+
 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))
@@ -72,7 +76,9 @@ typedef struct {
 static guint32
 get_generic_capabilities (NMDevice *dev)
 {
-	return (NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_NONSTANDARD_CARRIER);
+	return (  NM_DEVICE_CAP_CARRIER_DETECT
+	        | NM_DEVICE_CAP_NONSTANDARD_CARRIER
+	        | NM_DEVICE_CAP_IS_NON_KERNEL);
 }
 
 static gboolean
@@ -119,7 +125,8 @@ complete_connection (NMDevice *device,
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_ADSL_SETTING_NAME,
 	                           existing_connections,
-	                           _("ADSL connection %d"),
+	                           NULL,
+	                           _("ADSL connection"),
 	                           NULL,
 	                           FALSE); /* No IPv6 yet by default */
 
@@ -142,18 +149,14 @@ set_nas_iface (NMDeviceAdsl *self, int idx, const char *name)
 
 	g_warn_if_fail (priv->nas_ifname == NULL);
 	priv->nas_ifname = g_strdup (name);
-
-	/* Update NAS interface's MAC address */
-	nm_device_update_hw_address (NM_DEVICE (self));
 }
 
 static gboolean
 br2684_create_iface (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 {
 	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
-	const char *iface = nm_device_get_iface (NM_DEVICE (self));
 	struct atm_newif_br2684 ni;
-	int err, fd;
+	int err, fd, errsv;
 	gboolean success = FALSE;
 	guint num = 0;
 
@@ -161,8 +164,8 @@ br2684_create_iface (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 
 	fd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
 	if (fd < 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to open ATM control socket (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv);
 		return FALSE;
 	}
 
@@ -183,17 +186,19 @@ br2684_create_iface (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 		err = ioctl (fd, ATM_NEWBACKENDIF, &ni);
 		if (err == 0) {
 			set_nas_iface (self, -1, ni.ifname);
-			nm_log_info (LOGD_ADSL, "(%s): using NAS interface %s (%d)",
-			             iface, priv->nas_ifname, priv->nas_ifindex);
+			_LOGI (LOGD_ADSL, "using NAS interface %s (%d)",
+			       priv->nas_ifname, priv->nas_ifindex);
 			success = TRUE;
 			break;
-		} else if (errno == -EEXIST) {
-			/* Try again */
-			num++;
 		} else {
-			nm_log_warn (LOGD_ADSL, "(%s): failed to create br2684 interface (%d)",
-			             iface, errno);
-			break;
+			errsv = errno;
+			if (errsv == -EEXIST) {
+				/* Try again */
+				num++;
+			} else {
+				_LOGW (LOGD_ADSL, "failed to create br2684 interface (%d)", errsv);
+				break;
+			}
 		}
 	}
 
@@ -205,11 +210,10 @@ static gboolean
 br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 {
 	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
-	const char *iface = nm_device_get_iface (NM_DEVICE (self));
 	struct sockaddr_atmpvc addr;
 	struct atm_backend_br2684 be;
 	struct atm_qos qos;
-	int err, bufsize = 8192;
+	int errsv, err, bufsize = 8192;
 	const char *encapsulation;
 	gboolean is_llc;
 
@@ -218,15 +222,15 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 
 	priv->brfd = socket (PF_ATMPVC, SOCK_DGRAM, ATM_AAL5);
 	if (priv->brfd < 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to open ATM control socket (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv);
 		return FALSE;
 	}
 
 	err = setsockopt (priv->brfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof (bufsize));
 	if (err != 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to set SNDBUF option (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to set SNDBUF option (%d)", errsv);
 		goto error;
 	}
 
@@ -240,8 +244,8 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 
 	err = setsockopt (priv->brfd, SOL_ATM, SO_ATMQOS, &qos, sizeof (qos));
 	if (err != 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to set QoS (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to set QoS (%d)", errsv);
 		goto error;
 	}
 
@@ -254,15 +258,14 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 	addr.sap_addr.vpi = (guint16) nm_setting_adsl_get_vpi (s_adsl);
 	addr.sap_addr.vci = (int) nm_setting_adsl_get_vci (s_adsl);
 
-	nm_log_dbg (LOGD_ADSL, "(%s): assigning address %d.%d.%d encapsulation %s",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            priv->atm_index, addr.sap_addr.vpi, addr.sap_addr.vci,
-	            encapsulation);
+	_LOGD (LOGD_ADSL, "assigning address %d.%d.%d encapsulation %s",
+	       priv->atm_index, addr.sap_addr.vpi, addr.sap_addr.vci,
+	       encapsulation ? encapsulation : "(none)");
 
 	err = connect (priv->brfd, (struct sockaddr*) &addr, sizeof (addr));
 	if (err != 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to set VPI/VCI (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to set VPI/VCI (%d)", errsv);
 		goto error;
 	}
 
@@ -278,8 +281,8 @@ br2684_assign_vcc (NMDeviceAdsl *self, NMSettingAdsl *s_adsl)
 	be.encaps = is_llc ? BR2684_ENCAPS_LLC : BR2684_ENCAPS_VC;
 	err = ioctl (priv->brfd, ATM_SETBACKEND, &be);
 	if (err != 0) {
-		nm_log_err (LOGD_ADSL, "(%s): failed to attach VCC (%d)",
-		            iface, errno);
+		errsv = errno;
+		_LOGE (LOGD_ADSL, "failed to attach VCC (%d)", errsv);
 		goto error;
 	}
 
@@ -292,18 +295,17 @@ error:
 }
 
 static void
-link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *device_adsl)
+link_changed_cb (NMPlatform *platform, 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 (device_adsl);
-		NMDevice *device = NM_DEVICE (device_adsl);
+		NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
+		NMDevice *device = NM_DEVICE (self);
 
 		/* This only gets called for PPPoE connections and "nas" interfaces */
 
 		if (priv->nas_ifindex >= 0 && ifindex == priv->nas_ifindex) {
 				/* NAS device went away for some reason; kill the connection */
-				nm_log_dbg (LOGD_ADSL, "(%s): NAS interface disappeared",
-				            nm_device_get_iface (device));
+				_LOGD (LOGD_ADSL, "NAS interface disappeared");
 				nm_device_state_changed (device,
 				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_BR2684_FAILED);
@@ -326,8 +328,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 	g_assert (s_adsl);
 
 	protocol = nm_setting_adsl_get_protocol (s_adsl);
-	nm_log_dbg (LOGD_ADSL, "(%s): using ADSL protocol '%s'",
-	             nm_device_get_iface (device), protocol);
+	_LOGD (LOGD_ADSL, "using ADSL protocol '%s'", protocol);
 
 	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {
 
@@ -348,7 +349,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 		                  G_CALLBACK (link_changed_cb),
 		                  self);
 
-		nm_log_dbg (LOGD_ADSL, "(%s): ATM setup successful", nm_device_get_iface (device));
+		_LOGD (LOGD_ADSL, "ATM setup successful");
 
 		/* otherwise we're good for stage3 */
 		nm_platform_link_set_up (priv->nas_ifindex);
@@ -357,10 +358,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *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 {
-		nm_log_warn (LOGD_ADSL, "(%s): unhandled ADSL protocol '%s'",
-		             nm_device_get_iface (device), protocol);
-	}
+	} else
+		_LOGW (LOGD_ADSL, "unhandled ADSL protocol '%s'", protocol);
 
 done:
 	return ret;
@@ -410,7 +409,6 @@ act_stage3_ip4_config_start (NMDevice *device,
 	NMActRequest *req;
 	GError *err = NULL;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	const char *iface = nm_device_get_iface (device);
 	const char *ppp_iface;
 
 	req = nm_device_get_act_request (device);
@@ -427,11 +425,10 @@ act_stage3_ip4_config_start (NMDevice *device,
 		g_assert (priv->nas_ifname);
 		ppp_iface = priv->nas_ifname;
 
-		nm_log_dbg (LOGD_ADSL, "(%s): starting PPPoE on NAS interface %s",
-			        iface, priv->nas_ifname);
+		_LOGD (LOGD_ADSL, "starting PPPoE on NAS interface %s", priv->nas_ifname);
 	} else {
-		ppp_iface = iface;
-		nm_log_dbg (LOGD_ADSL, "(%s): starting PPPoA", iface);
+		ppp_iface = nm_device_get_iface (device);
+		_LOGD (LOGD_ADSL, "starting PPPoA");
 	}
 
 	priv->ppp_manager = nm_ppp_manager_new (ppp_iface);
@@ -444,7 +441,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 		                  self);
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
-		nm_log_warn (LOGD_ADSL, "(%s): PPP failed to start: %s", iface, err->message);
+		_LOGW (LOGD_ADSL, "PPP failed to start: %s", err->message);
 		g_error_free (err);
 
 		g_object_unref (priv->ppp_manager);
@@ -482,33 +479,19 @@ deactivate (NMDevice *device)
 		priv->nas_ifindex = -1;
 	g_free (priv->nas_ifname);
 	priv->nas_ifname = NULL;
-
-	/* Poke NMDevice to notice that our hw_address is no longer valid */
-	nm_device_update_hw_address (NM_DEVICE (self));
 }
 
 /**************************************************************/
 
-static guint
-get_hw_address_length (NMDevice *device, gboolean *out_permanent)
-{
-	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (device);
-
-	return priv->nas_ifname ? ETH_ALEN : 0;
-}
-
 static gboolean
 carrier_update_cb (gpointer user_data)
 {
 	NMDeviceAdsl *self = NM_DEVICE_ADSL (user_data);
 	int carrier;
 	char *path;
-	const char *iface;
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
 
 	path  = g_strdup_printf ("/sys/class/atm/%s/carrier",
-	                         ASSERT_VALID_PATH_COMPONENT (iface));
+	                         ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self))));
 	carrier = (int) nm_platform_sysctl_get_int_checked (path, 10, 0, 1, -1);
 	g_free (path);
 
@@ -555,6 +538,7 @@ constructor (GType type,
 			 GObjectConstructParam *construct_params)
 {
 	GObject *object;
+	NMDeviceAdsl *self;
 	NMDeviceAdslPrivate *priv;
 
 	object = G_OBJECT_CLASS (nm_device_adsl_parent_class)->constructor (type,
@@ -563,18 +547,16 @@ constructor (GType type,
 	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) {
-		nm_log_err (LOGD_ADSL, "(%s): error reading ATM device index",
-		                       nm_device_get_iface (NM_DEVICE (object)));
+		_LOGE (LOGD_ADSL, "error reading ATM device index");
 		g_object_unref (object);
 		return NULL;
-	} else {
-		nm_log_dbg (LOGD_ADSL, "(%s): ATM device index %d",
-				    nm_device_get_iface (NM_DEVICE (object)), priv->atm_index);
-	}
+	} else
+		_LOGD (LOGD_ADSL, "ATM device index %d", priv->atm_index);
 
 	/* Poll the carrier */
 	priv->carrier_poll_id = g_timeout_add_seconds (5, carrier_update_cb, object);
@@ -629,7 +611,6 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
 
-	parent_class->get_hw_address_length = get_hw_address_length;
 	parent_class->act_stage2_config = act_stage2_config;
 	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	parent_class->deactivate = deactivate;
diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h
index bbd0e63f..f21381c2 100644
--- a/src/devices/adsl/nm-device-adsl.h
+++ b/src/devices/adsl/nm-device-adsl.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2009 - 2011 Red Hat Inc.
  */
 
-#ifndef NM_DEVICE_ADSL_H
-#define NM_DEVICE_ADSL_H
+#ifndef __NETWORKMANAGER_DEVICE_ADSL_H__
+#define __NETWORKMANAGER_DEVICE_ADSL_H__
 
 #include <glib-object.h>
 
diff --git a/src/devices/bluetooth/Makefile.am b/src/devices/bluetooth/Makefile.am
index 639a1ad7..8ab561ac 100644
--- a/src/devices/bluetooth/Makefile.am
+++ b/src/devices/bluetooth/Makefile.am
@@ -5,26 +5,22 @@ include $(GLIB_MAKEFILE)
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/devices/wwan \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
+	$(DBUS_CFLAGS)
 
 GLIB_GENERATED = nm-bt-enum-types.h nm-bt-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
 GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_bt_enum_types_sources = $(srcdir)/nm-device-bt.h
+nm_bt_enum_types_sources = $(srcdir)/nm-bt-error.h
 
 nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
 	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
@@ -47,6 +43,8 @@ libnm_device_plugin_bluetooth_la_SOURCES = \
 	nm-bluez4-manager.h \
 	nm-bluez5-manager.c \
 	nm-bluez5-manager.h \
+	nm-bt-error.h \
+	nm-bt-error.c \
 	\
 	nm-device-bt.c \
 	nm-device-bt.h \
@@ -62,6 +60,16 @@ libnm_device_plugin_bluetooth_la_LIBADD = \
 	$(DBUS_LIBS) \
 	$(GUDEV_LIBS)
 
+if WITH_BLUEZ5_DUN
+AM_CPPFLAGS += $(BLUEZ5_CFLAGS)
+
+libnm_device_plugin_bluetooth_la_SOURCES += \
+	nm-bluez5-dun.c \
+	nm-bluez5-dun.h
+
+libnm_device_plugin_bluetooth_la_LIBADD += $(BLUEZ5_LIBS)
+endif
+
 CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
 
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
index 3145c52d..0a285fda 100644
--- a/src/devices/bluetooth/Makefile.in
+++ b/src/devices/bluetooth/Makefile.in
@@ -78,6 +78,12 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+@WITH_BLUEZ5_DUN_TRUE@am__append_1 = $(BLUEZ5_CFLAGS)
+@WITH_BLUEZ5_DUN_TRUE@am__append_2 = \
+@WITH_BLUEZ5_DUN_TRUE@	nm-bluez5-dun.c \
+@WITH_BLUEZ5_DUN_TRUE@	nm-bluez5-dun.h
+
+@WITH_BLUEZ5_DUN_TRUE@am__append_3 = $(BLUEZ5_LIBS)
 subdir = src/devices/bluetooth
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(top_srcdir)/build-aux/depcomp
@@ -131,14 +137,26 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
+@WITH_BLUEZ5_DUN_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
 libnm_device_plugin_bluetooth_la_DEPENDENCIES =  \
 	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_2)
+am__libnm_device_plugin_bluetooth_la_SOURCES_DIST =  \
+	nm-bluez-manager.c nm-bluez-manager.h nm-bluez-common.h \
+	nm-bluez-device.c nm-bluez-device.h nm-bluez4-adapter.c \
+	nm-bluez4-adapter.h nm-bluez4-manager.c nm-bluez4-manager.h \
+	nm-bluez5-manager.c nm-bluez5-manager.h nm-bt-error.h \
+	nm-bt-error.c nm-device-bt.c nm-device-bt.h nm-bt-enum-types.h \
+	nm-bt-enum-types.c nm-device-bt-glue.h nm-bluez5-dun.c \
+	nm-bluez5-dun.h
 am__objects_1 = nm-bt-enum-types.lo
 am__objects_2 = $(am__objects_1)
+@WITH_BLUEZ5_DUN_TRUE@am__objects_3 = nm-bluez5-dun.lo
 am_libnm_device_plugin_bluetooth_la_OBJECTS = nm-bluez-manager.lo \
 	nm-bluez-device.lo nm-bluez4-adapter.lo nm-bluez4-manager.lo \
-	nm-bluez5-manager.lo nm-device-bt.lo $(am__objects_2)
+	nm-bluez5-manager.lo nm-bt-error.lo nm-device-bt.lo \
+	$(am__objects_2) $(am__objects_3)
 libnm_device_plugin_bluetooth_la_OBJECTS =  \
 	$(am_libnm_device_plugin_bluetooth_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
@@ -184,7 +202,7 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
 SOURCES = $(libnm_device_plugin_bluetooth_la_SOURCES)
-DIST_SOURCES = $(libnm_device_plugin_bluetooth_la_SOURCES)
+DIST_SOURCES = $(am__libnm_device_plugin_bluetooth_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -219,6 +237,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -342,6 +362,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -361,6 +382,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -387,7 +409,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -466,59 +488,35 @@ with_dhcpcd = @with_dhcpcd@
 with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
-AM_CPPFLAGS = \
-	-I${top_srcdir}/src \
-	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
-	-I${top_srcdir}/src/devices \
-	-I${top_srcdir}/src/settings \
-	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/src/devices/wwan \
-	-I${top_builddir}/include \
-	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+AM_CPPFLAGS = -I${top_srcdir}/src -I${top_builddir}/src \
+	-I${top_srcdir}/src/devices -I${top_srcdir}/src/settings \
+	-I${top_srcdir}/src/platform -I${top_srcdir}/src/devices/wwan \
+	-I${top_srcdir}/include -I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
-	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
-
+	-DNETWORKMANAGER_COMPILATION \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(DBUS_CFLAGS) \
+	$(am__append_1)
 GLIB_GENERATED = nm-bt-enum-types.h nm-bt-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
 GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_bt_enum_types_sources = $(srcdir)/nm-device-bt.h
+nm_bt_enum_types_sources = $(srcdir)/nm-bt-error.h
 BUILT_SOURCES = $(GLIB_GENERATED) nm-device-bt-glue.h
 pkglib_LTLIBRARIES = libnm-device-plugin-bluetooth.la
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
-libnm_device_plugin_bluetooth_la_SOURCES = \
-	nm-bluez-manager.c \
-	nm-bluez-manager.h \
-	nm-bluez-common.h \
-	nm-bluez-device.c \
-	nm-bluez-device.h \
-	nm-bluez4-adapter.c \
-	nm-bluez4-adapter.h \
-	nm-bluez4-manager.c \
-	nm-bluez4-manager.h \
-	nm-bluez5-manager.c \
-	nm-bluez5-manager.h \
-	\
-	nm-device-bt.c \
-	nm-device-bt.h \
-	\
-	$(BUILT_SOURCES)
-
+libnm_device_plugin_bluetooth_la_SOURCES = nm-bluez-manager.c \
+	nm-bluez-manager.h nm-bluez-common.h nm-bluez-device.c \
+	nm-bluez-device.h nm-bluez4-adapter.c nm-bluez4-adapter.h \
+	nm-bluez4-manager.c nm-bluez4-manager.h nm-bluez5-manager.c \
+	nm-bluez5-manager.h nm-bt-error.h nm-bt-error.c nm-device-bt.c \
+	nm-device-bt.h $(BUILT_SOURCES) $(am__append_2)
 libnm_device_plugin_bluetooth_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
-libnm_device_plugin_bluetooth_la_LIBADD = \
-	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
-	$(DBUS_LIBS) \
-	$(GUDEV_LIBS)
-
+libnm_device_plugin_bluetooth_la_LIBADD =  \
+	$(top_builddir)/src/devices/wwan/libnm-wwan.la $(DBUS_LIBS) \
+	$(GUDEV_LIBS) $(am__append_3)
 CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
 all: $(BUILT_SOURCES)
@@ -605,8 +603,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez-manager.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez4-adapter.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez4-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez5-dun.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bluez5-manager.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bt-enum-types.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bt-error.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-bt.Plo@am__quote@
 
 .c.o:
diff --git a/src/devices/bluetooth/exports.ver b/src/devices/bluetooth/exports.ver
index d2c45124..24cd848c 100644
--- a/src/devices/bluetooth/exports.ver
+++ b/src/devices/bluetooth/exports.ver
@@ -1,7 +1,6 @@
 {
 global:
 	nm_device_factory_create;
-	nm_device_factory_get_device_type;
 local:
 	*;
 };
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index f80cfc2e..f0cab43b 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ_COMMON_H
-#define NM_BLUEZ_COMMON_H
+#ifndef __NETWORKMANAGER_BLUEZ_COMMON_H__
+#define __NETWORKMANAGER_BLUEZ_COMMON_H__
 
 #include <config.h>
 
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 4c448a37..fe1a97bb 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -19,22 +19,24 @@
  * Copyright (C) 2013 Intel Corporation.
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <string.h>
-#include <net/ethernet.h>
-#include <netinet/ether.h>
 
-#include "NetworkManager.h"
-#include "nm-setting-bluetooth.h"
+#include "nm-core-internal.h"
 
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "nm-settings-connection.h"
+#include "NetworkManagerUtils.h"
 
+#if WITH_BLUEZ5_DUN
+#include "nm-bluez5-dun.h"
+#endif
 
 G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
 
@@ -55,19 +57,21 @@ typedef struct {
 	gboolean usable;
 	NMBluetoothCapabilities connection_bt_type;
 
+	char *adapter_address;
 	char *address;
-	guint8 bin_address[ETH_ALEN];
 	char *name;
 	guint32 capabilities;
 	gboolean connected;
 
-	char *bt_iface;
+	char *b4_iface;
+#if WITH_BLUEZ5_DUN
+	NMBluez5DunContext *b5_dun_context;
+#endif
 
 	NMConnectionProvider *provider;
 	GSList *connections;
 
 	NMConnection *pan_connection;
-	NMConnection *pan_connection_original;
 	gboolean pan_connection_no_autocreate;
 } NMBluezDevicePrivate;
 
@@ -156,9 +160,12 @@ nm_bluez_device_get_capabilities (NMBluezDevice *self)
 gboolean
 nm_bluez_device_get_connected (NMBluezDevice *self)
 {
+	NMBluezDevicePrivate *priv;
+
 	g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
 
-	return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->connected;
+	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	return priv->connected;
 }
 
 static void
@@ -168,7 +175,6 @@ pan_connection_check_create (NMBluezDevice *self)
 	NMConnection *added;
 	NMSetting *setting;
 	char *uuid, *id;
-	GByteArray *bdaddr_array;
 	GError *error = NULL;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
@@ -187,7 +193,7 @@ pan_connection_check_create (NMBluezDevice *self)
 
 	/* create a new connection */
 
-	connection = nm_connection_new ();
+	connection = nm_simple_connection_new ();
 
 	/* Setting: Connection */
 	uuid = nm_utils_uuid_generate ();
@@ -202,29 +208,26 @@ pan_connection_check_create (NMBluezDevice *self)
 	nm_connection_add_setting (connection, setting);
 
 	/* Setting: Bluetooth */
-	bdaddr_array = g_byte_array_sized_new (sizeof (priv->bin_address));
-	g_byte_array_append (bdaddr_array, priv->bin_address, sizeof (priv->bin_address));
 	setting = nm_setting_bluetooth_new ();
 	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_BLUETOOTH_BDADDR, bdaddr_array,
+	              NM_SETTING_BLUETOOTH_BDADDR, priv->address,
 	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
 	              NULL);
 	nm_connection_add_setting (connection, setting);
-	g_byte_array_free (bdaddr_array, TRUE);
 
 	/* Setting: IPv4 */
 	setting = nm_setting_ip4_config_new ();
 	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-	              NM_SETTING_IP4_CONFIG_MAY_FAIL, FALSE,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP_CONFIG_MAY_FAIL, FALSE,
 	              NULL);
 	nm_connection_add_setting (connection, setting);
 
 	/* Setting: IPv6 */
 	setting = nm_setting_ip6_config_new ();
 	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
-	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 	nm_connection_add_setting (connection, setting);
 
@@ -240,9 +243,10 @@ pan_connection_check_create (NMBluezDevice *self)
 		g_assert (connection_compatible (self, added));
 		g_assert (nm_connection_compare (added, connection, NM_SETTING_COMPARE_FLAG_EXACT));
 
+		nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED, TRUE);
+
 		priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
 		priv->pan_connection = added;
-		priv->pan_connection_original = connection;
 		nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
 	} else {
 		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %d / %s",
@@ -250,8 +254,8 @@ pan_connection_check_create (NMBluezDevice *self)
 		             (error && error->message) ? error->message : "(unknown)");
 		g_clear_error (&error);
 
-		g_object_unref (connection);
 	}
+	g_object_unref (connection);
 
 	g_free (id);
 	g_free (uuid);
@@ -264,13 +268,12 @@ check_emit_usable (NMBluezDevice *self)
 	gboolean new_usable;
 
 	/* only expect the supported capabilities set. */
-	g_assert (priv->bluez_version != 4 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE ));
-	g_assert (priv->bluez_version != 5 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP                       )) == NM_BT_CAPABILITY_NONE ));
+	g_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
 
 	new_usable = (priv->initialized && priv->capabilities && priv->name &&
 	              ((priv->bluez_version == 4) ||
 	               (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered) ) &&
-	              priv->dbus_connection && priv->address);
+	              priv->dbus_connection && priv->address && priv->adapter_address);
 
 	if (!new_usable)
 		goto END;
@@ -302,7 +305,7 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection)
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 	NMSettingBluetooth *s_bt;
 	const char *bt_type;
-	const GByteArray *bdaddr;
+	const char *bdaddr;
 
 	if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
 		return FALSE;
@@ -311,14 +314,13 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection)
 	if (!s_bt)
 		return FALSE;
 
-	if (!priv->address) {
-		/* unless address is set, bin_address is not initialized. */
+	if (!priv->address)
 		return FALSE;
-	}
+
 	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
-	if (!bdaddr || bdaddr->len != ETH_ALEN)
+	if (!bdaddr)
 		return FALSE;
-	if (memcmp (bdaddr->data, priv->bin_address, ETH_ALEN) != 0)
+	if (!nm_utils_hwaddr_matches (bdaddr, -1, priv->address, -1))
 		return FALSE;
 
 	bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
@@ -362,10 +364,8 @@ cp_connection_removed (NMConnectionProvider *provider,
 
 	if (g_slist_find (priv->connections, connection)) {
 		priv->connections = g_slist_remove (priv->connections, connection);
-		if (priv->pan_connection == connection) {
+		if (priv->pan_connection == connection)
 			priv->pan_connection = NULL;
-			g_clear_object (&priv->pan_connection_original);
-		}
 		g_object_unref (connection);
 		check_emit_usable (self);
 	}
@@ -420,26 +420,37 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
 {
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 	GVariant *args = NULL;
-	const char *dbus_iface;
+	const char *dbus_iface = NULL;
 
 	g_return_if_fail (priv->dbus_connection);
 
-	if (priv->bluez_version == 5) {
-		g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
-		dbus_iface = BLUEZ5_NETWORK_INTERFACE;
-	} else if (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
-		/* Can't pass a NULL interface name through dbus to bluez, so just
-		 * ignore the disconnect if the interface isn't known.
-		 */
-		if (!priv->bt_iface)
-			return;
-
-		args = g_variant_new ("(s)", priv->bt_iface),
-		dbus_iface = BLUEZ4_SERIAL_INTERFACE;
-	} else {
-		g_return_if_fail (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
-		dbus_iface = BLUEZ4_NETWORK_INTERFACE;
-	}
+	if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		if (priv->bluez_version == 4) {
+			/* Can't pass a NULL interface name through dbus to bluez, so just
+			 * ignore the disconnect if the interface isn't known.
+			 */
+			if (!priv->b4_iface)
+				goto out;
+			args = g_variant_new ("(s)", priv->b4_iface),
+			dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+		} else if (priv->bluez_version == 5) {
+#if WITH_BLUEZ5_DUN
+			nm_bluez5_dun_cleanup (priv->b5_dun_context);
+#else
+			g_assert_not_reached ();
+#endif
+			priv->connected = FALSE;
+			goto out;
+		}
+	} else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
+		if (priv->bluez_version == 4)
+			dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+		else if (priv->bluez_version == 5)
+			dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+		else
+			g_assert_not_reached ();
+	} else
+		g_assert_not_reached ();
 
 	g_dbus_connection_call (priv->dbus_connection,
 	                        BLUEZ_SERVICE,
@@ -454,6 +465,8 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
 	                        (GAsyncReadyCallback) bluez_disconnect_cb,
 	                        g_object_ref (self));
 
+out:
+	g_clear_pointer (&priv->b4_iface, g_free);
 	priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
 }
 
@@ -480,7 +493,7 @@ bluez_connect_cb (GDBusConnection *dbus_connection,
 		g_simple_async_result_set_op_res_gpointer (result,
 		                                           g_strdup (device),
 		                                           g_free);
-		priv->bt_iface = device;
+		priv->b4_iface = device;
 		g_variant_unref (variant);
 	}
 
@@ -489,6 +502,28 @@ bluez_connect_cb (GDBusConnection *dbus_connection,
 	g_object_unref (result_object);
 }
 
+#if WITH_BLUEZ5_DUN
+static void
+bluez5_dun_connect_cb (NMBluez5DunContext *context,
+                   const char *device,
+                   GError *error,
+                   gpointer user_data)
+{
+	GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+
+	if (error) {
+		g_simple_async_result_take_error (result, error);
+	} else {
+		g_simple_async_result_set_op_res_gpointer (result,
+		                                           g_strdup (device),
+		                                           g_free);
+	}
+
+	g_simple_async_result_complete (result);
+	g_object_unref (result);
+}
+#endif
+
 void
 nm_bluez_device_connect_async (NMBluezDevice *self,
                                NMBluetoothCapabilities connection_bt_type,
@@ -497,26 +532,39 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
 {
 	GSimpleAsyncResult *simple;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	const char *dbus_iface;
-	const char *connect_type = BLUETOOTH_CONNECT_NAP;
+	const char *dbus_iface = NULL;
+	const char *connect_type = NULL;
 
 	g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
 
-	if (priv->bluez_version == 5) {
-		g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP);
-		dbus_iface = BLUEZ5_NETWORK_INTERFACE;
-	} else if (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_DUN) {
-		dbus_iface = BLUEZ4_SERIAL_INTERFACE;
-		connect_type = BLUETOOTH_CONNECT_DUN;
-	} else {
-		g_return_if_fail (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_NAP);
-		dbus_iface = BLUEZ4_NETWORK_INTERFACE;
-	}
-
 	simple = g_simple_async_result_new (G_OBJECT (self),
 	                                    callback,
 	                                    user_data,
 	                                    nm_bluez_device_connect_async);
+	priv->connection_bt_type = connection_bt_type;
+
+	if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
+		connect_type = BLUETOOTH_CONNECT_NAP;
+		if (priv->bluez_version == 4)
+			dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+		else if (priv->bluez_version == 5)
+			dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+	} else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+		connect_type = BLUETOOTH_CONNECT_DUN;
+		if (priv->bluez_version == 4)
+			dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+		else if (priv->bluez_version == 5) {
+#if WITH_BLUEZ5_DUN
+			if (priv->b5_dun_context == NULL)
+				priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
+			nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple);
+#else
+			g_assert_not_reached ();
+#endif
+			return;
+		}
+	} else
+		g_assert_not_reached ();
 
 	g_dbus_connection_call (priv->dbus_connection,
 	                        BLUEZ_SERVICE,
@@ -530,8 +578,6 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
 	                        NULL,
 	                        (GAsyncReadyCallback) bluez_connect_cb,
 	                        simple);
-
-	priv->connection_bt_type = connection_bt_type;
 }
 
 const char *
@@ -539,6 +585,7 @@ nm_bluez_device_connect_finish (NMBluezDevice *self,
                                 GAsyncResult *result,
                                 GError **error)
 {
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 	GSimpleAsyncResult *simple;
 	const char *device;
 
@@ -553,13 +600,28 @@ nm_bluez_device_connect_finish (NMBluezDevice *self,
 		return NULL;
 
 	device = (const char *) g_simple_async_result_get_op_res_gpointer (simple);
+	if (device && priv->bluez_version == 5)
+		priv->connected = TRUE;
+
 	return device;
 }
 
 /***********************************************************/
 
+static void
+set_adapter_address (NMBluezDevice *self, const char *address)
+{
+	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+	g_return_if_fail (address);
+
+	if (priv->adapter_address)
+		g_free (priv->adapter_address);
+	priv->adapter_address = g_strdup (address);
+}
+
 static guint32
-convert_uuids_to_capabilities (const char **strings, int bluez_version)
+convert_uuids_to_capabilities (const char **strings)
 {
 	const char **iter;
 	guint32 capabilities = 0;
@@ -571,8 +633,7 @@ convert_uuids_to_capabilities (const char **strings, int bluez_version)
 		if (parts && parts[0]) {
 			switch (g_ascii_strtoull (parts[0], NULL, 16)) {
 			case 0x1103:
-				if (bluez_version == 4)
-					capabilities |= NM_BT_CAPABILITY_DUN;
+				capabilities |= NM_BT_CAPABILITY_DUN;
 				break;
 			case 0x1116:
 				capabilities |= NM_BT_CAPABILITY_NAP;
@@ -593,7 +654,7 @@ _set_property_capabilities (NMBluezDevice *self, const char **uuids)
 	guint32 uint_val;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
-	uint_val = convert_uuids_to_capabilities (uuids, priv->bluez_version);
+	uint_val = convert_uuids_to_capabilities (uuids);
 	if (priv->capabilities != uint_val) {
 		if (priv->capabilities) {
 			/* changing (relevant) capabilities is not supported and ignored -- except setting initially */
@@ -613,13 +674,10 @@ _set_property_capabilities (NMBluezDevice *self, const char **uuids)
 /**
  * priv->address can only be set one to a certain (non NULL) value. Every later attempt
  * to reset it to another value will be ignored and a warning will be logged.
- *
- * When setting the address for the first time, we also set bin_address.
  **/
 static void
 _set_property_address (NMBluezDevice *self, const char *addr)
 {
-	struct ether_addr *tmp;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
 	if (g_strcmp0 (priv->address, addr) == 0)
@@ -635,15 +693,11 @@ _set_property_address (NMBluezDevice *self, const char *addr)
 		return;
 	}
 
-	tmp = ether_aton (addr);
-	if (!tmp) {
-		if (priv->address)
-			nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from '%s' to '%s' (invalid value)", priv->path, priv->address, addr);
-		else
-			nm_log_warn (LOGD_BT, "bluez[%s] cannot reset address from NULL to '%s' (invalid value)", priv->path, addr);
+	if (!nm_utils_hwaddr_valid (addr, ETH_ALEN)) {
+		nm_log_warn (LOGD_BT, "bluez[%s] cannot set address to '%s' (invalid value)", priv->path, addr);
 		return;
 	}
-	memcpy (priv->bin_address, tmp->ether_addr_octet, ETH_ALEN);
+
 	priv->address = g_strdup (addr);
 	g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_ADDRESS);
 }
@@ -752,6 +806,10 @@ adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
 		if (v)
 			g_variant_unref (v);
 
+		v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
+		if (VARIANT_IS_OF_TYPE_STRING (v))
+			set_adapter_address (self, g_variant_get_string (v, NULL));
+
 		priv->initialized = TRUE;
 		g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
 
@@ -957,7 +1015,10 @@ on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
 /********************************************************************/
 
 NMBluezDevice *
-nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version)
+nm_bluez_device_new (const char *path,
+                     const char *adapter_address,
+                     NMConnectionProvider *provider,
+                     int bluez_version)
 {
 	NMBluezDevice *self;
 	NMBluezDevicePrivate *priv;
@@ -978,8 +1039,10 @@ nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez
 	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
 	priv->bluez_version = bluez_version;
-
 	priv->provider = provider;
+	g_return_val_if_fail (bluez_version == 5 || (bluez_version == 4 && adapter_address), NULL);
+	if (adapter_address)
+		set_adapter_address (self, adapter_address);
 
 	g_signal_connect (priv->provider,
 	                  NM_CP_SIGNAL_CONNECTION_ADDED,
@@ -1037,14 +1100,19 @@ dispose (GObject *object)
 	if (priv->pan_connection) {
 		/* Check whether we want to remove the created connection. If so, we take a reference
 		 * and delete it at the end of dispose(). */
-		if (   nm_settings_connection_get_unsaved (NM_SETTINGS_CONNECTION (priv->pan_connection))
-		    && nm_connection_compare (priv->pan_connection, priv->pan_connection_original, NM_SETTING_COMPARE_FLAG_EXACT))
+		if (nm_settings_connection_get_nm_generated (NM_SETTINGS_CONNECTION (priv->pan_connection)))
 			to_delete = g_object_ref (priv->pan_connection);
 
 		priv->pan_connection = NULL;
-		g_clear_object (&priv->pan_connection_original);
 	}
 
+#if WITH_BLUEZ5_DUN
+	if (priv->b5_dun_context) {
+		nm_bluez5_dun_free (priv->b5_dun_context);
+		priv->b5_dun_context = NULL;
+	}
+#endif
+
 	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_added, self);
 	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_removed, self);
 	g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_updated, self);
@@ -1073,9 +1141,10 @@ finalize (GObject *object)
 	nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path);
 
 	g_free (priv->path);
+	g_free (priv->adapter_address);
 	g_free (priv->address);
 	g_free (priv->name);
-	g_free (priv->bt_iface);
+	g_free (priv->b4_iface);
 
 	if (priv->proxy)
 		g_signal_handlers_disconnect_by_data (priv->proxy, object);
@@ -1148,51 +1217,45 @@ nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_PATH,
-		 g_param_spec_string (NM_BLUEZ_DEVICE_PATH,
-		                      "DBus Path",
-		                      "DBus Path",
+		 g_param_spec_string (NM_BLUEZ_DEVICE_PATH, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ADDRESS,
-		 g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS,
-		                      "Address",
-		                      "Address",
+		 g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_NAME,
-		 g_param_spec_string (NM_BLUEZ_DEVICE_NAME,
-		                      "Name",
-		                      "Name",
+		 g_param_spec_string (NM_BLUEZ_DEVICE_NAME, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CAPABILITIES,
-		 g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES,
-		                      "Capabilities",
-		                      "Capabilities",
-		                      0, G_MAXUINT, 0,
-		                      G_PARAM_READABLE));
+		 g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_USABLE,
-		 g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE,
-		                       "Usable",
-		                       "Usable",
+		 g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CONNECTED,
-		 g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED,
-		                       "Connected",
-		                       "Connected",
+		 g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[INITIALIZED] = g_signal_new ("initialized",
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
index 0bf7d898..92d99fa7 100644
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 - 2014 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ_DEVICE_H
-#define NM_BLUEZ_DEVICE_H
+#ifndef __NETWORKMANAGER_BLUEZ_DEVICE_H__
+#define __NETWORKMANAGER_BLUEZ_DEVICE_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -62,7 +62,10 @@ typedef struct {
 
 GType nm_bluez_device_get_type (void);
 
-NMBluezDevice *nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version);
+NMBluezDevice *nm_bluez_device_new (const char *path,
+                                    const char *adapter_address,
+                                    NMConnectionProvider *provider,
+                                    int bluez_version);
 
 const char *nm_bluez_device_get_path (NMBluezDevice *self);
 
@@ -94,5 +97,5 @@ nm_bluez_device_connect_finish (NMBluezDevice *self,
 void
 nm_bluez_device_disconnect (NMBluezDevice *self);
 
-#endif /* NM_BLUEZ_DEVICE_H */
+#endif /* __NETWORKMANAGER_BLUEZ_DEVICE_H__ */
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index 04ffb0a4..e176a4b4 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -18,6 +18,8 @@
  * Copyright (C) 2013 - 2014 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
@@ -62,20 +64,12 @@ static void check_bluez_and_try_setup (NMBluezManager *self);
 
 /**************************************************************************/
 
-#define PLUGIN_TYPE NM_DEVICE_TYPE_BT
-
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL);
 }
 
-G_MODULE_EXPORT NMDeviceType
-nm_device_factory_get_device_type (void)
-{
-	return PLUGIN_TYPE;
-}
-
 /************************************************************************/
 
 struct AsyncData {
@@ -368,6 +362,18 @@ check_bluez_and_try_setup (NMBluezManager *self)
 	                          async_data_pack (self));
 }
 
+static void
+start (NMDeviceFactory *factory)
+{
+	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory));
+}
+
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_BT;
+}
+
 /*********************************************************************/
 
 static void
@@ -391,16 +397,6 @@ dispose (GObject *object)
 }
 
 static void
-constructed (GObject *object)
-{
-	NMBluezManager *self = NM_BLUEZ_MANAGER (object);
-
-	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->constructed (object);
-
-	check_bluez_and_try_setup (self);
-}
-
-static void
 nm_bluez_manager_init (NMBluezManager *self)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
@@ -412,6 +408,8 @@ nm_bluez_manager_init (NMBluezManager *self)
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
+	factory_iface->get_device_type = get_device_type;
+	factory_iface->start = start;
 }
 
 static void
@@ -423,6 +421,5 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 
 	/* virtual methods */
 	object_class->dispose = dispose;
-	object_class->constructed = constructed;
 }
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
index 68d6dbe5..8320b298 100644
--- a/src/devices/bluetooth/nm-bluez-manager.h
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2014 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ_MANAGER_H
-#define NM_BLUEZ_MANAGER_H
+#ifndef __NETWORKMANAGER_BLUEZ_MANAGER_H__
+#define __NETWORKMANAGER_BLUEZ_MANAGER_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -40,5 +40,5 @@ typedef struct {
 	GObjectClass parent;
 } NMBluezManagerClass;
 
-#endif /* NM_BLUEZ_MANAGER_H */
+#endif /* __NETWORKMANAGER_BLUEZ_MANAGER_H__ */
 
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index ad1786f0..9d1cfe1f 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -18,10 +18,12 @@
  * Copyright (C) 2009 - 2012 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <string.h>
 
-#include "NetworkManager.h"
+#include "nm-dbus-interface.h"
 #include "nm-dbus-manager.h"
 #include "nm-bluez4-adapter.h"
 #include "nm-bluez-device.h"
@@ -162,7 +164,7 @@ device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	device = nm_bluez_device_new (path, priv->provider, 4);
+	device = nm_bluez_device_new (path, priv->address, priv->provider, 4);
 	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
 	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
 	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
@@ -371,19 +373,17 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_PATH,
-		 g_param_spec_string (NM_BLUEZ4_ADAPTER_PATH,
-		                      "DBus Path",
-		                      "DBus Path",
+		 g_param_spec_string (NM_BLUEZ4_ADAPTER_PATH, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ADDRESS,
-		 g_param_spec_string (NM_BLUEZ4_ADAPTER_ADDRESS,
-		                      "Address",
-		                      "Address",
+		 g_param_spec_string (NM_BLUEZ4_ADAPTER_ADDRESS, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[INITIALIZED] = g_signal_new ("initialized",
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
index 454ca557..a6256412 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.h
+++ b/src/devices/bluetooth/nm-bluez4-adapter.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 - 2012 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ4_ADAPTER_H
-#define NM_BLUEZ4_ADAPTER_H
+#ifndef __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
+#define __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -65,5 +65,5 @@ gboolean nm_bluez4_adapter_get_initialized (NMBluez4Adapter *self);
 
 GSList *nm_bluez4_adapter_get_devices (NMBluez4Adapter *self);
 
-#endif /* NM_BLUEZ4_ADAPTER_H */
+#endif /* __NETWORKMANAGER_BLUEZ4_ADAPTER_H__ */
 
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
index 2660cbd9..23b13d32 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.c
+++ b/src/devices/bluetooth/nm-bluez4-manager.c
@@ -19,6 +19,8 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
diff --git a/src/devices/bluetooth/nm-bluez4-manager.h b/src/devices/bluetooth/nm-bluez4-manager.h
index 19b1c65a..13cfef93 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.h
+++ b/src/devices/bluetooth/nm-bluez4-manager.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ4_MANAGER_H
-#define NM_BLUEZ4_MANAGER_H
+#ifndef __NETWORKMANAGER_BLUEZ4_MANAGER_H__
+#define __NETWORKMANAGER_BLUEZ4_MANAGER_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -58,5 +58,5 @@ NMBluez4Manager *nm_bluez4_manager_new (NMConnectionProvider *provider);
 
 void nm_bluez4_manager_query_devices (NMBluez4Manager *manager);
 
-#endif /* NM_BLUEZ4_MANAGER_H */
+#endif /* __NETWORKMANAGER_BLUEZ4_MANAGER_H__ */
 
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
new file mode 100644
index 00000000..a97eaa77
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -0,0 +1,410 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <sys/socket.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <net/ethernet.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "nm-bluez5-dun.h"
+#include "nm-bt-error.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+
+struct _NMBluez5DunContext {
+	bdaddr_t src;
+	bdaddr_t dst;
+	char *src_str;
+	char *dst_str;
+	int rfcomm_channel;
+	int rfcomm_fd;
+	int rfcomm_tty_fd;
+	int rfcomm_id;
+	NMBluez5DunFunc callback;
+	gpointer user_data;
+	sdp_session_t *sdp_session;
+	guint sdp_watch_id;
+};
+
+static void
+dun_connect (NMBluez5DunContext *context)
+{
+	struct sockaddr_rc sa;
+	int devid, try = 30;
+	char tty[100];
+	const int ttylen = sizeof (tty) - 1;
+	GError *error = NULL;
+
+	struct rfcomm_dev_req req = {
+		.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+		.dev_id = -1,
+		.channel = context->rfcomm_channel
+	};
+
+	context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+	if (context->rfcomm_fd < 0) {
+		int errsv = errno;
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to create RFCOMM socket: (%d) %s",
+		                     errsv, strerror (errsv));
+		goto done;
+	}
+
+	/* Connect to the remote device */
+	sa.rc_family = AF_BLUETOOTH;
+	sa.rc_channel = 0;
+	memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
+	if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
+		int errsv = errno;
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to bind socket: (%d) %s",
+		                     errsv, strerror (errsv));
+		goto done;
+	}
+
+	sa.rc_channel = context->rfcomm_channel;
+	memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
+	if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) {
+		int errsv = errno;
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to connect to remote device: (%d) %s",
+		                     errsv, strerror (errsv));
+		goto done;
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): connected to %s on channel %d",
+	            context->src_str, context->dst_str, context->rfcomm_channel);
+
+	/* Create an RFCOMM kernel device for the DUN channel */
+	memcpy (&req.src, &context->src, ETH_ALEN);
+	memcpy (&req.dst, &context->dst, ETH_ALEN);
+	devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req);
+	if (devid < 0) {
+		int errsv = errno;
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to create rfcomm device: (%d) %s",
+		                     errsv, strerror (errsv));
+		goto done;
+	}
+	context->rfcomm_id = devid;
+
+	snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
+	while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY)) < 0 && try--) {
+		if (try) {
+			g_usleep (100 * 1000);
+			continue;
+		}
+
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to find rfcomm device: %s",
+		                     tty);
+		break;
+	}
+
+done:
+	context->callback (context, tty, error, context->user_data);
+}
+
+static void
+sdp_search_cleanup (NMBluez5DunContext *context)
+{
+	if (context->sdp_session) {
+		sdp_close (context->sdp_session);
+		context->sdp_session = NULL;
+	}
+
+	if (context->sdp_watch_id) {
+		g_source_remove (context->sdp_watch_id);
+		context->sdp_watch_id = 0;
+	}
+}
+
+static void
+sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+{
+	NMBluez5DunContext *context = user_data;
+	int scanned, seqlen = 0, bytesleft = size;
+	uint8_t dataType;
+	int channel = -1;
+
+	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished with type=%d status=%d",
+	            context->src_str, context->dst_str, status, type);
+
+	/* SDP response received */
+	if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+		GError *error = g_error_new (NM_BT_ERROR,
+		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                             "Did not get a Service Discovery response");
+		context->callback (context, NULL, error, context->user_data);
+		goto done;
+	}
+
+	scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
+
+	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP sequence type scanned=%d length=%d",
+	            context->src_str, context->dst_str, scanned, seqlen);
+
+	scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
+	if (!scanned || !seqlen) {
+		/* Short read or unknown sequence type */
+		GError *error = g_error_new (NM_BT_ERROR,
+		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                             "Improper Service Discovery response");
+		context->callback (context, NULL, error, context->user_data);
+		goto done;
+	}
+
+	rsp += scanned;
+	bytesleft -= scanned;
+	do {
+		sdp_record_t *rec;
+		int recsize = 0;
+		sdp_list_t *protos;
+
+		rec = sdp_extract_pdu (rsp, bytesleft, &recsize);
+		if (!rec)
+			break;
+
+		if (!recsize) {
+			sdp_record_free (rec);
+			break;
+		}
+
+		if (sdp_get_access_protos (rec, &protos) == 0) {
+			/* Extract the DUN channel number */
+			channel = sdp_get_proto_port (protos, RFCOMM_UUID);
+			sdp_list_free (protos, NULL);
+
+			nm_log_dbg (LOGD_BT, "(%s -> %s): SDP channel=%d",
+			            context->src_str, context->dst_str, channel);
+		}
+		sdp_record_free (rec);
+
+		scanned += recsize;
+		rsp += recsize;
+		bytesleft -= recsize;
+	} while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
+
+done:
+	if (channel != -1) {
+		context->rfcomm_channel = channel;
+		dun_connect (context);
+	}
+
+	sdp_search_cleanup (context);
+}
+
+static gboolean
+sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+
+	nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search progressed with condition=%d",
+	            context->src_str, context->dst_str, condition);
+
+	if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+		GError *error = g_error_new (NM_BT_ERROR,
+		                             NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                             "Service Discovery interrupted");
+		context->callback (context, NULL, error, context->user_data);
+		sdp_search_cleanup (context);
+		return FALSE;
+	}
+
+	if (sdp_process (context->sdp_session) < 0) {
+		nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished",
+		            context->src_str, context->dst_str);
+
+		/* Search finished successfully. */
+		return FALSE;
+	}
+
+	/* Search progressed successfully. */
+	return TRUE;
+}
+
+static gboolean
+sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+	NMBluez5DunContext *context = user_data;
+	sdp_list_t *search, *attrs;
+	uuid_t svclass;
+	uint16_t attr;
+	int fd, err, fd_err = 0;
+	socklen_t len = sizeof (fd_err);
+	GError *error = NULL;
+
+	context->sdp_watch_id = 0;
+
+	fd = g_io_channel_unix_get_fd (channel);
+	if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) {
+		nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d",
+		            context->src_str, context->dst_str, errno);
+		err = errno;
+	} else {
+		nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d",
+		            context->src_str, context->dst_str, fd_err);
+		err = fd_err;
+	}
+
+	if (err != 0) {
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Error on Service Discovery socket: (%d) %s",
+		                     err, strerror (err));
+		goto done;
+	}
+
+	if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, context) < 0) {
+		/* Should not be reached, only can fail if we passed bad sdp_session. */
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Could not request Service Discovery notification");
+		goto done;
+	}
+
+	sdp_uuid16_create (&svclass, DIALUP_NET_SVCLASS_ID);
+	search = sdp_list_append (NULL, &svclass);
+	attr = SDP_ATTR_PROTO_DESC_LIST;
+	attrs = sdp_list_append (NULL, &attr);
+
+	if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
+		/* Set callback responsible for update the internal SDP transaction */
+		context->sdp_watch_id = g_io_add_watch (channel,
+		                                        G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+		                                        sdp_search_process_cb,
+		                                        context);
+	} else {
+		err = sdp_get_error (context->sdp_session);
+		error = g_error_new (NM_BT_ERROR,
+		                     NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Error starting Service Discovery: (%d) %s",
+		                     err, strerror (err));
+	}
+
+	sdp_list_free (attrs, NULL);
+	sdp_list_free (search, NULL);
+
+done:
+	if (error) {
+		context->callback (context, NULL, error, context->user_data);
+		sdp_search_cleanup (context);
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+NMBluez5DunContext *
+nm_bluez5_dun_new (const char *adapter,
+                   const char *remote)
+
+{
+	NMBluez5DunContext *context;
+
+	context = g_slice_new0 (NMBluez5DunContext);
+	str2ba (adapter, &context->src);
+	str2ba (remote, &context->dst);
+	context->src_str = g_strdup (adapter);
+	context->dst_str = g_strdup (remote);
+	context->rfcomm_channel = -1;
+	context->rfcomm_id = -1;
+	context->rfcomm_fd = -1;
+	return context;
+}
+
+void
+nm_bluez5_dun_connect (NMBluez5DunContext *context,
+                       NMBluez5DunFunc callback,
+                       gpointer user_data)
+{
+	GIOChannel *channel;
+
+	context->callback = callback;
+	context->user_data = user_data;
+
+	if (context->rfcomm_channel != -1) {
+		nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d",
+			    context->src_str, context->dst_str, context->rfcomm_channel);
+		dun_connect (context);
+		return;
+	}
+
+	nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s",
+		    context->src_str, context->dst_str);
+
+	context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+	if (!context->sdp_session) {
+		GError *error;
+		int err = sdp_get_error (context->sdp_session);
+
+		error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+		                     "Failed to connect to the SDP server: (%d) %s",
+		                      err, strerror (err));
+		context->callback (context, NULL, error, context->user_data);
+		return;
+	}
+
+	channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
+	context->sdp_watch_id = g_io_add_watch (channel,
+	                                        G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+	                                        sdp_connect_watch,
+	                                        context);
+	g_io_channel_unref (channel);
+}
+
+/* Only clean up connection-related stuff to allow reconnect */
+void
+nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+{
+	g_return_if_fail (context != NULL);
+
+	sdp_search_cleanup (context);
+
+	if (context->rfcomm_fd >= 0) {
+		if (context->rfcomm_id >= 0) {
+			struct rfcomm_dev_req req = { 0 };
+
+			req.dev_id = context->rfcomm_id;
+			ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
+			context->rfcomm_id = -1;
+		}
+		close (context->rfcomm_fd);
+		context->rfcomm_fd = -1;
+	}
+
+	close (context->rfcomm_tty_fd);
+	context->rfcomm_tty_fd = -1;
+}
+
+void
+nm_bluez5_dun_free (NMBluez5DunContext *context)
+{
+	g_return_if_fail (context != NULL);
+
+	nm_bluez5_dun_cleanup (context);
+	g_clear_pointer (&context->src_str, g_free);
+	g_clear_pointer (&context->dst_str, g_free);
+	g_slice_free (NMBluez5DunContext, context);
+}
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
new file mode 100644
index 00000000..7e259725
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef _NM_BLUEZ5_UTILS_H_
+#define _NM_BLUEZ5_UTILS_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct _NMBluez5DunContext NMBluez5DunContext;
+
+typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
+                                 const char *rfcomm_dev,
+                                 GError *error,
+                                 gpointer user_data);
+
+NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter,
+                                       const char *remote);
+
+void nm_bluez5_dun_connect (NMBluez5DunContext *context,
+                            NMBluez5DunFunc callback, gpointer user_data);
+
+/* Clean up connection resources */
+void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
+
+/* Clean up and dispose all resources */
+void nm_bluez5_dun_free (NMBluez5DunContext *context);
+
+#endif  /* _NM_BLUEZ5_UTILS_H_ */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 63006b3a..b8568523 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -20,6 +20,8 @@
  * Copyright (C) 2013 Intel Corporation.
  */
 
+#include "config.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
@@ -143,7 +145,7 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	device = nm_bluez_device_new (path, priv->provider, 5);
+	device = nm_bluez_device_new (path, NULL, priv->provider, 5);
 	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
 	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
 	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
index 79f347bc..8152976d 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ b/src/devices/bluetooth/nm-bluez5-manager.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
-#ifndef NM_BLUEZ5_MANAGER_H
-#define NM_BLUEZ5_MANAGER_H
+#ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
+#define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -58,5 +58,5 @@ NMBluez5Manager *nm_bluez5_manager_new (NMConnectionProvider *provider);
 
 void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
 
-#endif /* NM_BLUEZ5_MANAGER_H */
+#endif /* __NETWORKMANAGER_BLUEZ5_MANAGER_H__ */
 
diff --git a/src/devices/bluetooth/nm-bt-enum-types.c b/src/devices/bluetooth/nm-bt-enum-types.c
index 2595caeb..16741c9f 100644
--- a/src/devices/bluetooth/nm-bt-enum-types.c
+++ b/src/devices/bluetooth/nm-bt-enum-types.c
@@ -5,7 +5,7 @@
 
 #include "nm-bt-enum-types.h"
 
-#include "nm-device-bt.h"
+#include "nm-bt-error.h"
 
 GType
 nm_bt_error_get_type (void)
@@ -18,6 +18,7 @@ nm_bt_error_get_type (void)
         { NM_BT_ERROR_CONNECTION_NOT_BT, "NM_BT_ERROR_CONNECTION_NOT_BT", "ConnectionNotBt" },
         { NM_BT_ERROR_CONNECTION_INVALID, "NM_BT_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
         { NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "NM_BT_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
+        { NM_BT_ERROR_DUN_CONNECT_FAILED, "NM_BT_ERROR_DUN_CONNECT_FAILED", "DunConnectFailed" },
         { 0, NULL, NULL }
       };
       GType g_define_type_id =
diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c
new file mode 100644
index 00000000..e3b8e7f5
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-error.c
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include "nm-bt-error.h"
+
+GQuark
+nm_bt_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-bt-error");
+	return quark;
+}
+
+
diff --git a/src/devices/bluetooth/nm-bt-error.h b/src/devices/bluetooth/nm-bt-error.h
new file mode 100644
index 00000000..fa3a9570
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-error.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef _NM_BLUEZ5_ERROR_H_
+#define _NM_BLUEZ5_ERROR_H_
+
+typedef enum {
+	NM_BT_ERROR_CONNECTION_NOT_BT = 0,   /*< nick=ConnectionNotBt >*/
+	NM_BT_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
+	NM_BT_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
+	NM_BT_ERROR_DUN_CONNECT_FAILED,      /*< nick=DunConnectFailed >*/
+} NMBtError;
+
+#define NM_BT_ERROR (nm_bt_error_quark ())
+GQuark nm_bt_error_quark (void);
+
+#endif  /* _NM_BT_ERROR_H_ */
+
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 0bd2f17d..689f37c9 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -22,8 +22,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <net/ethernet.h>
-#include <netinet/ether.h>
 
 #include <glib/gi18n.h>
 #include <gio/gio.h>
@@ -46,9 +44,13 @@
 #include "NetworkManagerUtils.h"
 #include "nm-bt-enum-types.h"
 #include "nm-utils.h"
+#include "nm-bt-error.h"
+#include "nm-bt-enum-types.h"
+
+#define MM_DBUS_SERVICE  "org.freedesktop.ModemManager1"
 
-#define MM_OLD_DBUS_SERVICE  "org.freedesktop.ModemManager"
-#define MM_NEW_DBUS_SERVICE  "org.freedesktop.ModemManager1"
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceBt);
 
 G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
 
@@ -63,7 +65,7 @@ typedef struct {
 
 	NMBluezDevice *bt_device;
 
-	guint8 bdaddr[ETH_ALEN];
+	char *bdaddr;
 	char *name;
 	guint32 capabilities;
 
@@ -94,17 +96,6 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 
-#define NM_BT_ERROR (nm_bt_error_quark ())
-
-static GQuark
-nm_bt_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-bt-error");
-	return quark;
-}
-
 guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
@@ -112,15 +103,6 @@ guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
 	return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
 }
 
-static guint
-get_hw_address_length (NMDevice *device, gboolean *out_permanent)
-{
-	/* HW address is the Bluetooth HW address of the remote device */
-	if (out_permanent)
-		*out_permanent = TRUE;   /* the bdaddr of the remote device will never change */
-	return ETH_ALEN;
-}
-
 static guint32
 get_connection_bt_type (NMConnection *connection)
 {
@@ -142,6 +124,12 @@ get_connection_bt_type (NMConnection *connection)
 	return NM_BT_CAPABILITY_NONE;
 }
 
+static guint32
+get_generic_capabilities (NMDevice *device)
+{
+	return NM_DEVICE_CAP_IS_NON_KERNEL;
+}
+
 static gboolean
 can_auto_connect (NMDevice *device,
                   NMConnection *connection,
@@ -167,7 +155,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMSettingConnection *s_con;
 	NMSettingBluetooth *s_bt;
-	const GByteArray *array;
+	const char *bdaddr;
 	guint32 bt_type;
 
 	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection))
@@ -187,11 +175,10 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	if (!(bt_type & priv->capabilities))
 		return FALSE;
 
-	array = nm_setting_bluetooth_get_bdaddr (s_bt);
-	if (!array || (array->len != ETH_ALEN))
+	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+	if (!bdaddr)
 		return FALSE;
-
-	if (memcmp (priv->bdaddr, array->data, ETH_ALEN) != 0)
+	if (!nm_utils_hwaddr_matches (priv->bdaddr, -1, bdaddr, -1))
 		return FALSE;
 
 	return TRUE;
@@ -225,14 +212,14 @@ complete_connection (NMDevice *device,
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMSettingBluetooth *s_bt;
-	const GByteArray *setting_bdaddr;
+	const char *setting_bdaddr;
 	const char *ctype;
 	gboolean is_dun = FALSE, is_pan = FALSE;
 	NMSettingGsm *s_gsm;
 	NMSettingCdma *s_cdma;
 	NMSettingSerial *s_serial;
-	NMSettingPPP *s_ppp;
-	const char *format = NULL, *preferred = NULL;
+	NMSettingPpp *s_ppp;
+	const char *fallback_prefix = NULL, *preferred = NULL;
 
 	s_gsm = nm_connection_get_setting_gsm (connection);
 	s_cdma = nm_connection_get_setting_cdma (connection);
@@ -262,18 +249,24 @@ complete_connection (NMDevice *device,
 		/* Make sure the device supports PAN */
 		if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_BLUETOOTH_ERROR,
-			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-			                     "PAN required but Bluetooth device does not support NAP");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("PAN requested, but Bluetooth device does not support NAP"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
 			return FALSE;
 		}
 
 		/* PAN can't use any DUN-related settings */
 		if (s_gsm || s_cdma || s_serial || s_ppp) {
 			g_set_error_literal (error,
-			                     NM_SETTING_BLUETOOTH_ERROR,
-			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-			                     "PAN incompatible with GSM, CDMA, or serial settings");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("PAN connections cannot specify GSM, CDMA, or serial settings"));
+			g_prefix_error (error, "%s: ",
+			                s_gsm ? NM_SETTING_GSM_SETTING_NAME :
+			                s_cdma ? NM_SETTING_CDMA_SETTING_NAME :
+			                s_serial ? NM_SETTING_SERIAL_SETTING_NAME :
+			                NM_SETTING_PPP_SETTING_NAME);
 			return FALSE;
 		}
 
@@ -281,23 +274,25 @@ complete_connection (NMDevice *device,
 		              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
 		              NULL);
 
-		format = _("PAN connection %d");
+		fallback_prefix = _("PAN connection");
 	} else if (is_dun) {
 		/* Make sure the device supports PAN */
 		if (!(priv->capabilities & NM_BT_CAPABILITY_DUN)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_BLUETOOTH_ERROR,
-			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-			                     "DUN required but Bluetooth device does not support DUN");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("DUN requested, but Bluetooth device does not support DUN"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
 			return FALSE;
 		}
 
 		/* Need at least a GSM or a CDMA setting */
 		if (!s_gsm && !s_cdma) {
 			g_set_error_literal (error,
-			                     NM_SETTING_BLUETOOTH_ERROR,
-			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-			                     "Setting requires DUN but no GSM or CDMA setting is present");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("DUN connection must include a GSM or CDMA setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME);
 			return FALSE;
 		}
 
@@ -306,51 +301,46 @@ complete_connection (NMDevice *device,
 		              NULL);
 
 		if (s_gsm) {
-			format = _("GSM connection %d");
+			fallback_prefix = _("GSM connection");
 			if (!nm_setting_gsm_get_number (s_gsm))
 				g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL);
-		} else if (s_cdma) {
-			format = _("CDMA connection %d");
+		} else {
+			fallback_prefix = _("CDMA connection");
 			if (!nm_setting_cdma_get_number (s_cdma))
 				g_object_set (G_OBJECT (s_cdma), NM_SETTING_GSM_NUMBER, "#777", NULL);
-		} else
-			format = _("DUN connection %d");
+		}
 	} else {
 		g_set_error_literal (error,
-		                     NM_SETTING_BLUETOOTH_ERROR,
-		                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-		                     "Unknown/unhandled Bluetooth connection type");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("Unknown/unhandled Bluetooth connection type"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
 		return FALSE;
 	}
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_BLUETOOTH_SETTING_NAME,
 	                           existing_connections,
-	                           format,
 	                           preferred,
+	                           fallback_prefix,
+	                           NULL,
 	                           is_dun ? FALSE : TRUE); /* No IPv6 yet for DUN */
 
 	setting_bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
 	if (setting_bdaddr) {
 		/* Make sure the setting BT Address (if any) matches the device's */
-		if (memcmp (setting_bdaddr->data, priv->bdaddr, ETH_ALEN)) {
+		if (!nm_utils_hwaddr_matches (setting_bdaddr, -1, priv->bdaddr, -1)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_BLUETOOTH_ERROR,
-			                     NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
-			                     NM_SETTING_BLUETOOTH_BDADDR);
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("connection does not match device"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR);
 			return FALSE;
 		}
 	} else {
-		GByteArray *bdaddr;
-		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-
 		/* Lock the connection to this device by default */
-		if (memcmp (priv->bdaddr, null_mac, ETH_ALEN)) {
-			bdaddr = g_byte_array_sized_new (ETH_ALEN);
-			g_byte_array_append (bdaddr, priv->bdaddr, ETH_ALEN);
-			g_object_set (G_OBJECT (s_bt), NM_SETTING_BLUETOOTH_BDADDR, bdaddr, NULL);
-			g_byte_array_free (bdaddr, TRUE);
-		}
+		if (!nm_utils_hwaddr_matches (priv->bdaddr, -1, NULL, ETH_ALEN))
+			g_object_set (G_OBJECT (s_bt), NM_SETTING_BLUETOOTH_BDADDR, priv->bdaddr, NULL);
 	}
 
 	return TRUE;
@@ -437,7 +427,8 @@ modem_prepare_result (NMModem *modem,
                       NMDeviceStateReason reason,
                       gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState state;
 
 	state = nm_device_get_state (device);
@@ -470,8 +461,7 @@ modem_prepare_result (NMModem *modem,
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
 			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL);
-			nm_log_info (LOGD_MB, "(%s): disabling autoconnect due to failed SIM PIN",
-			             nm_device_get_iface (device));
+			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
 		}
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
@@ -491,21 +481,20 @@ device_state_changed (NMDevice *device,
 }
 
 static void
-modem_ip4_config_result (NMModem *self,
+modem_ip4_config_result (NMModem *modem,
                          NMIP4Config *config,
                          GError *error,
                          gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDevice *device = NM_DEVICE (self);
 
 	g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
 
 	if (error) {
-		nm_log_warn (LOGD_MB | LOGD_IP4 | LOGD_BT,
-		             "(%s): retrieving IP4 configuration failed: (%d) %s",
-		             nm_device_get_ip_iface (device),
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_MB | LOGD_IP4 | LOGD_BT,
+		       "retrieving IP4 configuration failed: (%d) %s",
+		       error->code, error->message ? error->message : "(unknown)");
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else
@@ -648,16 +637,14 @@ component_added (NMDevice *device, GObject *component)
 	 */
 	state = nm_device_get_state (NM_DEVICE (self));
 	if (state != NM_DEVICE_STATE_CONFIG) {
-		nm_log_warn (LOGD_BT | LOGD_MB,
-		             "(%s): modem found but device not in correct state (%d)",
-		             nm_device_get_iface (NM_DEVICE (self)),
-		             nm_device_get_state (NM_DEVICE (self)));
+		_LOGW (LOGD_BT | LOGD_MB,
+		       "modem found but device not in correct state (%d)",
+		       nm_device_get_state (NM_DEVICE (self)));
 		return TRUE;
 	}
 
-	nm_log_info (LOGD_BT | LOGD_MB,
-	             "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) modem found.",
-	             nm_device_get_iface (NM_DEVICE (self)));
+	_LOGI (LOGD_BT | LOGD_MB,
+	       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) modem found.");
 
 	if (priv->modem) {
 		g_warn_if_reached ();
@@ -674,11 +661,6 @@ component_added (NMDevice *device, GObject *component)
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
 	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
 
-	/* In the old ModemManager the data port is known from the very beginning;
-	 * while in the new ModemManager the data port is set afterwards when the bearer gets
-	 * created */
-	if (modem_data_port)
-		nm_device_set_ip_iface (NM_DEVICE (self), modem_data_port);
 	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
 
 	/* Kick off the modem connection */
@@ -711,11 +693,9 @@ check_connect_continue (NMDeviceBt *self)
 	if (!priv->connected || !priv->have_iface)
 		return;
 
-	nm_log_info (LOGD_BT, "Activation (%s %s/bluetooth) Stage 2 of 5 (Device Configure) "
-	             "successful.  Will connect via %s.",
-	             nm_device_get_iface (device),
-	             nm_device_get_ip_iface (device),
-	             dun ? "DUN" : (pan ? "PAN" : "unknown"));
+	_LOGI (LOGD_BT,
+	       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.",
+	       dun ? "DUN" : (pan ? "PAN" : "unknown"));
 
 	/* Kill the connect timeout since we're connected now */
 	if (priv->timeout_id) {
@@ -730,9 +710,8 @@ check_connect_continue (NMDeviceBt *self)
 		/* Wait for ModemManager to find the modem */
 		priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
 
-		nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) "
-		             "waiting for modem to appear.",
-		             nm_device_get_iface (device));
+		_LOGI (LOGD_BT | LOGD_MB,
+		       "Activation: (bluetooth) Stage 2 of 5 (Device Configure) waiting for modem to appear.");
 	} else
 		g_assert_not_reached ();
 }
@@ -751,13 +730,14 @@ bluez_connect_cb (GObject *object,
 	                                         res, &error);
 
 	if (!device) {
-		nm_log_warn (LOGD_BT, "Error connecting with bluez: %s",
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_BT, "Error connecting with bluez: %s",
+		       error && error->message ? error->message : "(unknown)");
 		g_clear_error (&error);
 
 		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		g_object_unref (self);
 		return;
 	}
 
@@ -768,12 +748,12 @@ bluez_connect_cb (GObject *object,
 		nm_device_set_ip_iface (NM_DEVICE (self), device);
 	}
 
-	nm_log_dbg (LOGD_BT, "(%s): connect request successful",
-	            nm_device_get_iface (NM_DEVICE (self)));
+	_LOGD (LOGD_BT, "connect request successful");
 
 	/* Stage 3 gets scheduled when Bluez says we're connected */
 	priv->have_iface = TRUE;
 	check_connect_continue (self);
+	g_object_unref (self);
 }
 
 static void
@@ -790,8 +770,7 @@ bluez_connected_changed (NMBluezDevice *bt_device,
 	connected = nm_bluez_device_get_connected (bt_device);
 	if (connected) {
 		if (state == NM_DEVICE_STATE_CONFIG) {
-			nm_log_dbg (LOGD_BT, "(%s): connected to the device",
-			            nm_device_get_iface (device));
+			_LOGD (LOGD_BT, "connected to the device");
 
 			priv->connected = TRUE;
 			check_connect_continue (self);
@@ -802,13 +781,10 @@ bluez_connected_changed (NMBluezDevice *bt_device,
 		/* Bluez says we're disconnected from the device.  Suck. */
 
 		if (nm_device_is_activating (device)) {
-			nm_log_info (LOGD_BT,
-			             "Activation (%s/bluetooth): bluetooth link disconnected.",
-			             nm_device_get_iface (device));
+			_LOGI (LOGD_BT, "Activation: (bluetooth) bluetooth link disconnected.");
 			fail = TRUE;
 		} else if (state == NM_DEVICE_STATE_ACTIVATED) {
-			nm_log_info (LOGD_BT, "(%s): bluetooth link disconnected.",
-			             nm_device_get_iface (device));
+			_LOGI (LOGD_BT, "bluetooth link disconnected.");
 			fail = TRUE;
 		}
 
@@ -824,8 +800,7 @@ bt_connect_timeout (gpointer user_data)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 
-	nm_log_dbg (LOGD_BT, "(%s): initial connection timed out",
-	            nm_device_get_iface (NM_DEVICE (self)));
+	_LOGD (LOGD_BT, "initial connection timed out");
 
 	NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0;
 	nm_device_state_changed (NM_DEVICE (self),
@@ -837,6 +812,7 @@ bt_connect_timeout (gpointer user_data)
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
+	NMDeviceBt *self = NM_DEVICE_BT (device);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMConnection *connection;
 
@@ -853,13 +829,12 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	nm_log_dbg (LOGD_BT, "(%s): requesting connection to the device",
-	            nm_device_get_iface (device));
+	_LOGD (LOGD_BT, "requesting connection to the device");
 
 	/* Connect to the BT device */
 	nm_bluez_device_connect_async (priv->bt_device,
 	                               priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
-	                               bluez_connect_cb, device);
+	                               bluez_connect_cb, g_object_ref (device));
 
 	if (priv->timeout_id)
 		g_source_remove (priv->timeout_id);
@@ -897,8 +872,7 @@ act_stage3_ip6_config_start (NMDevice *device,
 
 	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
 		ret = nm_modem_stage3_ip6_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem,
-		                                        device,
-		                                        NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                        nm_device_get_act_request (device),
 		                                        reason);
 	} else
 		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
@@ -979,8 +953,7 @@ handle_availability_change (NMDeviceBt *self,
 
 	state = nm_device_get_state (device);
 	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
-		nm_log_dbg (LOGD_BT, "(%s): availability blocked by UNMANAGED state",
-		            nm_device_get_iface (device));
+		_LOGD (LOGD_BT, "availability blocked by UNMANAGED state");
 		return;
 	}
 
@@ -990,7 +963,7 @@ handle_availability_change (NMDeviceBt *self,
 
 	if (available) {
 		if (state != NM_DEVICE_STATE_UNAVAILABLE)
-			nm_log_warn (LOGD_CORE | LOGD_BT, "not in expected unavailable state!");
+			_LOGW (LOGD_CORE | LOGD_BT, "not in expected unavailable state!");
 
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_DISCONNECTED,
@@ -1011,9 +984,8 @@ set_mm_running (NMDeviceBt *self, gboolean running)
 	if (priv->mm_running == running)
 		return;
 
-	nm_log_dbg (LOGD_BT, "(%s): ModemManager now %s",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            running ? "available" : "unavailable");
+	_LOGD (LOGD_BT, "ModemManager now %s",
+	       running ? "available" : "unavailable");
 
 	old_available = nm_device_is_available (NM_DEVICE (self));
 	priv->mm_running = running;
@@ -1038,11 +1010,7 @@ mm_name_owner_changed (NMDBusManager *dbus_mgr,
 	gboolean new_owner_good;
 
 	/* Can't handle the signal if its not from the modem service */
-	if (   strcmp (MM_OLD_DBUS_SERVICE, name) != 0
-#if WITH_MODEM_MANAGER_1
-	    && strcmp (MM_NEW_DBUS_SERVICE, name) != 0
-#endif
-	    )
+	if (strcmp (MM_DBUS_SERVICE, name) != 0)
 		return;
 
 	old_owner_good = (old_owner && strlen (old_owner));
@@ -1096,11 +1064,7 @@ nm_device_bt_init (NMDeviceBt *self)
 	                                      self);
 
 	/* Initial check to see if ModemManager is running */
-	mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_OLD_DBUS_SERVICE);
-#if WITH_MODEM_MANAGER_1
-	if (!mm_running)
-		mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_NEW_DBUS_SERVICE);
-#endif
+	mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE);
 	set_mm_running (self, mm_running);
 }
 
@@ -1108,15 +1072,13 @@ static void
 constructed (GObject *object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
-	const guint8 *my_hwaddr;
-	guint my_hwaddr_len = 0;
+	const char *my_hwaddr;
 
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
 
-	my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object), &my_hwaddr_len);
+	my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object));
 	g_assert (my_hwaddr);
-	g_assert_cmpint (my_hwaddr_len, ==, ETH_ALEN);
-	memcpy (priv->bdaddr, my_hwaddr, ETH_ALEN);
+	priv->bdaddr = g_strdup (my_hwaddr);
 
 	/* Watch for BT device property changes */
 	g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
@@ -1203,6 +1165,7 @@ finalize (GObject *object)
 
 	g_free (priv->rfcomm_iface);
 	g_free (priv->name);
+	g_free (priv->bdaddr);
 
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object);
 }
@@ -1221,7 +1184,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	device_class->get_hw_address_length = get_hw_address_length;
+	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->can_auto_connect = can_auto_connect;
 	device_class->deactivate = deactivate;
 	device_class->act_stage2_config = act_stage2_config;
@@ -1238,27 +1201,24 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_BT_NAME,
-		 g_param_spec_string (NM_DEVICE_BT_NAME,
-		                      "Bluetooth device name",
-		                      "Bluetooth device name",
+		 g_param_spec_string (NM_DEVICE_BT_NAME, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_BT_CAPABILITIES,
-		 g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES,
-		                    "Bluetooth device capabilities",
-		                    "Bluetooth device capabilities",
+		 g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES, "", "",
 		                    NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_BT_DEVICE,
-		 g_param_spec_object (NM_DEVICE_BT_DEVICE,
-		                      "NMBluezDevice object for the Device",
-		                      "NMBluezDevice object for the Device",
+		 g_param_spec_object (NM_DEVICE_BT_DEVICE, "", "",
 		                      NM_TYPE_BLUEZ_DEVICE,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[PPP_STATS] =
@@ -1273,6 +1233,4 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_bt_object_info);
-
-	dbus_g_error_domain_register (NM_BT_ERROR, NULL, NM_TYPE_BT_ERROR);
 }
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 83732bc0..c1e95d91 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_BT_H
-#define NM_DEVICE_BT_H
+#ifndef __NETWORKMANAGER_DEVICE_BT_H__
+#define __NETWORKMANAGER_DEVICE_BT_H__
 
 #include <nm-device.h>
 #include "nm-bluez-device.h"
@@ -34,12 +34,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BT))
 #define NM_DEVICE_BT_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
 
-typedef enum {
-	NM_BT_ERROR_CONNECTION_NOT_BT = 0,   /*< nick=ConnectionNotBt >*/
-	NM_BT_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_BT_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMBtError;
-
 #define NM_DEVICE_BT_NAME         "name"
 #define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
 #define NM_DEVICE_BT_DEVICE       "bt-device"
@@ -71,4 +65,4 @@ gboolean nm_device_bt_modem_added (NMDeviceBt *device,
 
 G_END_DECLS
 
-#endif /* NM_DEVICE_BT_H */
+#endif /* __NETWORKMANAGER_DEVICE_BT_H__ */
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 6f8caeb4..9d9fe426 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -23,30 +23,30 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 
-#include <netinet/ether.h>
 #include <errno.h>
 #include <stdlib.h>
 
 #include "gsystem-local-alloc.h"
 #include "nm-device-bond.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-platform.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dbus-manager.h"
 #include "nm-enum-types.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-bond-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceBond);
 
 G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BOND, NMDeviceBondPrivate))
 
-#define NM_BOND_ERROR (nm_bond_error_quark ())
-
 typedef struct {
 	int dummy;
 } NMDeviceBondPrivate;
@@ -60,17 +60,6 @@ enum {
 
 /******************************************************************/
 
-static GQuark
-nm_bond_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-bond-error");
-	return quark;
-}
-
-/******************************************************************/
-
 static guint32
 get_generic_capabilities (NMDevice *dev)
 {
@@ -110,7 +99,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	/* Bond connections must specify the virtual interface name */
-	iface = nm_connection_get_virtual_iface_name (connection);
+	iface = nm_connection_get_interface_name (connection);
 	if (!iface || strcmp (nm_device_get_iface (device), iface))
 		return FALSE;
 
@@ -126,17 +115,14 @@ complete_connection (NMDevice *device,
                      const GSList *existing_connections,
                      GError **error)
 {
-	NMSettingBond *s_bond, *tmp;
-	guint32 i = 0;
-	char *name;
-	const GSList *iter;
-	gboolean found;
+	NMSettingBond *s_bond;
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_BOND_SETTING_NAME,
 	                           existing_connections,
-	                           _("Bond connection %d"),
 	                           NULL,
+	                           _("Bond connection"),
+	                           "bond",
 	                           TRUE);
 
 	s_bond = nm_connection_get_setting_bond (connection);
@@ -145,34 +131,6 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_bond));
 	}
 
-	/* Grab the first name that doesn't exist in either our connections
-	 * or a device on the system.
-	 */
-	while (i < 500 && !nm_setting_bond_get_interface_name (s_bond)) {
-		name = g_strdup_printf ("bond%u", i);
-		/* check interface names */
-		if (!nm_platform_link_exists (name)) {
-			/* check existing bond connections */
-			for (iter = existing_connections, found = FALSE; iter; iter = g_slist_next (iter)) {
-				NMConnection *candidate = iter->data;
-
-				tmp = nm_connection_get_setting_bond (candidate);
-				if (tmp && nm_connection_is_type (candidate, NM_SETTING_BOND_SETTING_NAME)) {
-					if (g_strcmp0 (nm_setting_bond_get_interface_name (tmp), name) == 0) {
-						found = TRUE;
-						break;
-					}
-				}
-			}
-
-			if (!found)
-				g_object_set (G_OBJECT (s_bond), NM_SETTING_BOND_INTERFACE_NAME, name, NULL);
-		}
-
-		g_free (name);
-		i++;
-	}
-
 	return TRUE;
 }
 
@@ -181,14 +139,13 @@ complete_connection (NMDevice *device,
 static gboolean
 set_bond_attr (NMDevice *device, const char *attr, const char *value)
 {
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	gboolean ret;
 	int ifindex = nm_device_get_ifindex (device);
 
 	ret = nm_platform_master_set_option (ifindex, attr, value);
-	if (!ret) {
-		nm_log_warn (LOGD_HW, "(%s): failed to set bonding attribute "
-		             "'%s' to '%s'", nm_device_get_ip_iface (device), attr, value);
-	}
+	if (!ret)
+		_LOGW (LOGD_HW, "failed to set bonding attribute '%s' to '%s'", attr, value);
 	return ret;
 }
 
@@ -209,14 +166,12 @@ static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMSettingBond *s_bond = nm_connection_get_setting_bond (connection);
-	const char *ifname = nm_device_get_iface (device);
 	int ifindex = nm_device_get_ifindex (device);
 	const char **options;
 
 	if (!s_bond) {
 		s_bond = (NMSettingBond *) nm_setting_bond_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_bond);
-		g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, ifname, NULL);
 	}
 
 	/* Read bond options from sysfs and update the Bond setting to match */
@@ -243,6 +198,19 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 }
 
+static gboolean
+master_update_slave_connection (NMDevice *self,
+                                NMDevice *slave,
+                                NMConnection *connection,
+                                GError **error)
+{
+	g_object_set (nm_connection_get_setting_connection (connection),
+	              NM_SETTING_CONNECTION_MASTER, nm_device_get_iface (self),
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
+	              NULL);
+	return TRUE;
+}
+
 static void
 set_arp_targets (NMDevice *device,
                  const char *value,
@@ -380,6 +348,10 @@ apply_bonding_config (NMDevice *device)
 	set_simple_option (device, "xmit_hash_policy", s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
 	set_simple_option (device, "resend_igmp", s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);
 
+	if (   g_strcmp0 (mode, "4") == 0
+	    || g_strcmp0 (mode, "802.3ad") == 0)
+		set_simple_option (device, "lacp_rate", s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
+
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
@@ -410,8 +382,8 @@ enslave_slave (NMDevice *device,
                NMConnection *connection,
                gboolean configure)
 {
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	gboolean success = TRUE, no_firmware = FALSE;
-	const char *iface = nm_device_get_ip_iface (device);
 	const char *slave_iface = nm_device_get_ip_iface (slave);
 
 	nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND);
@@ -425,9 +397,9 @@ enslave_slave (NMDevice *device,
 		if (!success)
 			return FALSE;
 
-		nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface);
+		_LOGI (LOGD_BOND, "enslaved bond slave %s", slave_iface);
 	} else
-		nm_log_info (LOGD_BOND, "(%s): bond slave %s was enslaved", iface, slave_iface);
+		_LOGI (LOGD_BOND, "bond slave %s was enslaved", slave_iface);
 
 	g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES);
 	return TRUE;
@@ -438,6 +410,7 @@ release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	gboolean success = TRUE, no_firmware = FALSE;
 
 	if (configure) {
@@ -445,17 +418,14 @@ release_slave (NMDevice *device,
 		                                    nm_device_get_ip_ifindex (slave));
 
 		if (success) {
-			nm_log_info (LOGD_BOND, "(%s): released bond slave %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
+			_LOGI (LOGD_BOND, "released bond slave %s",
+			       nm_device_get_ip_iface (slave));
 		} else {
-			nm_log_warn (LOGD_BOND, "(%s): failed to release bond slave %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
+			_LOGW (LOGD_BOND, "failed to release bond slave %s",
+			       nm_device_get_ip_iface (slave));
 		}
 	} else {
-		nm_log_info (LOGD_BOND, "(%s): bond slave %s was released",
-		             nm_device_get_ip_iface (device),
+		_LOGI (LOGD_BOND, "bond slave %s was released",
 		             nm_device_get_ip_iface (slave));
 	}
 
@@ -467,10 +437,8 @@ release_slave (NMDevice *device,
 		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
 		 * other state is noticed by the now-released slave.
 		 */
-		if (!nm_device_bring_up (slave, TRUE, &no_firmware)) {
-			nm_log_warn (LOGD_BOND, "(%s): released bond slave could not be brought up.",
-			             nm_device_get_iface (slave));
-		}
+		if (!nm_device_bring_up (slave, TRUE, &no_firmware))
+			_LOGW (LOGD_BOND, "released bond slave could not be brought up.");
 	}
 
 	return success;
@@ -478,57 +446,6 @@ release_slave (NMDevice *device,
 
 /******************************************************************/
 
-NMDevice *
-nm_device_bond_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_DRIVER, "bonding",
-	                                  NM_DEVICE_TYPE_DESC, "Bond",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
-	                                  NM_DEVICE_IS_MASTER, TRUE,
-	                                  NULL);
-}
-
-NMDevice *
-nm_device_bond_new_for_connection (NMConnection *connection)
-{
-	const char *iface;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-
-	iface = nm_connection_get_virtual_iface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
-
-	if (   !nm_platform_bond_add (iface)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_BOND, "(%s): failed to create bonding master interface for '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
-		return NULL;
-	}
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
-	                                  NM_DEVICE_IFACE, iface,
-	                                  NM_DEVICE_DRIVER, "bonding",
-	                                  NM_DEVICE_TYPE_DESC, "Bond",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
-	                                  NM_DEVICE_IS_MASTER, TRUE,
-	                                  NULL);
-}
-
-static void
-constructed (GObject *object)
-{
-	G_OBJECT_CLASS (nm_device_bond_parent_class)->constructed (object);
-
-	nm_log_dbg (LOGD_HW | LOGD_BOND, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (NM_DEVICE (object)),
-	            nm_device_get_ifindex (NM_DEVICE (object)));
-}
-
 static void
 nm_device_bond_init (NMDeviceBond * self)
 {
@@ -579,7 +496,6 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->connection_type = NM_SETTING_BOND_SETTING_NAME;
 
 	/* virtual methods */
-	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
@@ -590,6 +506,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->complete_connection = complete_connection;
 
 	parent_class->update_connection = update_connection;
+	parent_class->master_update_slave_connection = master_update_slave_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->enslave_slave = enslave_slave;
@@ -598,15 +515,69 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_BOND_SLAVES,
-		                     "Slaves",
-		                     "Slaves",
+		 g_param_spec_boxed (NM_DEVICE_BOND_SLAVES, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_bond_object_info);
+}
+
+/*************************************************************/
+
+#define NM_TYPE_BOND_FACTORY (nm_bond_factory_get_type ())
+#define NM_BOND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BOND_FACTORY, NMBondFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_BOND) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_DRIVER, "bonding",
+		                                  NM_DEVICE_TYPE_DESC, "Bond",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
+		                                  NM_DEVICE_IS_MASTER, TRUE,
+		                                  NULL);
+	}
+	return NULL;
+}
+
+static NMDevice *
+create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                      NMConnection *connection,
+                                      NMDevice *parent,
+                                      GError **error)
+{
+	const char *iface;
+
+	if (!nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME))
+		return NULL;
+
+	iface = nm_connection_get_interface_name (connection);
+	g_return_val_if_fail (iface != NULL, NULL);
 
-	dbus_g_error_domain_register (NM_BOND_ERROR, NULL, NM_TYPE_BOND_ERROR);
+	if (   !nm_platform_bond_add (iface)
+		&& nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
+		nm_log_warn (LOGD_DEVICE | LOGD_BOND, "(%s): failed to create bonding master interface for '%s': %s",
+			         iface, nm_connection_get_id (connection),
+			         nm_platform_get_error_msg ());
+		return NULL;
+	}
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
+		                              NM_DEVICE_IFACE, iface,
+		                              NM_DEVICE_DRIVER, "bonding",
+		                              NM_DEVICE_TYPE_DESC, "Bond",
+		                              NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
+		                              NM_DEVICE_IS_MASTER, TRUE,
+		                              NULL);
 }
+
+DEFINE_DEVICE_FACTORY_INTERNAL(BOND, Bond, bond,
+	factory_iface->new_link = new_link;
+	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	)
+
diff --git a/src/devices/nm-device-bond.h b/src/devices/nm-device-bond.h
index f4683ad8..17217a57 100644
--- a/src/devices/nm-device-bond.h
+++ b/src/devices/nm-device-bond.h
@@ -18,8 +18,8 @@
  * Copyright 2012 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_BOND_H
-#define NM_DEVICE_BOND_H
+#ifndef __NETWORKMANAGER_DEVICE_BOND_H__
+#define __NETWORKMANAGER_DEVICE_BOND_H__
 
 #include <glib-object.h>
 
@@ -34,29 +34,13 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BOND))
 #define NM_DEVICE_BOND_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BOND, NMDeviceBondClass))
 
-typedef enum {
-	NM_BOND_ERROR_CONNECTION_NOT_BOND = 0, /*< nick=ConnectionNotBond >*/
-	NM_BOND_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_BOND_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMBondError;
-
 #define NM_DEVICE_BOND_SLAVES "slaves"
 
-typedef struct {
-	NMDevice parent;
-} NMDeviceBond;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceBondClass;
-
+typedef NMDevice NMDeviceBond;
+typedef NMDeviceClass NMDeviceBondClass;
 
 GType nm_device_bond_get_type (void);
 
-NMDevice *nm_device_bond_new (NMPlatformLink *platform_device);
-NMDevice *nm_device_bond_new_for_connection (NMConnection *connection);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_BOND_H */
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index edc41838..a70cfd95 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -23,29 +23,29 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 
-#include <netinet/ether.h>
 #include <stdlib.h>
 
 #include "gsystem-local-alloc.h"
 #include "nm-device-bridge.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dbus-manager.h"
 #include "nm-enum-types.h"
 #include "nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-bridge-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceBridge);
 
 G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgePrivate))
 
-#define NM_BRIDGE_ERROR (nm_bridge_error_quark ())
-
 typedef struct {
 	int dummy;
 } NMDeviceBridgePrivate;
@@ -59,17 +59,6 @@ enum {
 
 /******************************************************************/
 
-static GQuark
-nm_bridge_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-bridge-error");
-	return quark;
-}
-
-/******************************************************************/
-
 static guint32
 get_generic_capabilities (NMDevice *dev)
 {
@@ -100,7 +89,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
 	const char *iface;
 	NMSettingBridge *s_bridge;
-	const GByteArray *mac_address;
+	const char *mac_address;
 
 	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_compatible (device, connection))
 		return FALSE;
@@ -110,19 +99,16 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	/* Bridge connections must specify the virtual interface name */
-	iface = nm_connection_get_virtual_iface_name (connection);
+	iface = nm_connection_get_interface_name (connection);
 	if (!iface || strcmp (nm_device_get_iface (device), iface))
 		return FALSE;
 
 	mac_address = nm_setting_bridge_get_mac_address (s_bridge);
 	if (mac_address) {
-		guint hw_len;
-		const guint8 *hw_addr;
+		const char *hw_addr;
 
-		hw_addr = nm_device_get_hw_address (device, &hw_len);
-		if (   !hw_addr
-		    || hw_len != mac_address->len
-		    || memcmp (mac_address->data, hw_addr, hw_len) != 0)
+		hw_addr = nm_device_get_hw_address (device);
+		if (!hw_addr || !nm_utils_hwaddr_matches (hw_addr, -1, mac_address, -1))
 			return FALSE;
 	}
 
@@ -136,17 +122,14 @@ complete_connection (NMDevice *device,
                      const GSList *existing_connections,
                      GError **error)
 {
-	NMSettingBridge *s_bridge, *tmp;
-	guint32 i = 0;
-	char *name;
-	const GSList *iter;
-	gboolean found;
+	NMSettingBridge *s_bridge;
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_BRIDGE_SETTING_NAME,
 	                           existing_connections,
-	                           _("Bridge connection %d"),
 	                           NULL,
+	                           _("Bridge connection"),
+	                           "bridge",
 	                           TRUE);
 
 	s_bridge = nm_connection_get_setting_bridge (connection);
@@ -155,34 +138,6 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_bridge));
 	}
 
-	/* Grab the first name that doesn't exist in either our connections
-	 * or a device on the system.
-	 */
-	while (i < 500 && !nm_setting_bridge_get_interface_name (s_bridge)) {
-		name = g_strdup_printf ("br%u", i);
-		/* check interface names */
-		if (!nm_platform_link_exists (name)) {
-			/* check existing bridge connections */
-			for (iter = existing_connections, found = FALSE; iter; iter = g_slist_next (iter)) {
-				NMConnection *candidate = iter->data;
-
-				tmp = nm_connection_get_setting_bridge (candidate);
-				if (tmp && nm_connection_is_type (candidate, NM_SETTING_BRIDGE_SETTING_NAME)) {
-					if (g_strcmp0 (nm_setting_bridge_get_interface_name (tmp), name) == 0) {
-						found = TRUE;
-						break;
-					}
-				}
-			}
-
-			if (!found)
-				g_object_set (G_OBJECT (s_bridge), NM_SETTING_BRIDGE_INTERFACE_NAME, name, NULL);
-		}
-
-		g_free (name);
-		i++;
-	}
-
 	return TRUE;
 }
 
@@ -292,15 +247,14 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 	NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection);
-	const char *ifname = nm_device_get_iface (device);
 	int ifindex = nm_device_get_ifindex (device);
 	const Option *option;
 
 	if (!s_bridge) {
 		s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_bridge);
-		g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, ifname, NULL);
 	}
 
 	for (option = master_options; option->name; option++) {
@@ -315,34 +269,27 @@ update_connection (NMDevice *device, NMConnection *connection)
 				value /= 100;
 
 			g_object_set (s_bridge, option->name, value, NULL);
-		} else {
-			nm_log_warn (LOGD_BRIDGE, "(%s): failed to read bridge setting '%s'",
-			             nm_device_get_iface (device), option->sysname);
-		}
+		} else
+			_LOGW (LOGD_BRIDGE, "failed to read bridge setting '%s'", option->sysname);
 	}
 }
 
-/**
- * nm_bridge_update_slave_connection:
- * @slave: the slave #NMDevice, is *not* necessarily a bridge interface
- * @connection: the #NMConnection to update with the bridge port settings
- *
- * Reads bridge port configuration and updates @connection with those
- * properties.
- *
- * Returns: %TRUE if the port configuration was read and @connection updated,
- * %FALSE if not.
- */
-gboolean
-nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection)
+static gboolean
+master_update_slave_connection (NMDevice *device,
+                                NMDevice *slave,
+                                NMConnection *connection,
+                                GError **error)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+	NMSettingConnection *s_con;
 	NMSettingBridgePort *s_port;
-	int ifindex = nm_device_get_ifindex (slave);
+	int ifindex_slave = nm_device_get_ifindex (slave);
+	const char *iface = nm_device_get_iface (device);
 	const Option *option;
 
-	g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (ifindex_slave > 0, FALSE);
 
+	s_con = nm_connection_get_setting_connection (connection);
 	s_port = nm_connection_get_setting_bridge_port (connection);
 	if (!s_port) {
 		s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
@@ -350,7 +297,7 @@ nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection)
 	}
 
 	for (option = slave_options; option->name; option++) {
-		gs_free char *str = nm_platform_slave_get_option (ifindex, option->sysname);
+		gs_free char *str = nm_platform_slave_get_option (ifindex_slave, option->sysname);
 		int value;
 
 		if (str) {
@@ -361,12 +308,14 @@ nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection)
 				value /= 100;
 
 			g_object_set (s_port, option->name, value, NULL);
-		} else {
-			nm_log_warn (LOGD_BRIDGE, "(%s): failed to read bridge port setting '%s'",
-			             nm_device_get_iface (slave), option->sysname);
-		}
+		} else
+			_LOGW (LOGD_BRIDGE, "failed to read bridge port setting '%s'", option->sysname);
 	}
 
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_MASTER, iface,
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+	              NULL);
 	return TRUE;
 }
 
@@ -393,19 +342,19 @@ enslave_slave (NMDevice *device,
                NMConnection *connection,
                gboolean configure)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+
 	if (configure) {
 		if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
 			return FALSE;
 
 		commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
 
-		nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s",
-		             nm_device_get_ip_iface (device),
-		             nm_device_get_ip_iface (slave));
+		_LOGI (LOGD_BRIDGE, "attached bridge port %s",
+		       nm_device_get_ip_iface (slave));
 	} else {
-		nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was attached",
-		             nm_device_get_ip_iface (device),
-		             nm_device_get_ip_iface (slave));
+		_LOGI (LOGD_BRIDGE, "bridge port %s was attached",
+		       nm_device_get_ip_iface (slave));
 	}
 
 	g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
@@ -418,6 +367,7 @@ release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 	gboolean success = TRUE;
 
 	if (configure) {
@@ -425,18 +375,15 @@ release_slave (NMDevice *device,
 		                                    nm_device_get_ip_ifindex (slave));
 
 		if (success) {
-			nm_log_info (LOGD_BRIDGE, "(%s): detached bridge port %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
+			_LOGI (LOGD_BRIDGE, "detached bridge port %s",
+			       nm_device_get_ip_iface (slave));
 		} else {
-			nm_log_warn (LOGD_BRIDGE, "(%s): failed to detach bridge port %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
+			_LOGW (LOGD_BRIDGE, "failed to detach bridge port %s",
+			       nm_device_get_ip_iface (slave));
 		}
 	} else {
-		nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was detached",
-		             nm_device_get_ip_iface (device),
-		             nm_device_get_ip_iface (slave));
+		_LOGI (LOGD_BRIDGE, "bridge port %s was detached",
+		       nm_device_get_ip_iface (slave));
 	}
 
 	g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
@@ -445,66 +392,6 @@ release_slave (NMDevice *device,
 
 /******************************************************************/
 
-NMDevice *
-nm_device_bridge_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_DRIVER, "bridge",
-	                                  NM_DEVICE_TYPE_DESC, "Bridge",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
-	                                  NM_DEVICE_IS_MASTER, TRUE,
-	                                  NULL);
-}
-
-NMDevice *
-nm_device_bridge_new_for_connection (NMConnection *connection)
-{
-	const char *iface;
-	NMSettingBridge *s_bridge;
-	const GByteArray *mac_address;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-
-	iface = nm_connection_get_virtual_iface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
-
-	s_bridge = nm_connection_get_setting_bridge (connection);
-	g_return_val_if_fail (s_bridge, NULL);
-
-	mac_address = nm_setting_bridge_get_mac_address (s_bridge);
-
-	if (   !nm_platform_bridge_add (iface,
-	                                mac_address ? mac_address->data : NULL,
-	                                mac_address ? mac_address->len : 0)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_BRIDGE, "(%s): failed to create bridge master interface for '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
-		return NULL;
-	}
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
-	                                  NM_DEVICE_IFACE, iface,
-	                                  NM_DEVICE_DRIVER, "bridge",
-	                                  NM_DEVICE_TYPE_DESC, "Bridge",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
-	                                  NM_DEVICE_IS_MASTER, TRUE,
-	                                  NULL);
-}
-
-static void
-constructed (GObject *object)
-{
-	G_OBJECT_CLASS (nm_device_bridge_parent_class)->constructed (object);
-
-	nm_log_dbg (LOGD_HW | LOGD_BRIDGE, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (NM_DEVICE (object)),
-	            nm_device_get_ifindex (NM_DEVICE (object)));
-}
-
 static void
 nm_device_bridge_init (NMDeviceBridge * self)
 {
@@ -518,7 +405,6 @@ get_property (GObject *object, guint prop_id,
 	GSList *list, *iter;
 
 	switch (prop_id) {
-		break;
 	case PROP_SLAVES:
 		slaves = g_ptr_array_new ();
 		list = nm_device_master_get_slaves (NM_DEVICE (object));
@@ -555,7 +441,6 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	parent_class->connection_type = NM_SETTING_BRIDGE_SETTING_NAME;
 
 	/* virtual methods */
-	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
@@ -566,6 +451,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	parent_class->complete_connection = complete_connection;
 
 	parent_class->update_connection = update_connection;
+	parent_class->master_update_slave_connection = master_update_slave_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->enslave_slave = enslave_slave;
@@ -574,15 +460,85 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_BRIDGE_SLAVES,
-		                     "Slaves",
-		                     "Slaves",
+		 g_param_spec_boxed (NM_DEVICE_BRIDGE_SLAVES, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_bridge_object_info);
+}
+
+/*************************************************************/
 
-	dbus_g_error_domain_register (NM_BRIDGE_ERROR, NULL, NM_TYPE_BRIDGE_ERROR);
+#define NM_TYPE_BRIDGE_FACTORY (nm_bridge_factory_get_type ())
+#define NM_BRIDGE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BRIDGE_FACTORY, NMBridgeFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_BRIDGE) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_DRIVER, "bridge",
+		                                  NM_DEVICE_TYPE_DESC, "Bridge",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
+		                                  NM_DEVICE_IS_MASTER, TRUE,
+		                                  NULL);
+	}
+	return NULL;
 }
+
+static NMDevice *
+create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                      NMConnection *connection,
+                                      NMDevice *parent,
+                                      GError **error)
+{
+	const char *iface;
+	NMSettingBridge *s_bridge;
+	const char *mac_address_str;
+	guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX];
+
+	if (!nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME))
+		return NULL;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	iface = nm_connection_get_interface_name (connection);
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	s_bridge = nm_connection_get_setting_bridge (connection);
+	g_return_val_if_fail (s_bridge, NULL);
+
+	mac_address_str = nm_setting_bridge_get_mac_address (s_bridge);
+	if (mac_address_str) {
+		if (!nm_utils_hwaddr_aton (mac_address_str, mac_address, ETH_ALEN))
+			mac_address_str = NULL;
+	}
+
+	if (   !nm_platform_bridge_add (iface,
+	                                mac_address_str ? mac_address : NULL,
+	                                mac_address_str ? ETH_ALEN : 0)
+	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
+		nm_log_warn (LOGD_DEVICE | LOGD_BRIDGE, "(%s): failed to create bridge master interface for '%s': %s",
+		             iface, nm_connection_get_id (connection),
+		             nm_platform_get_error_msg ());
+		return NULL;
+	}
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_DRIVER, "bridge",
+	                                  NM_DEVICE_TYPE_DESC, "Bridge",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
+	                                  NM_DEVICE_IS_MASTER, TRUE,
+	                                  NULL);
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL(BRIDGE, Bridge, bridge,
+	factory_iface->new_link = new_link;
+	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	)
+
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index 4194f5a4..c11a2c40 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -18,8 +18,8 @@
  * Copyright 2012 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_BRIDGE_H
-#define NM_DEVICE_BRIDGE_H
+#ifndef __NETWORKMANAGER_DEVICE_BRIDGE_H__
+#define __NETWORKMANAGER_DEVICE_BRIDGE_H__
 
 #include <glib-object.h>
 
@@ -34,31 +34,13 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_BRIDGE))
 #define NM_DEVICE_BRIDGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass))
 
-typedef enum {
-	NM_BRIDGE_ERROR_CONNECTION_NOT_BRIDGE = 0, /*< nick=ConnectionNotBridge >*/
-	NM_BRIDGE_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_BRIDGE_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMBridgeError;
-
 #define NM_DEVICE_BRIDGE_SLAVES "slaves"
 
-typedef struct {
-	NMDevice parent;
-} NMDeviceBridge;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceBridgeClass;
-
+typedef NMDevice NMDeviceBridge;
+typedef NMDeviceClass NMDeviceBridgeClass;
 
 GType nm_device_bridge_get_type (void);
 
-NMDevice *nm_device_bridge_new (NMPlatformLink *platform_device);
-NMDevice *nm_device_bridge_new_for_connection (NMConnection *connection);
-
-gboolean nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_BRIDGE_H */
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
new file mode 100644
index 00000000..585f6a0e
--- /dev/null
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2011 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <nm-connection.h>
+#include "nm-device-ethernet-utils.h"
+
+char *
+nm_device_ethernet_utils_get_default_wired_name (const GSList *connections)
+{
+	const GSList *iter;
+	char *cname = NULL;
+	int i = 0;
+
+	/* Find the next available unique connection name */
+	while (!cname && (i++ < 10000)) {
+		char *temp;
+		gboolean found = FALSE;
+
+		temp = g_strdup_printf (_("Wired connection %d"), i);
+		for (iter = connections; iter; iter = iter->next) {
+			if (g_strcmp0 (nm_connection_get_id (NM_CONNECTION (iter->data)), temp) == 0) {
+				found = TRUE;
+				g_free (temp);
+				break;
+			}
+		}
+
+		if (found == FALSE)
+			cname = temp;
+	}
+
+	return cname;
+}
+
diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h
new file mode 100644
index 00000000..5848f7e6
--- /dev/null
+++ b/src/devices/nm-device-ethernet-utils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
+#define __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
+
+#include <glib.h>
+
+char *nm_device_ethernet_utils_get_default_wired_name (const GSList *connections);
+
+#endif  /* NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H */
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index f4a90c74..ac408d3d 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -20,6 +20,7 @@
  */
 
 #include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <netinet/in.h>
@@ -30,9 +31,7 @@
 #include <linux/version.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include <linux/if.h>
 #include <errno.h>
-#include <netinet/ether.h>
 
 #include <gudev/gudev.h>
 
@@ -44,22 +43,23 @@
 #include "nm-supplicant-manager.h"
 #include "nm-supplicant-interface.h"
 #include "nm-supplicant-config.h"
-#include "nm-setting-connection.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-8021x.h"
-#include "nm-setting-pppoe.h"
-#include "nm-setting-bond.h"
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "nm-enum-types.h"
 #include "nm-dbus-manager.h"
 #include "nm-platform.h"
 #include "nm-dcb.h"
 #include "nm-settings-connection.h"
+#include "nm-config.h"
+#include "nm-device-ethernet-utils.h"
+#include "nm-connection-provider.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-ethernet-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceEthernet);
 
 G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
 
@@ -69,8 +69,6 @@ G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
 
 #define PPPOE_RECONNECT_DELAY 7
 
-#define NM_ETHERNET_ERROR (nm_ethernet_error_quark ())
-
 static NMSetting *device_get_setting (NMDevice *device, GType setting_type);
 
 typedef struct Supplicant {
@@ -101,8 +99,8 @@ typedef enum {
 } DcbWait;
 
 typedef struct {
-	guint8              perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
-	guint8              initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
+	char *              perm_hw_addr;    /* Permanent MAC address */
+	char *              initial_hw_addr; /* Initial MAC address (as seen when NM starts) */
 
 	guint32             speed;
 
@@ -138,15 +136,6 @@ enum {
 };
 
 
-static GQuark
-nm_ethernet_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-ethernet-error");
-	return quark;
-}
-
 static char *
 get_link_basename (const char *parent_path, const char *name, GError **error)
 {
@@ -167,7 +156,6 @@ static void
 _update_s390_subchannels (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	const char *iface;
 	GUdevClient *client;
 	GUdevDevice *dev;
 	GUdevDevice *parent = NULL;
@@ -176,17 +164,16 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	GDir *dir;
 	GError *error = NULL;
 
-	iface = nm_device_get_iface (NM_DEVICE (self));
-
 	client = g_udev_client_new (subsystems);
 	if (!client) {
-		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to initialize GUdev client", iface);
+		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to initialize GUdev client");
 		return;
 	}
 
-	dev = g_udev_client_query_by_subsystem_and_name (client, "net", iface);
+	dev = g_udev_client_query_by_subsystem_and_name (client, "net",
+	                                                 nm_device_get_iface (NM_DEVICE (self)));
 	if (!dev) {
-		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to find device with udev", iface);
+		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device with udev");
 		goto out;
 	}
 
@@ -203,9 +190,8 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	parent_path = g_udev_device_get_sysfs_path (parent);
 	dir = g_dir_open (parent_path, 0, &error);
 	if (!dir) {
-		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to open directory '%s': %s",
-		             iface, parent_path,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to open directory '%s': %s",
+		       parent_path, error && error->message ? error->message : "(unknown)");
 		g_clear_error (&error);
 		goto out;
 	}
@@ -228,12 +214,12 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 			if (value && *value)
 				g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value));
 			else
-				nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): error reading %s", iface, path);
+				_LOGW (LOGD_DEVICE | LOGD_HW, "error reading %s", path);
 			g_free (path);
 			g_free (value);
 		}
 		if (error) {
-			nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): %s", iface, error->message);
+			_LOGW (LOGD_DEVICE | LOGD_HW, "%s", error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -253,9 +239,8 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 		priv->subchannels = g_strdup (priv->subchan1);
 
 	driver = nm_device_get_driver (NM_DEVICE (self));
-	nm_log_info (LOGD_DEVICE | LOGD_HW,
-	             "(%s): found s390 '%s' subchannels [%s]",
-	             iface, driver ? driver : "(unknown driver)", priv->subchannels);
+	_LOGI (LOGD_DEVICE | LOGD_HW, "found s390 '%s' subchannels [%s]",
+	       driver ? driver : "(unknown driver)", priv->subchannels);
 
 out:
 	if (parent)
@@ -271,25 +256,22 @@ constructor (GType type,
              GObjectConstructParam *construct_params)
 {
 	GObject *object;
-	NMDevice *self;
-	int ifindex;
 
 	object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type,
 	                                                                        n_construct_params,
 	                                                                        construct_params);
 	if (object) {
-		self = NM_DEVICE (object);
-		ifindex = nm_device_get_ifindex (self);
+#ifndef G_DISABLE_ASSERT
+		int ifindex = nm_device_get_ifindex (NM_DEVICE (object));
+		NMLinkType link_type = nm_platform_link_get_type (ifindex);
 
-		g_assert (   nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_ETHERNET
-		          || nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_VETH);
-
-		nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): kernel ifindex %d",
-		            nm_device_get_iface (NM_DEVICE (self)),
-		            nm_device_get_ifindex (NM_DEVICE (self)));
+		g_assert (   link_type == NM_LINK_TYPE_ETHERNET
+		          || link_type == NM_LINK_TYPE_VETH
+		          || link_type == NM_LINK_TYPE_NONE);
+#endif
 
 		/* s390 stuff */
-		_update_s390_subchannels (NM_DEVICE_ETHERNET (self));
+		_update_s390_subchannels (NM_DEVICE_ETHERNET (object));
 	}
 
 	return object;
@@ -328,18 +310,6 @@ nm_device_ethernet_init (NMDeviceEthernet *self)
 	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
-NMDevice *
-nm_device_ethernet_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Ethernet",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
-	                                  NULL);
-}
-
 static void
 update_permanent_hw_address (NMDevice *dev)
 {
@@ -347,12 +317,14 @@ update_permanent_hw_address (NMDevice *dev)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	struct ifreq req;
 	struct ethtool_perm_addr *epaddr = NULL;
-	int fd, ret;
-	const guint8 *mac;
+	int fd, ret, errsv;
+	const char *mac;
+
+	g_return_if_fail (priv->perm_hw_addr == NULL);
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		nm_log_warn (LOGD_HW, "couldn't open control socket.");
+		_LOGW (LOGD_HW, "couldn't open control socket.");
 		return;
 	}
 
@@ -367,21 +339,18 @@ update_permanent_hw_address (NMDevice *dev)
 
 	errno = 0;
 	ret = ioctl (fd, SIOCETHTOOL, &req);
-	if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
-		nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
-		            nm_device_get_iface (dev), errno);
+	errsv = errno;
+	if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) {
+		_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)", errsv);
 		/* Fall back to current address */
-		mac = nm_device_get_hw_address (dev, NULL);
+		mac = nm_device_get_hw_address (dev);
 		if (mac)
-			memcpy (epaddr->data, mac, ETH_ALEN);
+			nm_utils_hwaddr_aton (mac, epaddr->data, ETH_ALEN);
 		else
 			memset (epaddr->data, 0, ETH_ALEN);
 	}
 
-	if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
-		memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
-		g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
-	}
+	priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN);
 
 	g_free (epaddr);
 	close (fd);
@@ -392,32 +361,25 @@ update_initial_hw_address (NMDevice *dev)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	char *mac_str;
-	const guint8 *mac;
 
 	/* This sets initial MAC address from current MAC address. It should only
 	 * be called from NMDevice constructor() to really get the initial address.
 	 */
-	mac = nm_device_get_hw_address (dev, NULL);
-	if (mac)
-		memcpy (priv->initial_hw_addr, mac, ETH_ALEN);
-
-	mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER);
-	nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
-	            nm_device_get_iface (dev), mac_str);
-	g_free (mac_str);
+	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev));
+
+	_LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr);
 }
 
 static guint32
-get_generic_capabilities (NMDevice *dev)
+get_generic_capabilities (NMDevice *device)
 {
-	if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (dev)))
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+
+	if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (device)))
 	    return NM_DEVICE_CAP_CARRIER_DETECT;
 	else {
-		nm_log_info (LOGD_HW,
-		             "(%s): driver '%s' does not support carrier detection.",
-		             nm_device_get_iface (dev),
-		             nm_device_get_driver (dev));
+		_LOGI (LOGD_HW, "driver '%s' does not support carrier detection.",
+		       nm_device_get_driver (device));
 		return NM_DEVICE_CAP_NONE;
 	}
 }
@@ -426,7 +388,7 @@ static gboolean
 match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	const GPtrArray *subchans;
+	const char * const *subchans;
 	int i;
 
 	*try_mac = TRUE;
@@ -440,8 +402,8 @@ match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_m
 		return FALSE;
 
 	/* Make sure each subchannel in the connection is a subchannel of this device */
-	for (i = 0; i < subchans->len; i++) {
-		const char *candidate = g_ptr_array_index (subchans, i);
+	for (i = 0; subchans[i]; i++) {
+		const char *candidate = subchans[i];
 
 		if (   (priv->subchan1 && !strcmp (priv->subchan1, candidate))
 		    || (priv->subchan2 && !strcmp (priv->subchan2, candidate))
@@ -476,29 +438,27 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	if (s_wired) {
-		const GByteArray *mac;
+		const char *mac;
 		gboolean try_mac = TRUE;
-		const GSList *mac_blacklist, *mac_blacklist_iter;
+		const char * const *mac_blacklist;
+		int i;
 
 		if (!match_subchans (self, s_wired, &try_mac))
 			return FALSE;
 
 		mac = nm_setting_wired_get_mac_address (s_wired);
-		if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
+		if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1))
 			return FALSE;
 
 		/* Check for MAC address blacklist */
 		mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
-		for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
-			 mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
-			struct ether_addr addr;
-
-			if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+		for (i = 0; mac_blacklist[i]; i++) {
+			if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
 				g_warn_if_reached ();
 				return FALSE;
 			}
 
-			if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0)
+			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1))
 				return FALSE;
 		}
 	}
@@ -588,14 +548,15 @@ wired_secrets_cb (NMActRequest *req,
                   GError *error,
                   gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDevice *dev = NM_DEVICE (self);
 
 	g_return_if_fail (req == nm_device_get_act_request (dev));
 	g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH);
 	g_return_if_fail (nm_act_request_get_connection (req) == connection);
 
 	if (error) {
-		nm_log_warn (LOGD_ETHER, "%s", error->message);
+		_LOGW (LOGD_ETHER, "%s", error->message);
 		nm_device_state_changed (dev,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
@@ -637,16 +598,14 @@ link_timeout_cb (gpointer user_data)
 	if (!setting_name)
 		goto time_out;
 
-	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-	             "Activation (%s/wired): disconnected during authentication,"
-	             " asking for new key.",
-	             nm_device_get_iface (dev));
+	_LOGI (LOGD_DEVICE | LOGD_ETHER,
+	       "Activation: (ethernet) disconnected during authentication, asking for new key.");
 	supplicant_interface_release (self);
 
 	nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 	nm_act_request_get_secrets (req,
 	                            setting_name,
-	                            NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW,
+	                            NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW,
 	                            NULL,
 	                            wired_secrets_cb,
 	                            self);
@@ -654,8 +613,7 @@ link_timeout_cb (gpointer user_data)
 	return FALSE;
 
 time_out:
-	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
-	             "(%s): link timed out.", nm_device_get_iface (dev));
+	_LOGW (LOGD_DEVICE | LOGD_ETHER, "link timed out.");
 	nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 
 	return FALSE;
@@ -677,7 +635,7 @@ build_supplicant_config (NMDeviceEthernet *self)
 
 	security = nm_connection_get_setting_802_1x (connection);
 	if (!nm_supplicant_config_add_setting_8021x (config, security, con_uuid, TRUE)) {
-		nm_log_warn (LOGD_DEVICE, "Couldn't add 802.1X security setting to supplicant config.");
+		_LOGW (LOGD_DEVICE, "Couldn't add 802.1X security setting to supplicant config.");
 		g_object_unref (config);
 		config = NULL;
 	}
@@ -702,11 +660,9 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	if (new_state == old_state)
 		return;
 
-	nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-	             "(%s): supplicant interface state: %s -> %s",
-	             nm_device_get_iface (device),
-	             nm_supplicant_interface_state_to_string (old_state),
-	             nm_supplicant_interface_state_to_string (new_state));
+	_LOGI (LOGD_DEVICE | LOGD_ETHER, "supplicant interface state: %s -> %s",
+	       nm_supplicant_interface_state_to_string (old_state),
+	       nm_supplicant_interface_state_to_string (new_state));
 
 	devstate = nm_device_get_state (device);
 
@@ -718,15 +674,12 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			g_object_unref (config);
 
 			if (!success) {
-				nm_log_err (LOGD_DEVICE | LOGD_ETHER,
-				            "Activation (%s/wired): couldn't send security "
-						    "configuration to the supplicant.",
-						    nm_device_get_iface (device));
+				_LOGE (LOGD_DEVICE | LOGD_ETHER,
+				       "Activation: (ethernet) couldn't send security configuration to the supplicant.");
 			}
 		} else {
-			nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
-			             "Activation (%s/wired): couldn't build security configuration.",
-			             nm_device_get_iface (device));
+			_LOGW (LOGD_DEVICE | LOGD_ETHER,
+			       "Activation: (ethernet) couldn't build security configuration.");
 		}
 
 		if (!success) {
@@ -743,9 +696,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		 * schedule the next activation stage.
 		 */
 		if (devstate == NM_DEVICE_STATE_CONFIG) {
-			nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-			             "Activation (%s/wired) Stage 2 of 5 (Device Configure) successful.",
-				         nm_device_get_iface (device));
+			_LOGI (LOGD_DEVICE | LOGD_ETHER,
+			       "Activation: (ethernet) Stage 2 of 5 (Device Configure) successful.");
 			nm_device_activate_schedule_stage3_ip_config_start (device);
 		}
 		break;
@@ -794,9 +746,9 @@ supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	guint id;
 
-	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
-	             "Activation (%s/wired): association request to the supplicant failed: %s - %s",
-	             nm_device_get_iface (NM_DEVICE (self)), name, message);
+	_LOGW (LOGD_DEVICE | LOGD_ETHER,
+	       "Activation: (ethernet) association request to the supplicant failed: %s - %s",
+	       name, message);
 
 	if (priv->supplicant.iface_con_error_cb_id)
 		g_source_remove (priv->supplicant.iface_con_error_cb_id);
@@ -826,16 +778,15 @@ handle_auth_or_fail (NMDeviceEthernet *self,
 	nm_connection_clear_secrets (connection);
 	setting_name = nm_connection_need_secrets (connection, NULL);
 	if (setting_name) {
-		NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+		NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
 		if (new_secrets)
-			flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+			flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 		nm_act_request_get_secrets (req, setting_name, flags, NULL, wired_secrets_cb, self);
 
 		g_object_set_data (G_OBJECT (connection), WIRED_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
-	} else {
-		nm_log_info (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
-	}
+	} else
+		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -848,19 +799,16 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	NMDevice *device = NM_DEVICE (self);
 	NMActRequest *req;
 	NMConnection *connection;
-	const char *iface;
 	guint64 timestamp = 0;
 	gboolean new_secrets = TRUE;
 
 	priv->supplicant.con_timeout_id = 0;
 
-	iface = nm_device_get_iface (device);
-
 	/* Authentication failed; either driver problems, the encryption key is
 	 * wrong, the passwords or certificates were wrong or the Ethernet switch's
 	 * port is not configured for 802.1x. */
-	nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
-	             "Activation (%s/wired): association took too long.", iface);
+	_LOGW (LOGD_DEVICE | LOGD_ETHER,
+	       "Activation: (ethernet) association took too long.");
 
 	supplicant_interface_release (self);
 	req = nm_device_get_act_request (device);
@@ -876,10 +824,9 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp))
 		new_secrets = !timestamp;
 
-	if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) {
-		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-		             "Activation (%s/wired): asking for new secrets", iface);
-	} else
+	if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
+		_LOGW (LOGD_DEVICE | LOGD_ETHER, "Activation: (ethernet) asking for new secrets");
+	else
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS);
 
 	return FALSE;
@@ -889,16 +836,14 @@ static gboolean
 supplicant_interface_init (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	const char *iface;
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
 
 	/* Create supplicant interface */
-	priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr, iface, FALSE);
+	priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr,
+	                                                          nm_device_get_iface (NM_DEVICE (self)),
+	                                                          FALSE);
 	if (!priv->supplicant.iface) {
-		nm_log_err (LOGD_DEVICE | LOGD_ETHER,
-		            "Couldn't initialize supplicant interface for %s.",
-		            iface);
+		_LOGE (LOGD_DEVICE | LOGD_ETHER,
+		       "Couldn't initialize supplicant interface");
 		supplicant_interface_release (self);
 		return FALSE;
 	}
@@ -924,13 +869,12 @@ supplicant_interface_init (NMDeviceEthernet *self)
 static gboolean
 pppoe_reconnect_delay (gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	priv->pppoe_wait_id = 0;
-	nm_log_info (LOGD_DEVICE, "(%s) PPPoE reconnect delay complete, resuming connection...",
-	             nm_device_get_iface (device));
-	nm_device_activate_schedule_stage2_device_config (device);
+	_LOGI (LOGD_DEVICE, "PPPoE reconnect delay complete, resuming connection...");
+	nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
 	return FALSE;
 }
 
@@ -941,7 +885,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMActRequest *req;
 	NMSettingWired *s_wired;
-	const GByteArray *cloned_mac;
+	const char *cloned_mac;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -955,8 +899,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 		if (s_wired) {
 			/* Set device MAC address if the connection wants to change it */
 			cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-			if (cloned_mac && (cloned_mac->len == ETH_ALEN))
-				nm_device_set_hw_addr (dev, cloned_mac->data, "set", LOGD_ETHER);
+			if (cloned_mac)
+				nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER);
 		}
 
 		/* If we're re-activating a PPPoE connection a short while after
@@ -968,8 +912,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 			gint32 delay = nm_utils_get_monotonic_timestamp_s () - priv->last_pppoe_time;
 
 			if (delay < PPPOE_RECONNECT_DELAY && device_get_setting (dev, NM_TYPE_SETTING_PPPOE)) {
-				nm_log_info (LOGD_DEVICE, "(%s) delaying PPPoE reconnect for %d seconds to ensure peer is ready...",
-				             nm_device_get_iface (dev), delay);
+				_LOGI (LOGD_DEVICE, "delaying PPPoE reconnect for %d seconds to ensure peer is ready...",
+				       delay);
 				g_assert (!priv->pppoe_wait_id);
 				priv->pppoe_wait_id = g_timeout_add_seconds (delay,
 				                                             pppoe_reconnect_delay,
@@ -990,14 +934,13 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	NMSetting8021x *security;
 	const char *setting_name;
-	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
 	connection = nm_device_get_connection (NM_DEVICE (self));
 	g_assert (connection);
 	security = nm_connection_get_setting_802_1x (connection);
 	if (!security) {
-		nm_log_err (LOGD_DEVICE, "Invalid or missing 802.1X security");
+		_LOGE (LOGD_DEVICE, "Invalid or missing 802.1X security");
 		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 		return ret;
 	}
@@ -1005,24 +948,22 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 	if (!priv->supplicant.mgr)
 		priv->supplicant.mgr = nm_supplicant_manager_get ();
 
-	iface = nm_device_get_iface (NM_DEVICE (self));
-
 	/* If we need secrets, get them */
 	setting_name = nm_connection_need_secrets (connection, NULL);
 	if (setting_name) {
 		NMActRequest *req = nm_device_get_act_request (NM_DEVICE (self));
 
-		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-		             "Activation (%s/wired): connection '%s' has security, but secrets are required.",
-				     iface, nm_connection_get_id (connection));
+		_LOGI (LOGD_DEVICE | LOGD_ETHER,
+		       "Activation: (ethernet) connection '%s' has security, but secrets are required.",
+		       nm_connection_get_id (connection));
 
 		ret = handle_auth_or_fail (self, req, FALSE);
 		if (ret != NM_ACT_STAGE_RETURN_POSTPONE)
 			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
 	} else {
-		nm_log_info (LOGD_DEVICE | LOGD_ETHER,
-		             "Activation (%s/wired): connection '%s' requires no security. No secrets needed.",
-				     iface, nm_connection_get_id (connection));
+		_LOGI (LOGD_DEVICE | LOGD_ETHER,
+		       "Activation: (ethernet) connection '%s' requires no security. No secrets needed.",
+		       nm_connection_get_id (connection));
 
 		if (supplicant_interface_init (self))
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
@@ -1073,7 +1014,7 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMConnection *connection;
-	NMSettingPPPOE *s_pppoe;
+	NMSettingPppoe *s_pppoe;
 	NMActRequest *req;
 	GError *err = NULL;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
@@ -1097,8 +1038,7 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 					   self);
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
-		nm_log_warn (LOGD_DEVICE, "(%s): PPPoE failed to start: %s",
-		             nm_device_get_iface (NM_DEVICE (self)), err->message);
+		_LOGW (LOGD_DEVICE, "PPPoE failed to start: %s", err->message);
 		g_error_free (err);
 
 		g_object_unref (priv->ppp_manager);
@@ -1139,17 +1079,16 @@ static void dcb_state (NMDevice *device, gboolean timeout);
 static gboolean
 dcb_carrier_timeout (gpointer user_data)
 {
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (user_data);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 
 	g_return_val_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG, G_SOURCE_REMOVE);
 
 	priv->dcb_timeout_id = 0;
 	if (priv->dcb_wait != DCB_WAIT_CARRIER_POSTCONFIG_DOWN) {
-		nm_log_warn (LOGD_DCB,
-		             "(%s): DCB: timed out waiting for carrier (step %d)",
-		             nm_device_get_iface (device),
-		             priv->dcb_wait);
+		_LOGW (LOGD_DCB, "DCB: timed out waiting for carrier (step %d)",
+		       priv->dcb_wait);
 	}
 	dcb_state (device, TRUE);
 	return G_SOURCE_REMOVE;
@@ -1158,19 +1097,18 @@ dcb_carrier_timeout (gpointer user_data)
 static gboolean
 dcb_configure (NMDevice *device)
 {
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	NMSettingDcb *s_dcb;
-	const char *iface = nm_device_get_iface (device);
 	GError *error = NULL;
 
 	dcb_timeout_cleanup (device);
 
 	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
 	g_assert (s_dcb);
-	if (!nm_dcb_setup (iface, s_dcb, &error)) {
-		nm_log_warn (LOGD_DCB,
-		             "Activation (%s/wired) failed to enable DCB/FCoE: %s",
-		             iface, error->message);
+	if (!nm_dcb_setup (nm_device_get_iface (device), s_dcb, &error)) {
+		_LOGW (LOGD_DCB, "Activation: (ethernet) failed to enable DCB/FCoE: %s",
+		       error->message);
 		g_clear_error (&error);
 		return FALSE;
 	}
@@ -1178,7 +1116,7 @@ dcb_configure (NMDevice *device)
 	/* Pause again just in case the device takes the carrier down when
 	 * setting specific DCB attributes.
 	 */
-	nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (postconfig down)", iface);
+	_LOGD (LOGD_DCB, "waiting for carrier (postconfig down)");
 	priv->dcb_wait = DCB_WAIT_CARRIER_POSTCONFIG_DOWN;
 	priv->dcb_timeout_id = g_timeout_add_seconds (3, dcb_carrier_timeout, device);
 	return TRUE;
@@ -1187,15 +1125,14 @@ dcb_configure (NMDevice *device)
 static gboolean
 dcb_enable (NMDevice *device)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-	const char *iface = nm_device_get_iface (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	GError *error = NULL;
 
 	dcb_timeout_cleanup (device);
-	if (!nm_dcb_enable (iface, TRUE, &error)) {
-		nm_log_warn (LOGD_DCB,
-		             "Activation (%s/wired) failed to enable DCB/FCoE: %s",
-		             iface, error->message);
+	if (!nm_dcb_enable (nm_device_get_iface (device), TRUE, &error)) {
+		_LOGW (LOGD_DCB, "Activation: (ethernet) failed to enable DCB/FCoE: %s",
+		       error->message);
 		g_clear_error (&error);
 		return FALSE;
 	}
@@ -1207,7 +1144,7 @@ dcb_enable (NMDevice *device)
 	 * is down.  But NM might get the carrier-down signal long after calling
 	 * "dcbtool dcb on", so we have to first wait for the carrier to go down.
 	 */
-	nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preconfig down)", iface);
+	_LOGD (LOGD_DCB, "waiting for carrier (preconfig down)");
 	priv->dcb_wait = DCB_WAIT_CARRIER_PRECONFIG_DOWN;
 	priv->dcb_timeout_id = g_timeout_add_seconds (3, dcb_carrier_timeout, device);
 	return TRUE;
@@ -1216,25 +1153,25 @@ dcb_enable (NMDevice *device)
 static void
 dcb_state (NMDevice *device, gboolean timeout)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-	const char *iface = nm_device_get_iface (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	gboolean carrier;
 
 	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
 
 
 	carrier = nm_platform_link_is_connected (nm_device_get_ifindex (device));
-	nm_log_dbg (LOGD_DCB, "(%s): dcb_state() wait %d carrier %d timeout %d", iface, priv->dcb_wait, carrier, timeout);
+	_LOGD (LOGD_DCB, "dcb_state() wait %d carrier %d timeout %d", priv->dcb_wait, carrier, timeout);
 
 	switch (priv->dcb_wait) {
 	case DCB_WAIT_CARRIER_PREENABLE_UP:
 		if (timeout || carrier) {
-			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() enabling DCB", iface);
+			_LOGD (LOGD_DCB, "dcb_state() enabling DCB");
 			dcb_timeout_cleanup (device);
 			if (!dcb_enable (device)) {
 				dcb_carrier_cleanup (device);
 				nm_device_state_changed (device,
-				                         NM_ACT_STAGE_RETURN_FAILURE,
+				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
 			}
 		}
@@ -1245,20 +1182,20 @@ dcb_state (NMDevice *device, gboolean timeout)
 
 		if (!carrier) {
 			/* Wait for the carrier to come back up */
-			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preconfig up)", iface);
+			_LOGD (LOGD_DCB, "waiting for carrier (preconfig up)");
 			priv->dcb_timeout_id = g_timeout_add_seconds (5, dcb_carrier_timeout, device);
 			break;
 		}
-		nm_log_dbg (LOGD_DCB, "(%s): dcb_state() preconfig down falling through", iface);
+		_LOGD (LOGD_DCB, "dcb_state() preconfig down falling through");
 		/* carrier never went down? fall through */
 	case DCB_WAIT_CARRIER_PRECONFIG_UP:
 		if (timeout || carrier) {
-			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() preconfig up configuring DCB", iface);
+			_LOGD (LOGD_DCB, "dcb_state() preconfig up configuring DCB");
 			dcb_timeout_cleanup (device);
 			if (!dcb_configure (device)) {
 				dcb_carrier_cleanup (device);
 				nm_device_state_changed (device,
-				                         NM_ACT_STAGE_RETURN_FAILURE,
+				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
 			}
 		}
@@ -1269,15 +1206,15 @@ dcb_state (NMDevice *device, gboolean timeout)
 
 		if (!carrier) {
 			/* Wait for the carrier to come back up */
-			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (postconfig up)", iface);
+			_LOGD (LOGD_DCB, "waiting for carrier (postconfig up)");
 			priv->dcb_timeout_id = g_timeout_add_seconds (5, dcb_carrier_timeout, device);
 			break;
 		}
-		nm_log_dbg (LOGD_DCB, "(%s): dcb_state() postconfig down falling through", iface);
+		_LOGD (LOGD_DCB, "dcb_state() postconfig down falling through");
 		/* carrier never went down? fall through */
 	case DCB_WAIT_CARRIER_POSTCONFIG_UP:
 		if (timeout || carrier) {
-			nm_log_dbg (LOGD_DCB, "(%s): dcb_state() postconfig up starting IP", iface);
+			_LOGD (LOGD_DCB, "dcb_state() postconfig up starting IP");
 			dcb_timeout_cleanup (device);
 			dcb_carrier_cleanup (device);
 			priv->dcb_wait = DCB_WAIT_UNKNOWN;
@@ -1292,12 +1229,13 @@ dcb_state (NMDevice *device, gboolean timeout)
 static void
 dcb_carrier_changed (NMDevice *device, GParamSpec *pspec, gpointer unused)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
 
 	if (priv->dcb_timeout_id) {
-		nm_log_dbg (LOGD_DCB, "(%s): carrier_changed() calling dcb_state()", nm_device_get_iface (device));
+		_LOGD (LOGD_DCB, "carrier_changed() calling dcb_state()");
 		dcb_state (device, FALSE);
 	}
 }
@@ -1307,7 +1245,8 @@ dcb_carrier_changed (NMDevice *device, GParamSpec *pspec, gpointer unused)
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
 	const char *connection_type;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
@@ -1331,7 +1270,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		security = (NMSetting8021x *) device_get_setting (device, NM_TYPE_SETTING_802_1X);
 		if (security) {
 			/* FIXME: for now 802.1x is mutually exclusive with DCB */
-			return nm_8021x_stage2_config (NM_DEVICE_ETHERNET (device), reason);
+			return nm_8021x_stage2_config (self, reason);
 		}
 	}
 
@@ -1345,8 +1284,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 				return NM_ACT_STAGE_RETURN_FAILURE;
 			}
 		} else {
-			nm_log_dbg (LOGD_DCB, "(%s): waiting for carrier (preenable up)",
-			            nm_device_get_iface (device));
+			_LOGD (LOGD_DCB, "waiting for carrier (preenable up)");
 			priv->dcb_wait = DCB_WAIT_CARRIER_PREENABLE_UP;
 			priv->dcb_timeout_id = g_timeout_add_seconds (4, dcb_carrier_timeout, device);
 		}
@@ -1403,7 +1341,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	/* MTU override */
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	if (mtu)
-		nm_ip4_config_set_mtu (config, mtu);
+		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 }
 
 static void
@@ -1442,9 +1380,8 @@ deactivate (NMDevice *device)
 	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
 	if (s_dcb) {
 		if (!nm_dcb_cleanup (nm_device_get_iface (device), &error)) {
-			nm_log_warn (LOGD_DEVICE | LOGD_HW,
-			             "(%s) failed to disable DCB/FCoE: %s",
-			             nm_device_get_iface (device), error->message);
+			_LOGW (LOGD_DEVICE | LOGD_HW, "failed to disable DCB/FCoE: %s",
+			       error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -1454,7 +1391,8 @@ deactivate (NMDevice *device)
 		NM_DEVICE_ETHERNET_GET_PRIVATE (device)->last_pppoe_time = nm_utils_get_monotonic_timestamp_s ();
 
 	/* Reset MAC address back to initial address */
-	nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_ETHER);
+	if (priv->initial_hw_addr)
+		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_ETHER);
 }
 
 static gboolean
@@ -1466,8 +1404,8 @@ complete_connection (NMDevice *device,
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	NMSettingWired *s_wired;
-	NMSettingPPPOE *s_pppoe;
-	const GByteArray *setting_mac;
+	NMSettingPppoe *s_pppoe;
+	const char *setting_mac;
 
 	s_pppoe = nm_connection_get_setting_pppoe (connection);
 
@@ -1483,7 +1421,8 @@ complete_connection (NMDevice *device,
 	nm_utils_complete_generic (connection,
 	                           s_pppoe ? NM_SETTING_PPPOE_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME,
 	                           existing_connections,
-	                           s_pppoe ? _("PPPoE connection %d") : _("Wired connection %d"),
+	                           NULL,
+	                           s_pppoe ? _("PPPoE connection") : _("Wired connection"),
 	                           NULL,
 	                           s_pppoe ? FALSE : TRUE); /* No IPv6 by default yet for PPPoE */
 
@@ -1496,29 +1435,66 @@ complete_connection (NMDevice *device,
 	setting_mac = nm_setting_wired_get_mac_address (s_wired);
 	if (setting_mac) {
 		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) {
+		if (!nm_utils_hwaddr_matches (setting_mac, -1, priv->perm_hw_addr, -1)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRED_ERROR,
-			                     NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
-			                     NM_SETTING_WIRED_MAC_ADDRESS);
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("connection does not match device"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
 			return FALSE;
 		}
 	} else {
-		GByteArray *mac;
-		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-
-		/* Lock the connection to this device by default */
-		if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) {
-			mac = g_byte_array_sized_new (ETH_ALEN);
-			g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN);
-			g_object_set (G_OBJECT (s_wired), NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
-			g_byte_array_free (mac, TRUE);
+		if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) {
+			g_object_set (G_OBJECT (s_wired),
+			              NM_SETTING_WIRED_MAC_ADDRESS, priv->perm_hw_addr,
+			              NULL);
 		}
 	}
 
 	return TRUE;
 }
 
+static NMConnection *
+new_default_connection (NMDevice *self)
+{
+	NMConnection *connection;
+	const GSList *connections;
+	NMSetting *setting;
+	const char *hw_address;
+	char *defname, *uuid;
+
+	if (!nm_config_get_ethernet_can_auto_default (nm_config_get (), self))
+		return NULL;
+
+	hw_address = nm_device_get_hw_address (self);
+	if (!hw_address)
+		return NULL;
+
+	connection = nm_simple_connection_new ();
+	setting = nm_setting_connection_new ();
+	nm_connection_add_setting (connection, setting);
+
+	connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
+	defname = nm_device_ethernet_utils_get_default_wired_name (connections);
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, defname,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
+	              NULL);
+	g_free (uuid);
+	g_free (defname);
+
+	/* Lock the connection to the device */
+	setting = nm_setting_wired_new ();
+	g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, hw_address, NULL);
+	nm_connection_add_setting (connection, setting);
+
+	return connection;
+}
+
 static gboolean
 spec_match_list (NMDevice *device, const GSList *specs)
 {
@@ -1535,11 +1511,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	NMSettingWired *s_wired = nm_connection_get_setting_wired (connection);
-	guint maclen;
-	const guint8 *mac = nm_device_get_hw_address (device, &maclen);
-	static const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	const char *mac = nm_device_get_hw_address (device);
 	const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
-	GByteArray *array;
 	GHashTableIter iter;
 	gpointer key, value;
 
@@ -1551,37 +1524,29 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* If the device reports a permanent address, use that for the MAC address
 	 * and the current MAC, if different, is the cloned MAC.
 	 */
-	if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) {
-		array = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (array, priv->perm_hw_addr, ETH_ALEN);
-		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL);
-		g_byte_array_unref (array);
+	if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) {
+		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->perm_hw_addr, NULL);
 
 		mac_prop = NULL;
-		if (mac && memcmp (priv->perm_hw_addr, mac, ETH_ALEN))
+		if (mac && !nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, mac, -1))
 			mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS;
 	}
 
-	if (mac_prop && mac && maclen == ETH_ALEN) {
-		array = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (array, (guint8 *) mac, maclen);
-		g_object_set (s_wired, mac_prop, array, NULL);
-		g_byte_array_unref (array);
-	}
+	if (mac_prop && mac && nm_utils_hwaddr_valid (mac, ETH_ALEN))
+		g_object_set (s_wired, mac_prop, mac, NULL);
 
 	/* We don't set the MTU as we don't know whether it was set explicitly */
 
 	/* s390 */
 	if (priv->subchannels) {
-		GPtrArray *subchan_arr = g_ptr_array_sized_new (3);
-		if (priv->subchan1)
-			 g_ptr_array_add (subchan_arr, priv->subchan1);
-		if (priv->subchan2)
-			 g_ptr_array_add (subchan_arr, priv->subchan2);
-		if (priv->subchan3)
-			 g_ptr_array_add (subchan_arr, priv->subchan3);
-		g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchan_arr, NULL);
-		g_ptr_array_free (subchan_arr, TRUE);
+		char **subchannels = g_new (char *, 3 + 1);
+
+		subchannels[0] = g_strdup (priv->subchan1);
+		subchannels[1] = g_strdup (priv->subchan2);
+		subchannels[2] = g_strdup (priv->subchan3);
+		subchannels[3] = NULL;
+		g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchannels, NULL);
+		g_strfreev (subchannels);
 	}
 	if (priv->s390_nettype)
 		g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, priv->s390_nettype, NULL);
@@ -1595,7 +1560,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 static void
 get_link_speed (NMDevice *device)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	struct ifreq ifr;
 	struct ethtool_cmd edata = {
 		.cmd = ETHTOOL_GSET,
@@ -1605,7 +1571,7 @@ get_link_speed (NMDevice *device)
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		nm_log_warn (LOGD_HW | LOGD_ETHER, "couldn't open ethtool control socket.");
+		_LOGW (LOGD_HW | LOGD_ETHER, "couldn't open ethtool control socket.");
 		return;
 	}
 
@@ -1633,8 +1599,7 @@ get_link_speed (NMDevice *device)
 	priv->speed = speed;
 	g_object_notify (G_OBJECT (device), "speed");
 
-	nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): speed is now %d Mb/s",
-	            nm_device_get_iface (device), speed);
+	_LOGD (LOGD_HW | LOGD_ETHER, "speed is now %d Mb/s", speed);
 }
 
 static void
@@ -1669,6 +1634,8 @@ finalize (GObject *object)
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
+	g_free (priv->perm_hw_addr);
+	g_free (priv->initial_hw_addr);
 	g_clear_object (&priv->supplicant.mgr);
 	g_free (priv->subchan1);
 	g_free (priv->subchan2);
@@ -1689,7 +1656,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PERM_HW_ADDRESS:
-		g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER));
+		g_value_set_string (value, priv->perm_hw_addr);
 		break;
 	case PROP_SPEED:
 		g_value_set_uint (value, priv->speed);
@@ -1733,6 +1700,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->update_initial_hw_address = update_initial_hw_address;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
+	parent_class->new_default_connection = new_default_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->act_stage2_config = act_stage2_config;
@@ -1748,23 +1716,42 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PERM_HW_ADDRESS,
-		 g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS,
-							  "Permanent MAC Address",
-							  "Permanent hardware MAC address",
-							  NULL,
-							  G_PARAM_READABLE));
+		 g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SPEED,
-		 g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED,
-						   "Speed",
-						   "Speed",
-						   0, G_MAXUINT32, 0,
-						   G_PARAM_READABLE));
+		 g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_ethernet_object_info);
+}
+
+/*************************************************************/
 
-	dbus_g_error_domain_register (NM_ETHERNET_ERROR, NULL, NM_TYPE_ETHERNET_ERROR);
+#define NM_TYPE_ETHERNET_FACTORY (nm_ethernet_factory_get_type ())
+#define NM_ETHERNET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ETHERNET_FACTORY, NMEthernetFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_ETHERNET) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "Ethernet",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+		                                  NULL);
+	}
+	return NULL;
 }
+
+DEFINE_DEVICE_FACTORY_INTERNAL(ETHERNET, Ethernet, ethernet, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h
index bcaf270a..ad9ce856 100644
--- a/src/devices/nm-device-ethernet.h
+++ b/src/devices/nm-device-ethernet.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef NM_DEVICE_ETHERNET_H
-#define NM_DEVICE_ETHERNET_H
+#ifndef __NETWORKMANAGER_DEVICE_ETHERNET_H__
+#define __NETWORKMANAGER_DEVICE_ETHERNET_H__
 
 #include <glib-object.h>
 
@@ -35,31 +35,14 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ETHERNET))
 #define NM_DEVICE_ETHERNET_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
 
-typedef enum
-{
-	NM_ETHERNET_ERROR_CONNECTION_NOT_WIRED = 0, /*< nick=ConnectionNotWired >*/
-	NM_ETHERNET_ERROR_CONNECTION_INVALID,       /*< nick=ConnectionInvalid >*/
-	NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,  /*< nick=ConnectionIncompatible >*/
-} NMEthernetError;
-
 #define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address"
 #define NM_DEVICE_ETHERNET_SPEED "speed"
 
-typedef struct {
-	NMDevice parent;
-} NMDeviceEthernet;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceEthernetClass;
-
+typedef NMDevice NMDeviceEthernet;
+typedef NMDeviceClass NMDeviceEthernetClass;
 
 GType nm_device_ethernet_get_type (void);
 
-
-NMDevice *nm_device_ethernet_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_ETHERNET_H */
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index fc4d1c24..abfb8d4a 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -18,6 +18,8 @@
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include "nm-device-factory.h"
 
 enum {
@@ -27,6 +29,21 @@ enum {
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static GSList *internal_types = NULL;
+
+void
+_nm_device_factory_internal_register_type (GType factory_type)
+{
+	g_return_if_fail (g_slist_find (internal_types, GUINT_TO_POINTER (factory_type)) == NULL);
+	internal_types = g_slist_prepend (internal_types, GUINT_TO_POINTER (factory_type));
+}
+
+const GSList *
+nm_device_factory_get_internal_factory_types (void)
+{
+	return internal_types;
+}
+
 gboolean
 nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
 {
@@ -91,6 +108,23 @@ nm_device_factory_get_type (void)
 	return device_factory_type;
 }
 
+NMDeviceType
+nm_device_factory_get_device_type (NMDeviceFactory *factory)
+{
+	g_return_val_if_fail (factory != NULL, NM_DEVICE_TYPE_UNKNOWN);
+
+	return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_device_type (factory);
+}
+
+void
+nm_device_factory_start (NMDeviceFactory *factory)
+{
+	g_return_if_fail (factory != NULL);
+
+	if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start)
+		NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start (factory);
+}
+
 NMDevice *
 nm_device_factory_new_link (NMDeviceFactory *factory,
                             NMPlatformLink *plink,
@@ -104,3 +138,21 @@ nm_device_factory_new_link (NMDeviceFactory *factory,
 	return NULL;
 }
 
+NMDevice *
+nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                                        NMConnection *connection,
+                                                        NMDevice *parent,
+                                                        GError **error)
+{
+	NMDeviceFactory *interface;
+
+	g_return_val_if_fail (factory, NULL);
+	g_return_val_if_fail (connection, NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
+	if (interface->create_virtual_device_for_connection)
+		return interface->create_virtual_device_for_connection (factory, connection, parent, error);
+	return NULL;
+}
+
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index f0e3dc19..ce672b8b 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -18,14 +18,13 @@
  * Copyright (C) 2007 - 2014 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_FACTORY_H
-#define NM_DEVICE_FACTORY_H
+#ifndef __NETWORKMANAGER_DEVICE_FACTORY_H__
+#define __NETWORKMANAGER_DEVICE_FACTORY_H__
 
 #include <glib.h>
 #include <glib-object.h>
 
-#include "NetworkManager.h"
-#include "nm-platform.h"
+#include "nm-dbus-interface.h"
 #include "nm-device.h"
 
 /* WARNING: this file is private API between NetworkManager and its internal
@@ -43,7 +42,7 @@ typedef struct _NMDeviceFactory NMDeviceFactory;
  * Creates a #GObject that implements the #NMDeviceFactory interface. This
  * function must not emit any signals or perform any actions that would cause
  * devices or components to be created immediately.  Instead these should be
- * deferred to an idle handler.
+ * deferred to the "start" interface method.
  *
  * Returns: the #GObject implementing #NMDeviceFactory or %NULL
  */
@@ -52,16 +51,6 @@ NMDeviceFactory *nm_device_factory_create (GError **error);
 /* Should match nm_device_factory_create() */
 typedef NMDeviceFactory * (*NMDeviceFactoryCreateFunc) (GError **error);
 
-/**
- * nm_device_factory_get_device_type:
- *
- * Returns: the #NMDeviceType that this plugin creates
- */
-NMDeviceType nm_device_factory_get_device_type (void);
-
-/* Should match nm_device_factory_get_device_type() */
-typedef NMDeviceType (*NMDeviceFactoryDeviceTypeFunc) (void);
-
 /********************************************************************/
 
 #define NM_TYPE_DEVICE_FACTORY               (nm_device_factory_get_type ())
@@ -77,6 +66,25 @@ struct _NMDeviceFactory {
 	GTypeInterface g_iface;
 
 	/**
+	 * get_device_type:
+	 * @factory: the #NMDeviceFactory
+	 *
+	 * This function MUST be implemented.
+	 *
+	 * Returns: the #NMDeviceType that this plugin creates
+	 */
+	NMDeviceType (*get_device_type) (NMDeviceFactory *factory);
+
+	/**
+	 * start:
+	 * @factory: the #NMDeviceFactory
+	 *
+	 * Start the factory and discover any existing devices that the factory
+	 * can manage.
+	 */
+	void (*start)                   (NMDeviceFactory *factory);
+
+	/**
 	 * new_link:
 	 * @factory: the #NMDeviceFactory
 	 * @link: the new link
@@ -95,6 +103,25 @@ struct _NMDeviceFactory {
 	                               NMPlatformLink *plink,
 	                               GError **error);
 
+	/**
+	 * create_virtual_device_for_connection:
+	 * @factory: the #NMDeviceFactory
+	 * @connection: the #NMConnection
+	 * @error: a @GError in case of failure
+	 *
+	 * Virtual device types (such as team, bond, bridge) may need to be created.
+	 * This function tries to create a device based on the given @connection.
+	 *
+	 * Returns: the newly created #NMDevice. If the factory does not support the
+	 * connection type, it should return %NULL and leave @error unset. On error
+	 * it should set @error and return %NULL.
+	 */
+	NMDevice * (*create_virtual_device_for_connection) (NMDeviceFactory *factory,
+	                                                    NMConnection *connection,
+	                                                    NMDevice *parent,
+	                                                    GError **error);
+
+
 	/* Signals */
 
 	/**
@@ -124,13 +151,77 @@ struct _NMDeviceFactory {
 
 GType      nm_device_factory_get_type    (void);
 
+NMDeviceType nm_device_factory_get_device_type (NMDeviceFactory *factory);
+
+void       nm_device_factory_start       (NMDeviceFactory *factory);
+
 NMDevice * nm_device_factory_new_link    (NMDeviceFactory *factory,
                                           NMPlatformLink *plink,
                                           GError **error);
 
+NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                                                   NMConnection *connection,
+                                                                   NMDevice *parent,
+                                                                   GError **error);
+
 /* For use by implementations */
 gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
                                                    GObject *component);
 
-#endif /* NM_DEVICE_FACTORY_H */
-
+/**************************************************************************
+ * INTERNAL DEVICE FACTORY FUNCTIONS - devices provided by plugins should
+ * not use these functions.
+ **************************************************************************/
+
+#define DEFINE_DEVICE_FACTORY_INTERNAL(upper, mixed, lower, dfi_code) \
+	DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, upper, dfi_code)
+
+#define DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, devtype, dfi_code) \
+	typedef GObject NM##mixed##Factory; \
+	typedef GObjectClass NM##mixed##FactoryClass; \
+ \
+	static GType nm_##lower##_factory_get_type (void); \
+	static void device_factory_interface_init (NMDeviceFactory *factory_iface); \
+ \
+	G_DEFINE_TYPE_EXTENDED (NM##mixed##Factory, nm_##lower##_factory, G_TYPE_OBJECT, 0, \
+	                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init) \
+	                        _nm_device_factory_internal_register_type (g_define_type_id);) \
+ \
+	/* Use a module constructor to register the factory's GType at load \
+	 * time, which then calls _nm_device_factory_internal_register_type() \
+	 * to register the factory's GType with the Manager. \
+	 */ \
+	static void __attribute__((constructor)) \
+	register_device_factory_internal_##lower (void) \
+	{ \
+		g_type_init (); \
+		g_type_ensure (NM_TYPE_##upper##_FACTORY); \
+	} \
+ \
+	static NMDeviceType \
+	get_device_type (NMDeviceFactory *factory) \
+	{ \
+		return NM_DEVICE_TYPE_##devtype; \
+	} \
+ \
+	static void \
+	device_factory_interface_init (NMDeviceFactory *factory_iface) \
+	{ \
+		factory_iface->get_device_type = get_device_type; \
+		dfi_code \
+	} \
+ \
+	static void \
+	nm_##lower##_factory_init (NM##mixed##Factory *self) \
+	{ \
+	} \
+ \
+	static void \
+	nm_##lower##_factory_class_init (NM##mixed##FactoryClass *lower##_class) \
+	{ \
+	}
+
+void _nm_device_factory_internal_register_type (GType factory_type);
+const GSList *nm_device_factory_get_internal_factory_types (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_FACTORY_H__ */
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 5261c133..f103ef83 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -24,9 +24,9 @@
 #include "nm-device-private.h"
 #include "nm-enum-types.h"
 #include "nm-platform.h"
-#include "nm-utils.h"
 #include "nm-glib-compat.h"
 #include "nm-dbus-manager.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-generic-glue.h"
 
@@ -45,19 +45,6 @@ enum {
 	LAST_PROP
 };
 
-#define NM_DEVICE_GENERIC_ERROR (nm_device_generic_error_quark ())
-
-static GQuark
-nm_device_generic_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-device-generic-error");
-	return quark;
-}
-
-/**************************************************************/
-
 static guint32
 get_generic_capabilities (NMDevice *dev)
 {
@@ -203,15 +190,12 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_TYPE_DESCRIPTION,
-		 g_param_spec_string (NM_DEVICE_GENERIC_TYPE_DESCRIPTION,
-		                      "Type Description",
-		                      "Type description",
+		 g_param_spec_string (NM_DEVICE_GENERIC_TYPE_DESCRIPTION, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      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_generic_object_info);
-
-	dbus_g_error_domain_register (NM_DEVICE_GENERIC_ERROR, NULL, NM_TYPE_DEVICE_GENERIC_ERROR);
 }
diff --git a/src/devices/nm-device-generic.h b/src/devices/nm-device-generic.h
index e7b7090b..c889b1dd 100644
--- a/src/devices/nm-device-generic.h
+++ b/src/devices/nm-device-generic.h
@@ -18,8 +18,8 @@
  * Copyright 2013 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_GENERIC_H
-#define NM_DEVICE_GENERIC_H
+#ifndef __NETWORKMANAGER_DEVICE_GENERIC_H__
+#define __NETWORKMANAGER_DEVICE_GENERIC_H__
 
 #include <glib-object.h>
 
@@ -34,13 +34,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_GENERIC))
 #define NM_DEVICE_GENERIC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass))
 
-typedef enum
-{
-	NM_DEVICE_GENERIC_ERROR_CONNECTION_NOT_GENERIC = 0, /*< nick=ConnectionNotGeneric >*/
-	NM_DEVICE_GENERIC_ERROR_CONNECTION_INVALID,         /*< nick=ConnectionInvalid >*/
-	NM_DEVICE_GENERIC_ERROR_CONNECTION_INCOMPATIBLE,    /*< nick=ConnectionIncompatible >*/
-} NMDeviceGenericError;
-
 #define NM_DEVICE_GENERIC_TYPE_DESCRIPTION "type-description"
 
 typedef struct {
diff --git a/src/devices/nm-device-gre.c b/src/devices/nm-device-gre.c
index 0412e993..9c25b398 100644
--- a/src/devices/nm-device-gre.c
+++ b/src/devices/nm-device-gre.c
@@ -21,6 +21,7 @@
 #include "config.h"
 
 #include <string.h>
+#include <arpa/inet.h>
 
 #include "nm-device-gre.h"
 #include "nm-device-private.h"
@@ -28,9 +29,14 @@
 #include "nm-logging.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-gre-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceGre);
+
 G_DEFINE_TYPE (NMDeviceGre, nm_device_gre, NM_TYPE_DEVICE_GENERIC)
 
 #define NM_DEVICE_GRE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_GRE, NMDeviceGrePrivate))
@@ -60,13 +66,13 @@ enum {
 static void
 update_properties (NMDevice *device)
 {
-	NMDeviceGrePrivate *priv = NM_DEVICE_GRE_GET_PRIVATE (device);
+	NMDeviceGre *self = NM_DEVICE_GRE (device);
+	NMDeviceGrePrivate *priv = NM_DEVICE_GRE_GET_PRIVATE (self);
 	GObject *object = G_OBJECT (device);
 	NMPlatformGreProperties props;
 
 	if (!nm_platform_gre_get_properties (nm_device_get_ifindex (device), &props)) {
-		nm_log_warn (LOGD_HW, "(%s): could not read gre properties",
-		             nm_device_get_iface (device));
+		_LOGW (LOGD_HW, "could not read gre properties");
 		return;
 	}
 
@@ -107,18 +113,6 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 
 /**************************************************************/
 
-NMDevice *
-nm_device_gre_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Gre",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-	                                  NULL);
-}
-
 static void
 nm_device_gre_init (NMDeviceGre *self)
 {
@@ -194,85 +188,98 @@ nm_device_gre_class_init (NMDeviceGreClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_GRE_PARENT,
-		                     "Parent",
-		                     "Parent device",
+		 g_param_spec_boxed (NM_DEVICE_GRE_PARENT, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_INPUT_FLAGS,
-		 g_param_spec_uint (NM_DEVICE_GRE_INPUT_FLAGS,
-		                    "Input flags",
-		                    "Input flags",
+		 g_param_spec_uint (NM_DEVICE_GRE_INPUT_FLAGS, "", "",
 		                    0, G_MAXUINT16, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_OUTPUT_FLAGS,
-		 g_param_spec_uint (NM_DEVICE_GRE_OUTPUT_FLAGS,
-		                    "Output flags",
-		                    "Output flags",
+		 g_param_spec_uint (NM_DEVICE_GRE_OUTPUT_FLAGS, "", "",
 		                    0, G_MAXUINT16, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_INPUT_KEY,
-		 g_param_spec_uint (NM_DEVICE_GRE_INPUT_KEY,
-		                    "Input key",
-		                    "Input key",
+		 g_param_spec_uint (NM_DEVICE_GRE_INPUT_KEY, "", "",
 		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_OUTPUT_KEY,
-		 g_param_spec_uint (NM_DEVICE_GRE_OUTPUT_KEY,
-		                    "Output key",
-		                    "Output key",
+		 g_param_spec_uint (NM_DEVICE_GRE_OUTPUT_KEY, "", "",
 		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_DEVICE_GRE_LOCAL,
-		                      "Local",
-		                      "Local",
+		 g_param_spec_string (NM_DEVICE_GRE_LOCAL, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_REMOTE,
-		 g_param_spec_string (NM_DEVICE_GRE_REMOTE,
-		                      "Remote",
-		                      "Remote",
+		 g_param_spec_string (NM_DEVICE_GRE_REMOTE, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TTL,
-		 g_param_spec_uchar (NM_DEVICE_GRE_TTL,
-		                     "TTL",
-		                     "TTL",
+		 g_param_spec_uchar (NM_DEVICE_GRE_TTL, "", "",
 		                     0, 255, 0,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TOS,
-		 g_param_spec_uchar (NM_DEVICE_GRE_TOS,
-		                     "ToS",
-		                     "ToS",
+		 g_param_spec_uchar (NM_DEVICE_GRE_TOS, "", "",
 		                     0, 255, 0,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PATH_MTU_DISCOVERY,
-		 g_param_spec_boolean (NM_DEVICE_GRE_PATH_MTU_DISCOVERY,
-		                       "Path MTU Discovery",
-		                       "Path MTU Discovery",
+		 g_param_spec_boolean (NM_DEVICE_GRE_PATH_MTU_DISCOVERY, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_gre_object_info);
 }
+
+/*************************************************************/
+
+#define NM_TYPE_GRE_FACTORY (nm_gre_factory_get_type ())
+#define NM_GRE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GRE_FACTORY, NMGreFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_GRE || plink->type == NM_LINK_TYPE_GRETAP) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "Gre",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+		                                  NULL);
+	}
+	return NULL;
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(GRE, Gre, gre, ETHERNET, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-gre.h b/src/devices/nm-device-gre.h
index 610b3805..7acff5f7 100644
--- a/src/devices/nm-device-gre.h
+++ b/src/devices/nm-device-gre.h
@@ -18,8 +18,8 @@
  * Copyright 2013 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_GRE_H
-#define NM_DEVICE_GRE_H
+#ifndef __NETWORKMANAGER_DEVICE_GRE_H__
+#define __NETWORKMANAGER_DEVICE_GRE_H__
 
 #include <glib-object.h>
 
@@ -45,19 +45,11 @@ G_BEGIN_DECLS
 #define NM_DEVICE_GRE_TOS                "tos"
 #define NM_DEVICE_GRE_PATH_MTU_DISCOVERY "path-mtu-discovery"
 
-typedef struct {
-	NMDeviceGeneric parent;
-} NMDeviceGre;
-
-typedef struct {
-	NMDeviceGenericClass parent;
-
-} NMDeviceGreClass;
+typedef NMDeviceGeneric NMDeviceGre;
+typedef NMDeviceGenericClass NMDeviceGreClass;
 
 GType nm_device_gre_get_type (void);
 
-NMDevice *nm_device_gre_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_GRE_H */
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 8ec79157..e1af8995 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -24,15 +24,18 @@
 #include <glib/gi18n.h>
 
 #include <linux/if_infiniband.h>
-#include <netinet/ether.h>
 
 #include "nm-device-infiniband.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-enum-types.h"
 #include "nm-dbus-manager.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
+#include "nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-infiniband-glue.h"
 
@@ -41,8 +44,6 @@ G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate))
 
-#define NM_INFINIBAND_ERROR (nm_infiniband_error_quark ())
-
 typedef struct {
 	int dummy;
 } NMDeviceInfinibandPrivate;
@@ -53,88 +54,11 @@ enum {
 	LAST_PROP
 };
 
-static GQuark
-nm_infiniband_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-infiniband-error");
-	return quark;
-}
-
-static GObject*
-constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
-{
-	GObject *object;
-	NMDevice *self;
-
-	object = G_OBJECT_CLASS (nm_device_infiniband_parent_class)->constructor (type,
-	                                                                          n_construct_params,
-	                                                                          construct_params);
-	if (!object)
-		return NULL;
-
-	self = NM_DEVICE (object);
-
-	nm_log_dbg (LOGD_HW | LOGD_INFINIBAND, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (self),
-	            nm_device_get_ifindex (self));
-	return object;
-}
-
 static void
 nm_device_infiniband_init (NMDeviceInfiniband * self)
 {
 }
 
-NMDevice *
-nm_device_infiniband_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
-	                                  NULL);
-}
-
-NMDevice *
-nm_device_infiniband_new_partition (NMConnection *connection,
-                                    NMDevice     *parent)
-{
-	NMSettingInfiniband *s_infiniband;
-	int p_key, parent_ifindex;
-	const char *iface;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (parent), NULL);
-
-	iface = nm_connection_get_virtual_iface_name (connection);
-	g_return_val_if_fail (iface != NULL, NULL);
-
-	parent_ifindex = nm_device_get_ifindex (parent);
-	s_infiniband = nm_connection_get_setting_infiniband (connection);
-	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
-
-	if (   !nm_platform_infiniband_partition_add (parent_ifindex, p_key)
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_INFINIBAND, "(%s): failed to add InfiniBand P_Key interface for '%s': %s",
-		             iface, nm_connection_get_id (connection),
-		             nm_platform_get_error_msg ());
-		return NULL;
-	}
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
-	                                  NM_DEVICE_IFACE, iface,
-	                                  NM_DEVICE_DRIVER, nm_device_get_driver (parent),
-	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
-	                                  NULL);
-}
-
 static guint32
 get_generic_capabilities (NMDevice *dev)
 {
@@ -207,14 +131,13 @@ ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
 	/* MTU override */
 	mtu = nm_setting_infiniband_get_mtu (s_infiniband);
 	if (mtu)
-		nm_ip4_config_set_mtu (config, mtu);
+		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 }
 
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
 	NMSettingInfiniband *s_infiniband;
-	const GByteArray *mac;
 
 	if (!NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->check_connection_compatible (device, connection))
 		return FALSE;
@@ -227,11 +150,10 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	if (s_infiniband) {
+		const char *mac;
+
 		mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-		/* We only compare the last 8 bytes */
-		if (mac && memcmp (mac->data + INFINIBAND_ALEN - 8,
-		                   nm_device_get_hw_address (device, NULL) + INFINIBAND_ALEN - 8,
-		                   8))
+		if (mac && !nm_utils_hwaddr_matches (mac, -1, nm_device_get_hw_address (device), -1))
 			return FALSE;
 	}
 
@@ -246,13 +168,14 @@ complete_connection (NMDevice *device,
                      GError **error)
 {
 	NMSettingInfiniband *s_infiniband;
-	const GByteArray *setting_mac;
-	const guint8 *hw_address;
+	const char *setting_mac;
+	const char *hw_address;
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_INFINIBAND_SETTING_NAME,
 	                           existing_connections,
-	                           _("InfiniBand connection %d"),
+	                           NULL,
+	                           _("InfiniBand connection"),
 	                           NULL,
 	                           TRUE);
 
@@ -263,24 +186,20 @@ complete_connection (NMDevice *device,
 	}
 
 	setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-	hw_address = nm_device_get_hw_address (device, NULL);
+	hw_address = nm_device_get_hw_address (device);
 	if (setting_mac) {
 		/* Make sure the setting MAC (if any) matches the device's MAC */
-		if (memcmp (setting_mac->data, hw_address, INFINIBAND_ALEN)) {
+		if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_INFINIBAND_ERROR,
-			                     NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
-			                     NM_SETTING_INFINIBAND_MAC_ADDRESS);
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("connection does not match device"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MAC_ADDRESS);
 			return FALSE;
 		}
 	} else {
-		GByteArray *mac;
-
 		/* Lock the connection to this device by default */
-		mac = g_byte_array_sized_new (INFINIBAND_ALEN);
-		g_byte_array_append (mac, hw_address, INFINIBAND_ALEN);
-		g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
-		g_byte_array_free (mac, TRUE);
+		g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_MAC_ADDRESS, hw_address, NULL);
 	}
 
 	if (!nm_setting_infiniband_get_transport_mode (s_infiniband))
@@ -293,10 +212,7 @@ static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection);
-	guint maclen;
-	gconstpointer mac = nm_device_get_hw_address (device, &maclen);
-	static const guint8 null_mac[INFINIBAND_ALEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-	GByteArray *array;
+	const char *mac = nm_device_get_hw_address (device);
 	char *mode_path, *contents = NULL;
 	const char *transport_mode = "datagram";
 
@@ -305,12 +221,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 		nm_connection_add_setting (connection, (NMSetting *) s_infiniband);
 	}
 
-	if (mac && (maclen == INFINIBAND_ALEN) && (memcmp (mac, null_mac, maclen) != 0)) {
-		array = g_byte_array_sized_new (maclen);
-		g_byte_array_append (array, (guint8 *) mac, maclen);
-		g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL);
-		g_byte_array_unref (array);
-	}
+	if (mac && !nm_utils_hwaddr_matches (mac, -1, NULL, INFINIBAND_ALEN))
+		g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
 
 	mode_path = g_strdup_printf ("/sys/class/net/%s/mode",
 	                             ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (device)));
@@ -326,38 +238,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 	g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL);
 }
 
-static gboolean
-spec_match_list (NMDevice *device, const GSList *specs)
-{
-	char *hwaddr_str, *spec_str;
-	const GSList *iter;
-
-	if (NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->spec_match_list (device, specs))
-		return TRUE;
-
-	hwaddr_str = nm_utils_hwaddr_ntoa (nm_device_get_hw_address (device, NULL),
-	                                   ARPHRD_INFINIBAND);
-
-	/* InfiniBand hardware address matches only need to match the last
-	 * 8 bytes. In string format, that means we skip the first 36
-	 * characters of hwaddr_str, and the first 40 of the spec (to skip
-	 * "mac:" too).
-	 */
-	for (iter = specs; iter; iter = g_slist_next (iter)) {
-		spec_str = iter->data;
-
-		if (   !g_ascii_strncasecmp (spec_str, "mac:", 4)
-		    && strlen (spec_str) > 40
-		    && !g_ascii_strcasecmp (spec_str + 40, hwaddr_str + 36)) {
-			g_free (hwaddr_str);
-			return TRUE;
-		}
-	}
-
-	g_free (hwaddr_str);
-	return FALSE;
-}
-
 static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
@@ -389,7 +269,6 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	g_type_class_add_private (object_class, sizeof (NMDeviceInfinibandPrivate));
 
 	/* virtual methods */
-	object_class->constructor = constructor;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
@@ -397,7 +276,6 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
 	parent_class->update_connection = update_connection;
-	parent_class->spec_match_list = spec_match_list;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
@@ -407,6 +285,67 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_infiniband_object_info);
+}
 
-	dbus_g_error_domain_register (NM_INFINIBAND_ERROR, NULL, NM_TYPE_INFINIBAND_ERROR);
+/*************************************************************/
+
+#define NM_TYPE_INFINIBAND_FACTORY (nm_infiniband_factory_get_type ())
+#define NM_INFINIBAND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INFINIBAND_FACTORY, NMInfinibandFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_INFINIBAND) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
+		                                  NULL);
+	}
+	return NULL;
 }
+
+static NMDevice *
+create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                      NMConnection *connection,
+                                      NMDevice *parent,
+                                      GError **error)
+{
+	NMSettingInfiniband *s_infiniband;
+	int p_key, parent_ifindex;
+	const char *iface;
+
+	if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME))
+		return NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (parent), NULL);
+
+	s_infiniband = nm_connection_get_setting_infiniband (connection);
+
+	iface = nm_setting_infiniband_get_virtual_interface_name (s_infiniband);
+	g_return_val_if_fail (iface != NULL, NULL);
+
+	parent_ifindex = nm_device_get_ifindex (parent);
+	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
+
+	if (   !nm_platform_infiniband_partition_add (parent_ifindex, p_key)
+	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
+		nm_log_warn (LOGD_DEVICE | LOGD_INFINIBAND, "(%s): failed to add InfiniBand P_Key interface for '%s': %s",
+		             iface, nm_connection_get_id (connection),
+		             nm_platform_get_error_msg ());
+		return NULL;
+	}
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_DRIVER, nm_device_get_driver (parent),
+	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
+	                                  NULL);
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL(INFINIBAND, Infiniband, infiniband, \
+	factory_iface->new_link = new_link; \
+	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	)
+
diff --git a/src/devices/nm-device-infiniband.h b/src/devices/nm-device-infiniband.h
index 45dc1ab6..b81b762a 100644
--- a/src/devices/nm-device-infiniband.h
+++ b/src/devices/nm-device-infiniband.h
@@ -18,8 +18,8 @@
  * Copyright 2011 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_INFINIBAND_H
-#define NM_DEVICE_INFINIBAND_H
+#ifndef __NETWORKMANAGER_DEVICE_INFINIBAND_H__
+#define __NETWORKMANAGER_DEVICE_INFINIBAND_H__
 
 #include <glib-object.h>
 
@@ -34,28 +34,11 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_INFINIBAND_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_INFINIBAND))
 #define NM_DEVICE_INFINIBAND_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass))
 
-typedef enum {
-	NM_INFINIBAND_ERROR_CONNECTION_NOT_INFINIBAND = 0, /*< nick=ConnectionNotInfiniband >*/
-	NM_INFINIBAND_ERROR_CONNECTION_INVALID,            /*< nick=ConnectionInvalid >*/
-	NM_INFINIBAND_ERROR_CONNECTION_INCOMPATIBLE,       /*< nick=ConnectionIncompatible >*/
-} NMInfinibandError;
-
-typedef struct {
-	NMDevice parent;
-} NMDeviceInfiniband;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceInfinibandClass;
-
+typedef NMDevice NMDeviceInfiniband;
+typedef NMDeviceClass NMDeviceInfinibandClass;
 
 GType nm_device_infiniband_get_type (void);
 
-NMDevice *nm_device_infiniband_new (NMPlatformLink *platform_device);
-NMDevice *nm_device_infiniband_new_partition (NMConnection *connection,
-                                              NMDevice     *parent);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_INFINIBAND_H */
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
new file mode 100644
index 00000000..acca32e5
--- /dev/null
+++ b/src/devices/nm-device-logging.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_LOGGING_H__
+#define __NETWORKMANAGER_DEVICE_LOGGING_H__
+
+#include "nm-logging.h"
+#include "nm-device.h"
+
+#define _LOG_DECLARE_SELF(t) \
+inline static NMDevice * \
+_nm_device_log_self_to_device (t *self) \
+{ \
+    return (NMDevice *) self; \
+}
+
+#define _LOG(level, domain, ...) \
+    nm_log_obj ((level), (domain), (self), \
+                "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                (self) ? str_if_set (nm_device_get_iface (_nm_device_log_self_to_device (self)), "(null)") : "(none)" \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__))
+
+#define _LOGT(domain, ...)      _LOG (LOGL_TRACE, domain, __VA_ARGS__)
+#define _LOGD(domain, ...)      _LOG (LOGL_DEBUG, domain, __VA_ARGS__)
+#define _LOGI(domain, ...)      _LOG (LOGL_INFO,  domain, __VA_ARGS__)
+#define _LOGW(domain, ...)      _LOG (LOGL_WARN,  domain, __VA_ARGS__)
+#define _LOGE(domain, ...)      _LOG (LOGL_ERR,   domain, __VA_ARGS__)
+
+#endif /* __NETWORKMANAGER_DEVICE_LOGGING_H__ */
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 22848fe7..bcd38617 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -28,9 +28,13 @@
 #include "nm-logging.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
+#include "nm-device-factory.h"
 
 #include "nm-device-macvlan-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceMacvlan);
+
 G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE_GENERIC)
 
 #define NM_DEVICE_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanPrivate))
@@ -55,13 +59,13 @@ enum {
 static void
 update_properties (NMDevice *device)
 {
+	NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (device);
 	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
 	GObject *object = G_OBJECT (device);
 	NMPlatformMacvlanProperties props;
 
 	if (!nm_platform_macvlan_get_properties (nm_device_get_ifindex (device), &props)) {
-		nm_log_warn (LOGD_HW, "(%s): could not read macvlan properties",
-		             nm_device_get_iface (device));
+		_LOGW (LOGD_HW, "could not read macvlan properties");
 		return;
 	}
 
@@ -88,18 +92,6 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 
 /**************************************************************/
 
-NMDevice *
-nm_device_macvlan_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Macvlan",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-	                                  NULL);
-}
-
 static void
 nm_device_macvlan_init (NMDeviceMacvlan *self)
 {
@@ -153,29 +145,49 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_MACVLAN_PARENT,
-		                     "Parent",
-		                     "Parent device",
+		 g_param_spec_boxed (NM_DEVICE_MACVLAN_PARENT, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MODE,
-		 g_param_spec_string (NM_DEVICE_MACVLAN_MODE,
-		                      "Mode",
-		                      "Mode: 'private', 'vepa', 'bridge', or 'passthru'",
+		 g_param_spec_string (NM_DEVICE_MACVLAN_MODE, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_NO_PROMISC,
-		 g_param_spec_boolean (NM_DEVICE_MACVLAN_NO_PROMISC,
-		                       "No-promisc",
-		                       "No promiscuous mode",
+		 g_param_spec_boolean (NM_DEVICE_MACVLAN_NO_PROMISC, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_macvlan_object_info);
 }
+
+/*************************************************************/
+
+#define NM_TYPE_MACVLAN_FACTORY (nm_macvlan_factory_get_type ())
+#define NM_MACVLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MACVLAN_FACTORY, NMMacvlanFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_MACVLAN || plink->type == NM_LINK_TYPE_MACVTAP) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "Macvlan",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+		                                  NULL);
+	}
+	return NULL;
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(MACVLAN, Macvlan, macvlan, ETHERNET, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index 348ed2f9..24f15437 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -18,8 +18,8 @@
  * Copyright 2013 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_MACVLAN_H
-#define NM_DEVICE_MACVLAN_H
+#ifndef __NETWORKMANAGER_DEVICE_MACVLAN_H__
+#define __NETWORKMANAGER_DEVICE_MACVLAN_H__
 
 #include <glib-object.h>
 
@@ -38,19 +38,11 @@ G_BEGIN_DECLS
 #define NM_DEVICE_MACVLAN_MODE       "mode"
 #define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc"
 
-typedef struct {
-	NMDeviceGeneric parent;
-} NMDeviceMacvlan;
-
-typedef struct {
-	NMDeviceGenericClass parent;
-
-} NMDeviceMacvlanClass;
+typedef NMDeviceGeneric NMDeviceMacvlan;
+typedef NMDeviceGenericClass NMDeviceMacvlanClass;
 
 GType nm_device_macvlan_get_type (void);
 
-NMDevice *nm_device_macvlan_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_MACVLAN_H */
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 24bb0b33..65a9a71a 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -19,8 +19,8 @@
  * Copyright (C) 2007 - 2011 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_PRIVATE_H
-#define NM_DEVICE_PRIVATE_H
+#ifndef __NETWORKMANAGER_DEVICE_PRIVATE_H__
+#define __NETWORKMANAGER_DEVICE_PRIVATE_H__
 
 #include "nm-device.h"
 
@@ -37,8 +37,9 @@ enum NMActStageReturn {
 };
 
 #define NM_DEVICE_CAP_NONSTANDARD_CARRIER 0x80000000
+#define NM_DEVICE_CAP_IS_NON_KERNEL       0x40000000
 
-#define NM_DEVICE_CAP_INTERNAL_MASK 0x80000000
+#define NM_DEVICE_CAP_INTERNAL_MASK 0xc0000000
 
 void nm_device_set_ip_iface (NMDevice *self, const char *iface);
 
@@ -52,12 +53,9 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar
 
 void nm_device_take_down (NMDevice *self, gboolean block);
 
-gboolean nm_device_update_hw_address (NMDevice *self);
-gboolean nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
+gboolean nm_device_set_hw_addr (NMDevice *device, const char *addr,
                                 const char *detail, guint64 hw_log_domain);
 
-gboolean nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6);
-
 void nm_device_set_firmware_missing (NMDevice *self, gboolean missing);
 
 void nm_device_activate_schedule_stage1_device_prepare (NMDevice *device);
@@ -76,7 +74,7 @@ gboolean nm_device_activate_ip6_state_in_conf (NMDevice *device);
 gboolean nm_device_activate_ip6_state_in_wait (NMDevice *device);
 
 void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout);
-void nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr);
+void nm_device_set_dhcp_anycast_address (NMDevice *device, const char *addr);
 
 gboolean nm_device_dhcp4_renew (NMDevice *device, gboolean release);
 gboolean nm_device_dhcp6_renew (NMDevice *device, gboolean release);
@@ -91,12 +89,17 @@ gboolean nm_device_get_enslaved (NMDevice *device);
 
 NMDevice *nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex);
 
-void nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
+void nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave,
                                                  guint64 log_domain);
 
-void nm_device_set_carrier (NMDevice *device, gboolean carrier);
+void nm_device_set_carrier (NMDevice *self, gboolean carrier);
 
 void nm_device_emit_recheck_auto_activate (NMDevice *device);
 void nm_device_queue_recheck_assume (NMDevice *device);
 
+void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config);
+void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config);
+
+gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value);
+
 #endif	/* NM_DEVICE_PRIVATE_H */
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index d52228f9..9a3fab5e 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -28,9 +28,13 @@
 #include "nm-dbus-manager.h"
 #include "nm-logging.h"
 #include "nm-platform.h"
+#include "nm-device-factory.h"
 
 #include "nm-device-tun-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceTun);
+
 G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE_GENERIC)
 
 #define NM_DEVICE_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TUN, NMDeviceTunPrivate))
@@ -55,15 +59,14 @@ enum {
 };
 
 static void
-reload_tun_properties (NMDeviceTun *device)
+reload_tun_properties (NMDeviceTun *self)
 {
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (device);
-	GObject *object = G_OBJECT (device);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	GObject *object = G_OBJECT (self);
 	NMPlatformTunProperties props;
 
-	if (!nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (device)), &props)) {
-		nm_log_warn (LOGD_HW, "(%s): could not read tun properties",
-		             nm_device_get_iface (NM_DEVICE (device)));
+	if (!nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &props)) {
+		_LOGD (LOGD_HW, "could not read tun properties");
 		return;
 	}
 
@@ -108,27 +111,6 @@ delay_tun_get_properties_cb (gpointer user_data)
 
 /**************************************************************/
 
-NMDevice *
-nm_device_tun_new (NMPlatformLink *platform_device)
-{
-	const char *mode = NULL;
-
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	if (platform_device->type == NM_LINK_TYPE_TUN)
-		mode = "tun";
-	else if (platform_device->type == NM_LINK_TYPE_TAP)
-		mode = "tap";
-	g_return_val_if_fail (mode != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Tun",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-	                                  NM_DEVICE_TUN_MODE, mode,
-	                                  NULL);
-}
-
 static void
 nm_device_tun_init (NMDeviceTun *self)
 {
@@ -137,18 +119,18 @@ nm_device_tun_init (NMDeviceTun *self)
 static void
 constructed (GObject *object)
 {
+	NMDeviceTun *self = NM_DEVICE_TUN (object);
 	gboolean properties_read;
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (object);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
 
-	properties_read = nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (object)), &priv->props);
+	properties_read = nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &priv->props);
 
 	G_OBJECT_CLASS (nm_device_tun_parent_class)->constructed (object);
 
 	if (!properties_read) {
 		/* Error reading the tun properties. Maybe this was due to a race. Try again a bit later. */
-		nm_log_dbg (LOGD_HW, "(%s): could not read tun properties (retry)",
-		                     nm_device_get_iface (NM_DEVICE (object)));
-		priv->delay_tun_get_properties_id = g_timeout_add_seconds (1, delay_tun_get_properties_cb, object);
+		_LOGD (LOGD_HW, "could not read tun properties (retry)");
+		priv->delay_tun_get_properties_id = g_timeout_add_seconds (1, delay_tun_get_properties_cb, self);
 	}
 }
 
@@ -174,10 +156,10 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_OWNER:
-		g_value_set_uint (value, priv->props.owner);
+		g_value_set_int64 (value, priv->props.owner);
 		break;
 	case PROP_GROUP:
-		g_value_set_uint (value, priv->props.group);
+		g_value_set_int64 (value, priv->props.group);
 		break;
 	case PROP_MODE:
 		g_value_set_string (value, priv->mode);
@@ -242,49 +224,37 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_OWNER,
-		 g_param_spec_int64 (NM_DEVICE_TUN_OWNER,
-		                     "Owner",
-		                     "Owner",
+		 g_param_spec_int64 (NM_DEVICE_TUN_OWNER, "", "",
 		                     -1, G_MAXUINT32, -1,
 		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_GROUP,
-		 g_param_spec_int64 (NM_DEVICE_TUN_GROUP,
-		                     "Group",
-		                     "Group",
+		 g_param_spec_int64 (NM_DEVICE_TUN_GROUP, "", "",
 		                     -1, G_MAXUINT32, -1,
 		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MODE,
-		 g_param_spec_string (NM_DEVICE_TUN_MODE,
-		                      "Mode",
-		                      "Mode",
+		 g_param_spec_string (NM_DEVICE_TUN_MODE, "", "",
 		                      "tun",
 		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_NO_PI,
-		 g_param_spec_boolean (NM_DEVICE_TUN_NO_PI,
-		                      "No Protocol Info",
-		                      "No Protocol Info",
-		                      FALSE,
-		                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+		 g_param_spec_boolean (NM_DEVICE_TUN_NO_PI, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_VNET_HDR,
-		 g_param_spec_boolean (NM_DEVICE_TUN_VNET_HDR,
-		                       "Virtio networking header",
-		                       "Virtio networking header",
+		 g_param_spec_boolean (NM_DEVICE_TUN_VNET_HDR, "", "",
 		                       FALSE,
 		                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MULTI_QUEUE,
-		 g_param_spec_boolean (NM_DEVICE_TUN_MULTI_QUEUE,
-		                       "Multi-queue",
-		                       "Multi-queue",
+		 g_param_spec_boolean (NM_DEVICE_TUN_MULTI_QUEUE, "", "",
 		                       FALSE,
 		                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
@@ -292,3 +262,34 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_tun_object_info);
 }
+
+
+/*************************************************************/
+
+#define NM_TYPE_TUN_FACTORY (nm_tun_factory_get_type ())
+#define NM_TUN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TUN_FACTORY, NMTunFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	const char *mode = NULL;
+
+	if (plink->type == NM_LINK_TYPE_TUN)
+		mode = "tun";
+	else if (plink->type == NM_LINK_TYPE_TAP)
+		mode = "tap";
+	else
+		return NULL;
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN,
+	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_TYPE_DESC, "Tun",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NM_DEVICE_TUN_MODE, mode,
+	                                  NULL);
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(TUN, Tun, tun, GENERIC, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-tun.h b/src/devices/nm-device-tun.h
index cfcf4d7b..393c5801 100644
--- a/src/devices/nm-device-tun.h
+++ b/src/devices/nm-device-tun.h
@@ -18,8 +18,8 @@
  * Copyright 2013 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_TUN_H
-#define NM_DEVICE_TUN_H
+#ifndef __NETWORKMANAGER_DEVICE_TUN_H__
+#define __NETWORKMANAGER_DEVICE_TUN_H__
 
 #include <glib-object.h>
 
@@ -41,19 +41,11 @@ G_BEGIN_DECLS
 #define NM_DEVICE_TUN_VNET_HDR    "vnet-hdr"
 #define NM_DEVICE_TUN_MULTI_QUEUE "multi-queue"
 
-typedef struct {
-	NMDeviceGeneric parent;
-} NMDeviceTun;
-
-typedef struct {
-	NMDeviceGenericClass parent;
-
-} NMDeviceTunClass;
+typedef NMDeviceGeneric NMDeviceTun;
+typedef NMDeviceGenericClass NMDeviceTunClass;
 
 GType nm_device_tun_get_type (void);
 
-NMDevice *nm_device_tun_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_TUN_H */
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index df107523..db29c00d 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -33,9 +33,13 @@
 #include "nm-manager.h"
 #include "nm-platform.h"
 #include "nm-dbus-manager.h"
+#include "nm-device-factory.h"
 
 #include "nm-device-veth-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceVeth);
+
 G_DEFINE_TYPE (NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET)
 
 #define NM_DEVICE_VETH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VETH, NMDeviceVethPrivate))
@@ -79,8 +83,7 @@ get_peer (NMDeviceVeth *self)
 		return priv->peer;
 
 	if (!nm_platform_veth_get_properties (nm_device_get_ifindex (device), &props)) {
-		nm_log_warn (LOGD_HW, "(%s): could not read veth properties",
-		             nm_device_get_iface (device));
+		_LOGW (LOGD_HW, "could not read veth properties");
 		return NULL;
 	}
 
@@ -96,18 +99,6 @@ get_peer (NMDeviceVeth *self)
 
 /**************************************************************/
 
-NMDevice *
-nm_device_veth_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Veth",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
-	                                  NULL);
-}
-
 static void
 nm_device_veth_init (NMDeviceVeth *self)
 {
@@ -159,13 +150,35 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PEER,
-		 g_param_spec_boxed (NM_DEVICE_VETH_PEER,
-		                     "Peer",
-		                     "Peer device",
+		 g_param_spec_boxed (NM_DEVICE_VETH_PEER, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_veth_object_info);
 }
+
+/*************************************************************/
+
+#define NM_TYPE_VETH_FACTORY (nm_veth_factory_get_type ())
+#define NM_VETH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VETH_FACTORY, NMVethFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_VETH) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "Veth",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+		                                  NULL);
+	}
+	return NULL;
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VETH, Veth, veth, ETHERNET, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-veth.h b/src/devices/nm-device-veth.h
index bc8fedff..51e8db45 100644
--- a/src/devices/nm-device-veth.h
+++ b/src/devices/nm-device-veth.h
@@ -18,8 +18,8 @@
  * Copyright 2013 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_VETH_H
-#define NM_DEVICE_VETH_H
+#ifndef __NETWORKMANAGER_DEVICE_VETH_H__
+#define __NETWORKMANAGER_DEVICE_VETH_H__
 
 #include <glib-object.h>
 
@@ -36,19 +36,11 @@ G_BEGIN_DECLS
 
 #define NM_DEVICE_VETH_PEER "peer"
 
-typedef struct {
-	NMDeviceEthernet parent;
-} NMDeviceVeth;
-
-typedef struct {
-	NMDeviceEthernetClass parent;
-
-} NMDeviceVethClass;
+typedef NMDeviceEthernet NMDeviceVeth;
+typedef NMDeviceEthernetClass NMDeviceVethClass;
 
 GType nm_device_veth_get_type (void);
 
-NMDevice *nm_device_veth_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_VETH_H */
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 348be352..c69bcded 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -24,8 +24,6 @@
 #include <glib/gi18n.h>
 
 #include <sys/socket.h>
-#include <linux/if.h>
-#include <netinet/ether.h>
 
 #include "nm-device-vlan.h"
 #include "nm-manager.h"
@@ -35,20 +33,25 @@
 #include "nm-device-private.h"
 #include "nm-enum-types.h"
 #include "nm-dbus-manager.h"
+#include "nm-connection-provider.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
 #include "nm-platform.h"
-#include "nm-utils.h"
+#include "nm-device-factory.h"
+#include "nm-manager.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-vlan-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceVlan);
 
 G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate))
 
-#define NM_VLAN_ERROR (nm_vlan_error_quark ())
-
 typedef struct {
-	guint8 initial_hw_addr[ETH_ALEN];
+	char *initial_hw_addr;
 
 	gboolean disposed;
 	gboolean invalid;
@@ -64,35 +67,21 @@ enum {
 	PROP_PARENT,
 	PROP_VLAN_ID,
 
+	PROP_INT_PARENT_DEVICE,
+
 	LAST_PROP
 };
 
 /******************************************************************/
 
-static GQuark
-nm_vlan_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-vlan-error");
-	return quark;
-}
-
-/******************************************************************/
-
 static void
 update_initial_hw_address (NMDevice *dev)
 {
 	NMDeviceVlan *self = NM_DEVICE_VLAN (dev);
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
-	char *mac_str;
 
-	memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
-
-	mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER);
-	nm_log_dbg (LOGD_DEVICE | LOGD_VLAN, "(%s): read initial MAC address %s",
-	            nm_device_get_iface (dev), mac_str);
-	g_free (mac_str);
+	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev));
+	_LOGD (LOGD_DEVICE | LOGD_VLAN, "read initial MAC address %s", priv->initial_hw_addr);
 }
 
 static guint32
@@ -156,22 +145,20 @@ static gboolean
 match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr)
 {
 	  NMSettingWired *s_wired;
-	  const GByteArray *mac;
-	  const guint8 *device_mac;
-	  guint device_mac_len;
+	  const char *setting_mac;
+	  const char *device_mac;
 
 	  s_wired = nm_connection_get_setting_wired (connection);
 	  if (!s_wired)
 		  return !fail_if_no_hwaddr;
 
-	  mac = nm_setting_wired_get_mac_address (s_wired);
-	  if (!mac)
+	  setting_mac = nm_setting_wired_get_mac_address (s_wired);
+	  if (!setting_mac)
 		  return !fail_if_no_hwaddr;
 
-	  device_mac = nm_device_get_hw_address (device, &device_mac_len);
+	  device_mac = nm_device_get_hw_address (device);
 
-	  return (   mac->len == device_mac_len
-	          && memcmp (mac->data, device_mac, device_mac_len) == 0);
+	  return nm_utils_hwaddr_matches (setting_mac, -1, device_mac, -1);
 }
 
 static gboolean
@@ -206,7 +193,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	 * since both the parent interface and the VLAN ID matched by the time we
 	 * get here.
 	 */
-	iface = nm_connection_get_virtual_iface_name (connection);
+	iface = nm_connection_get_interface_name (connection);
 	if (iface) {
 		if (g_strcmp0 (nm_device_get_ip_iface (device), iface) != 0)
 			return FALSE;
@@ -227,13 +214,14 @@ complete_connection (NMDevice *device,
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_VLAN_SETTING_NAME,
 	                           existing_connections,
-	                           _("VLAN connection %d"),
+	                           NULL,
+	                           _("VLAN connection"),
 	                           NULL,
 	                           TRUE);
 
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	if (!s_vlan) {
-		g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "A 'vlan' setting is required.");
 		return FALSE;
 	}
@@ -243,7 +231,7 @@ complete_connection (NMDevice *device,
 	 */
 	if (   !nm_setting_vlan_get_parent (s_vlan)
 	    && !match_hwaddr (device, connection, TRUE)) {
-		g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "The 'vlan' setting had no interface name, parent, or hardware address.");
 		return FALSE;
 	}
@@ -280,6 +268,7 @@ nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent)
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
+	NMDeviceVlan *self = NM_DEVICE_VLAN (device);
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
 	NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection);
 	int ifindex = nm_device_get_ifindex (device);
@@ -290,12 +279,10 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (!s_vlan) {
 		s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_vlan);
-		g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, nm_device_get_iface (device), NULL);
 	}
 
 	if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) {
-		nm_log_warn (LOGD_VLAN, "(%s): failed to get VLAN interface info while updating connection.",
-		             nm_device_get_iface (device));
+		_LOGW (LOGD_VLAN, "failed to get VLAN interface info while updating connection.");
 		return;
 	}
 
@@ -334,7 +321,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	NMSettingVlan *s_vlan;
 	NMSettingWired *s_wired;
-	const GByteArray *cloned_mac;
+	const char *cloned_mac;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -353,8 +340,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	if (s_wired) {
 		/* Set device MAC address if the connection wants to change it */
 		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		if (cloned_mac && (cloned_mac->len == ETH_ALEN))
-			nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_VLAN);
+		if (cloned_mac)
+			nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN);
 	}
 
 	s_vlan = nm_connection_get_setting_vlan (connection);
@@ -392,7 +379,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	if (s_wired) {
 		mtu = nm_setting_wired_get_mtu (s_wired);
 		if (mtu)
-			nm_ip4_config_set_mtu (config, mtu);
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 	}
 }
 
@@ -403,7 +390,8 @@ deactivate (NMDevice *device)
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
 
 	/* Reset MAC address back to initial address */
-	nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_VLAN);
+	if (priv->initial_hw_addr)
+		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_VLAN);
 }
 
 /******************************************************************/
@@ -421,89 +409,11 @@ parent_state_changed (NMDevice *parent,
 	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
 		return;
 
-	if (new_state < NM_DEVICE_STATE_DISCONNECTED) {
-		/* If the parent becomes unavailable or unmanaged so does the VLAN */
-		nm_device_state_changed (NM_DEVICE (self), new_state, reason);
-	} else if (   new_state == NM_DEVICE_STATE_DISCONNECTED
-	           && old_state < NM_DEVICE_STATE_DISCONNECTED) {
-		/* Mark VLAN interface as available/disconnected when the parent
-		 * becomes available as a result of becoming initialized.
-		 */
-		nm_device_state_changed (NM_DEVICE (self), new_state, reason);
-	}
+	nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
 }
 
 /******************************************************************/
 
-NMDevice *
-nm_device_vlan_new (NMPlatformLink *platform_device, NMDevice *parent)
-{
-	NMDevice *device;
-
-	g_return_val_if_fail (platform_device != NULL, NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
-
-	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
-	                                    NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                    NM_DEVICE_VLAN_PARENT, parent,
-	                                    NM_DEVICE_DRIVER, "8021q",
-	                                    NM_DEVICE_TYPE_DESC, "VLAN",
-	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
-	                                    NULL);
-	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
-		g_object_unref (device);
-		device = NULL;
-	}
-
-	return device;
-}
-
-NMDevice *
-nm_device_vlan_new_for_connection (NMConnection *connection, NMDevice *parent)
-{
-	NMDevice *device;
-	NMSettingVlan *s_vlan;
-	char *iface;
-
-	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
-
-	s_vlan = nm_connection_get_setting_vlan (connection);
-	g_return_val_if_fail (s_vlan != NULL, NULL);
-
-	iface = g_strdup (nm_connection_get_virtual_iface_name (connection));
-	if (!iface) {
-		iface = nm_utils_new_vlan_name (nm_device_get_ip_iface (parent),
-		                                nm_setting_vlan_get_id (s_vlan));
-	}
-
-	if (   !nm_platform_vlan_add (iface,
-	                              nm_device_get_ifindex (parent),
-	                              nm_setting_vlan_get_id (s_vlan),
-	                              nm_setting_vlan_get_flags (s_vlan))
-	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_VLAN, "(%s): failed to add VLAN interface for '%s'",
-		             iface, nm_connection_get_id (connection));
-		g_free (iface);
-		return NULL;
-	}
-
-	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
-	                                    NM_DEVICE_IFACE, iface,
-	                                    NM_DEVICE_VLAN_PARENT, parent,
-	                                    NM_DEVICE_DRIVER, "8021q",
-	                                    NM_DEVICE_TYPE_DESC, "VLAN",
-	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
-	                                    NULL);
-	g_free (iface);
-	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
-		g_object_unref (device);
-		device = NULL;
-	}
-
-	return device;
-}
-
 static void
 nm_device_vlan_init (NMDeviceVlan * self)
 {
@@ -512,10 +422,9 @@ nm_device_vlan_init (NMDeviceVlan * self)
 static void
 constructed (GObject *object)
 {
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
-	NMDevice *device = NM_DEVICE (object);
-	const char *iface = nm_device_get_iface (device);
-	int ifindex = nm_device_get_ifindex (device);
+	NMDeviceVlan *self = NM_DEVICE_VLAN (object);
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
 	int parent_ifindex = -1, itype;
 	int vlan_id;
 
@@ -523,20 +432,20 @@ constructed (GObject *object)
 		G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object);
 
 	if (!priv->parent) {
-		nm_log_err (LOGD_VLAN, "(%s): no parent specified.", iface);
+		_LOGE (LOGD_VLAN, "no parent specified.");
 		priv->invalid = TRUE;
 		return;
 	}
 
 	itype = nm_platform_link_get_type (ifindex);
 	if (itype != NM_LINK_TYPE_VLAN) {
-		nm_log_err (LOGD_VLAN, "(%s): failed to get VLAN interface type.", iface);
+		_LOGE (LOGD_VLAN, "failed to get VLAN interface type.");
 		priv->invalid = TRUE;
 		return;
 	}
 
 	if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) {
-		nm_log_warn (LOGD_VLAN, "(%s): failed to get VLAN interface info.", iface);
+		_LOGW (LOGD_VLAN, "failed to get VLAN interface info.");
 		priv->invalid = TRUE;
 		return;
 	}
@@ -544,16 +453,15 @@ constructed (GObject *object)
 	if (   parent_ifindex < 0
 	    || parent_ifindex != nm_device_get_ip_ifindex (priv->parent)
 	    || vlan_id < 0) {
-		nm_log_warn (LOGD_VLAN, "(%s): VLAN parent ifindex (%d) or VLAN ID (%d) invalid.",
-		             iface, parent_ifindex, priv->vlan_id);
+		_LOGW (LOGD_VLAN, "VLAN parent ifindex (%d) or VLAN ID (%d) invalid.",
+		       parent_ifindex, priv->vlan_id);
 		priv->invalid = TRUE;
 		return;
 	}
 
 	priv->vlan_id = vlan_id;
-	nm_log_dbg (LOGD_HW | LOGD_VLAN, "(%s): kernel ifindex %d", iface, ifindex);
-	nm_log_info (LOGD_HW | LOGD_VLAN, "(%s): VLAN ID %d with parent %s",
-	             iface, priv->vlan_id, nm_device_get_iface (priv->parent));
+	_LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s",
+	       priv->vlan_id, nm_device_get_iface (priv->parent));
 }
 
 static void
@@ -563,6 +471,12 @@ get_property (GObject *object, guint prop_id,
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
 
 	switch (prop_id) {
+	case PROP_PARENT:
+		g_value_set_boxed (value, priv->parent ? nm_device_get_path (priv->parent) : "/");
+		break;
+	case PROP_INT_PARENT_DEVICE:
+		g_value_set_object (value, priv->parent);
+		break;
 	case PROP_VLAN_ID:
 		g_value_set_uint (value, priv->vlan_id);
 		break;
@@ -579,7 +493,7 @@ set_property (GObject *object, guint prop_id,
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
 
 	switch (prop_id) {
-	case PROP_PARENT:
+	case PROP_INT_PARENT_DEVICE:
 		nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value));
 		break;
 	case PROP_VLAN_ID:
@@ -609,6 +523,17 @@ dispose (GObject *object)
 }
 
 static void
+finalize (GObject *object)
+{
+	NMDeviceVlan *self = NM_DEVICE_VLAN (object);
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
+
+	g_free (priv->initial_hw_addr);
+
+	G_OBJECT_CLASS (nm_device_vlan_parent_class)->finalize (object);
+}
+
+static void
 nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -623,6 +548,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
+	object_class->finalize = finalize;
 
 	parent_class->update_initial_hw_address = update_initial_hw_address;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
@@ -638,22 +564,137 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_object (NM_DEVICE_VLAN_PARENT,
-		                      "Parent",
-		                      "Parent",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_boxed (NM_DEVICE_VLAN_PARENT, "", "",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 	g_object_class_install_property
 		(object_class, PROP_VLAN_ID,
-		 g_param_spec_uint (NM_DEVICE_VLAN_ID,
-		                    "VLAN ID",
-		                    "VLAN ID",
+		 g_param_spec_uint (NM_DEVICE_VLAN_ID, "", "",
 		                    0, 4095, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+
+	/* Internal properties */
+	g_object_class_install_property
+	    (object_class, PROP_INT_PARENT_DEVICE,
+	     g_param_spec_object (NM_DEVICE_VLAN_INT_PARENT_DEVICE, "", "",
+	                          NM_TYPE_DEVICE,
+	                          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_vlan_object_info);
+}
+
+/*************************************************************/
 
-	dbus_g_error_domain_register (NM_VLAN_ERROR, NULL, NM_TYPE_VLAN_ERROR);
+#define NM_TYPE_VLAN_FACTORY (nm_vlan_factory_get_type ())
+#define NM_VLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VLAN_FACTORY, NMVlanFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	int parent_ifindex = -1;
+	NMDevice *parent, *device;
+
+	if (plink->type != NM_LINK_TYPE_VLAN)
+		return NULL;
+
+	/* Have to find the parent device */
+	if (!nm_platform_vlan_get_info (plink->ifindex, &parent_ifindex, NULL)) {
+		nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", plink->name);
+		return NULL;
+	}
+
+	parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+	if (!parent) {
+		/* If udev signaled the VLAN interface before it signaled
+		 * the VLAN's parent at startup we may not know about the
+		 * parent device yet.  But we'll find it on the second pass
+		 * from nm_manager_start().
+		 */
+		nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name);
+		return NULL;
+	}
+
+	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
+	                                    NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                    NM_DEVICE_VLAN_INT_PARENT_DEVICE, parent,
+	                                    NM_DEVICE_DRIVER, "8021q",
+	                                    NM_DEVICE_TYPE_DESC, "VLAN",
+	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
+	                                    NULL);
+	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
+		g_object_unref (device);
+		device = NULL;
+	}
+
+	/* Set initial parent-dependent unmanaged flag */
+	if (device)
+		nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent));
+
+	return device;
 }
+
+static NMDevice *
+create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                      NMConnection *connection,
+                                      NMDevice *parent,
+                                      GError **error)
+{
+	NMDevice *device;
+	NMSettingVlan *s_vlan;
+	char *iface;
+
+	if (!nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME))
+		return NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
+
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_return_val_if_fail (s_vlan != NULL, NULL);
+
+	iface = g_strdup (nm_connection_get_interface_name (connection));
+	if (!iface) {
+		iface = nm_utils_new_vlan_name (nm_device_get_ip_iface (parent),
+		                                nm_setting_vlan_get_id (s_vlan));
+	}
+
+	if (   !nm_platform_vlan_add (iface,
+	                              nm_device_get_ifindex (parent),
+	                              nm_setting_vlan_get_id (s_vlan),
+	                              nm_setting_vlan_get_flags (s_vlan))
+	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
+		nm_log_warn (LOGD_DEVICE | LOGD_VLAN, "(%s) failed to add VLAN interface for '%s'",
+		             iface, nm_connection_get_id (connection));
+		g_free (iface);
+		return NULL;
+	}
+
+	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
+	                                    NM_DEVICE_IFACE, iface,
+	                                    NM_DEVICE_VLAN_INT_PARENT_DEVICE, parent,
+	                                    NM_DEVICE_DRIVER, "8021q",
+	                                    NM_DEVICE_TYPE_DESC, "VLAN",
+	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
+	                                    NULL);
+	g_free (iface);
+	if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
+		g_object_unref (device);
+		device = NULL;
+	}
+
+	/* Set initial parent-dependent unmanaged flag */
+	if (device)
+		nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent));
+
+	return device;
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL(VLAN, Vlan, vlan, \
+	factory_iface->new_link = new_link; \
+	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	)
+
diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h
index 0f2ac70d..20fc3453 100644
--- a/src/devices/nm-device-vlan.h
+++ b/src/devices/nm-device-vlan.h
@@ -18,8 +18,8 @@
  * Copyright 2012 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_VLAN_H
-#define NM_DEVICE_VLAN_H
+#ifndef __NETWORKMANAGER_DEVICE_VLAN_H__
+#define __NETWORKMANAGER_DEVICE_VLAN_H__
 
 #include <glib-object.h>
 
@@ -40,26 +40,18 @@ typedef enum {
 	NM_VLAN_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
 } NMVlanError;
 
+/* D-Bus exported properties */
 #define NM_DEVICE_VLAN_PARENT     "parent"
 #define NM_DEVICE_VLAN_ID         "vlan-id"
 
-typedef struct {
-	NMDevice parent;
-} NMDeviceVlan;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceVlanClass;
+/* Internal non-exported properties */
+#define NM_DEVICE_VLAN_INT_PARENT_DEVICE     "int-parent-device"
 
+typedef NMDevice NMDeviceVlan;
+typedef NMDeviceClass NMDeviceVlanClass;
 
 GType nm_device_vlan_get_type (void);
 
-NMDevice *nm_device_vlan_new (NMPlatformLink *platform_link,
-                              NMDevice       *parent);
-NMDevice *nm_device_vlan_new_for_connection (NMConnection *connection,
-                                             NMDevice     *parent);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_VLAN_H */
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index 0be57106..ed6b8fa0 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -29,9 +29,13 @@
 #include "nm-manager.h"
 #include "nm-platform.h"
 #include "nm-utils.h"
+#include "nm-device-factory.h"
 
 #include "nm-device-vxlan-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceVxlan);
+
 G_DEFINE_TYPE (NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE_GENERIC)
 
 #define NM_DEVICE_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlanPrivate))
@@ -67,13 +71,13 @@ enum {
 static void
 update_properties (NMDevice *device)
 {
+	NMDeviceVxlan *self = NM_DEVICE_VXLAN (device);
 	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device);
 	GObject *object = G_OBJECT (device);
 	NMPlatformVxlanProperties props;
 
 	if (!nm_platform_vxlan_get_properties (nm_device_get_ifindex (device), &props)) {
-		nm_log_warn (LOGD_HW, "(%s): could not read vxlan properties",
-		             nm_device_get_iface (device));
+		_LOGW (LOGD_HW, "could not read vxlan properties");
 		return;
 	}
 
@@ -130,18 +134,6 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 
 /**************************************************************/
 
-NMDevice *
-nm_device_vxlan_new (NMPlatformLink *platform_device)
-{
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "Vxlan",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
-	                                  NULL);
-}
-
 static void
 nm_device_vxlan_init (NMDeviceVxlan *self)
 {
@@ -240,133 +232,140 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_VXLAN_PARENT,
-		                     "Parent",
-		                     "Parent device",
+		 g_param_spec_boxed (NM_DEVICE_VXLAN_PARENT, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ID,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_ID,
-		                    "Id",
-		                    "Id",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_ID, "", "",
 		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_GROUP,
-		 g_param_spec_string (NM_DEVICE_VXLAN_GROUP,
-		                      "Group",
-		                      "Group",
+		 g_param_spec_string (NM_DEVICE_VXLAN_GROUP, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_DEVICE_VXLAN_LOCAL,
-		                      "Local",
-		                      "Local",
+		 g_param_spec_string (NM_DEVICE_VXLAN_LOCAL, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TOS,
-		 g_param_spec_uchar (NM_DEVICE_VXLAN_TOS,
-		                     "ToS",
-		                     "ToS",
+		 g_param_spec_uchar (NM_DEVICE_VXLAN_TOS, "", "",
 		                     0, 255, 0,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TTL,
-		 g_param_spec_uchar (NM_DEVICE_VXLAN_TTL,
-		                     "TTL",
-		                     "TTL",
+		 g_param_spec_uchar (NM_DEVICE_VXLAN_TTL, "", "",
 		                     0, 255, 0,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_LEARNING,
-		 g_param_spec_boolean (NM_DEVICE_VXLAN_LEARNING,
-		                       "Learning",
-		                       "Learning",
+		 g_param_spec_boolean (NM_DEVICE_VXLAN_LEARNING, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_AGEING,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_AGEING,
-		                    "Ageing",
-		                    "Ageing",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_AGEING, "", "",
 		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_LIMIT,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_LIMIT,
-		                    "Limit",
-		                    "Limit",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_LIMIT, "", "",
 		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DST_PORT,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_DST_PORT,
-		                    "Destination port",
-		                    "Destination port",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_DST_PORT, "", "",
 		                    0, 65535, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SRC_PORT_MIN,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MIN,
-		                    "Source port min",
-		                    "Minimum source port",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MIN, "", "",
 		                    0, 65535, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SRC_PORT_MAX,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MAX,
-		                    "Source port max",
-		                    "Maximum source port",
+		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MAX, "", "",
 		                    0, 65535, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PROXY,
-		 g_param_spec_boolean (NM_DEVICE_VXLAN_PROXY,
-		                       "Proxy",
-		                       "Proxy",
+		 g_param_spec_boolean (NM_DEVICE_VXLAN_PROXY, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_RSC,
-		 g_param_spec_boolean (NM_DEVICE_VXLAN_RSC,
-		                       "RSC",
-		                       "RSC",
+		 g_param_spec_boolean (NM_DEVICE_VXLAN_RSC, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_L2MISS,
-		 g_param_spec_boolean (NM_DEVICE_VXLAN_L2MISS,
-		                       "L2miss",
-		                       "L2miss",
+		 g_param_spec_boolean (NM_DEVICE_VXLAN_L2MISS, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_L3MISS,
-		 g_param_spec_boolean (NM_DEVICE_VXLAN_L3MISS,
-		                       "L3miss",
-		                       "L3miss",
+		 g_param_spec_boolean (NM_DEVICE_VXLAN_L3MISS, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_vxlan_object_info);
 }
+
+/*************************************************************/
+
+#define NM_TYPE_VXLAN_FACTORY (nm_vxlan_factory_get_type ())
+#define NM_VXLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VXLAN_FACTORY, NMVxlanFactory))
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_VXLAN) {
+		return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN,
+		                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+		                                  NM_DEVICE_TYPE_DESC, "Vxlan",
+		                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+		                                  NULL);
+	}
+	return NULL;
+}
+
+DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VXLAN, Vxlan, vxlan, GENERIC, \
+	factory_iface->new_link = new_link; \
+	)
+
diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h
index 14b158de..cc6081e6 100644
--- a/src/devices/nm-device-vxlan.h
+++ b/src/devices/nm-device-vxlan.h
@@ -18,8 +18,8 @@
  * Copyright 2013, 2014 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_VXLAN_H
-#define NM_DEVICE_VXLAN_H
+#ifndef __NETWORKMANAGER_DEVICE_VXLAN_H__
+#define __NETWORKMANAGER_DEVICE_VXLAN_H__
 
 #include <glib-object.h>
 
@@ -51,19 +51,11 @@ G_BEGIN_DECLS
 #define NM_DEVICE_VXLAN_L2MISS       "l2miss"
 #define NM_DEVICE_VXLAN_L3MISS       "l3miss"
 
-typedef struct {
-	NMDeviceGeneric parent;
-} NMDeviceVxlan;
-
-typedef struct {
-	NMDeviceGenericClass parent;
-
-} NMDeviceVxlanClass;
+typedef NMDeviceGeneric NMDeviceVxlan;
+typedef NMDeviceGenericClass NMDeviceVxlanClass;
 
 GType nm_device_vxlan_get_type (void);
 
-NMDevice *nm_device_vxlan_new (NMPlatformLink *platform_device);
-
 G_END_DECLS
 
 #endif	/* NM_DEVICE_VXLAN_H */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 4788a604..a9899535 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -19,7 +19,8 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#include <config.h>
+#include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <dbus/dbus.h>
@@ -35,10 +36,9 @@
 #include <sys/wait.h>
 #include <arpa/inet.h>
 #include <fcntl.h>
-#include <linux/if.h>
 #include <netlink/route/addr.h>
 
-#include "libgsystem.h"
+#include "gsystem-local-alloc.h"
 #include "nm-glib-compat.h"
 #include "nm-device.h"
 #include "nm-device-private.h"
@@ -49,13 +49,13 @@
 #include "nm-lndp-rdisc.h"
 #include "nm-dhcp-manager.h"
 #include "nm-dbus-manager.h"
-#include "nm-utils.h"
 #include "nm-logging.h"
-#include "nm-setting-ip4-config.h"
-#include "nm-setting-ip6-config.h"
-#include "nm-setting-connection.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
 #include "nm-dnsmasq-manager.h"
 #include "nm-dhcp4-config.h"
+#include "nm-dhcp6-config.h"
 #include "nm-rfkill-manager.h"
 #include "nm-firewall-manager.h"
 #include "nm-properties-changed-signal.h"
@@ -63,25 +63,23 @@
 #include "nm-settings-connection.h"
 #include "nm-connection-provider.h"
 #include "nm-posix-signals.h"
-#include "nm-manager-auth.h"
+#include "nm-auth-utils.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dispatcher.h"
-#include "nm-config-device.h"
 #include "nm-config.h"
 #include "nm-dns-manager.h"
+#include "nm-core-internal.h"
+#include "nm-default-route-manager.h"
 
-#include "nm-device-bridge.h"
-#include "nm-device-bond.h"
-#include "nm-device-team.h"
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF (NMDevice);
 
-static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);
+static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context);
+static void impl_device_delete     (NMDevice *self, DBusGMethodInvocation *context);
 
 #include "nm-device-glue.h"
 
-static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface);
-
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT,
-                                  G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init))
+G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT)
 
 #define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
 
@@ -176,6 +174,7 @@ typedef struct {
 
 typedef struct {
 	gboolean in_state_changed;
+	gboolean initialized;
 
 	NMDeviceState state;
 	NMDeviceStateReason state_reason;
@@ -199,7 +198,7 @@ typedef struct {
 	RfKillType    rfkill_type;
 	gboolean      firmware_missing;
 	GHashTable *  available_connections;
-	guint8        hw_addr[NM_UTILS_HWADDR_LEN_MAX];
+	char *        hw_addr;
 	guint         hw_addr_len;
 	char *        physical_port_id;
 
@@ -230,22 +229,32 @@ typedef struct {
 	guint           carrier_wait_id;
 	gboolean        ignore_carrier;
 	guint32         mtu;
+	gboolean        up;   /* IFF_UP */
 
 	/* Generic DHCP stuff */
 	guint32         dhcp_timeout;
-	GByteArray *    dhcp_anycast_address;
+	char *          dhcp_anycast_address;
 
 	/* IP4 configuration info */
 	NMIP4Config *   ip4_config;     /* Combined config from VPN, settings, and device */
 	IpState         ip4_state;
 	NMIP4Config *   dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
 	NMIP4Config *   ext_ip4_config; /* Stuff added outside NM */
+	gboolean        ext_ip4_config_had_any_addresses;
+	NMIP4Config *   wwan_ip4_config; /* WWAN configuration */
+	struct {
+		gboolean v4_has;
+		gboolean v4_is_assumed;
+		NMPlatformIP4Route v4;
+		gboolean v6_has;
+		gboolean v6_is_assumed;
+		NMPlatformIP6Route v6;
+	} default_route;
 
 	/* DHCPv4 tracking */
-	NMDHCPClient *  dhcp4_client;
+	NMDhcpClient *  dhcp4_client;
 	gulong          dhcp4_state_sigid;
-	gulong          dhcp4_timeout_sigid;
-	NMDHCP4Config * dhcp4_config;
+	NMDhcp4Config * dhcp4_config;
 	NMIP4Config *   vpn4_config;  /* routes added by a VPN which uses this device */
 
 	guint           arp_round2_id;
@@ -255,9 +264,8 @@ typedef struct {
 	NMDnsMasqManager *dnsmasq_manager;
 	gulong            dnsmasq_state_id;
 
-	/* Firewall Manager */
-	NMFirewallManager *fw_manager;
-	DBusGProxyCall    *fw_call;
+	/* Firewall */
+	NMFirewallPendingCall fw_call;
 
 	/* avahi-autoipd stuff */
 	GPid    aipd_pid;
@@ -268,10 +276,14 @@ typedef struct {
 	NMIP6Config *  ip6_config;
 	IpState        ip6_state;
 	NMIP6Config *  vpn6_config;  /* routes added by a VPN which uses this device */
+	NMIP6Config *  wwan_ip6_config;
 	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
+	gboolean       ext_ip6_config_had_any_addresses;
+	gboolean       nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
 
 	NMRDisc *      rdisc;
-	gulong         rdisc_config_changed_sigid;
+	gulong         rdisc_changed_id;
+	gulong         rdisc_timeout_id;
 	NMSettingIP6ConfigPrivacy rdisc_use_tempaddr;
 	/* IP6 config from autoconf */
 	NMIP6Config *  ac_ip6_config;
@@ -280,11 +292,10 @@ typedef struct {
 
 	GHashTable *   ip6_saved_properties;
 
-	NMDHCPClient *  dhcp6_client;
+	NMDhcpClient *  dhcp6_client;
 	NMRDiscDHCPLevel dhcp6_mode;
 	gulong          dhcp6_state_sigid;
-	gulong          dhcp6_timeout_sigid;
-	NMDHCP6Config * dhcp6_config;
+	NMDhcp6Config * dhcp6_config;
 	/* IP6 config from DHCP */
 	NMIP6Config *   dhcp6_ip6_config;
 
@@ -303,8 +314,9 @@ typedef struct {
 	NMConnectionProvider *con_provider;
 } NMDevicePrivate;
 
-static gboolean nm_device_set_ip4_config (NMDevice *dev,
+static gboolean nm_device_set_ip4_config (NMDevice *self,
                                           NMIP4Config *config,
+                                          guint32 default_route_metric,
                                           gboolean commit,
                                           NMDeviceStateReason *reason);
 static gboolean ip4_config_merge_and_apply (NMDevice *self,
@@ -312,36 +324,27 @@ static gboolean ip4_config_merge_and_apply (NMDevice *self,
                                             gboolean commit,
                                             NMDeviceStateReason *out_reason);
 
-static gboolean nm_device_set_ip6_config (NMDevice *dev,
+static gboolean nm_device_set_ip6_config (NMDevice *self,
                                           NMIP6Config *config,
                                           gboolean commit,
                                           NMDeviceStateReason *reason);
 
-static gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
-static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success);
-static void nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason);
+static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
+static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success);
+static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason);
 
-static void addrconf6_start_with_link_ready (NMDevice *self);
+static gboolean addrconf6_start_with_link_ready (NMDevice *self);
+static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection);
+static NMActStageReturn linklocal6_start (NMDevice *self);
 
-static gboolean nm_device_get_default_unmanaged (NMDevice *device);
+static gboolean nm_device_get_default_unmanaged (NMDevice *self);
 
-static void _set_state_full (NMDevice *device,
+static void _set_state_full (NMDevice *self,
                              NMDeviceState state,
                              NMDeviceStateReason reason,
                              gboolean quitting);
 
-/***********************************************************/
-
-static GQuark
-nm_device_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-device-error");
-	return quark;
-}
-
-#define NM_DEVICE_ERROR (nm_device_error_quark ())
+static void nm_device_update_hw_address (NMDevice *self);
 
 /***********************************************************/
 
@@ -366,7 +369,7 @@ static const char *state_table[] = {
 static const char *
 queued_state_to_string (NMDeviceState state)
 {
-	if (state >= 0 && state < G_N_ELEMENTS (state_table))
+	if ((gsize) state < G_N_ELEMENTS (state_table))
 		return state_table[state];
 	return state_table[NM_DEVICE_STATE_UNKNOWN];
 }
@@ -442,41 +445,41 @@ static const char *reason_table[] = {
 static const char *
 reason_to_string (NMDeviceStateReason reason)
 {
-	if (reason >= 0 && reason < G_N_ELEMENTS (reason_table))
+	if ((gsize) reason < G_N_ELEMENTS (reason_table))
 		return reason_table[reason];
 	return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN];
 }
 
 /***********************************************************/
 
-static inline gboolean
+gboolean
 nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value)
 {
 	return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value);
 }
 
 static gboolean
-device_has_capability (NMDevice *device, NMDeviceCapabilities caps)
+device_has_capability (NMDevice *self, NMDeviceCapabilities caps)
 {
-	return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps);
+	return !!(NM_DEVICE_GET_PRIVATE (self)->capabilities & caps);
 }
 
 /***********************************************************/
 
 void
-nm_device_dbus_export (NMDevice *device)
+nm_device_dbus_export (NMDevice *self)
 {
 	static guint32 devcount = 0;
 	NMDevicePrivate *priv;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->path == NULL);
 
 	priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
-	nm_log_info (LOGD_DEVICE, "(%s): exported as %s", priv->iface, priv->path);
-	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, device);
+	_LOGI (LOGD_DEVICE, "exported as %s", priv->path);
+	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self);
 }
 
 const char *
@@ -498,7 +501,7 @@ nm_device_get_udi (NMDevice *self)
 const char *
 nm_device_get_iface (NMDevice *self)
 {
-	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), 0);
 
 	return NM_DEVICE_GET_PRIVATE (self)->iface;
 }
@@ -512,9 +515,9 @@ nm_device_get_ifindex (NMDevice *self)
 }
 
 gboolean
-nm_device_is_software (NMDevice *device)
+nm_device_is_software (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	return priv->is_software;
 }
@@ -562,11 +565,14 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	if (priv->ip_iface) {
 		priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
 		if (priv->ip_ifindex > 0) {
+			if (nm_platform_check_support_user_ipv6ll ())
+				nm_platform_link_set_user_ipv6ll_enabled (priv->ip_ifindex, TRUE);
+
 			if (!nm_platform_link_is_up (priv->ip_ifindex))
 				nm_platform_link_set_up (priv->ip_ifindex);
 		} else {
 			/* Device IP interface must always be a kernel network interface */
-			nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
+			_LOGW (LOGD_HW, "failed to look up interface index");
 		}
 	}
 
@@ -579,6 +585,43 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	g_free (old_ip_iface);
 }
 
+static gboolean
+get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
+{
+	NMLinkType link_type;
+	const guint8 *hwaddr = NULL;
+	size_t hwaddr_len = 0;
+	int ifindex;
+	gboolean success;
+
+	/* If we get here, we *must* have a kernel netdev, which implies an ifindex */
+	ifindex = nm_device_get_ip_ifindex (self);
+	g_assert (ifindex);
+
+	link_type = nm_platform_link_get_type (ifindex);
+	g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0);
+
+	hwaddr = nm_platform_link_get_address (ifindex, &hwaddr_len);
+	if (!hwaddr_len)
+		return FALSE;
+
+	success = nm_utils_get_ipv6_interface_identifier (link_type,
+	                                                  hwaddr,
+	                                                  hwaddr_len,
+	                                                  out_iid);
+	if (!success) {
+		_LOGW (LOGD_HW, "failed to generate interface identifier "
+		       "for link type %u hwaddr_len %zu", link_type, hwaddr_len);
+	}
+	return success;
+}
+
+static gboolean
+nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid)
+{
+	return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid);
+}
+
 const char *
 nm_device_get_driver (NMDevice *self)
 {
@@ -606,54 +649,124 @@ nm_device_get_device_type (NMDevice *self)
 
 /**
  * nm_device_get_priority():
- * @dev: the #NMDevice
+ * @self: the #NMDevice
  *
  * Returns: the device's routing priority.  Lower numbers means a "better"
  *  device, eg higher priority.
  */
 int
-nm_device_get_priority (NMDevice *dev)
+nm_device_get_priority (NMDevice *self)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (dev), 100);
+	g_return_val_if_fail (NM_IS_DEVICE (self), 1000);
 
-	/* Device 'priority' is used for two things:
+	/* Device 'priority' is used for the default route-metric and is based on
+	 * the device type. The settings ipv4.route-metric and ipv6.route-metric
+	 * can overwrite this default.
+	 *
+	 * Currently for both IPv4 and IPv6 we use the same default values.
+	 *
+	 * The route-metric is used for the metric of the routes of device.
+	 * This also applies to the default route. Therefore it affects also
+	 * which device is the "best".
 	 *
-	 * a) two devices on the same IP subnet: the "better" (ie, lower number)
-	 *     device is the default outgoing device for that subnet
-	 * b) default route: the "better" device gets the default route.  This can
-	 *     always be modified by setting a connection to never-default=TRUE, in
-	 *     which case that device will never take the default route when
-	 *     it's using that connection.
+	 * For comparison, note that iproute2 by default adds IPv4 routes with
+	 * metric 0, and IPv6 routes with metric 1024. The latter is the IPv6
+	 * "user default" in the kernel (NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6).
+	 * In kernel, the full uint32_t range is available for route
+	 * metrics (except for IPv6, where 0 means 1024).
 	 */
 
-	switch (nm_device_get_device_type (dev)) {
+	switch (nm_device_get_device_type (self)) {
+	/* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */
 	case NM_DEVICE_TYPE_ETHERNET:
-		return 1;
+		return 100;
 	case NM_DEVICE_TYPE_INFINIBAND:
-		return 2;
+		return 150;
 	case NM_DEVICE_TYPE_ADSL:
-		return 3;
+		return 200;
 	case NM_DEVICE_TYPE_WIMAX:
-		return 4;
+		return 250;
 	case NM_DEVICE_TYPE_BOND:
-		return 5;
+		return 300;
 	case NM_DEVICE_TYPE_TEAM:
-		return 6;
+		return 350;
 	case NM_DEVICE_TYPE_VLAN:
-		return 7;
+		return 400;
 	case NM_DEVICE_TYPE_MODEM:
-		return 8;
+		return 450;
 	case NM_DEVICE_TYPE_BT:
-		return 9;
+		return 550;
 	case NM_DEVICE_TYPE_WIFI:
-		return 10;
+		return 600;
 	case NM_DEVICE_TYPE_OLPC_MESH:
-		return 11;
+		return 650;
 	default:
-		return 20;
+		return 950;
 	}
 }
 
+guint32
+nm_device_get_ip4_route_metric (NMDevice *self)
+{
+	NMConnection *connection;
+	gint64 route_metric = -1;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
+
+	connection = nm_device_get_connection (self);
+
+	if (connection)
+		route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip4_config (connection));
+
+	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+}
+
+guint32
+nm_device_get_ip6_route_metric (NMDevice *self)
+{
+	NMConnection *connection;
+	gint64 route_metric = -1;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
+
+	connection = nm_device_get_connection (self);
+
+	if (connection)
+		route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip6_config (connection));
+
+	return route_metric >= 0 ? route_metric : nm_device_get_priority (self);
+}
+
+const NMPlatformIP4Route *
+nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (out_is_assumed)
+		*out_is_assumed = priv->default_route.v4_is_assumed;
+
+	return priv->default_route.v4_has ? &priv->default_route.v4 : NULL;
+}
+
+const NMPlatformIP6Route *
+nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (out_is_assumed)
+		*out_is_assumed = priv->default_route.v6_is_assumed;
+
+	return priv->default_route.v6_has ? &priv->default_route.v6 : NULL;
+}
+
 const char *
 nm_device_get_type_desc (NMDevice *self)
 {
@@ -663,9 +776,9 @@ nm_device_get_type_desc (NMDevice *self)
 }
 
 gboolean
-nm_device_has_carrier (NMDevice *device)
+nm_device_has_carrier (NMDevice *self)
 {
-	return NM_DEVICE_GET_PRIVATE (device)->carrier;
+	return NM_DEVICE_GET_PRIVATE (self)->carrier;
 }
 
 NMActRequest *
@@ -693,22 +806,38 @@ nm_device_get_rfkill_type (NMDevice *self)
 }
 
 static const char *
-nm_device_get_physical_port_id (NMDevice *device)
+nm_device_get_physical_port_id (NMDevice *self)
 {
-	return NM_DEVICE_GET_PRIVATE (device)->physical_port_id;
+	return NM_DEVICE_GET_PRIVATE (self)->physical_port_id;
 }
 
 /***********************************************************/
 
 static gboolean
-nm_device_uses_generated_connection (NMDevice *self)
+nm_device_uses_generated_assumed_connection (NMDevice *self)
 {
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 
-	connection = nm_device_get_connection (self);
-	if (!connection)
-		return FALSE;
-	return nm_settings_connection_get_nm_generated (NM_SETTINGS_CONNECTION (connection));
+	if (   priv->act_request
+	    && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request))) {
+		connection = nm_act_request_get_connection (priv->act_request);
+		if (   connection
+		    && nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection)))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean
+nm_device_uses_assumed_connection (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (   priv->act_request
+	    && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request)))
+		return TRUE;
+	return FALSE;
 }
 
 static SlaveInfo *
@@ -737,28 +866,28 @@ free_slave_info (SlaveInfo *info)
 
 /**
  * nm_device_enslave_slave:
- * @dev: the master device
+ * @self: the master device
  * @slave: the slave device to enslave
  * @connection: (allow-none): the slave device's connection
  *
- * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
+ * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
  * etc) then this function enslaves @slave.
  *
  * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
  *  other devices.
  */
 static gboolean
-nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection)
+nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection)
 {
 	SlaveInfo *info;
 	gboolean success = FALSE;
 	gboolean configure;
 
-	g_return_val_if_fail (dev != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (slave != NULL, FALSE);
-	g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE);
+	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE);
 
-	info = find_slave_info (dev, slave);
+	info = find_slave_info (self, slave);
 	if (!info)
 		return FALSE;
 
@@ -769,7 +898,7 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio
 		if (configure)
 			g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
-		success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, configure);
+		success = NM_DEVICE_GET_CLASS (self)->enslave_slave (self, slave, connection, configure);
 		info->enslaved = success;
 	}
 
@@ -778,18 +907,18 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio
 	/* Ensure the device's hardware address is up-to-date; it often changes
 	 * when slaves change.
 	 */
-	nm_device_update_hw_address (dev);
+	nm_device_update_hw_address (self);
 
 	/* Restart IP configuration if we're waiting for slaves.  Do this
 	 * after updating the hardware address as IP config may need the
 	 * new address.
 	 */
 	if (success) {
-		if (NM_DEVICE_GET_PRIVATE (dev)->ip4_state == IP_WAIT)
-			nm_device_activate_stage3_ip4_start (dev);
+		if (NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT)
+			nm_device_activate_stage3_ip4_start (self);
 
-		if (NM_DEVICE_GET_PRIVATE (dev)->ip6_state == IP_WAIT)
-			nm_device_activate_stage3_ip6_start (dev);
+		if (NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT)
+			nm_device_activate_stage3_ip6_start (self);
 	}
 
 	return success;
@@ -797,42 +926,42 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio
 
 /**
  * nm_device_release_one_slave:
- * @dev: the master device
+ * @self: the master device
  * @slave: the slave device to release
- * @configure: whether @dev needs to actually release @slave
+ * @configure: whether @self needs to actually release @slave
  * @reason: the state change reason for the @slave
  *
- * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
+ * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
  * etc) then this function releases the previously enslaved @slave and/or
- * updates the state of @dev and @slave to reflect its release.
+ * updates the state of @self and @slave to reflect its release.
  *
  * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave
  *  other devices, or if @slave was never enslaved.
  */
 static gboolean
-nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure, NMDeviceStateReason reason)
+nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	SlaveInfo *info;
 	gboolean success = FALSE;
 
 	g_return_val_if_fail (slave != NULL, FALSE);
-	g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->release_slave != NULL, FALSE);
+	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL, FALSE);
 
-	info = find_slave_info (dev, slave);
+	info = find_slave_info (self, slave);
 	if (!info)
 		return FALSE;
 	priv->slaves = g_slist_remove (priv->slaves, info);
 
 	if (info->enslaved) {
-		success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave, configure);
+		success = NM_DEVICE_GET_CLASS (self)->release_slave (self, slave, configure);
 		/* The release_slave() implementation logs success/failure (in the
 		 * correct device-specific log domain), so we don't have to do anything.
 		 */
 	}
 
 	if (!configure) {
-		g_warn_if_fail (reason == NM_DEVICE_STATE_REASON_NONE);
+		g_warn_if_fail (reason == NM_DEVICE_STATE_REASON_NONE || reason == NM_DEVICE_STATE_REASON_REMOVED);
 		reason = NM_DEVICE_STATE_REASON_NONE;
 	} else if (reason == NM_DEVICE_STATE_REASON_NONE) {
 		g_warn_if_reached ();
@@ -845,20 +974,53 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure,
 	/* Ensure the device's hardware address is up-to-date; it often changes
 	 * when slaves change.
 	 */
-	nm_device_update_hw_address (dev);
+	nm_device_update_hw_address (self);
 
 	return success;
 }
 
+static gboolean
+is_software_external (NMDevice *self)
+{
+	return   nm_device_is_software (self)
+	      && !nm_device_get_is_nm_owned (self);
+}
+
+/**
+ * nm_device_finish_init:
+ * @self: the master device
+ *
+ * Whatever needs to be done post-initialization, when the device has a DBus
+ * object name.
+ */
+void
+nm_device_finish_init (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_assert (priv->initialized == FALSE);
+
+	/* Do not manage externally created software devices until they are IFF_UP */
+	if (   is_software_external (self)
+	    && !nm_platform_link_is_up (priv->ifindex)
+	    && priv->ifindex > 0)
+		nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
+
+	if (priv->master)
+		nm_device_enslave_slave (priv->master, self, NULL);
+
+	priv->initialized = TRUE;
+}
+
 static void
-carrier_changed (NMDevice *device, gboolean carrier)
+carrier_changed (NMDevice *self, gboolean carrier)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!nm_device_get_managed (device))
+	if (!nm_device_get_managed (self))
 		return;
 
-	nm_device_recheck_available_connections (device);
+	nm_device_recheck_available_connections (self);
 
 	/* ignore-carrier devices ignore all carrier-down events */
 	if (priv->ignore_carrier && !carrier)
@@ -872,13 +1034,13 @@ carrier_changed (NMDevice *device, gboolean carrier)
 		if (!carrier)
 			return;
 
-		if (nm_device_activate_ip4_state_in_wait (device))
-			nm_device_activate_stage3_ip4_start (device);
-		if (nm_device_activate_ip6_state_in_wait (device))
-			nm_device_activate_stage3_ip6_start (device);
+		if (nm_device_activate_ip4_state_in_wait (self))
+			nm_device_activate_stage3_ip4_start (self);
+		if (nm_device_activate_ip6_state_in_wait (self))
+			nm_device_activate_stage3_ip6_start (self);
 
 		return;
-	} else if (nm_device_get_enslaved (device) && !carrier) {
+	} else if (nm_device_get_enslaved (self) && !carrier) {
 		/* Slaves don't deactivate when they lose carrier; for
 		 * bonds/teams in particular that would be actively
 		 * counterproductive.
@@ -890,7 +1052,7 @@ carrier_changed (NMDevice *device, gboolean carrier)
 		g_warn_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);
 
 		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
-			nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED,
+			nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED,
 			                       NM_DEVICE_STATE_REASON_CARRIER);
 		} else if (priv->state == NM_DEVICE_STATE_DISCONNECTED) {
 			/* If the device is already in DISCONNECTED state without a carrier
@@ -898,14 +1060,16 @@ carrier_changed (NMDevice *device, gboolean carrier)
 			 * when the carrier appears, auto connections are rechecked for
 			 * the device.
 			 */
-			nm_device_emit_recheck_auto_activate (device);
+			nm_device_emit_recheck_auto_activate (self);
 		}
 	} else {
+		g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE);
+
 		if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) {
-			if (nm_device_queued_state_peek (device) >= NM_DEVICE_STATE_DISCONNECTED)
-				nm_device_queued_state_clear (device);
-		} else if (priv->state >= NM_DEVICE_STATE_DISCONNECTED) {
-			nm_device_queue_state (device, NM_DEVICE_STATE_UNAVAILABLE,
+			if (nm_device_queued_state_peek (self) >= NM_DEVICE_STATE_DISCONNECTED)
+				nm_device_queued_state_clear (self);
+		} else {
+			nm_device_queue_state (self, NM_DEVICE_STATE_UNAVAILABLE,
 			                       NM_DEVICE_STATE_REASON_CARRIER);
 		}
 	}
@@ -916,18 +1080,16 @@ carrier_changed (NMDevice *device, gboolean carrier)
 static gboolean
 link_disconnect_action_cb (gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (calling deferred action) (id=%u)",
-	             nm_device_get_iface (device), priv->carrier_defer_id);
+	_LOGD (LOGD_DEVICE, "link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id);
 
 	priv->carrier_defer_id = 0;
 
-	nm_log_info (LOGD_DEVICE, "(%s): link disconnected (calling deferred action)",
-	             nm_device_get_iface (device));
+	_LOGI (LOGD_DEVICE, "link disconnected (calling deferred action)");
 
-	NM_DEVICE_GET_CLASS (device)->carrier_changed (device, FALSE);
+	NM_DEVICE_GET_CLASS (self)->carrier_changed (self, FALSE);
 
 	return FALSE;
 }
@@ -939,67 +1101,64 @@ link_disconnect_action_cancel (NMDevice *self)
 
 	if (priv->carrier_defer_id) {
 		g_source_remove (priv->carrier_defer_id);
-		nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (canceling deferred action) (id=%u)",
-		            nm_device_get_iface (self), priv->carrier_defer_id);
+		_LOGD (LOGD_DEVICE, "link disconnected (canceling deferred action) (id=%u)", priv->carrier_defer_id);
 		priv->carrier_defer_id = 0;
 	}
 }
 
 void
-nm_device_set_carrier (NMDevice *device, gboolean carrier)
+nm_device_set_carrier (NMDevice *self, gboolean carrier)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
-	NMDeviceState state = nm_device_get_state (device);
-	const char *iface = nm_device_get_iface (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
+	NMDeviceState state = nm_device_get_state (self);
 
 	if (priv->carrier == carrier)
 		return;
 
 	priv->carrier = carrier;
-	g_object_notify (G_OBJECT (device), NM_DEVICE_CARRIER);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_CARRIER);
 
 	if (priv->carrier) {
-		nm_log_info (LOGD_DEVICE, "(%s): link connected", iface);
-		link_disconnect_action_cancel (device);
-		klass->carrier_changed (device, TRUE);
+		_LOGI (LOGD_DEVICE, "link connected");
+		link_disconnect_action_cancel (self);
+		klass->carrier_changed (self, TRUE);
 
 		if (priv->carrier_wait_id) {
 			g_source_remove (priv->carrier_wait_id);
 			priv->carrier_wait_id = 0;
-			nm_device_remove_pending_action (device, "carrier wait", TRUE);
+			nm_device_remove_pending_action (self, "carrier wait", TRUE);
 		}
 	} else if (state <= NM_DEVICE_STATE_DISCONNECTED) {
-		nm_log_info (LOGD_DEVICE, "(%s): link disconnected", iface);
-		klass->carrier_changed (device, FALSE);
+		_LOGI (LOGD_DEVICE, "link disconnected");
+		klass->carrier_changed (self, FALSE);
 	} else {
-		nm_log_info (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds)",
-		             iface, LINK_DISCONNECT_DELAY);
+		_LOGI (LOGD_DEVICE, "link disconnected (deferring action for %d seconds)", LINK_DISCONNECT_DELAY);
 		priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY,
-		                                                link_disconnect_action_cb, device);
-		nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds) (id=%u)",
-		             iface, LINK_DISCONNECT_DELAY, priv->carrier_defer_id);
+		                                                link_disconnect_action_cb, self);
+		_LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)",
+		       LINK_DISCONNECT_DELAY, priv->carrier_defer_id);
 	}
 }
 
 static void
-update_for_ip_ifname_change (NMDevice *device)
+update_for_ip_ifname_change (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
 	if (priv->dhcp4_client) {
-		if (!nm_device_dhcp4_renew (device, FALSE)) {
-			nm_device_state_changed (device,
+		if (!nm_device_dhcp4_renew (self, FALSE)) {
+			nm_device_state_changed (self,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
 			return;
 		}
 	}
 	if (priv->dhcp6_client) {
-		if (!nm_device_dhcp6_renew (device, FALSE)) {
-			nm_device_state_changed (device,
+		if (!nm_device_dhcp6_renew (self, FALSE)) {
+			nm_device_state_changed (self,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
 			return;
@@ -1014,98 +1173,152 @@ update_for_ip_ifname_change (NMDevice *device)
 }
 
 static void
-device_link_changed (NMDevice *device, NMPlatformLink *info)
+device_set_master (NMDevice *self, int ifindex)
+{
+	NMDevice *master;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	master = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex);
+	if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) {
+		g_clear_object (&priv->master);
+		priv->master = g_object_ref (master);
+		nm_device_master_add_slave (master, self, FALSE);
+	} else if (master) {
+		_LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring",
+		       nm_device_get_iface (master));
+	} else {
+		_LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
+		       ifindex,
+		       nm_platform_link_get_name (ifindex));
+	}
+}
+
+static void
+device_link_changed (NMDevice *self, NMPlatformLink *info)
 {
-	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean ip_ifname_changed = FALSE;
 
 	if (info->udi && g_strcmp0 (info->udi, priv->udi)) {
 		/* Update UDI to what udev gives us */
 		g_free (priv->udi);
 		priv->udi = g_strdup (info->udi);
-		g_object_notify (G_OBJECT (device), NM_DEVICE_UDI);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
 	}
 
 	/* Update MTU if it has changed. */
 	if (priv->mtu != info->mtu) {
 		priv->mtu = info->mtu;
-		g_object_notify (G_OBJECT (device), NM_DEVICE_MTU);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
 	}
 
 	if (info->name[0] && strcmp (priv->iface, info->name) != 0) {
-		nm_log_info (LOGD_DEVICE, "(%s): interface index %d renamed iface from '%s' to '%s'",
-		             priv->iface, priv->ifindex, priv->iface, info->name);
+		_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
+		       priv->ifindex, priv->iface, info->name);
 		g_free (priv->iface);
 		priv->iface = g_strdup (info->name);
 
 		/* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */
 		ip_ifname_changed = !priv->ip_iface;
 
-		g_object_notify (G_OBJECT (device), NM_DEVICE_IFACE);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE);
 		if (ip_ifname_changed)
-			g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE);
+			g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
 
 		/* Re-match available connections against the new interface name */
-		nm_device_recheck_available_connections (device);
+		nm_device_recheck_available_connections (self);
 
 		/* Let any connections that use the new interface name have a chance
 		 * to auto-activate on the device.
 		 */
-		nm_device_emit_recheck_auto_activate (device);
+		nm_device_emit_recheck_auto_activate (self);
 	}
 
 	/* Update slave status for external changes */
+	if (priv->enslaved && info->master != nm_device_get_ifindex (priv->master))
+		nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_NONE);
 	if (info->master && !priv->enslaved) {
-		NMDevice *master;
-
-		master = nm_manager_get_device_by_ifindex (nm_manager_get (), info->master);
-		if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) {
-			g_clear_object (&priv->master);
-			priv->master = g_object_ref (master);
-			nm_device_master_add_slave (master, device, FALSE);
-			nm_device_enslave_slave (master, device, NULL);
-		} else if (master) {
-			nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring",
-			             nm_device_get_iface (device),
-			             nm_device_get_iface (master));
-		} else {
-			nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device %d %s",
-			             nm_device_get_iface (device),
-			             info->master,
-			             nm_platform_link_get_name (info->master));
-		}
-	} else if (priv->enslaved && !info->master)
-		nm_device_release_one_slave (priv->master, device, FALSE, NM_DEVICE_STATE_REASON_NONE);
+		device_set_master (self, info->master);
+		if (priv->master)
+			nm_device_enslave_slave (priv->master, self, NULL);
+	}
 
 	if (klass->link_changed)
-		klass->link_changed (device, info);
-
+		klass->link_changed (self, info);
 
 	/* Update DHCP, etc, if needed */
 	if (ip_ifname_changed)
-		update_for_ip_ifname_change (device);
+		update_for_ip_ifname_change (self);
+
+	if (priv->up != info->up) {
+		priv->up = info->up;
+
+		/* Manage externally-created software interfaces only when they are IFF_UP */
+		g_assert (priv->ifindex > 0);
+		if (is_software_external (self)) {
+			gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN);
+
+			if (external_down && info->up) {
+				if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) {
+					/* Ensure the assume check is queued before any queued state changes
+					 * from the transition to UNAVAILABLE.
+					 */
+					nm_device_queue_recheck_assume (self);
+
+					/* Resetting the EXTERNAL_DOWN flag may change the device's state
+					 * to UNAVAILABLE.  To ensure that the state change doesn't touch
+					 * the device before assumption occurs, pass
+					 * NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED as the reason.
+					 */
+					nm_device_set_unmanaged (self,
+					                         NM_UNMANAGED_EXTERNAL_DOWN,
+					                         FALSE,
+					                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+				} else {
+					/* Don't trigger a state change; if the device is in a
+					 * state higher than UNAVAILABLE, it is already IFF_UP
+					 * or an explicit activation request was received.
+					 */
+					priv->unmanaged_flags &= ~NM_UNMANAGED_EXTERNAL_DOWN;
+				}
+			} else if (!external_down && !info->up && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) {
+				/* If the device is already disconnected and is set !IFF_UP,
+				 * unmanage it.
+				 */
+				nm_device_set_unmanaged (self,
+				                         NM_UNMANAGED_EXTERNAL_DOWN,
+				                         TRUE,
+				                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			}
+		}
+	}
 }
 
 static void
-device_ip_link_changed (NMDevice *device, NMPlatformLink *info)
+device_ip_link_changed (NMDevice *self, NMPlatformLink *info)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (info->name[0] && g_strcmp0 (priv->ip_iface, info->name)) {
-		nm_log_info (LOGD_DEVICE, "(%s): interface index %d renamed ip_iface (%d) from '%s' to '%s'",
-		             priv->iface, priv->ifindex, nm_device_get_ip_ifindex (device),
-		             priv->ip_iface, info->name);
+		_LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'",
+		       priv->ifindex, nm_device_get_ip_ifindex (self),
+		       priv->ip_iface, info->name);
 		g_free (priv->ip_iface);
 		priv->ip_iface = g_strdup (info->name);
 
-		g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE);
-		update_for_ip_ifname_change (device);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+		update_for_ip_ifname_change (self);
 	}
 }
 
 static void
-link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *device)
+link_changed_cb (NMPlatform *platform,
+                 int ifindex,
+                 NMPlatformLink *info,
+                 NMPlatformSignalChangeType change_type,
+                 NMPlatformReason reason,
+                 NMDevice *self)
 {
 	if (change_type != NM_PLATFORM_SIGNAL_CHANGED)
 		return;
@@ -1116,24 +1329,24 @@ link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlat
 	 * and it results in also setting IFF_LOWER_UP.
 	 */
 
-	if (ifindex == nm_device_get_ifindex (device))
-		device_link_changed (device, info);
-	else if (ifindex == nm_device_get_ip_ifindex (device))
-		device_ip_link_changed (device, info);
+	if (ifindex == nm_device_get_ifindex (self))
+		device_link_changed (self, info);
+	else if (ifindex == nm_device_get_ip_ifindex (self))
+		device_ip_link_changed (self, info);
 }
 
 static void
-link_changed (NMDevice *device, NMPlatformLink *info)
+link_changed (NMDevice *self, NMPlatformLink *info)
 {
 	/* Update carrier from link event if applicable. */
-	if (   device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)
-	    && !device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
-		nm_device_set_carrier (device, info->connected);
+	if (   device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)
+	    && !device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
+		nm_device_set_carrier (self, info->connected);
 }
 
 /**
  * nm_device_notify_component_added():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  * @component: the component being added by a plugin
  *
  * Called by the manager to notify the device that a new component has
@@ -1144,16 +1357,16 @@ link_changed (NMDevice *device, NMPlatformLink *info)
  * claimed.
  */
 gboolean
-nm_device_notify_component_added (NMDevice *device, GObject *component)
+nm_device_notify_component_added (NMDevice *self, GObject *component)
 {
-	if (NM_DEVICE_GET_CLASS (device)->component_added)
-		return NM_DEVICE_GET_CLASS (device)->component_added (device, component);
+	if (NM_DEVICE_GET_CLASS (self)->component_added)
+		return NM_DEVICE_GET_CLASS (self)->component_added (self, component);
 	return FALSE;
 }
 
 /**
  * nm_device_owns_iface():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  * @iface: an interface name
  *
  * Called by the manager to ask if the device or any of its components owns
@@ -1162,17 +1375,25 @@ nm_device_notify_component_added (NMDevice *device, GObject *component)
  * because that ethernet interface is controlled by the WWAN device and cannot
  * be used independently of the WWAN device.
  *
- * Returns: %TRUE if @device or it's components owns the interface name,
+ * Returns: %TRUE if @self or it's components owns the interface name,
  * %FALSE if not
  */
 gboolean
-nm_device_owns_iface (NMDevice *device, const char *iface)
+nm_device_owns_iface (NMDevice *self, const char *iface)
 {
-	if (NM_DEVICE_GET_CLASS (device)->owns_iface)
-		return NM_DEVICE_GET_CLASS (device)->owns_iface (device, iface);
+	if (NM_DEVICE_GET_CLASS (self)->owns_iface)
+		return NM_DEVICE_GET_CLASS (self)->owns_iface (self, iface);
 	return FALSE;
 }
 
+NMConnection *
+nm_device_new_default_connection (NMDevice *self)
+{
+	if (NM_DEVICE_GET_CLASS (self)->new_default_connection)
+		return NM_DEVICE_GET_CLASS (self)->new_default_connection (self);
+	return NULL;
+}
+
 static void
 slave_state_changed (NMDevice *slave,
                      NMDeviceState slave_new_state,
@@ -1183,13 +1404,12 @@ slave_state_changed (NMDevice *slave,
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean release = FALSE;
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d (%s) -> %d (%s)",
-	            nm_device_get_iface (self),
-	            nm_device_get_iface (slave),
-	            slave_old_state,
-	            state_to_string (slave_old_state),
-	            slave_new_state,
-	            state_to_string (slave_new_state));
+	_LOGD (LOGD_DEVICE, "slave %s state change %d (%s) -> %d (%s)",
+	       nm_device_get_iface (slave),
+	       slave_old_state,
+	       state_to_string (slave_old_state),
+	       slave_new_state,
+	       state_to_string (slave_new_state));
 
 	/* Don't try to enslave slaves until the master is ready */
 	if (priv->state < NM_DEVICE_STATE_CONFIG)
@@ -1208,46 +1428,45 @@ slave_state_changed (NMDevice *slave,
 	if (release) {
 		nm_device_release_one_slave (self, slave, TRUE, reason);
 		/* Bridge/bond/team interfaces are left up until manually deactivated */
-		if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) {
-			nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated",
-			            nm_device_get_iface (self));
-		}
+		if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED)
+			_LOGD (LOGD_DEVICE, "last slave removed; remaining activated");
 	}
 }
 
 /**
  * nm_device_master_add_slave:
- * @dev: the master device
+ * @self: the master device
  * @slave: the slave device to enslave
  * @configure: pass %TRUE if the slave should be configured by the master, or
  * %FALSE if it is already configured outside NetworkManager
  *
- * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
+ * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
  * etc) then this function adds @slave to the slave list for later enslavement.
  *
  * Returns: %TRUE on success, %FALSE on failure
  */
 static gboolean
-nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure)
+nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	SlaveInfo *info;
 
-	g_return_val_if_fail (dev != NULL, FALSE);
+	g_return_val_if_fail (self != NULL, FALSE);
 	g_return_val_if_fail (slave != NULL, FALSE);
-	g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE);
+	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE);
 
 	if (configure)
 		g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
-	if (!find_slave_info (dev, slave)) {
+	if (!find_slave_info (self, slave)) {
 		info = g_malloc0 (sizeof (SlaveInfo));
 		info->slave = g_object_ref (slave);
 		info->configure = configure;
 		info->watch_id = g_signal_connect (slave, "state-changed",
-		                                   G_CALLBACK (slave_state_changed), dev);
+		                                   G_CALLBACK (slave_state_changed), self);
 		priv->slaves = g_slist_append (priv->slaves, info);
 	}
+	nm_device_queue_recheck_assume (self);
 
 	return TRUE;
 }
@@ -1255,14 +1474,14 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure)
 
 /**
  * nm_device_master_get_slaves:
- * @dev: the master device
+ * @self: the master device
  *
- * Returns: any slaves of which @device is the master.  Caller owns returned list.
+ * Returns: any slaves of which @self is the master.  Caller owns returned list.
  */
 GSList *
-nm_device_master_get_slaves (NMDevice *dev)
+nm_device_master_get_slaves (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	GSList *slaves = NULL, *iter;
 
 	for (iter = priv->slaves; iter; iter = g_slist_next (iter))
@@ -1273,18 +1492,18 @@ nm_device_master_get_slaves (NMDevice *dev)
 
 /**
  * nm_device_master_get_slave_by_ifindex:
- * @dev: the master device
+ * @self: the master device
  * @ifindex: the slave's interface index
  *
- * Returns: the slave with the given @ifindex of which @device is the master,
- *   or %NULL if no device with @ifindex is a slave of @device.
+ * Returns: the slave with the given @ifindex of which @self is the master,
+ *   or %NULL if no device with @ifindex is a slave of @self.
  */
 NMDevice *
-nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex)
+nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex)
 {
 	GSList *iter;
 
-	for (iter = NM_DEVICE_GET_PRIVATE (dev)->slaves; iter; iter = g_slist_next (iter)) {
+	for (iter = NM_DEVICE_GET_PRIVATE (self)->slaves; iter; iter = g_slist_next (iter)) {
 		SlaveInfo *info = iter->data;
 
 		if (nm_device_get_ip_ifindex (info->slave) == ifindex)
@@ -1295,18 +1514,18 @@ nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex)
 
 /**
  * nm_device_master_check_slave_physical_port:
- * @dev: the master device
+ * @self: the master device
  * @slave: a slave device
  * @log_domain: domain to log a warning in
  *
- * Checks if @dev already has a slave with the same #NMDevice:physical-port-id
+ * Checks if @self already has a slave with the same #NMDevice:physical-port-id
  * as @slave, and logs a warning if so.
  */
 void
-nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
+nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave,
                                             guint64 log_domain)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *slave_physical_port_id, *existing_physical_port_id;
 	SlaveInfo *info;
 	GSList *iter;
@@ -1322,10 +1541,9 @@ nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
 
 		existing_physical_port_id = nm_device_get_physical_port_id (info->slave);
 		if (!g_strcmp0 (slave_physical_port_id, existing_physical_port_id)) {
-			nm_log_warn (log_domain, "(%s): slave %s shares a physical port with existing slave %s",
-			             nm_device_get_ip_iface (dev),
-			             nm_device_get_ip_iface (slave),
-			             nm_device_get_ip_iface (info->slave));
+			_LOGW (log_domain, "slave %s shares a physical port with existing slave %s",
+			       nm_device_get_ip_iface (slave),
+			       nm_device_get_ip_iface (info->slave));
 			/* Since this function will get called for every slave, we only have
 			 * to warn about the first match we find; if there are other matches
 			 * later in the list, we will have already warned about them matching
@@ -1344,7 +1562,7 @@ nm_device_master_release_slaves (NMDevice *self)
 	NMDeviceStateReason reason;
 
 	/* Don't release the slaves if this connection doesn't belong to NM. */
-	if (nm_device_uses_generated_connection (self))
+	if (nm_device_uses_generated_assumed_connection (self))
 		return;
 
 	reason = priv->state_reason;
@@ -1360,19 +1578,19 @@ nm_device_master_release_slaves (NMDevice *self)
 
 /**
  * nm_device_get_master:
- * @dev: the device
+ * @self: the device
  *
- * If @dev has been enslaved by another device, this returns that
+ * If @self has been enslaved by another device, this returns that
  * device. Otherwise it returns %NULL. (In particular, note that if
- * @dev is in the process of activating as a slave, but has not yet
+ * @self is in the process of activating as a slave, but has not yet
  * been enslaved by its master, this will return %NULL.)
  *
- * Returns: (transfer none): @dev's master, or %NULL
+ * Returns: (transfer none): @self's master, or %NULL
  */
 NMDevice *
-nm_device_get_master (NMDevice *dev)
+nm_device_get_master (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->enslaved)
 		return priv->master;
@@ -1382,17 +1600,17 @@ nm_device_get_master (NMDevice *dev)
 
 /**
  * nm_device_slave_notify_enslave:
- * @dev: the slave device
+ * @self: the slave device
  * @success: whether the enslaving operation succeeded
  *
  * Notifies a slave that either it has been enslaved, or else its master tried
  * to enslave it and failed.
  */
 static void
-nm_device_slave_notify_enslave (NMDevice *dev, gboolean success)
+nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
-	NMConnection *connection = nm_device_get_connection (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection = nm_device_get_connection (self);
 	gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG);
 
 	g_assert (priv->master);
@@ -1400,49 +1618,41 @@ nm_device_slave_notify_enslave (NMDevice *dev, gboolean success)
 	if (!priv->enslaved) {
 		if (success) {
 			if (activating) {
-				nm_log_info (LOGD_DEVICE,
-				             "Activation (%s) connection '%s' enslaved, continuing activation",
-				             nm_device_get_iface (dev),
-				             nm_connection_get_id (connection));
-			} else {
-				nm_log_info (LOGD_DEVICE,
-				             "(%s): enslaved to %s",
-				             nm_device_get_iface (dev),
-				             nm_device_get_iface (priv->master));
-			}
+				_LOGI (LOGD_DEVICE, "Activation: connection '%s' enslaved, continuing activation",
+				       nm_connection_get_id (connection));
+			} else
+				_LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master));
 
 			priv->enslaved = TRUE;
-			g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER);
+			g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
 		} else if (activating) {
-			nm_log_warn (LOGD_DEVICE,
-			             "Activation (%s) connection '%s' could not be enslaved",
-			             nm_device_get_iface (dev),
-			             nm_connection_get_id (connection));
+			_LOGW (LOGD_DEVICE, "Activation: connection '%s' could not be enslaved",
+			       nm_connection_get_id (connection));
 		}
 	}
 
 	if (activating) {
 		priv->ip4_state = IP_DONE;
 		priv->ip6_state = IP_DONE;
-		nm_device_queue_state (dev,
+		nm_device_queue_state (self,
 		                       success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED,
 		                       NM_DEVICE_STATE_REASON_NONE);
 	} else
-		nm_device_queue_recheck_assume (dev);
+		nm_device_queue_recheck_assume (self);
 }
 
 /**
  * nm_device_slave_notify_release:
- * @dev: the slave device
+ * @self: the slave device
  * @reason: the reason associated with the state change
  *
  * Notifies a slave that it has been released, and why.
  */
 static void
-nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason)
+nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
-	NMConnection *connection = nm_device_get_connection (dev);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection = nm_device_get_connection (self);
 	NMDeviceState new_state;
 	const char *master_status;
 
@@ -1460,43 +1670,56 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason)
 			master_status = "deactivated";
 		}
 
-		nm_log_dbg (LOGD_DEVICE,
-		            "Activation (%s) connection '%s' master %s",
-		            nm_device_get_iface (dev),
-		            nm_connection_get_id (connection),
-		            master_status);
+		_LOGD (LOGD_DEVICE, "Activation: connection '%s' master %s",
+		       nm_connection_get_id (connection),
+		       master_status);
 
-		nm_device_queue_state (dev, new_state, reason);
-	} else {
-		nm_log_info (LOGD_DEVICE,
-		             "(%s): released from master %s",
-		             nm_device_get_iface (dev),
-		             nm_device_get_iface (priv->master));
-	}
+		nm_device_queue_state (self, new_state, reason);
+	} else if (priv->master)
+		_LOGI (LOGD_DEVICE, "released from master %s", nm_device_get_iface (priv->master));
+	else
+		_LOGD (LOGD_DEVICE, "released from master%s", priv->enslaved ? "" : " (was not enslaved)");
 
 	if (priv->enslaved) {
 		priv->enslaved = FALSE;
-		g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
 	}
 }
 
 /**
  * nm_device_get_enslaved:
- * @device: the #NMDevice
+ * @self: the #NMDevice
  *
  * Returns: %TRUE if the device is enslaved to a master device (eg bridge or
  * bond or team), %FALSE if not
  */
 gboolean
-nm_device_get_enslaved (NMDevice *device)
+nm_device_get_enslaved (NMDevice *self)
 {
-	return NM_DEVICE_GET_PRIVATE (device)->enslaved;
+	return NM_DEVICE_GET_PRIVATE (self)->enslaved;
 }
 
+/**
+ * nm_device_removed:
+ * @self: the #NMDevice
+ *
+ * Called by the manager when the device was removed. Releases the device from
+ * the master in case it's enslaved.
+ */
+void
+nm_device_removed (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->enslaved)
+		nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
+}
+
+
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	return priv->carrier || priv->ignore_carrier;
 }
@@ -1548,12 +1771,44 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled)
 		NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled);
 }
 
+/**
+ * nm_device_get_autoconnect:
+ * @self: the #NMDevice
+ *
+ * Returns: %TRUE if the device allows autoconnect connections, or %FALSE if the
+ * device is explicitly blocking all autoconnect connections.  Does not take
+ * into account transient conditions like companion devices that may wish to
+ * block the device.
+ */
 gboolean
-nm_device_get_autoconnect (NMDevice *device)
+nm_device_get_autoconnect (NMDevice *self)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	return NM_DEVICE_GET_PRIVATE (device)->autoconnect;
+	return NM_DEVICE_GET_PRIVATE (self)->autoconnect;
+}
+
+static void
+nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->autoconnect == autoconnect)
+		return;
+
+	if (autoconnect) {
+		/* Default-unmanaged devices never autoconnect */
+		if (!nm_device_get_default_unmanaged (self)) {
+			priv->autoconnect = TRUE;
+			g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT);
+		}
+	} else {
+		priv->autoconnect = FALSE;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT);
+	}
 }
 
 static gboolean
@@ -1566,6 +1821,14 @@ autoconnect_allowed_accumulator (GSignalInvocationHint *ihint,
 	return TRUE;
 }
 
+/**
+ * nm_device_autoconnect_allowed:
+ * @self: the #NMDevice
+ *
+ * Returns: %TRUE if the device can be auto-connected immediately, taking
+ * transient conditions into account (like companion devices that may wish to
+ * block autoconnect for a time).
+ */
 gboolean
 nm_device_autoconnect_allowed (NMDevice *self)
 {
@@ -1573,6 +1836,16 @@ nm_device_autoconnect_allowed (NMDevice *self)
 	GValue instance = G_VALUE_INIT;
 	GValue retval = G_VALUE_INIT;
 
+	if (priv->state < NM_DEVICE_STATE_DISCONNECTED || !priv->autoconnect)
+		return FALSE;
+
+	/* The 'autoconnect-allowed' signal is emitted on a device to allow
+	 * other listeners to block autoconnect on the device if they wish.
+	 * This is mainly used by the OLPC Mesh devices to block autoconnect
+	 * on their companion WiFi device as they share radio resources and
+	 * cannot be connected at the same time.
+	 */
+
 	g_value_init (&instance, G_TYPE_OBJECT);
 	g_value_set_object (&instance, self);
 
@@ -1590,7 +1863,7 @@ nm_device_autoconnect_allowed (NMDevice *self)
 }
 
 static gboolean
-can_auto_connect (NMDevice *device,
+can_auto_connect (NMDevice *self,
                   NMConnection *connection,
                   char **specific_object)
 {
@@ -1600,13 +1873,44 @@ can_auto_connect (NMDevice *device,
 	if (!nm_setting_connection_get_autoconnect (s_con))
 		return FALSE;
 
-	return nm_device_connection_is_available (device, connection, FALSE);
+	return nm_device_connection_is_available (self, connection, FALSE);
+}
+
+/**
+ * nm_device_can_auto_connect:
+ * @self: an #NMDevice
+ * @connection: a #NMConnection
+ * @specific_object: (out) (transfer full): on output, the path of an
+ *   object associated with the returned connection, to be passed to
+ *   nm_manager_activate_connection(), or %NULL.
+ *
+ * Checks if @connection can be auto-activated on @self right now.
+ * This requires, at a minimum, that the connection be compatible with
+ * @self, and that it have the #NMSettingConnection:autoconnect property
+ * set, and that the device allow auto connections. Some devices impose
+ * additional requirements. (Eg, a Wi-Fi connection can only be activated
+ * if its SSID was seen in the last scan.)
+ *
+ * Returns: %TRUE, if the @connection can be auto-activated.
+ **/
+gboolean
+nm_device_can_auto_connect (NMDevice *self,
+                            NMConnection *connection,
+                            char **specific_object)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (specific_object && !*specific_object, FALSE);
+
+	if (nm_device_autoconnect_allowed (self))
+		return NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, connection, specific_object);
+	return FALSE;
 }
 
 static gboolean
-device_has_config (NMDevice *device)
+device_has_config (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Check for IP configuration. */
 	if (priv->ip4_config && nm_ip4_config_get_num_addresses (priv->ip4_config))
@@ -1615,7 +1919,7 @@ device_has_config (NMDevice *device)
 		return TRUE;
 
 	/* The existence of a software device is good enough. */
-	if (nm_device_is_software (device))
+	if (nm_device_is_software (self))
 		return TRUE;
 
 	/* Slaves are also configured by definition */
@@ -1625,20 +1929,64 @@ device_has_config (NMDevice *device)
 	return FALSE;
 }
 
+/**
+ * nm_device_master_update_slave_connection:
+ * @self: the master #NMDevice
+ * @slave: the slave #NMDevice
+ * @connection: the #NMConnection to update with the slave settings
+ * @GError: (out): error description
+ *
+ * Reads the slave configuration for @slave and updates @connection with those
+ * properties. This invokes a virtual function on the master device @self.
+ *
+ * Returns: %TRUE if the configuration was read and @connection updated,
+ * %FALSE on failure.
+ */
+gboolean
+nm_device_master_update_slave_connection (NMDevice *self,
+                                          NMDevice *slave,
+                                          NMConnection *connection,
+                                          GError **error)
+{
+	NMDeviceClass *klass;
+	gboolean success;
+
+	g_return_val_if_fail (self, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (slave, FALSE);
+	g_return_val_if_fail (connection, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+	g_return_val_if_fail (nm_connection_get_setting_connection (connection), FALSE);
+
+	g_return_val_if_fail (nm_device_get_iface (self), FALSE);
+
+	klass = NM_DEVICE_GET_CLASS (self);
+	if (klass->master_update_slave_connection) {
+		success = klass->master_update_slave_connection (self, slave, connection, error);
+
+		g_return_val_if_fail (!error || (success && !*error) || *error, success);
+		return success;
+	}
+
+	g_set_error (error,
+	             NM_DEVICE_ERROR,
+	             NM_DEVICE_ERROR_FAILED,
+	             "master device '%s' cannot update a slave connection for slave device '%s' (master type not supported?)",
+	             nm_device_get_iface (self), nm_device_get_iface (slave));
+	return FALSE;
+}
+
 NMConnection *
-nm_device_generate_connection (NMDevice *device)
+nm_device_generate_connection (NMDevice *self, NMDevice *master)
 {
-	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	const char *ifname = nm_device_get_iface (device);
-	int ifindex = nm_device_get_ifindex (device);
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *ifname = nm_device_get_iface (self);
 	NMConnection *connection;
 	NMSetting *s_con;
 	NMSetting *s_ip4;
 	NMSetting *s_ip6;
 	gs_free char *uuid = NULL;
-	gs_free char *name = NULL;
-	int master_ifindex = 0;
 	const char *ip4_method, *ip6_method;
 	GError *error = NULL;
 
@@ -1647,32 +1995,18 @@ nm_device_generate_connection (NMDevice *device)
 		return NULL;
 
 	/* Return NULL if device is unconfigured. */
-	if (!device_has_config (device)) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): device has no existing configuration", ifname);
+	if (!device_has_config (self)) {
+		_LOGD (LOGD_DEVICE, "device has no existing configuration");
 		return NULL;
 	}
 
-	if (ifindex)
-		master_ifindex = nm_platform_link_get_master (ifindex);
-	if (master_ifindex) {
-		NMDevice *master;
-
-		master = nm_manager_get_device_by_ifindex (nm_manager_get (), master_ifindex);
-		if (!master || !nm_device_get_act_request (master)) {
-			nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s)",
-			            ifname, nm_platform_link_get_name (master_ifindex));
-			return NULL;
-		}
-	}
-
-	connection = nm_connection_new ();
+	connection = nm_simple_connection_new ();
 	s_con = nm_setting_connection_new ();
 	uuid = nm_utils_uuid_generate ();
-	name = g_strdup_printf ("%s", ifname);
 
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NM_SETTING_CONNECTION_ID, name,
+	              NM_SETTING_CONNECTION_ID, ifname,
 	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
 	              NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
@@ -1682,36 +2016,18 @@ nm_device_generate_connection (NMDevice *device)
 	nm_connection_add_setting (connection, s_con);
 
 	/* If the device is a slave, update various slave settings */
-	if (master_ifindex) {
-		const char *master_iface = nm_platform_link_get_name (master_ifindex);
-		const char *slave_type = NULL;
-		gboolean success = FALSE;
-
-		switch (nm_platform_link_get_type (master_ifindex)) {
-		case NM_LINK_TYPE_BRIDGE:
-			slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
-			success = nm_bridge_update_slave_connection (device, connection);
-			break;
-		case NM_LINK_TYPE_BOND:
-			slave_type = NM_SETTING_BOND_SETTING_NAME;
-			success = TRUE;
-			break;
-		case NM_LINK_TYPE_TEAM:
-			slave_type = NM_SETTING_TEAM_SETTING_NAME;
-			success = nm_team_update_slave_connection (device, connection);
-			break;
-		default:
-			g_warn_if_reached ();
-			break;
+	if (master) {
+		if (!nm_device_master_update_slave_connection (master,
+		                                               self,
+		                                               connection,
+		                                               &error))
+		{
+			_LOGE (LOGD_DEVICE, "master device '%s' failed to update slave connection: %s",
+			       nm_device_get_iface (master), error ? error->message : "(unknown error)");
+			g_error_free (error);
+			g_object_unref (connection);
+			return NULL;
 		}
-
-		if (!success)
-			nm_log_err (LOGD_DEVICE, "(%s): failed to read slave configuration", ifname);
-
-		g_object_set (s_con,
-		              NM_SETTING_CONNECTION_MASTER, master_iface,
-		              NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
-		              NULL);
 	} else {
 		/* Only regular and master devices get IP configuration; slaves do not */
 		s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
@@ -1721,12 +2037,11 @@ nm_device_generate_connection (NMDevice *device)
 		nm_connection_add_setting (connection, s_ip6);
 	}
 
-	klass->update_connection (device, connection);
+	klass->update_connection (self, connection);
 
 	/* Check the connection in case of update_connection() bug. */
 	if (!nm_connection_verify (connection, &error)) {
-		nm_log_err (LOGD_DEVICE, "(%s): Generated connection does not verify: %s",
-		            nm_device_get_iface (device), error->message);
+		_LOGE (LOGD_DEVICE, "Generated connection does not verify: %s", error->message);
 		g_clear_error (&error);
 		g_object_unref (connection);
 		return NULL;
@@ -1739,8 +2054,9 @@ nm_device_generate_connection (NMDevice *device)
 	ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 	if (   g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
 	    && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0
-	    && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP and not slave)", ifname);
+	    && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
+	    && !priv->slaves) {
+		_LOGD (LOGD_DEVICE, "ignoring generated connection (no IP and not in master-slave relationship)");
 		g_object_unref (connection);
 		connection = NULL;
 	}
@@ -1748,46 +2064,6 @@ nm_device_generate_connection (NMDevice *device)
 	return connection;
 }
 
-/**
- * nm_device_get_best_auto_connection:
- * @dev: an #NMDevice
- * @connections: (element-type #NMConnection): a list of connections
- * @specific_object: (out) (transfer full): on output, the path of an
- *   object associated with the returned connection, to be passed to
- *   nm_manager_activate_connection(), or %NULL.
- *
- * Looks through @connections to see if there is a connection that can
- * be auto-activated on @dev right now. This requires, at a minimum,
- * that the connection be compatible with @dev, and that it have the
- * #NMSettingConnection:autoconnect property set. Some devices impose
- * additional requirements. (Eg, a Wi-Fi connection can only be
- * activated if its SSID was seen in the last scan.)
- *
- * Returns: an auto-activatable #NMConnection, or %NULL if none are
- * available.
- */
-
-NMConnection *
-nm_device_get_best_auto_connection (NMDevice *dev,
-                                    GSList *connections,
-                                    char **specific_object)
-{
-	GSList *iter;
-
-	g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
-	g_return_val_if_fail (specific_object != NULL, NULL);
-	g_return_val_if_fail (*specific_object == NULL, NULL);
-
-	for (iter = connections; iter; iter = iter->next) {
-		NMConnection *connection = NM_CONNECTION (iter->data);
-
-		if (NM_DEVICE_GET_CLASS (dev)->can_auto_connect (dev, connection, specific_object))
-			return connection;
-	}
-
-	return NULL;
-}
-
 gboolean
 nm_device_complete_connection (NMDevice *self,
                                NMConnection *connection,
@@ -1801,7 +2077,7 @@ nm_device_complete_connection (NMDevice *self,
 	g_return_val_if_fail (connection != NULL, FALSE);
 
 	if (!NM_DEVICE_GET_CLASS (self)->complete_connection) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CONNECTION_INVALID,
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		             "Device class %s had no complete_connection method",
 		             G_OBJECT_TYPE_NAME (self));
 		return FALSE;
@@ -1819,7 +2095,7 @@ nm_device_complete_connection (NMDevice *self,
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *self, NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 	const char *config_iface, *device_iface;
@@ -1828,7 +2104,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	g_assert (s_con);
 
 	config_iface = nm_setting_connection_get_interface_name (s_con);
-	device_iface = nm_device_get_iface (device);
+	device_iface = nm_device_get_iface (self);
 	if (config_iface && strcmp (config_iface, device_iface) != 0)
 		return FALSE;
 
@@ -1837,43 +2113,31 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 
 /**
  * nm_device_check_connection_compatible:
- * @device: an #NMDevice
+ * @self: an #NMDevice
  * @connection: an #NMConnection
  *
- * Checks if @connection could potentially be activated on @device.
- * This means only that @device has the proper capabilities, and that
+ * Checks if @connection could potentially be activated on @self.
+ * This means only that @self has the proper capabilities, and that
  * @connection is not locked to some other device. It does not
- * necessarily mean that @connection could be activated on @device
+ * necessarily mean that @connection could be activated on @self
  * right now. (Eg, it might refer to a Wi-Fi network that is not
  * currently available.)
  *
  * Returns: #TRUE if @connection could potentially be activated on
- *   @device.
+ *   @self.
  */
 gboolean
-nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection)
+nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
 
-	return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection);
-}
-
-static gboolean
-string_in_list (const char *str, const char **array, gsize array_len)
-{
-	gsize i;
-
-	for (i = 0; i < array_len; i++) {
-		if (strcmp (str, array[i]) == 0)
-			return TRUE;
-	}
-	return FALSE;
+	return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection);
 }
 
 /**
  * nm_device_can_assume_connections:
- * @device: #NMDevice instance
+ * @self: #NMDevice instance
  *
  * This is a convenience function to determine whether connection assumption
  * is available for this device.
@@ -1881,14 +2145,14 @@ string_in_list (const char *str, const char **array, gsize array_len)
  * Returns: %TRUE if the device is capable of assuming connections, %FALSE if not
  */
 static gboolean
-nm_device_can_assume_connections (NMDevice *device)
+nm_device_can_assume_connections (NMDevice *self)
 {
-	return !!NM_DEVICE_GET_CLASS (device)->update_connection;
+	return !!NM_DEVICE_GET_CLASS (self)->update_connection;
 }
 
 /**
  * nm_device_can_assume_active_connection:
- * @device: #NMDevice instance
+ * @self: #NMDevice instance
  *
  * This is a convenience function to determine whether the device's active
  * connection can be assumed if NetworkManager restarts.  This method returns
@@ -1900,9 +2164,9 @@ nm_device_can_assume_connections (NMDevice *device)
  * assumed.
  */
 gboolean
-nm_device_can_assume_active_connection (NMDevice *device)
+nm_device_can_assume_active_connection (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	const char *method;
 	const char *assumable_ip6_methods[] = {
@@ -1911,17 +2175,19 @@ nm_device_can_assume_active_connection (NMDevice *device)
 		NM_SETTING_IP6_CONFIG_METHOD_DHCP,
 		NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
 		NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
+		NULL
 	};
 	const char *assumable_ip4_methods[] = {
 		NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
 		NM_SETTING_IP6_CONFIG_METHOD_AUTO,
 		NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
+		NULL
 	};
 
-	if (!nm_device_can_assume_connections (device))
+	if (!nm_device_can_assume_connections (self))
 		return FALSE;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_connection (self);
 	if (!connection)
 		return FALSE;
 
@@ -1934,11 +2200,11 @@ nm_device_can_assume_active_connection (NMDevice *device)
 		return FALSE;
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	if (!string_in_list (method, assumable_ip6_methods, G_N_ELEMENTS (assumable_ip6_methods)))
+	if (!_nm_utils_string_in_list (method, assumable_ip6_methods))
 		return FALSE;
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
-	if (!string_in_list (method, assumable_ip4_methods, G_N_ELEMENTS (assumable_ip4_methods)))
+	if (!_nm_utils_string_in_list (method, assumable_ip4_methods))
 		return FALSE;
 
 	return TRUE;
@@ -1950,8 +2216,10 @@ nm_device_emit_recheck_assume (gpointer self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->recheck_assume_id = 0;
-	if (!nm_device_get_act_request (self) && (priv->ip4_config || priv->ip6_config))
+	if (!nm_device_get_act_request (self)) {
+		_LOGD (LOGD_DEVICE, "emit RECHECK_ASSUME signal");
 		g_signal_emit (self, signals[RECHECK_ASSUME], 0);
+	}
 	return G_SOURCE_REMOVE;
 }
 
@@ -2022,9 +2290,8 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
 		act_source_func = &priv->act_source_func;
 	}
 
-	if (*act_source_id) {
-		nm_log_err (LOGD_DEVICE, "activation stage already scheduled");
-	}
+	if (*act_source_id)
+		_LOGE (LOGD_DEVICE, "activation stage already scheduled");
 
 	/* Don't bother rescheduling the same function that's about to
 	 * run anyway.  Fixes issues with crappy wireless drivers sending
@@ -2038,12 +2305,11 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
 	}
 }
 
-gboolean
-nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6)
+static gboolean
+get_ip_config_may_fail (NMDevice *self, int family)
 {
 	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
-	NMSettingIP6Config *s_ip6;
+	NMSettingIPConfig *s_ip = NULL;
 
 	g_return_val_if_fail (self != NULL, TRUE);
 
@@ -2051,17 +2317,18 @@ nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6)
 	g_assert (connection);
 
 	/* Fail the connection if the failed IP method is required to complete */
-	if (ip6) {
-		s_ip6 = nm_connection_get_setting_ip6_config (connection);
-		if (!nm_setting_ip6_config_get_may_fail (s_ip6))
-			return TRUE;
-	} else {
-		s_ip4 = nm_connection_get_setting_ip4_config (connection);
-		if (!nm_setting_ip4_config_get_may_fail (s_ip4))
-			return TRUE;
+	switch (family) {
+	case AF_INET:
+		s_ip = nm_connection_get_setting_ip4_config (connection);
+		break;
+	case AF_INET6:
+		s_ip = nm_connection_get_setting_ip6_config (connection);
+		break;
+	default:
+		g_assert_not_reached ();
 	}
 
-	return FALSE;
+	return nm_setting_ip_config_get_may_fail (s_ip);
 }
 
 static void
@@ -2083,9 +2350,8 @@ master_ready_cb (NMActiveConnection *active,
 	                            self,
 	                            nm_active_connection_get_assumed (active) ? FALSE : TRUE);
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): master connection ready; master device %s",
-	            nm_device_get_iface (self),
-	            nm_device_get_iface (priv->master));
+	_LOGD (LOGD_DEVICE, "master connection ready; master device %s",
+	       nm_device_get_iface (priv->master));
 
 	if (priv->master_ready_id) {
 		g_signal_handler_disconnect (active, priv->master_ready_id);
@@ -2112,7 +2378,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
@@ -2125,8 +2390,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 	/* Notify the new ActiveConnection along with the state change */
 	g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
@@ -2146,8 +2410,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 		if (nm_active_connection_get_master_ready (active))
 			master_ready_cb (active, NULL, self);
 		else {
-			nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready",
-			            nm_device_get_iface (self));
+			_LOGD (LOGD_DEVICE, "waiting for master connection to become ready");
 
 			/* Attach a signal handler and wait for the master connection to begin activating */
 			g_assert (priv->master_ready_id == 0);
@@ -2161,7 +2424,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 		nm_device_activate_schedule_stage2_device_config (self);
 
 out:
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete.");
 	return FALSE;
 }
 
@@ -2184,12 +2447,11 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0);
 
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled...");
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *self, NMDeviceStateReason *reason)
 {
 	/* Nothing to do */
 	return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -2207,7 +2469,6 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	NMActStageReturn ret;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	gboolean no_firmware = FALSE;
@@ -2217,8 +2478,7 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, 0);
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
@@ -2248,17 +2508,17 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 
 		if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
 			nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave));
-		else if (   nm_device_uses_generated_connection (self)
+		else if (   nm_device_uses_generated_assumed_connection (self)
 		         && slave_state <= NM_DEVICE_STATE_DISCONNECTED)
 			nm_device_queue_recheck_assume (info->slave);
 	}
 
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful.");
 
 	nm_device_activate_schedule_stage3_ip_config_start (self);
 
 out:
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete.");
 	return FALSE;
 }
 
@@ -2281,8 +2541,7 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_stage2_device_config, 0);
 
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) scheduled...",
-	         nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled...");
 }
 
 /*********************************************/
@@ -2310,13 +2569,7 @@ aipd_cleanup (NMDevice *self)
 	}
 
 	if (priv->aipd_pid > 0) {
-		kill (priv->aipd_pid, SIGKILL);
-
-		/* ensure the child is reaped */
-		nm_log_dbg (LOGD_AUTOIP4, "waiting for avahi-autoipd pid %d to exit", priv->aipd_pid);
-		waitpid (priv->aipd_pid, NULL, 0);
-		nm_log_dbg (LOGD_AUTOIP4, "avahi-autoip pid %d cleaned up", priv->aipd_pid);
-
+		nm_utils_kill_child_sync (priv->aipd_pid, SIGKILL, LOGD_AUTOIP4, "avahi-autoipd", NULL, 0, 0);
 		priv->aipd_pid = -1;
 	}
 
@@ -2336,15 +2589,15 @@ aipd_get_ip4_config (NMDevice *self, guint32 lla)
 	memset (&address, 0, sizeof (address));
 	address.address = lla;
 	address.plen = 16;
-	address.source = NM_PLATFORM_SOURCE_IP4LL;
+	address.source = NM_IP_CONFIG_SOURCE_IP4LL;
 	nm_ip4_config_add_address (config, &address);
 
 	/* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */
 	memset (&route, 0, sizeof (route));
 	route.network = htonl (0xE0000000L);
 	route.plen = 4;
-	route.source = NM_PLATFORM_SOURCE_IP4LL;
-	route.metric = nm_device_get_priority (self);
+	route.source = NM_IP_CONFIG_SOURCE_IP4LL;
+	route.metric = nm_device_get_ip4_route_metric (self);
 	nm_ip4_config_add_route (config, &route);
 
 	return config;
@@ -2360,7 +2613,7 @@ nm_device_handle_autoip4_event (NMDevice *self,
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection = NULL;
-	const char *iface, *method;
+	const char *method;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	g_return_if_fail (event != NULL);
@@ -2376,29 +2629,25 @@ nm_device_handle_autoip4_event (NMDevice *self,
 	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0)
 		return;
 
-	iface = nm_device_get_iface (self);
-
 	if (strcmp (event, "BIND") == 0) {
 		guint32 lla;
 		NMIP4Config *config;
 
 		if (inet_pton (AF_INET, address, &lla) <= 0) {
-			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.",
-			            iface, address);
+			_LOGE (LOGD_AUTOIP4, "invalid address %s received from avahi-autoipd.", address);
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 			return;
 		}
 
 		if ((lla & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
-			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).",
-			            iface, address);
+			_LOGE (LOGD_AUTOIP4, "invalid address %s received from avahi-autoipd (not link-local).", address);
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 			return;
 		}
 
 		config = aipd_get_ip4_config (self, lla);
 		if (config == NULL) {
-			nm_log_err (LOGD_AUTOIP4, "failed to get autoip config");
+			_LOGE (LOGD_AUTOIP4, "failed to get autoip config");
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 			return;
 		}
@@ -2408,8 +2657,7 @@ nm_device_handle_autoip4_event (NMDevice *self,
 			nm_device_activate_schedule_ip4_config_result (self, config);
 		} else if (priv->ip4_state == IP_DONE) {
 			if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) {
-				nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config for autoip change.",
-							nm_device_get_iface (self));
+				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
 				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 			}
 		} else
@@ -2417,8 +2665,7 @@ nm_device_handle_autoip4_event (NMDevice *self,
 
 		g_object_unref (config);
 	} else {
-		nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.",
-		            iface, address, event);
+		_LOGW (LOGD_AUTOIP4, "autoip address %s no longer valid because '%s'.", address, event);
 
 		/* The address is gone; terminate the connection or fail activation */
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
@@ -2431,26 +2678,19 @@ aipd_watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceState state;
-	const char *iface;
 
 	if (!priv->aipd_watch)
 		return;
 	priv->aipd_watch = 0;
 
-	iface = nm_device_get_iface (self);
-
-	if (WIFEXITED (status)) {
-		nm_log_dbg (LOGD_AUTOIP4, "(%s): avahi-autoipd exited with error code %d",
-		            iface, WEXITSTATUS (status));
-	} else if (WIFSTOPPED (status)) {
-		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd stopped unexpectedly with signal %d",
-		            iface, WSTOPSIG (status));
-	} else if (WIFSIGNALED (status)) {
-		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died with signal %d",
-		             iface, WTERMSIG (status));
-	} else {
-		nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died from an unknown cause", iface);
-	}
+	if (WIFEXITED (status))
+		_LOGD (LOGD_AUTOIP4, "avahi-autoipd exited with error code %d", WEXITSTATUS (status));
+	else if (WIFSTOPPED (status))
+		_LOGW (LOGD_AUTOIP4, "avahi-autoipd stopped unexpectedly with signal %d", WSTOPSIG (status));
+	else if (WIFSIGNALED (status))
+		_LOGW (LOGD_AUTOIP4, "avahi-autoipd died with signal %d", WTERMSIG (status));
+	else
+		_LOGW (LOGD_AUTOIP4, "avahi-autoipd died from an unknown cause");
 
 	aipd_cleanup (self);
 
@@ -2466,7 +2706,7 @@ aipd_timeout_cb (gpointer user_data)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->aipd_timeout) {
-		nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self));
+		_LOGI (LOGD_AUTOIP4, "avahi-autoipd timed out.");
 		priv->aipd_timeout = 0;
 		aipd_cleanup (self);
 
@@ -2501,63 +2741,51 @@ static NMActStageReturn
 aipd_start (NMDevice *self, NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface = nm_device_get_iface (self);
-	char *argv[6], *cmdline;
-	const char **aipd_binary = NULL;
-	static const char *aipd_paths[] = {
-		"/usr/sbin/avahi-autoipd",
-		"/usr/local/sbin/avahi-autoipd",
-		NULL
-	};
+	const char *argv[6];
+	char *cmdline;
+	const char *aipd_binary;
 	int i = 0;
 	GError *error = NULL;
 
 	aipd_cleanup (self);
 
 	/* Find avahi-autoipd */
-	aipd_binary = aipd_paths;
-	while (*aipd_binary != NULL) {
-		if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS))
-			break;
-		aipd_binary++;
-	}
-
-	if (!*aipd_binary) {
-		nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4,
-		             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
-		             " to start avahi-autoipd: not found", iface);
+	aipd_binary = nm_utils_find_helper ("avahi-autoipd", NULL, NULL);
+	if (!aipd_binary) {
+		_LOGW (LOGD_DEVICE | LOGD_AUTOIP4,
+		       "Activation: Stage 3 of 5 (IP Configure Start) failed"
+		       " to start avahi-autoipd: not found");
 		*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	argv[i++] = (char *) (*aipd_binary);
+	argv[i++] = aipd_binary;
 	argv[i++] = "--script";
-	argv[i++] = (char *) nm_device_autoipd_helper_path;
+	argv[i++] = nm_device_autoipd_helper_path;
 
 	if (nm_logging_enabled (LOGL_DEBUG, LOGD_AUTOIP4))
 		argv[i++] = "--debug";
-	argv[i++] = (char *) nm_device_get_ip_iface (self);
+	argv[i++] = nm_device_get_ip_iface (self);
 	argv[i++] = NULL;
 
-	cmdline = g_strjoinv (" ", argv);
-	nm_log_dbg (LOGD_AUTOIP4, "running: %s", cmdline);
+	cmdline = g_strjoinv (" ", (char **) argv);
+	_LOGD (LOGD_AUTOIP4, "running: %s", cmdline);
 	g_free (cmdline);
 
-	if (!g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
 	                    &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) {
-		nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4,
-		             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
-		             " to start avahi-autoipd: %s",
-		             iface,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_DEVICE | LOGD_AUTOIP4,
+		       "Activation: Stage 3 of 5 (IP Configure Start) failed"
+		       " to start avahi-autoipd: %s",
+		       error && error->message ? error->message : "(unknown)");
 		g_clear_error (&error);
 		aipd_cleanup (self);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
-	             "Activation (%s) Stage 3 of 5 (IP Configure Start) started"
-	             " avahi-autoipd...", iface);
+	_LOGI (LOGD_DEVICE | LOGD_AUTOIP4,
+	       "Activation: Stage 3 of 5 (IP Configure Start) started"
+	       " avahi-autoipd...");
 
 	/* Monitor the child process so we know when it dies */
 	priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self);
@@ -2569,6 +2797,52 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason)
 }
 
 /*********************************************/
+
+static gboolean
+_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route)
+{
+	gboolean success = FALSE;
+	int ifindex = nm_device_get_ip_ifindex (self);
+	GArray *routes;
+
+	if (addr_family == AF_INET)
+		routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
+	else
+		routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT);
+
+	if (routes) {
+		guint route_metric = G_MAXUINT32, m;
+		const NMPlatformIPRoute *route = NULL, *r;
+		guint i;
+
+		/* if there are several default routes, find the one with the best metric */
+		for (i = 0; i < routes->len; i++) {
+			if (addr_family == AF_INET) {
+				r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i);
+				m = r->metric;
+			} else {
+				r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i);
+				m = nm_utils_ip6_route_metric_normalize (r->metric);
+			}
+			if (!route || m < route_metric) {
+				route = r;
+				route_metric = m;
+			}
+		}
+
+		if (route) {
+			if (addr_family == AF_INET)
+				*((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route);
+			else
+				*((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route);
+			success = TRUE;
+		}
+		g_array_free (routes, TRUE);
+	}
+	return success;
+}
+
+/*********************************************/
 /* DHCPv4 stuff */
 
 static void
@@ -2583,11 +2857,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
 			priv->dhcp4_state_sigid = 0;
 		}
 
-		if (priv->dhcp4_timeout_sigid) {
-			g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid);
-			priv->dhcp4_timeout_sigid = 0;
-		}
-
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 
 		if (stop)
@@ -2602,14 +2871,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
 	}
 }
 
-static void
-dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
-{
-	nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data),
-	                            (const char *) key,
-	                            (const char *) value);
-}
-
 static gboolean
 ip4_config_merge_and_apply (NMDevice *self,
                             NMIP4Config *config,
@@ -2620,6 +2881,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	NMConnection *connection;
 	gboolean success;
 	NMIP4Config *composite;
+	const guint32 default_route_metric = nm_device_get_ip4_route_metric (self);
 
 	/* Merge all the configs into the composite config */
 	if (config) {
@@ -2635,12 +2897,79 @@ ip4_config_merge_and_apply (NMDevice *self,
 	if (priv->ext_ip4_config)
 		nm_ip4_config_merge (composite, priv->ext_ip4_config);
 
-	/* Merge user overrides into the composite config */
+	/* Merge WWAN config *last* to ensure modem-given settings overwrite
+	 * any external stuff set by pppd or other scripts.
+	 */
+	if (priv->wwan_ip4_config)
+		nm_ip4_config_merge (composite, priv->wwan_ip4_config);
+
+	/* Merge user overrides into the composite config.  Generated+assumed
+	 * connections come from the system not the user and merging them would
+	 * be redundant, so don't bother.
+	 */
 	connection = nm_device_get_connection (self);
+	priv->default_route.v4_has = FALSE;
+	priv->default_route.v4_is_assumed = TRUE;
 	if (connection) {
-		nm_ip4_config_merge_setting (composite,
-		                             nm_connection_get_setting_ip4_config (connection),
-		                             nm_device_get_priority (self));
+		gboolean assumed = nm_device_uses_assumed_connection (self);
+		NMPlatformIP4Route *route = &priv->default_route.v4;
+
+		if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
+			nm_ip4_config_merge_setting (composite,
+			                             nm_connection_get_setting_ip4_config (connection),
+			                             default_route_metric);
+		}
+
+		/* Add the default route.
+		 *
+		 * We keep track of the default route of a device in a private field.
+		 * NMDevice needs to know the default route at this point, because the gateway
+		 * might require a direct route (see below).
+		 *
+		 * But also, we don't want to add the default route to priv->ip4_config,
+		 * because the default route from the setting might not be the same that
+		 * NMDefaultRouteManager eventually configures (because the it might
+		 * tweak the effective metric).
+		 */
+		if (   !assumed
+		    && nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) {
+			guint32 gateway = 0;
+
+			priv->default_route.v4_is_assumed = FALSE;
+			if (   (!commit && priv->ext_ip4_config_had_any_addresses)
+			    || ( commit && nm_ip4_config_get_num_addresses (composite))) {
+				/* For managed interfaces, we can only configure a gateway, if either the external config indicates
+				 * that we already have addresses, or if we are about to commit any addresses.
+				 * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
+				 * addresses for the route. */
+				gateway = nm_ip4_config_get_gateway (composite);
+				if (   gateway
+				    || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) {
+					memset (route, 0, sizeof (*route));
+					route->source = NM_IP_CONFIG_SOURCE_USER;
+					route->gateway = gateway;
+					route->metric = default_route_metric;
+					route->mss = nm_ip4_config_get_mss (composite);
+					priv->default_route.v4_has = TRUE;
+
+					if (   gateway
+					    && !nm_ip4_config_get_subnet_for_host (composite, gateway)
+					    && !nm_ip4_config_get_direct_route_for_host (composite, gateway)) {
+						/* add a direct route to the gateway */
+						NMPlatformIP4Route r = *route;
+
+						r.network = gateway;
+						r.plen = 32;
+						r.gateway = 0;
+						nm_ip4_config_add_route (composite, &r);
+					}
+				}
+			}
+		} else {
+			/* For interfaces that are assumed and that have no default-route by configuration, we assume
+			 * the default connection and pick up whatever is configured. */
+			priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route);
+		}
 	}
 
 	/* Allow setting MTU etc */
@@ -2649,7 +2978,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 			NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite);
 	}
 
-	success = nm_device_set_ip4_config (self, composite, commit, out_reason);
+	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, out_reason);
 	g_object_unref (composite);
 	return success;
 }
@@ -2662,8 +2991,7 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 	g_return_if_fail (config != NULL);
 
 	if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to update IPv4 config for DHCP change.",
-		             nm_device_get_ip_iface (self));
+		_LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change.");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	} else {
 		/* Notify dispatcher scripts of new DHCP4 config */
@@ -2677,94 +3005,93 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 }
 
 static void
-dhcp4_fail (NMDevice *device, gboolean timeout)
+dhcp4_fail (NMDevice *self, gboolean timeout)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-
-	nm_dhcp4_config_reset (priv->dhcp4_config);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	dhcp4_cleanup (self, TRUE, FALSE);
 	if (timeout || (priv->ip4_state == IP_CONF))
-		nm_device_activate_schedule_ip4_config_timeout (device);
-	else if (priv->ip4_state == IP_FAIL)
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		nm_device_activate_schedule_ip4_config_timeout (self);
+	else if (priv->ip4_state == IP_DONE)
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+	else
+		g_warn_if_reached ();
+}
+
+static void
+dhcp4_update_config (NMDevice *self, NMDhcp4Config *config, GHashTable *options)
+{
+	GHashTableIter iter;
+	const char *key, *value;
+
+	/* Update the DHCP4 config object with new DHCP options */
+	nm_dhcp4_config_reset (config);
+
+	g_hash_table_iter_init (&iter, options);
+	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+		nm_dhcp4_config_add_option (config, key, value);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
 }
 
 static void
-dhcp4_state_changed (NMDHCPClient *client,
-                     NMDHCPState state,
+dhcp4_state_changed (NMDhcpClient *client,
+                     NMDhcpState state,
+                     NMIP4Config *ip4_config,
+                     GHashTable *options,
                      gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	NMIP4Config *config;
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
+	g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
 
-	nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d",
-	            nm_device_get_iface (device), state);
+	_LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state);
 
 	switch (state) {
-	case DHC_BOUND4:     /* lease obtained */
-	case DHC_RENEW4:     /* lease renewed */
-	case DHC_REBOOT:     /* have valid lease, but now obtained a different one */
-	case DHC_REBIND4:    /* new, different lease */
-		config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
-		if (!config) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to get IPv4 config in response to DHCP event.",
-					     nm_device_get_ip_iface (device));
-			nm_device_state_changed (device,
+	case NM_DHCP_STATE_BOUND:
+		if (!ip4_config) {
+			_LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event.");
+			nm_device_state_changed (self,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 			break;
 		}
 
-		/* Update the DHCP4 config object with new DHCP options */
-		nm_dhcp4_config_reset (priv->dhcp4_config);
-		nm_dhcp_client_foreach_option (priv->dhcp4_client,
-			                           dhcp4_add_option_cb,
-			                           priv->dhcp4_config);
-		g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
+		dhcp4_update_config (self, priv->dhcp4_config, options);
 
 		if (priv->ip4_state == IP_CONF)
-			nm_device_activate_schedule_ip4_config_result (device, config);
+			nm_device_activate_schedule_ip4_config_result (self, ip4_config);
 		else if (priv->ip4_state == IP_DONE)
-			dhcp4_lease_change (device, config);
-		g_object_unref (config);
-
+			dhcp4_lease_change (self, ip4_config);
 		break;
-	case DHC_TIMEOUT: /* timed out contacting DHCP server */
-		dhcp4_fail (device, TRUE);
+	case NM_DHCP_STATE_TIMEOUT:
+		dhcp4_fail (self, TRUE);
 		break;
-	case DHC_END: /* dhclient exited normally */
-	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
-	case DHC_ABEND: /* dhclient exited abnormally */
-		/* dhclient quit and can't get/renew a lease; so kill the connection */
-		dhcp4_fail (device, FALSE);
+	case NM_DHCP_STATE_EXPIRE:
+		/* Ignore expiry before we even have a lease (NAK, old lease, etc) */
+		if (priv->ip4_state == IP_CONF)
+			break;
+		/* Fall through */
+	case NM_DHCP_STATE_DONE:
+	case NM_DHCP_STATE_FAIL:
+		dhcp4_fail (self, FALSE);
 		break;
 	default:
 		break;
 	}
 }
 
-static void
-dhcp4_timeout (NMDHCPClient *client, gpointer user_data)
-{
-	NMDevice *device = NM_DEVICE (user_data);
-
-	g_return_if_fail (nm_device_get_act_request (device) != NULL);
-	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
-
-	nm_dhcp_client_stop (client, FALSE);
-	dhcp4_fail (device, TRUE);
-}
-
 static NMActStageReturn
 dhcp4_start (NMDevice *self,
              NMConnection *connection,
              NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
 	GByteArray *tmp = NULL;
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -2774,21 +3101,26 @@ dhcp4_start (NMDevice *self,
 		g_object_unref (priv->dhcp4_config);
 	priv->dhcp4_config = nm_dhcp4_config_new ();
 
-	if (priv->hw_addr_len) {
-		tmp = g_byte_array_sized_new (priv->hw_addr_len);
-		g_byte_array_append (tmp, priv->hw_addr, priv->hw_addr_len);
+	hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len);
+	if (hw_addr_len) {
+		tmp = g_byte_array_sized_new (hw_addr_len);
+		g_byte_array_append (tmp, hw_addr, hw_addr_len);
 	}
 
 	/* Begin DHCP on the interface */
 	g_warn_if_fail (priv->dhcp4_client == NULL);
 	priv->dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
+	                                                nm_device_get_ip_ifindex (self),
 	                                                tmp,
 	                                                nm_connection_get_uuid (connection),
-	                                                nm_device_get_priority (self),
-	                                                s_ip4,
+	                                                nm_device_get_ip4_route_metric (self),
+	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip4),
+	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip4),
+	                                                nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)),
 	                                                priv->dhcp_timeout,
-	                                                priv->dhcp_anycast_address);
+	                                                priv->dhcp_anycast_address,
+	                                                NULL);
 
 	if (tmp)
 		g_byte_array_free (tmp, TRUE);
@@ -2802,10 +3134,6 @@ dhcp4_start (NMDevice *self,
 	                                            NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
 	                                            G_CALLBACK (dhcp4_state_changed),
 	                                            self);
-	priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client,
-	                                              NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
-	                                              G_CALLBACK (dhcp4_timeout),
-	                                              self);
 
 	nm_device_add_pending_action (self, PENDING_ACTION_DHCP4, TRUE);
 
@@ -2823,8 +3151,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 
 	g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE);
 
-	nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 lease renewal requested",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested");
 
 	/* Terminate old DHCP instance and release the old lease */
 	dhcp4_cleanup (self, TRUE, release);
@@ -2849,20 +3176,20 @@ release_shared_ip (gpointer data)
 }
 
 static gboolean
-reserve_shared_ip (NMSettingIP4Config *s_ip4, NMPlatformIP4Address *address)
+reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Address *address)
 {
 	if (G_UNLIKELY (shared_ips == NULL))
 		shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	memset (address, 0, sizeof (*address));
 
-	if (s_ip4 && nm_setting_ip4_config_get_num_addresses (s_ip4)) {
+	if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) {
 		/* Use the first user-supplied address */
-		NMIP4Address *user = nm_setting_ip4_config_get_address (s_ip4, 0);
+		NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0);
 
 		g_assert (user);
-		address->address = nm_ip4_address_get_address (user);
-		address->plen = nm_ip4_address_get_prefix (user);
+		nm_ip_address_get_address_binary (user, &address->address);
+		address->plen = nm_ip_address_get_prefix (user);
 	} else {
 		/* Find an unused address in the 10.42.x.x range */
 		guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */
@@ -2871,7 +3198,7 @@ reserve_shared_ip (NMSettingIP4Config *s_ip4, NMPlatformIP4Address *address)
 		while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) {
 			count += ntohl (0x100);
 			if (count > ntohl (0xFE00)) {
-				nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!");
+				_LOGE (LOGD_SHARING, "ran out of shared IP addresses!");
 				return FALSE;
 			}
 		}
@@ -2894,13 +3221,13 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso
 
 	g_return_val_if_fail (self != NULL, NULL);
 
-	if (!reserve_shared_ip (nm_connection_get_setting_ip4_config (connection), &address)) {
+	if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address)) {
 		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
 		return NULL;
 	}
 
 	config = nm_ip4_config_new ();
-	address.source = NM_PLATFORM_SOURCE_SHARED;
+	address.source = NM_IP_CONFIG_SOURCE_SHARED;
 	nm_ip4_config_add_address (config, &address);
 
 	/* Remove the address lock when the object gets disposed */
@@ -2914,7 +3241,79 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso
 /*********************************************/
 
 static gboolean
-have_any_ready_slaves (NMDevice *device, const GSList *slaves)
+connection_ip4_method_requires_carrier (NMConnection *connection,
+                                        gboolean *out_ip4_enabled)
+{
+	const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	static const char *ip4_carrier_methods[] = {
+		NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
+		NULL
+	};
+
+	if (out_ip4_enabled)
+		*out_ip4_enabled = !!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
+	return _nm_utils_string_in_list (method, ip4_carrier_methods);
+}
+
+static gboolean
+connection_ip6_method_requires_carrier (NMConnection *connection,
+                                        gboolean *out_ip6_enabled)
+{
+	const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	static const char *ip6_carrier_methods[] = {
+		NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+		NM_SETTING_IP6_CONFIG_METHOD_DHCP,
+		NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
+		NULL
+	};
+
+	if (out_ip6_enabled)
+		*out_ip6_enabled = !!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
+	return _nm_utils_string_in_list (method, ip6_carrier_methods);
+}
+
+static gboolean
+connection_requires_carrier (NMConnection *connection)
+{
+	NMSettingIPConfig *s_ip4, *s_ip6;
+	gboolean ip4_carrier_wanted, ip6_carrier_wanted;
+	gboolean ip4_used = FALSE, ip6_used = FALSE;
+
+	ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used);
+	if (ip4_carrier_wanted) {
+		/* If IPv4 wants a carrier and cannot fail, the whole connection
+		 * requires a carrier regardless of the IPv6 method.
+		 */
+		s_ip4 = nm_connection_get_setting_ip4_config (connection);
+		if (s_ip4 && !nm_setting_ip_config_get_may_fail (s_ip4))
+			return TRUE;
+	}
+
+	ip6_carrier_wanted = connection_ip6_method_requires_carrier (connection, &ip6_used);
+	if (ip6_carrier_wanted) {
+		/* If IPv6 wants a carrier and cannot fail, the whole connection
+		 * requires a carrier regardless of the IPv4 method.
+		 */
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		if (s_ip6 && !nm_setting_ip_config_get_may_fail (s_ip6))
+			return TRUE;
+	}
+
+	/* If an IP version wants a carrier and and the other IP version isn't
+	 * used, the connection requires carrier since it will just fail without one.
+	 */
+	if (ip4_carrier_wanted && !ip6_used)
+		return TRUE;
+	if (ip6_carrier_wanted && !ip4_used)
+		return TRUE;
+
+	/* If both want a carrier, the whole connection wants a carrier */
+	return ip4_carrier_wanted && ip6_carrier_wanted;
+}
+
+static gboolean
+have_any_ready_slaves (NMDevice *self, const GSList *slaves)
 {
 	const GSList *iter;
 
@@ -2955,16 +3354,11 @@ act_stage3_ip4_config_start (NMDevice *self,
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
-	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
-	if (priv->master)
-		g_assert_cmpstr (method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
-
-	if (   strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0
+	if (   connection_ip4_method_requires_carrier (connection, NULL)
 	    && priv->is_master
 	    && !priv->carrier) {
-		nm_log_info (LOGD_IP4 | LOGD_DEVICE,
-		             "(%s): IPv4 config waiting until carrier is on",
-		             nm_device_get_ip_iface (self));
+		_LOGI (LOGD_IP4 | LOGD_DEVICE,
+		       "IPv4 config waiting until carrier is on");
 		return NM_ACT_STAGE_RETURN_WAIT;
 	}
 
@@ -2977,13 +3371,14 @@ act_stage3_ip4_config_start (NMDevice *self,
 		g_slist_free (slaves);
 
 		if (ready_slaves == FALSE) {
-			nm_log_info (LOGD_DEVICE | LOGD_IP4,
-			             "(%s): IPv4 config waiting until slaves are ready",
-			             nm_device_get_ip_iface (self));
+			_LOGI (LOGD_DEVICE | LOGD_IP4,
+			       "IPv4 config waiting until slaves are ready");
 			return NM_ACT_STAGE_RETURN_WAIT;
 		}
 	}
 
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
 	/* Start IPv4 addressing based on the method requested */
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
 		ret = dhcp4_start (self, connection, reason);
@@ -3004,10 +3399,8 @@ act_stage3_ip4_config_start (NMDevice *self,
 	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) {
 		/* Nothing to do... */
 		ret = NM_ACT_STAGE_RETURN_STOP;
-	} else {
-		nm_log_warn (LOGD_IP4, "(%s): unhandled IPv4 config method '%s'; will fail",
-		             nm_device_get_ip_iface (self), method);
-	}
+	} else
+		_LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method);
 
 	return ret;
 }
@@ -3029,33 +3422,20 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
 			priv->dhcp6_state_sigid = 0;
 		}
 
-		if (priv->dhcp6_timeout_sigid) {
-			g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid);
-			priv->dhcp6_timeout_sigid = 0;
-		}
-
-		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
-
 		if (stop)
 			nm_dhcp_client_stop (priv->dhcp6_client, release);
 
 		g_clear_object (&priv->dhcp6_client);
 	}
 
+	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
+
 	if (priv->dhcp6_config) {
 		g_clear_object (&priv->dhcp6_config);
 		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
 	}
 }
 
-static void
-dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
-{
-	nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
-	                            (const char *) key,
-	                            (const char *) value);
-}
-
 static gboolean
 ip6_config_merge_and_apply (NMDevice *self,
                             gboolean commit,
@@ -3080,109 +3460,216 @@ ip6_config_merge_and_apply (NMDevice *self,
 	if (priv->ext_ip6_config)
 		nm_ip6_config_merge (composite, priv->ext_ip6_config);
 
-	/* Merge user overrides into the composite config */
+	/* Merge WWAN config *last* to ensure modem-given settings overwrite
+	 * any external stuff set by pppd or other scripts.
+	 */
+	if (priv->wwan_ip6_config)
+		nm_ip6_config_merge (composite, priv->wwan_ip6_config);
+
+	/* Merge user overrides into the composite config.  Generated+assumed
+	 * connections come from the system not the user and merging them would
+	 * be redundant, so don't bother.
+	 */
 	connection = nm_device_get_connection (self);
+	priv->default_route.v6_has = FALSE;
+	priv->default_route.v6_is_assumed = TRUE;
 	if (connection) {
-		nm_ip6_config_merge_setting (composite,
-		                             nm_connection_get_setting_ip6_config (connection),
-		                             nm_device_get_priority (self));
+		gboolean assumed = nm_device_uses_assumed_connection (self);
+		NMPlatformIP6Route *route = &priv->default_route.v6;
+
+		if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) {
+			nm_ip6_config_merge_setting (composite,
+			                             nm_connection_get_setting_ip6_config (connection),
+			                             nm_device_get_ip6_route_metric (self));
+		}
+
+		/* Add the default route.
+		 *
+		 * We keep track of the default route of a device in a private field.
+		 * NMDevice needs to know the default route at this point, because the gateway
+		 * might require a direct route (see below).
+		 *
+		 * But also, we don't want to add the default route to priv->ip4_config,
+		 * because the default route from the setting might not be the same that
+		 * NMDefaultRouteManager eventually configures (because the it might
+		 * tweak the effective metric).
+		 */
+		if (   !assumed
+		    && nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) {
+			const struct in6_addr *gateway = NULL;
+
+			priv->default_route.v6_is_assumed = FALSE;
+			if (   (!commit && priv->ext_ip6_config_had_any_addresses)
+			    || ( commit && nm_ip6_config_get_num_addresses (composite))) {
+				/* For managed interfaces, we can only configure a gateway, if either the external config indicates
+				 * that we already have addresses, or if we are about to commit any addresses.
+				 * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any
+				 * addresses for the route. */
+				gateway = nm_ip6_config_get_gateway (composite);
+				if (gateway) {
+					memset (route, 0, sizeof (*route));
+					route->source = NM_IP_CONFIG_SOURCE_USER;
+					route->gateway = *gateway;
+					route->metric = nm_device_get_ip6_route_metric (self);
+					route->mss = nm_ip6_config_get_mss (composite);
+					priv->default_route.v6_has = TRUE;
+
+					if (   gateway
+					    && !nm_ip6_config_get_subnet_for_host (composite, gateway)
+					    && !nm_ip6_config_get_direct_route_for_host (composite, gateway)) {
+						/* add a direct route to the gateway */
+						NMPlatformIP6Route r = *route;
+
+						r.network = *gateway;
+						r.plen = 128;
+						r.gateway = in6addr_any;
+						nm_ip6_config_add_route (composite, &r);
+					}
+				}
+			}
+		} else {
+			/* For interfaces that are assumed and that have no default-route by configuration, we assume
+			 * the default connection and pick up whatever is configured. */
+			priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route);
+		}
 	}
 
 	nm_ip6_config_addresses_sort (composite,
 	    priv->rdisc ? priv->rdisc_use_tempaddr : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 
+	/* Allow setting MTU etc */
+	if (commit) {
+		if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit)
+			NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite);
+	}
+
 	success = nm_device_set_ip6_config (self, composite, commit, out_reason);
 	g_object_unref (composite);
 	return success;
 }
 
 static void
-dhcp6_lease_change (NMDevice *device)
+dhcp6_lease_change (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	if (priv->dhcp6_ip6_config == NULL) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind",
-		             nm_device_get_ip_iface (device));
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		_LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind");
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
 		return;
 	}
 
 	g_assert (priv->dhcp6_client);  /* sanity check */
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	/* Apply the updated config */
-	if (ip6_config_merge_and_apply (device, TRUE, &reason) == FALSE) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.",
-		             nm_device_get_ip_iface (device));
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+	if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) {
+		_LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event.");
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	} else {
 		/* Notify dispatcher scripts of new DHCPv6 config */
-		nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL, NULL);
+		nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, self, NULL, NULL, NULL);
 	}
 }
 
 static void
-dhcp6_fail (NMDevice *device, gboolean timeout)
+dhcp6_fail (NMDevice *self, gboolean timeout)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	dhcp6_cleanup (self, TRUE, FALSE);
+
+	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
+		if (timeout || (priv->ip6_state == IP_CONF))
+			nm_device_activate_schedule_ip6_config_timeout (self);
+		else if (priv->ip6_state == IP_DONE)
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		else
+			g_warn_if_reached ();
+	} else {
+		/* not a hard failure; just live with the RA info */
+		if (priv->ip6_state == IP_CONF)
+			nm_device_activate_schedule_ip6_config_result (self);
+	}
+}
 
-	nm_dhcp6_config_reset (priv->dhcp6_config);
+static void
+dhcp6_timeout (NMDevice *self, NMDhcpClient *client)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (timeout || (priv->ip6_state == IP_CONF))
-		nm_device_activate_schedule_ip6_config_timeout (device);
-	else if (priv->ip6_state == IP_FAIL)
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
+		dhcp6_fail (self, TRUE);
+	else {
+		/* not a hard failure; just live with the RA info */
+		dhcp6_cleanup (self, TRUE, FALSE);
+		if (priv->ip6_state == IP_CONF)
+			nm_device_activate_schedule_ip6_config_result (self);
+	}
 }
 
 static void
-dhcp6_state_changed (NMDHCPClient *client,
-                     NMDHCPState state,
+dhcp6_update_config (NMDevice *self, NMDhcp6Config *config, GHashTable *options)
+{
+	GHashTableIter iter;
+	const char *key, *value;
+
+	/* Update the DHCP6 config object with new DHCP options */
+	nm_dhcp6_config_reset (config);
+
+	g_hash_table_iter_init (&iter, options);
+	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+		nm_dhcp6_config_add_option (config, key, value);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+}
+
+static void
+dhcp6_state_changed (NMDhcpClient *client,
+                     NMDhcpState state,
+                     NMIP6Config *ip6_config,
+                     GHashTable *options,
                      gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
+	g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config));
 
-	nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d",
-	            nm_device_get_iface (device), state);
+	_LOGD (LOGD_DHCP6, "new DHCPv6 client state %d", state);
 
 	switch (state) {
-	case DHC_BOUND6:
-	case DHC_RENEW6:     /* lease renewed */
-	case DHC_REBOOT:     /* have valid lease, but now obtained a different one */
-	case DHC_REBIND6:    /* new, different lease */
+	case NM_DHCP_STATE_BOUND:
 		g_clear_object (&priv->dhcp6_ip6_config);
-		priv->dhcp6_ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
-
-		/* Update the DHCP6 config object with new DHCP options */
-		nm_dhcp6_config_reset (priv->dhcp6_config);
-		if (priv->dhcp6_ip6_config) {
-			nm_dhcp_client_foreach_option (priv->dhcp6_client,
-			                               dhcp6_add_option_cb,
-			                               priv->dhcp6_config);
+		if (ip6_config) {
+			priv->dhcp6_ip6_config = g_object_ref (ip6_config);
+			dhcp6_update_config (self, priv->dhcp6_config, options);
 		}
-		g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
 
 		if (priv->ip6_state == IP_CONF) {
 			if (priv->dhcp6_ip6_config == NULL) {
 				/* FIXME: Initial DHCP failed; should we fail IPv6 entirely then? */
-				nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED);
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED);
 				break;
 			}
-			nm_device_activate_schedule_ip6_config_result (device);
+			nm_device_activate_schedule_ip6_config_result (self);
 		} else if (priv->ip6_state == IP_DONE)
-			dhcp6_lease_change (device);
+			dhcp6_lease_change (self);
 		break;
-	case DHC_TIMEOUT: /* timed out contacting DHCP server */
-		dhcp6_fail (device, TRUE);
+	case NM_DHCP_STATE_TIMEOUT:
+		dhcp6_timeout (self, client);
 		break;
-	case DHC_END: /* dhclient exited normally */
+	case NM_DHCP_STATE_EXPIRE:
+		/* Ignore expiry before we even have a lease (NAK, old lease, etc) */
+		if (priv->ip6_state != IP_CONF)
+			dhcp6_fail (self, FALSE);
+		break;
+	case NM_DHCP_STATE_DONE:
 		/* In IPv6 info-only mode, the client doesn't handle leases so it
 		 * may exit right after getting a response from the server.  That's
 		 * normal.  In that case we just ignore the exit.
@@ -3190,83 +3677,45 @@ dhcp6_state_changed (NMDHCPClient *client,
 		if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF)
 			break;
 		/* Otherwise, fall through */
-	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
-	case DHC_ABEND: /* dhclient exited abnormally */
-		/* dhclient quit and can't get/renew a lease; so kill the connection */
-		dhcp6_fail (device, FALSE);
+	case NM_DHCP_STATE_FAIL:
+		dhcp6_fail (self, FALSE);
 		break;
 	default:
 		break;
 	}
 }
 
-static void
-dhcp6_timeout (NMDHCPClient *client, gpointer user_data)
-{
-	NMDevice *device = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-
-	g_return_if_fail (nm_device_get_act_request (device) != NULL);
-	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
-
-	nm_dhcp_client_stop (client, FALSE);
-	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
-		dhcp6_fail (device, TRUE);
-	else {
-		/* not a hard failure; just live with the RA info */
-		nm_dhcp6_config_reset (priv->dhcp6_config);
-		if (priv->dhcp6_ip6_config)
-			g_object_unref (priv->dhcp6_ip6_config);
-		priv->dhcp6_ip6_config = NULL;
-
-		if (priv->ip6_state == IP_CONF)
-			nm_device_activate_schedule_ip6_config_result (device);
-	}
-}
-
-static NMActStageReturn
-dhcp6_start (NMDevice *self,
-             NMConnection *connection,
-             guint32 dhcp_opt,
-             NMDeviceStateReason *reason)
+static gboolean
+dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 {
-	NMSettingIP6Config *s_ip6;
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	NMSettingIPConfig *s_ip6;
 	GByteArray *tmp = NULL;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
 
-	if (!connection) {
-		connection = nm_device_get_connection (self);
-		g_assert (connection);
-	}
-
-	/* Begin a DHCP transaction on the interface */
-
-	/* Clear old exported DHCP options */
-	if (priv->dhcp6_config)
-		g_object_unref (priv->dhcp6_config);
-	priv->dhcp6_config = nm_dhcp6_config_new ();
-
-	g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
-	if (priv->dhcp6_ip6_config) {
-		g_object_unref (priv->dhcp6_ip6_config);
-		priv->dhcp6_ip6_config = NULL;
-	}
+	g_assert (connection);
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
 
-	if (priv->hw_addr_len) {
-		tmp = g_byte_array_sized_new (priv->hw_addr_len);
-		g_byte_array_append (tmp, priv->hw_addr, priv->hw_addr_len);
+	hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len);
+	if (hw_addr_len) {
+		tmp = g_byte_array_sized_new (hw_addr_len);
+		g_byte_array_append (tmp, hw_addr, hw_addr_len);
 	}
 
 	priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
+	                                                nm_device_get_ip_ifindex (self),
 	                                                tmp,
 	                                                nm_connection_get_uuid (connection),
-	                                                nm_device_get_priority (self),
-	                                                nm_connection_get_setting_ip6_config (connection),
+	                                                nm_device_get_ip6_route_metric (self),
+	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip6),
+	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip6),
 	                                                priv->dhcp_timeout,
 	                                                priv->dhcp_anycast_address,
-	                                                (dhcp_opt == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE);
+	                                                (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
+	                                                nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)));
 	if (tmp)
 		g_byte_array_free (tmp, TRUE);
 
@@ -3275,65 +3724,85 @@ dhcp6_start (NMDevice *self,
 		                                            NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
 		                                            G_CALLBACK (dhcp6_state_changed),
 		                                            self);
-		priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client,
-		                                              NM_DHCP_CLIENT_SIGNAL_TIMEOUT,
-		                                              G_CALLBACK (dhcp6_timeout),
-		                                              self);
+	}
 
-		s_ip6 = nm_connection_get_setting_ip6_config (connection);
-		if (!nm_setting_ip6_config_get_may_fail (s_ip6) ||
-		    !strcmp (nm_setting_ip6_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_DHCP))
-			nm_device_add_pending_action (self, PENDING_ACTION_DHCP6, TRUE);
+	return !!priv->dhcp6_client;
+}
 
-		/* DHCP devices will be notified by the DHCP manager when stuff happens */
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else {
+static gboolean
+dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMSettingIPConfig *s_ip6;
+
+	g_clear_object (&priv->dhcp6_config);
+	priv->dhcp6_config = nm_dhcp6_config_new ();
+
+	g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
+	g_clear_object (&priv->dhcp6_ip6_config);
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (!nm_setting_ip_config_get_may_fail (s_ip6) ||
+	    !strcmp (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_DHCP))
+		nm_device_add_pending_action (self, PENDING_ACTION_DHCP6, TRUE);
+
+	if (wait_for_ll) {
+		NMActStageReturn ret;
+
+		/* ensure link local is ready... */
+		ret = linklocal6_start (self);
+		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+			/* success; wait for the LL address to show up */
+			return TRUE;
+		}
+
+		/* success; already have the LL address; kick off DHCP */
+		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+	}
+
+	if (!dhcp6_start_with_link_ready (self, connection)) {
 		*reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		return FALSE;
 	}
 
-	return ret;
+	return TRUE;
 }
 
 gboolean
 nm_device_dhcp6_renew (NMDevice *self, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActStageReturn ret;
-	NMDeviceStateReason reason;
-	NMConnection *connection;
 
 	g_return_val_if_fail (priv->dhcp6_client != NULL, FALSE);
 
-	nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 lease renewal requested",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested");
 
 	/* Terminate old DHCP instance and release the old lease */
 	dhcp6_cleanup (self, TRUE, release);
 
-	connection = nm_device_get_connection (self);
-	g_assert (connection);
-
 	/* Start DHCP again on the interface */
-	ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason);
-
-	return (ret != NM_ACT_STAGE_RETURN_FAILURE);
+	return dhcp6_start (self, FALSE, NULL);
 }
 
 /******************************************/
 
 static gboolean
-linklocal6_config_is_ready (const NMIP6Config *ip6_config)
+have_ip6_address (const NMIP6Config *ip6_config, gboolean linklocal)
 {
-	int i;
+	guint i;
 
 	if (!ip6_config)
 		return FALSE;
 
+	linklocal = !!linklocal;
+
 	for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
 		const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ip6_config, i);
 
-		if (IN6_IS_ADDR_LINKLOCAL (&addr->address) &&
+		if ((IN6_IS_ADDR_LINKLOCAL (&addr->address) == linklocal) &&
 		    !(addr->flags & IFA_F_TENTATIVE))
 			return TRUE;
 	}
@@ -3359,8 +3828,7 @@ linklocal6_timeout_cb (gpointer user_data)
 
 	linklocal6_cleanup (self);
 
-	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses failed due to timeout",
-	             nm_device_get_iface (self));
+	_LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout");
 
 	nm_device_activate_schedule_ip6_config_timeout (self);
 	return G_SOURCE_REMOVE;
@@ -3374,7 +3842,7 @@ linklocal6_complete (NMDevice *self)
 	const char *method;
 
 	g_assert (priv->linklocal6_timeout_id);
-	g_assert (linklocal6_config_is_ready (priv->ip6_config));
+	g_assert (have_ip6_address (priv->ip6_config, TRUE));
 
 	linklocal6_cleanup (self);
 
@@ -3383,17 +3851,70 @@ linklocal6_complete (NMDevice *self)
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 
-	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses successful, continue with method %s",
-	             nm_device_get_iface (self), method);
+	_LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method);
 
-	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0)
-		addrconf6_start_with_link_ready (self);
-	else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
+		if (!addrconf6_start_with_link_ready (self)) {
+			/* Time out IPv6 instead of failing the entire activation */
+			nm_device_activate_schedule_ip6_config_timeout (self);
+		}
+	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
+		if (!dhcp6_start_with_link_ready (self, connection)) {
+			/* Time out IPv6 instead of failing the entire activation */
+			nm_device_activate_schedule_ip6_config_timeout (self);
+		}
+	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
 		nm_device_activate_schedule_ip6_config_result (self);
 	else
 		g_return_if_fail (FALSE);
 }
 
+static void
+check_and_add_ipv6ll_addr (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ip_ifindex = nm_device_get_ip_ifindex (self);
+	NMUtilsIPv6IfaceId iid;
+	struct in6_addr lladdr;
+	guint i, n;
+
+	if (priv->nm_ipv6ll == FALSE)
+		return;
+
+	if (priv->ip6_config) {
+		n = nm_ip6_config_get_num_addresses (priv->ip6_config);
+		for (i = 0; i < n; i++) {
+			const NMPlatformIP6Address *addr;
+
+			addr = nm_ip6_config_get_address (priv->ip6_config, i);
+			if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+				/* Already have an LL address, nothing to do */
+				return;
+			}
+		}
+	}
+
+	if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+		_LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 may be broken");
+		return;
+	}
+
+	memset (&lladdr, 0, sizeof (lladdr));
+	lladdr.s6_addr16[0] = htons (0xfe80);
+	nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
+	_LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
+	if (!nm_platform_ip6_address_add (ip_ifindex,
+	                                  lladdr,
+	                                  in6addr_any,
+	                                  64,
+	                                  NM_PLATFORM_LIFETIME_PERMANENT,
+	                                  NM_PLATFORM_LIFETIME_PERMANENT,
+	                                  0)) {
+		_LOGW (LOGD_IP6, "failed to add IPv6 link-local address %s",
+		       nm_utils_inet6_ntop (&lladdr, NULL));
+	}
+}
+
 static NMActStageReturn
 linklocal6_start (NMDevice *self)
 {
@@ -3403,15 +3924,16 @@ linklocal6_start (NMDevice *self)
 
 	linklocal6_cleanup (self);
 
-	if (linklocal6_config_is_ready (priv->ip6_config))
+	if (have_ip6_address (priv->ip6_config, TRUE))
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.",
-	            nm_device_get_iface (self), method);
+	_LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", method);
+
+	check_and_add_ipv6ll_addr (self);
 
 	priv->linklocal6_timeout_id = g_timeout_add_seconds (5, linklocal6_timeout_cb, self);
 
@@ -3468,12 +3990,10 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
 }
 
 static void
-rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device)
+rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	NMConnection *connection;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int i;
-	NMDeviceStateReason reason;
 	static int system_support = -1;
 	guint ifa_flags = 0x00;
 
@@ -3499,8 +4019,6 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
 	}
 
 	g_return_if_fail (priv->act_request);
-	connection = nm_device_get_connection (device);
-	g_assert (connection);
 
 	if (!priv->ac_ip6_config)
 		priv->ac_ip6_config = nm_ip6_config_new ();
@@ -3536,7 +4054,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
 			address.preferred = discovered_address->preferred;
 			if (address.preferred > address.lifetime)
 				address.preferred = address.lifetime;
-			address.source = NM_PLATFORM_SOURCE_RDISC;
+			address.source = NM_IP_CONFIG_SOURCE_RDISC;
 			address.flags = ifa_flags;
 
 			nm_ip6_config_add_address (priv->ac_ip6_config, &address);
@@ -3560,8 +4078,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
 				route.network = discovered_route->network;
 				route.plen = discovered_route->plen;
 				route.gateway = discovered_route->gateway;
-				route.source = NM_PLATFORM_SOURCE_RDISC;
-				route.metric = nm_device_get_priority (device);
+				route.source = NM_IP_CONFIG_SOURCE_RDISC;
+				route.metric = nm_device_get_ip6_route_metric (self);
 
 				nm_ip6_config_add_route (priv->ac_ip6_config, &route);
 			}
@@ -3591,39 +4109,99 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device
 	}
 
 	if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
-		dhcp6_cleanup (device, TRUE, TRUE);
+		dhcp6_cleanup (self, TRUE, TRUE);
 
 		priv->dhcp6_mode = rdisc->dhcp_level;
-
-		switch (priv->dhcp6_mode) {
-		case NM_RDISC_DHCP_LEVEL_NONE:
-			break;
-		default:
-			nm_log_info (LOGD_DEVICE | LOGD_DHCP6,
-			             "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6"
-			             " as requested by IPv6 router...",
-			             priv->iface);
-			switch (dhcp6_start (device, connection, priv->dhcp6_mode, &reason)) {
-			case NM_ACT_STAGE_RETURN_SUCCESS:
-				g_warn_if_reached ();
-				break;
-			case NM_ACT_STAGE_RETURN_POSTPONE:
-				return;
-			default:
-				nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
-				return;
+		if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) {
+			NMDeviceStateReason reason;
+
+			_LOGI (LOGD_DEVICE | LOGD_DHCP6,
+			       "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6"
+			       " as requested by IPv6 router...");
+			if (!dhcp6_start (self, FALSE, &reason)) {
+				if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
+					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 			}
+			return;
 		}
 	}
 
-	if (changed & NM_RDISC_CONFIG_HOP_LIMIT) {
+	/* hop_limit == 0 is a special value "unspecified", so do not touch
+	 * in this case */
+	if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
 		char val[16];
 
 		g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
-		nm_device_ipv6_sysctl_set (device, "hop_limit", val);
+		nm_device_ipv6_sysctl_set (self, "hop_limit", val);
 	}
 
-	nm_device_activate_schedule_ip6_config_result (device);
+	if (changed & NM_RDISC_CONFIG_MTU) {
+		char val[16];
+
+		g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
+		nm_device_ipv6_sysctl_set (self, "mtu", val);
+	}
+
+	nm_device_activate_schedule_ip6_config_result (self);
+}
+
+static void
+rdisc_ra_timeout (NMRDisc *rdisc, NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* We don't want to stop listening for router advertisements completely,
+	 * but instead let device activation continue activating.  If an RA
+	 * shows up later, we'll use it as long as the device is not disconnected.
+	 */
+
+	_LOGD (LOGD_IP6, "timed out waiting for IPv6 router advertisement");
+	if (priv->ip6_state == IP_CONF) {
+		/* If RA is our only source of addressing information and we don't
+		 * ever receive one, then time out IPv6.  But if there is other
+		 * IPv6 configuration, like manual IPv6 addresses or external IPv6
+		 * config, consider that sufficient for IPv6 success.
+		 */
+		if (have_ip6_address (priv->ip6_config, FALSE))
+			nm_device_activate_schedule_ip6_config_result (self);
+		else
+			nm_device_activate_schedule_ip6_config_timeout (self);
+	}
+}
+
+static gboolean
+addrconf6_start_with_link_ready (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMUtilsIPv6IfaceId iid;
+
+	g_assert (priv->rdisc);
+
+	if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+		_LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue");
+		return FALSE;
+	}
+	nm_rdisc_set_iid (priv->rdisc, iid);
+
+	/* Apply any manual configuration before starting RA */
+	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+		_LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration");
+
+	nm_device_ipv6_sysctl_set (self, "accept_ra", "1");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0");
+
+	priv->rdisc_changed_id = g_signal_connect (priv->rdisc,
+	                                           NM_RDISC_CONFIG_CHANGED,
+	                                           G_CALLBACK (rdisc_config_changed),
+	                                           self);
+	priv->rdisc_timeout_id = g_signal_connect (priv->rdisc,
+	                                           NM_RDISC_RA_TIMEOUT,
+	                                           G_CALLBACK (rdisc_ra_timeout),
+	                                           self);
+	nm_rdisc_start (priv->rdisc);
+	return TRUE;
 }
 
 static gboolean
@@ -3645,46 +4223,26 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 
 	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface);
 	if (!priv->rdisc) {
-		nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface);
+		_LOGE (LOGD_IP6, "failed to start router discovery (%s)", ip_iface);
 		return FALSE;
 	}
 
 	priv->rdisc_use_tempaddr = use_tempaddr;
 	print_support_extended_ifa_flags (use_tempaddr);
 
-	if (!nm_setting_ip6_config_get_may_fail (nm_connection_get_setting_ip6_config (connection)))
+	if (!nm_setting_ip_config_get_may_fail (nm_connection_get_setting_ip6_config (connection)))
 		nm_device_add_pending_action (self, PENDING_ACTION_AUTOCONF6, TRUE);
 
 	/* ensure link local is ready... */
 	ret = linklocal6_start (self);
-	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
-		addrconf6_start_with_link_ready (self);
-	else
-		g_return_val_if_fail (ret == NM_ACT_STAGE_RETURN_POSTPONE, TRUE);
-
-	return TRUE;
-}
-
-static void
-addrconf6_start_with_link_ready (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	g_assert (priv->rdisc);
-
-	/* FIXME: what if interface has no lladdr, like PPP? */
-	if (priv->hw_addr_len)
-		nm_rdisc_set_lladdr (priv->rdisc, (const char *) priv->hw_addr, priv->hw_addr_len);
-
-	nm_device_ipv6_sysctl_set (self, "accept_ra", "1");
-	nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0");
-	nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0");
-	nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0");
-
-	priv->rdisc_config_changed_sigid = g_signal_connect (priv->rdisc, NM_RDISC_CONFIG_CHANGED,
-	                                                     G_CALLBACK (rdisc_config_changed), self);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+		/* success; wait for the LL address to show up */
+		return TRUE;
+	}
 
-	nm_rdisc_start (priv->rdisc);
+	/* success; already have the LL address; kick off router discovery */
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+	return addrconf6_start_with_link_ready (self);
 }
 
 static void
@@ -3692,10 +4250,14 @@ addrconf6_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->rdisc_config_changed_sigid) {
-		g_signal_handler_disconnect (priv->rdisc,
-		                             priv->rdisc_config_changed_sigid);
-		priv->rdisc_config_changed_sigid = 0;
+	if (priv->rdisc_changed_id) {
+		g_signal_handler_disconnect (priv->rdisc, priv->rdisc_changed_id);
+		priv->rdisc_changed_id = 0;
+	}
+
+	if (priv->rdisc_timeout_id) {
+		g_signal_handler_disconnect (priv->rdisc, priv->rdisc_timeout_id);
+		priv->rdisc_timeout_id = 0;
 	}
 
 	nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE);
@@ -3744,8 +4306,53 @@ restore_ip6_properties (NMDevice *self)
 	gpointer key, value;
 
 	g_hash_table_iter_init (&iter, priv->ip6_saved_properties);
-	while (g_hash_table_iter_next (&iter, &key, &value))
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		/* Don't touch "disable_ipv6" if we're doing userland IPv6LL */
+		if (priv->nm_ipv6ll && strcmp (key, "disable_ipv6") == 0)
+			continue;
 		nm_device_ipv6_sysctl_set (self, key, value);
+	}
+}
+
+static inline void
+set_disable_ipv6 (NMDevice *self, const char *value)
+{
+	/* We only touch disable_ipv6 when NM is not managing the IPv6LL address */
+	if (NM_DEVICE_GET_PRIVATE (self)->nm_ipv6ll == FALSE)
+		nm_device_ipv6_sysctl_set (self, "disable_ipv6", value);
+}
+
+static inline void
+set_nm_ipv6ll (NMDevice *self, gboolean enable)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ip_ifindex (self);
+	const char *iface = nm_device_get_ip_iface (self);
+	char *value;
+
+	if (!nm_platform_check_support_user_ipv6ll ())
+		return;
+
+	priv->nm_ipv6ll = enable;
+	if (ifindex > 0) {
+		const char *detail = enable ? "enable" : "disable";
+
+		_LOGD (LOGD_IP6, "will %s userland IPv6LL", detail);
+		if (  !nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable)
+		   && nm_platform_get_error () != NM_PLATFORM_ERROR_NOT_FOUND)
+			_LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail);
+
+		if (enable) {
+			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
+			value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6"));
+			if (g_strcmp0 (value, "0") == 0) {
+				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
+				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0");
+			}
+			g_free (value);
+		}
+
+	}
 }
 
 static NMSettingIP6ConfigPrivacy
@@ -3838,15 +4445,11 @@ act_stage3_ip6_config_start (NMDevice *self,
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
-	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	if (priv->master)
-		g_assert_cmpstr (method, ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
-
-	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0
+	if (   connection_ip4_method_requires_carrier (connection, NULL)
 	    && priv->is_master
 	    && !priv->carrier) {
-		nm_log_info (LOGD_IP6 | LOGD_DEVICE,
-		             "(%s): IPv6 config waiting until carrier is on", ip_iface);
+		_LOGI (LOGD_IP6 | LOGD_DEVICE,
+		       "IPv6 config waiting until carrier is on");
 		return NM_ACT_STAGE_RETURN_WAIT;
 	}
 
@@ -3859,23 +4462,41 @@ act_stage3_ip6_config_start (NMDevice *self,
 		g_slist_free (slaves);
 
 		if (ready_slaves == FALSE) {
-			nm_log_info (LOGD_DEVICE | LOGD_IP6,
-			             "(%s): IPv6 config waiting until slaves are ready",
-			             ip_iface);
+			_LOGI (LOGD_DEVICE | LOGD_IP6,
+			       "IPv6 config waiting until slaves are ready");
 			return NM_ACT_STAGE_RETURN_WAIT;
 		}
 	}
 
 	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
 
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
-		if (!priv->master)
+		if (!priv->master) {
+			gboolean old_nm_ipv6ll = priv->nm_ipv6ll;
+
+			/* When activating an IPv6 'ignore' connection we need to revert back
+			 * to kernel IPv6LL, but the kernel won't actually assign an address
+			 * to the interface until disable_ipv6 is bounced.
+			 */
+			set_nm_ipv6ll (self, FALSE);
+			if (old_nm_ipv6ll == TRUE)
+				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 			restore_ip6_properties (self);
+		}
 		return NM_ACT_STAGE_RETURN_STOP;
 	}
 
+	/* Any method past this point requires an IPv6LL address. Use NM-controlled
+	 * IPv6LL if this is not an assumed connection, since assumed connections
+	 * will already have IPv6 set up.
+	 */
+	if (!nm_device_uses_assumed_connection (self))
+		set_nm_ipv6ll (self, TRUE);
+
 	/* Re-enable IPv6 on the interface */
-	nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0");
+	set_disable_ipv6 (self, "0");
 
 	/* Enable/disable IPv6 Privacy Extensions.
 	 * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
@@ -3883,10 +4504,10 @@ act_stage3_ip6_config_start (NMDevice *self,
 	 */
 	ip6_privacy = ip6_use_tempaddr ();
 	if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) {
-		NMSettingIP6Config *s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
 		if (s_ip6)
-			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
+			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6));
 	}
 	ip6_privacy = use_tempaddr_clamp (ip6_privacy);
 
@@ -3905,17 +4526,19 @@ act_stage3_ip6_config_start (NMDevice *self,
 		}
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
 		priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_MANAGED;
-		ret = dhcp6_start (self, connection, priv->dhcp6_mode, reason);
+		if (!dhcp6_start (self, TRUE, reason)) {
+			/* IPv6 might be disabled; allow IPv4 to proceed */
+			ret = NM_ACT_STAGE_RETURN_STOP;
+		} else
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) {
 		/* New blank config */
 		*out_config = nm_ip6_config_new ();
 		g_assert (*out_config);
 
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	} else {
-		nm_log_warn (LOGD_IP6, "(%s): unhandled IPv6 config method '%s'; will fail",
-		             nm_device_get_ip_iface (self), method);
-	}
+	} else
+		_LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method);
 
 	/* Other methods (shared) aren't implemented yet */
 
@@ -4015,6 +4638,42 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 }
 
 /*
+ * nm_device_check_ip_failed
+ *
+ * Progress the device to appropriate state if both IPv4 and IPv6 failed
+ */
+static void
+nm_device_check_ip_failed (NMDevice *self, gboolean may_fail)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	if (   priv->ip4_state != IP_FAIL
+	    || priv->ip6_state != IP_FAIL)
+		return;
+
+	if (nm_device_uses_assumed_connection (self)) {
+		/* We have assumed configuration, but couldn't
+		 * redo it. No problem, move to check state. */
+		priv->ip4_state = priv->ip6_state = IP_DONE;
+		state = NM_DEVICE_STATE_IP_CHECK;
+	} else if (   may_fail
+	           && get_ip_config_may_fail (self, AF_INET)
+	           && get_ip_config_may_fail (self, AF_INET6)) {
+		/* Couldn't start either IPv6 and IPv4 autoconfiguration,
+		 * but both are allowed to fail. */
+		state = NM_DEVICE_STATE_SECONDARIES;
+	} else {
+		/* Autoconfiguration attempted without success. */
+		state = NM_DEVICE_STATE_FAILED;
+	}
+
+	nm_device_state_changed (self,
+	                         state,
+	                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+}
+
+/*
  * nm_device_activate_stage3_ip_config_start
  *
  * Begin automatic/manual IP configuration
@@ -4025,7 +4684,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	NMActiveConnection *master;
 	NMDevice *master_device;
 
@@ -4034,15 +4692,12 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 
 	priv->ip4_state = priv->ip6_state = IP_WAIT;
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started...");
 	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Device should be up before we can do anything with it */
-	if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) {
-		nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration",
-		             iface, nm_device_get_ip_iface (self));
-	}
+	if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self)))
+		_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 
 	/* If the device is a slave, then we don't do any IP configuration but we
 	 * use the IP config stage to indicate to the master we're ready for
@@ -4057,10 +4712,9 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 	if (master) {
 		master_device = nm_active_connection_get_device (master);
 		if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) {
-			nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on master '%s'",
-			             nm_device_get_iface (self),
-			             nm_connection_get_id (nm_device_get_connection (self)),
-			             master_device ? nm_device_get_iface (master_device) : "(unknown)");
+			_LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'",
+			       nm_connection_get_id (nm_device_get_connection (self)),
+			       master_device ? nm_device_get_iface (master_device) : "(unknown)");
 		}
 		goto out;
 	}
@@ -4073,13 +4727,10 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 	if (!nm_device_activate_stage3_ip6_start (self))
 		goto out;
 
-	if (priv->ip4_state == IP_FAIL && priv->ip6_state == IP_FAIL) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	}
+	nm_device_check_ip_failed (self, TRUE);
 
 out:
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete.");
 	return FALSE;
 }
 
@@ -4087,8 +4738,14 @@ out:
 static void
 fw_change_zone_cb (GError *error, gpointer user_data)
 {
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevice *self;
+	NMDevicePrivate *priv;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_DEVICE (user_data);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->fw_call = NULL;
 
@@ -4097,9 +4754,7 @@ fw_change_zone_cb (GError *error, gpointer user_data)
 	}
 
 	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
-
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
 }
 
 /*
@@ -4120,15 +4775,24 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
+	g_return_if_fail (!priv->fw_call);
+
 	/* Add the interface to the specified firewall zone */
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
 	s_con = nm_connection_get_setting_connection (connection);
 
 	zone = nm_setting_connection_get_zone (s_con);
-	nm_log_dbg (LOGD_DEVICE, "Activation (%s) setting firewall zone '%s'",
-	            nm_device_get_iface (self), zone ? zone : "default");
-	priv->fw_call = nm_firewall_manager_add_or_change_zone (priv->fw_manager,
+
+	if (nm_device_uses_assumed_connection (self)) {
+		_LOGD (LOGD_DEVICE, "Activation: skip setting firewall zone '%s' for assumed device", zone ? zone : "default");
+		activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
+		_LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
+		return;
+	}
+
+	_LOGD (LOGD_DEVICE, "Activation: setting firewall zone '%s'", zone ? zone : "default");
+	priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
 	                                                        nm_device_get_ip_iface (self),
 	                                                        zone,
 	                                                        FALSE,
@@ -4139,7 +4803,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 static NMActStageReturn
 act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 {
-	if (nm_device_ip_config_should_fail (self, FALSE)) {
+	if (!get_ip_config_may_fail (self, AF_INET)) {
 		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -4158,17 +4822,14 @@ nm_device_activate_ip4_config_timeout (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET);
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) started...",
-	             iface);
+	_LOGI (LOGD_DEVICE | LOGD_IP4,
+	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started...");
 
 	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -4181,16 +4842,11 @@ nm_device_activate_ip4_config_timeout (gpointer user_data)
 
 	priv->ip4_state = IP_FAIL;
 
-	/* If IPv4 failed and IPv6 failed, the activation fails */
-	if (priv->ip6_state == IP_FAIL)
-		nm_device_state_changed (self,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	nm_device_check_ip_failed (self, FALSE);
 
 out:
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) complete.",
-	             iface);
+	_LOGI (LOGD_DEVICE | LOGD_IP4,
+	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete.");
 	return FALSE;
 }
 
@@ -4213,16 +4869,15 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET);
 
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) scheduled...",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE | LOGD_IP4,
+	       "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled...");
 }
 
 
 static NMActStageReturn
 act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 {
-	if (nm_device_ip_config_should_fail (self, TRUE)) {
+	if (!get_ip_config_may_fail (self, AF_INET6)) {
 		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -4242,17 +4897,14 @@ nm_device_activate_ip6_config_timeout (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET6);
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) started...",
-	             iface);
+	_LOGI (LOGD_DEVICE | LOGD_IP6,
+	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started...");
 
 	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -4265,16 +4917,11 @@ nm_device_activate_ip6_config_timeout (gpointer user_data)
 
 	priv->ip6_state = IP_FAIL;
 
-	/* If IPv6 failed and IPv4 failed, the activation fails */
-	if (priv->ip4_state == IP_FAIL)
-		nm_device_state_changed (self,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	nm_device_check_ip_failed (self, FALSE);
 
 out:
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) complete.",
-	             iface);
+	_LOGI (LOGD_DEVICE | LOGD_IP6,
+	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete.");
 	return FALSE;
 }
 
@@ -4297,9 +4944,8 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 
 	activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6);
 
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) scheduled...",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE | LOGD_IP6,
+	       "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled...");
 }
 
 static void
@@ -4320,16 +4966,19 @@ share_init (void)
 	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
 	                    NULL };
 	char **iter;
+	int errsv;
 
 	if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_forward", "1")) {
-		nm_log_err (LOGD_SHARING, "Error starting IP forwarding: (%d) %s",
-					errno, strerror (errno));
+		errsv = errno;
+		nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s",
+		            errsv, strerror (errsv));
 		return FALSE;
 	}
 
 	if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_dynaddr", "1")) {
-		nm_log_err (LOGD_SHARING, "error starting IP forwarding: (%d) %s",
-					errno, strerror (errno));
+		errsv = errno;
+		nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s",
+		            errsv, strerror (errsv));
 	}
 
 	for (iter = modules; *iter; iter++) {
@@ -4339,7 +4988,7 @@ share_init (void)
 
 		if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
 		                   share_child_setup, NULL, NULL, NULL, &status, &error)) {
-			nm_log_err (LOGD_SHARING, "error loading NAT module %s: (%d) %s",
+			nm_log_err (LOGD_SHARING, "share: error loading NAT module %s: (%d) %s",
 			            *iter, error ? error->code : 0,
 			            (error && error->message) ? error->message : "unknown");
 			if (error)
@@ -4412,9 +5061,8 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 	nm_act_request_set_shared (req, TRUE);
 
 	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) {
-		nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s",
-		            nm_device_get_iface (self), ip_iface,
-		            (error && error->message) ? error->message : "(unknown)");
+		_LOGE (LOGD_SHARING, "share: (%s) failed to start dnsmasq: %s",
+		       ip_iface, (error && error->message) ? error->message : "(unknown)");
 		g_error_free (error);
 		nm_act_request_set_shared (req, FALSE);
 		return FALSE;
@@ -4429,13 +5077,12 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 static void
 send_arps (NMDevice *self, const char *mode_arg)
 {
-	const char *argv[] = { "/sbin/arping", mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL };
+	const char *argv[] = { NULL, mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL };
 	int ip_arg = G_N_ELEMENTS (argv) - 2;
 	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
 	int i, num;
-	NMIP4Address *addr;
-	guint32 ipaddr;
+	NMIPAddress *addr;
 	GError *error = NULL;
 
 	connection = nm_device_get_connection (self);
@@ -4444,24 +5091,33 @@ send_arps (NMDevice *self, const char *mode_arg)
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (!s_ip4)
 		return;
-	num = nm_setting_ip4_config_get_num_addresses (s_ip4);
+	num = nm_setting_ip_config_get_num_addresses (s_ip4);
+	if (num == 0)
+		return;
+
+	argv[0] = nm_utils_find_helper ("arping", NULL, NULL);
+	if (!argv[0]) {
+		_LOGW (LOGD_DEVICE | LOGD_IP4, "arping could not be found; no ARPs will be sent");
+		return;
+	}
 
 	for (i = 0; i < num; i++) {
-		addr = nm_setting_ip4_config_get_address (s_ip4, i);
-		ipaddr = nm_ip4_address_get_address (addr);
-		argv[ip_arg] = (char *) nm_utils_inet4_ntop (ipaddr, NULL);
-
-		nm_log_dbg (LOGD_DEVICE | LOGD_IP4,
-		            "Running arping %s -I %s %s",
-		            mode_arg, nm_device_get_iface (self), argv[ip_arg]);
-		g_spawn_async (NULL, (char **) argv, NULL,
-		               G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
-		               nm_unblock_posix_signals,
-		               NULL, NULL, &error);
-		if (error) {
-			nm_log_warn (LOGD_DEVICE | LOGD_IP4,
-			             "Could not send ARP for local address %s: %s",
-			             argv[ip_arg], error->message);
+		gs_free char *tmp_str = NULL;
+		gboolean success;
+
+		addr = nm_setting_ip_config_get_address (s_ip4, i);
+		argv[ip_arg] = nm_ip_address_get_address (addr);
+
+		_LOGD (LOGD_DEVICE | LOGD_IP4,
+		       "arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv)));
+		success = g_spawn_async (NULL, (char **) argv, NULL,
+		                         G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+		                         nm_unblock_posix_signals,
+		                         NULL, NULL, &error);
+		if (!success) {
+			_LOGW (LOGD_DEVICE | LOGD_IP4,
+			       "arping: could not send ARP for local address %s: %s",
+			       argv[ip_arg], error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -4497,7 +5153,7 @@ arp_announce (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
 	int num;
 
 	arp_cleanup (self);
@@ -4511,7 +5167,7 @@ arp_announce (NMDevice *self)
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (!s_ip4)
 		return;
-	num = nm_setting_ip4_config_get_num_addresses (s_ip4);
+	num = nm_setting_ip_config_get_num_addresses (s_ip4);
 	if (num == 0)
 		return;
 
@@ -4525,33 +5181,33 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
-	const char *iface, *method;
+	const char *method;
 	NMConnection *connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	int ip_ifindex;
 
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET);
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) started...",
-	             iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started...");
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
 	connection = nm_act_request_get_connection (req);
 	g_assert (connection);
 
-	/* Device should be up before we can do anything with it */
-	if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) {
-		nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration",
-		             iface, nm_device_get_ip_iface (self));
+	/* Interface must be IFF_UP before IP config can be applied */
+	ip_ifindex = nm_device_get_ip_ifindex (self);
+	if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
+		nm_platform_link_set_up (ip_ifindex);
+		if (!nm_platform_link_is_up (ip_ifindex))
+			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
 	/* NULL to use the existing priv->dev_ip4_config */
 	if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
-		nm_log_info (LOGD_DEVICE | LOGD_IP4,
-			         "Activation (%s) Stage 5 of 5 (IPv4 Commit) failed",
-					 iface);
+		_LOGI (LOGD_DEVICE | LOGD_IP4,
+		       "Activation: Stage 5 of 5 (IPv4 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		goto out;
 	}
@@ -4561,7 +5217,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
 		if (!start_sharing (self, priv->ip4_config)) {
-			nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IPv4 Commit) start sharing failed.", iface);
+			_LOGW (LOGD_SHARING, "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed.");
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
 			goto out;
 		}
@@ -4593,8 +5249,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 		nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
 
 out:
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) complete.",
-	             iface);
+	_LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete.");
 
 	return FALSE;
 }
@@ -4605,17 +5260,16 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf
 	NMDevicePrivate *priv;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (NM_IS_IP4_CONFIG (config));
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	g_clear_object (&priv->dev_ip4_config);
-	priv->dev_ip4_config = g_object_ref (config);
+	if (config)
+		priv->dev_ip4_config = g_object_ref (config);
 
 	activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET);
 
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-		         "Activation (%s) Stage 5 of 5 (IPv4 Configure Commit) scheduled...",
-		         nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE | LOGD_IP4,
+	       "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled...");
 }
 
 gboolean
@@ -4639,27 +5293,27 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO;
 	NMActRequest *req;
-	const char *iface;
 	NMConnection *connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	int ip_ifindex;
 
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, AF_INET6);
 
-	iface = nm_device_get_iface (self);
-	nm_log (LOGD_DEVICE, level, "Activation (%s) Stage 5 of 5 (IPv6 Commit) started...", iface);
+	_LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started...");
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
 	connection = nm_act_request_get_connection (req);
 	g_assert (connection);
 
-	/* Device should be up before we can do anything with it */
-	g_warn_if_fail (nm_platform_link_is_up (nm_device_get_ip_ifindex (self)));
-
-	/* Allow setting MTU etc */
-	if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit)
-		NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self);
+	/* Interface must be IFF_UP before IP config can be applied */
+	ip_ifindex = nm_device_get_ip_ifindex (self);
+	if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
+		nm_platform_link_set_up (ip_ifindex);
+		if (!nm_platform_link_is_up (ip_ifindex))
+			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
+	}
 
 	if (ip6_config_merge_and_apply (self, TRUE, &reason)) {
 		/* If IPv6 wasn't the first IP to complete, and DHCP was used,
@@ -4686,13 +5340,12 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 		if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
 			nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE);
 	} else {
-		nm_log_warn (LOGD_DEVICE | LOGD_IP6,
-			         "Activation (%s) Stage 5 of 5 (IPv6 Commit) failed",
-					 iface);
+		_LOGW (LOGD_DEVICE | LOGD_IP6,
+		       "Activation: Stage 5 of 5 (IPv6 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	}
 
-	nm_log (LOGD_DEVICE, level, "Activation (%s) Stage 5 of 5 (IPv6 Commit) complete.", iface);
+	_LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete.");
 
 	return FALSE;
 }
@@ -4705,11 +5358,16 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
+	/* If IP had previously failed, move it back to IP_CONF since we
+	 * clearly now have configuration.
+	 */
+	if (priv->ip6_state == IP_FAIL)
+		priv->ip6_state = IP_CONF;
+
 	activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6);
 
-	nm_log (LOGD_DEVICE | LOGD_IP6, level,
-		    "Activation (%s) Stage 5 of 5 (IPv6 Commit) scheduled...",
-		    nm_device_get_iface (self));
+	_LOG (level, LOGD_DEVICE | LOGD_IP6,
+	      "Activation: Stage 5 of 5 (IPv6 Commit) scheduled...");
 }
 
 gboolean
@@ -4775,7 +5433,7 @@ _update_ip4_address (NMDevice *self)
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		nm_log_err (LOGD_IP4, "couldn't open control socket.");
+		_LOGE (LOGD_IP4, "couldn't open control socket.");
 		return;
 	}
 
@@ -4790,17 +5448,17 @@ _update_ip4_address (NMDevice *self)
 }
 
 gboolean
-nm_device_get_is_nm_owned (NMDevice *device)
+nm_device_get_is_nm_owned (NMDevice *self)
 {
-	return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned;
+	return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 }
 
 void
-nm_device_set_nm_owned (NMDevice *device)
+nm_device_set_nm_owned (NMDevice *self)
 {
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 
-	NM_DEVICE_GET_PRIVATE (device)->is_nm_owned = TRUE;
+	NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE;
 }
 
 /*
@@ -4814,6 +5472,7 @@ static gboolean
 delete_on_deactivate_link_delete (gpointer user_data)
 {
 	DeleteOnDeactivateData *data = user_data;
+	NMDevice *self = data->device;
 
 	if (data->device) {
 		NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (data->device);
@@ -4822,8 +5481,8 @@ delete_on_deactivate_link_delete (gpointer user_data)
 		priv->delete_on_deactivate_data = NULL;
 	}
 
-	nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
-	                         data->ifindex, data->idle_add_id);
+	_LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
+	       data->ifindex, data->idle_add_id);
 	nm_platform_link_delete (data->ifindex);
 	g_free (data);
 	return FALSE;
@@ -4841,8 +5500,8 @@ delete_on_deactivate_unschedule (NMDevice *self)
 
 		g_source_remove (data->idle_add_id);
 		g_object_remove_weak_pointer (G_OBJECT (self), (void **) &data->device);
-		nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)",
-		                         data->ifindex, data->idle_add_id);
+		_LOGD (LOGD_DEVICE, "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)",
+		       data->ifindex, data->idle_add_id);
 		g_free (data);
 	}
 }
@@ -4857,6 +5516,8 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
 		return;
 	if (!priv->is_nm_owned)
 		return;
+	if (priv->queued_act_request)
+		return;
 	if (!nm_device_is_software (self))
 		return;
 	if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED)
@@ -4872,17 +5533,17 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
 	data->idle_add_id = g_idle_add (delete_on_deactivate_link_delete, data);
 	priv->delete_on_deactivate_data = data;
 
-	nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: schedule cleanup and delete virtual link #%d for [%s] (id=%u)",
-	                         ifindex, nm_device_get_iface (self), data->idle_add_id);
+	_LOGD (LOGD_DEVICE, "delete_on_deactivate: schedule cleanup and delete virtual link #%d (id=%u)",
+	       ifindex, data->idle_add_id);
 }
 
 static void
-disconnect_cb (NMDevice *device,
+disconnect_cb (NMDevice *self,
                DBusGMethodInvocation *context,
                GError *error,
                gpointer user_data)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	GError *local = NULL;
 
 	if (error) {
@@ -4898,9 +5559,9 @@ disconnect_cb (NMDevice *device,
 		dbus_g_method_return_error (context, local);
 		g_error_free (local);
 	} else {
-		priv->autoconnect = FALSE;
+		nm_device_set_autoconnect (self, FALSE);
 
-		nm_device_state_changed (device,
+		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
 		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
 		dbus_g_method_return (context);
@@ -4908,12 +5569,12 @@ disconnect_cb (NMDevice *device,
 }
 
 static void
-impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
+impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context)
 {
 	NMConnection *connection;
 	GError *error = NULL;
 
-	if (NM_DEVICE_GET_PRIVATE (device)->act_request == NULL) {
+	if (NM_DEVICE_GET_PRIVATE (self)->act_request == NULL) {
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "This device is not active");
@@ -4922,11 +5583,11 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
 		return;
 	}
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	/* Ask the manager to authenticate this request for us */
-	g_signal_emit (device, signals[AUTH_REQUEST], 0,
+	g_signal_emit (self, signals[AUTH_REQUEST], 0,
 	               context,
 	               connection,
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
@@ -4936,6 +5597,46 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
 }
 
 static void
+delete_cb (NMDevice *self,
+           DBusGMethodInvocation *context,
+           GError *error,
+           gpointer user_data)
+{
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
+	/* Authorized */
+	nm_platform_link_delete (nm_device_get_ifindex (self));
+	dbus_g_method_return (context);
+}
+
+static void
+impl_device_delete (NMDevice *self, DBusGMethodInvocation *context)
+{
+	GError *error = NULL;
+
+	if (!nm_device_is_software (self)) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_SOFTWARE,
+		                             "This device is not a software device");
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		return;
+	}
+
+	/* Ask the manager to authenticate this request for us */
+	g_signal_emit (self, signals[AUTH_REQUEST], 0,
+	               context,
+	               NULL,
+	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+	               TRUE,
+	               delete_cb,
+	               NULL);
+}
+
+static void
 _device_activate (NMDevice *self, NMActRequest *req)
 {
 	NMDevicePrivate *priv;
@@ -4949,9 +5650,8 @@ _device_activate (NMDevice *self, NMActRequest *req)
 	connection = nm_act_request_get_connection (req);
 	g_assert (connection);
 
-	nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'",
-	             nm_device_get_iface (self),
-	             nm_connection_get_id (connection));
+	_LOGI (LOGD_DEVICE, "Activation: starting connection '%s'",
+	       nm_connection_get_id (connection));
 
 	delete_on_deactivate_unschedule (self);
 
@@ -4986,8 +5686,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 	priv->queued_act_request = g_object_ref (req);
 
 	/* Deactivate existing activation request first */
-	nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.",
-	             nm_device_get_iface (self));
+	_LOGI (LOGD_DEVICE, "disconnecting for new activation request.");
 	nm_device_state_changed (self,
 	                         NM_DEVICE_STATE_DEACTIVATING,
 	                         NM_DEVICE_STATE_REASON_NONE);
@@ -5000,14 +5699,14 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
  *
  */
 gboolean
-nm_device_is_activating (NMDevice *device)
+nm_device_is_activating (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceState state;
 
-	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	state = nm_device_get_state (device);
+	state = nm_device_get_state (self);
 	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_SECONDARIES)
 		return TRUE;
 
@@ -5021,7 +5720,7 @@ nm_device_is_activating (NMDevice *device)
 
 /* IP Configuration stuff */
 
-NMDHCP4Config *
+NMDhcp4Config *
 nm_device_get_dhcp4_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
@@ -5041,6 +5740,7 @@ nm_device_get_ip4_config (NMDevice *self)
 static gboolean
 nm_device_set_ip4_config (NMDevice *self,
                           NMIP4Config *new_config,
+                          guint32 default_route_metric,
                           gboolean commit,
                           NMDeviceStateReason *reason)
 {
@@ -5062,7 +5762,12 @@ nm_device_set_ip4_config (NMDevice *self,
 
 	/* Always commit to nm-platform to update lifetimes */
 	if (commit && new_config) {
-		success = nm_ip4_config_commit (new_config, ip_ifindex);
+		gboolean assumed = nm_device_uses_assumed_connection (self);
+
+		/* for assumed devices we set the device_route_metric to the default which will
+		 * stop nm_platform_ip4_address_sync() to replace the device routes. */
+		success = nm_ip4_config_commit (new_config, ip_ifindex,
+		                                assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric);
 		if (!success)
 			reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 	}
@@ -5073,8 +5778,8 @@ nm_device_set_ip4_config (NMDevice *self,
 			 * this causes a re-read and reset. This should only happen for relevant changes */
 			nm_ip4_config_replace (old_config, new_config, &has_changes);
 			if (has_changes) {
-				nm_log_dbg (LOGD_IP4, "(%s): update IP4Config instance (%s)",
-				            ip_iface, nm_ip4_config_get_dbus_path (old_config));
+				_LOGD (LOGD_IP4, "update IP4Config instance (%s)",
+				       nm_ip4_config_get_dbus_path (old_config));
 			}
 		} else {
 			has_changes = TRUE;
@@ -5085,18 +5790,20 @@ nm_device_set_ip4_config (NMDevice *self,
 				nm_ip4_config_export (new_config);
 			}
 
-			nm_log_dbg (LOGD_IP4, "(%s): set IP4Config instance (%s)",
-			            ip_iface, nm_ip4_config_get_dbus_path (new_config));
+			_LOGD (LOGD_IP4, "set IP4Config instance (%s)",
+			       nm_ip4_config_get_dbus_path (new_config));
 		}
 	} else if (old_config) {
 		has_changes = TRUE;
 		priv->ip4_config = NULL;
-		nm_log_dbg (LOGD_IP4, "(%s): clear IP4Config instance (%s)",
-		            ip_iface, nm_ip4_config_get_dbus_path (old_config));
+		_LOGD (LOGD_IP4, "clear IP4Config instance (%s)",
+		       nm_ip4_config_get_dbus_path (old_config));
 		/* Device config is invalid if combined config is invalid */
 		g_clear_object (&priv->dev_ip4_config);
 	}
 
+	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
+
 	if (has_changes) {
 		_update_ip4_address (self);
 
@@ -5107,7 +5814,7 @@ nm_device_set_ip4_config (NMDevice *self,
 		if (old_config != priv->ip4_config && old_config)
 			g_object_unref (old_config);
 
-		if (nm_device_uses_generated_connection (self)) {
+		if (nm_device_uses_generated_assumed_connection (self)) {
 			NMConnection *connection = nm_device_get_connection (self);
 			NMSetting *s_ip4;
 
@@ -5128,9 +5835,9 @@ nm_device_set_ip4_config (NMDevice *self,
 }
 
 void
-nm_device_set_vpn4_config (NMDevice *device, NMIP4Config *config)
+nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->vpn4_config == config)
 		return;
@@ -5140,10 +5847,25 @@ nm_device_set_vpn4_config (NMDevice *device, NMIP4Config *config)
 		priv->vpn4_config = g_object_ref (config);
 
 	/* NULL to use existing configs */
-	if (!ip4_config_merge_and_apply (device, NULL, TRUE, NULL)) {
-		nm_log_warn (LOGD_IP4, "(%s): failed to set VPN routes for device",
-			         nm_device_get_ip_iface (device));
-	}
+	if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+		_LOGW (LOGD_IP4, "failed to set VPN routes for device");
+}
+
+void
+nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->wwan_ip4_config == config)
+		return;
+
+	g_clear_object (&priv->wwan_ip4_config);
+	if (config)
+		priv->wwan_ip4_config = g_object_ref (config);
+
+	/* NULL to use existing configs */
+	if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+		_LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration");
 }
 
 static gboolean
@@ -5181,8 +5903,8 @@ nm_device_set_ip6_config (NMDevice *self,
 			 * this causes a re-read and reset. This should only happen for relevant changes */
 			nm_ip6_config_replace (old_config, new_config, &has_changes);
 			if (has_changes) {
-				nm_log_dbg (LOGD_IP6, "(%s): update IP6Config instance (%s)",
-				            ip_iface, nm_ip6_config_get_dbus_path (old_config));
+				_LOGD (LOGD_IP6, "update IP6Config instance (%s)",
+				       nm_ip6_config_get_dbus_path (old_config));
 			}
 		} else {
 			has_changes = TRUE;
@@ -5193,16 +5915,18 @@ nm_device_set_ip6_config (NMDevice *self,
 				nm_ip6_config_export (new_config);
 			}
 
-			nm_log_dbg (LOGD_IP4, "(%s): set IP6Config instance (%s)",
-			            ip_iface, nm_ip6_config_get_dbus_path (new_config));
+			_LOGD (LOGD_IP4, "set IP6Config instance (%s)",
+			       nm_ip6_config_get_dbus_path (new_config));
 		}
 	} else if (old_config) {
 		has_changes = TRUE;
 		priv->ip6_config = NULL;
-		nm_log_dbg (LOGD_IP6, "(%s): clear IP6Config instance (%s)",
-		            ip_iface, nm_ip6_config_get_dbus_path (old_config));
+		_LOGD (LOGD_IP6, "clear IP6Config instance (%s)",
+		       nm_ip6_config_get_dbus_path (old_config));
 	}
 
+	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+
 	if (has_changes) {
 		if (old_config != priv->ip6_config)
 			g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
@@ -5211,7 +5935,7 @@ nm_device_set_ip6_config (NMDevice *self,
 		if (old_config != priv->ip6_config && old_config)
 			g_object_unref (old_config);
 
-		if (nm_device_uses_generated_connection (self)) {
+		if (nm_device_uses_generated_assumed_connection (self)) {
 			NMConnection *connection = nm_device_get_connection (self);
 			NMSetting *s_ip6;
 
@@ -5232,9 +5956,9 @@ nm_device_set_ip6_config (NMDevice *self,
 }
 
 void
-nm_device_set_vpn6_config (NMDevice *device, NMIP6Config *config)
+nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->vpn6_config == config)
 		return;
@@ -5244,13 +5968,28 @@ nm_device_set_vpn6_config (NMDevice *device, NMIP6Config *config)
 		priv->vpn6_config = g_object_ref (config);
 
 	/* NULL to use existing configs */
-	if (!ip6_config_merge_and_apply (device, TRUE, NULL)) {
-		nm_log_warn (LOGD_IP6, "(%s): failed to set VPN routes for device",
-			         nm_device_get_ip_iface (device));
-	}
+	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+		_LOGW (LOGD_IP6, "failed to set VPN routes for device");
+}
+
+void
+nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->wwan_ip6_config == config)
+		return;
+
+	g_clear_object (&priv->wwan_ip6_config);
+	if (config)
+		priv->wwan_ip6_config = g_object_ref (config);
+
+	/* NULL to use existing configs */
+	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+		_LOGW (LOGD_IP6, "failed to set WWAN IPv6 configuration");
 }
 
-NMDHCP6Config *
+NMDhcp6Config *
 nm_device_get_dhcp6_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
@@ -5336,16 +6075,7 @@ ip_check_gw_ping_cleanup (NMDevice *self)
 	}
 
 	if (priv->gw_ping.pid) {
-		guint count = 20;
-		int status;
-
-		kill (priv->gw_ping.pid, SIGKILL);
-		do {
-			if (waitpid (priv->gw_ping.pid, &status, WNOHANG) != 0)
-				break;
-			g_usleep (G_USEC_PER_SEC / 20);
-		} while (count--);
-
+		nm_utils_kill_child_async (priv->gw_ping.pid, SIGTERM, priv->gw_ping.log_domain, "ping", 1000, NULL, NULL);
 		priv->gw_ping.pid = 0;
 	}
 }
@@ -5355,7 +6085,6 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *iface;
 	guint log_domain = priv->gw_ping.log_domain;
 
 	if (!priv->gw_ping.watch)
@@ -5363,17 +6092,15 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
 	priv->gw_ping.watch = 0;
 	priv->gw_ping.pid = 0;
 
-	iface = nm_device_get_iface (self);
-
 	if (WIFEXITED (status)) {
 		if (WEXITSTATUS (status) == 0)
-			nm_log_dbg (log_domain, "(%s): gateway ping succeeded", iface);
+			_LOGD (log_domain, "ping: gateway ping succeeded");
 		else {
-			nm_log_warn (log_domain, "(%s): gateway ping failed with error code %d",
-				         iface, WEXITSTATUS (status));
+			_LOGW (log_domain, "ping: gateway ping failed with error code %d",
+			       WEXITSTATUS (status));
 		}
 	} else
-		nm_log_warn (log_domain, "(%s): ping stopped unexpectedly with status %d", iface, status);
+		_LOGW (log_domain, "ping: stopped unexpectedly with status %d", status);
 
 	/* We've got connectivity, proceed to pre_up */
 	ip_check_gw_ping_cleanup (self);
@@ -5388,8 +6115,7 @@ ip_check_ping_timeout_cb (gpointer user_data)
 
 	priv->gw_ping.timeout = 0;
 
-	nm_log_warn (priv->gw_ping.log_domain, "(%s): gateway ping timed out",
-	             nm_device_get_iface (self));
+	_LOGW (priv->gw_ping.log_domain, "ping: gateway ping timed out");
 
 	ip_check_gw_ping_cleanup (self);
 	ip_check_pre_up (self);
@@ -5406,7 +6132,8 @@ spawn_ping (NMDevice *self,
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *args[] = { binary, "-I", nm_device_get_ip_iface (self), "-c", "1", "-w", NULL, address, NULL };
 	GError *error = NULL;
-	char *str_timeout, *cmd;
+	char *str_timeout;
+	gs_free char *tmp_str = NULL;
 	gboolean success;
 
 	g_return_val_if_fail (priv->gw_ping.watch == 0, FALSE);
@@ -5414,13 +6141,8 @@ spawn_ping (NMDevice *self,
 
 	args[6] = str_timeout = g_strdup_printf ("%u", timeout);
 
-	if (nm_logging_enabled (LOGL_DEBUG, log_domain)) {
-		cmd = g_strjoinv (" ", (gchar **) args);
-		nm_log_dbg (log_domain, "(%s): running '%s'",
-		            nm_device_get_iface (self),
-		            cmd);
-		g_free (cmd);
-	}
+	_LOGD (log_domain, "ping: running '%s'",
+	       (tmp_str = g_strjoinv (" ", (gchar **) args)));
 
 	success = g_spawn_async ("/",
 	                         (gchar **) args,
@@ -5435,7 +6157,7 @@ spawn_ping (NMDevice *self,
 		priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self);
 		priv->gw_ping.timeout = g_timeout_add_seconds (timeout + 1, ip_check_ping_timeout_cb, self);
 	} else {
-		nm_log_warn (log_domain, "could not spawn %s: %s", binary, error->message);
+		_LOGW (log_domain, "ping: could not spawn %s: %s", binary, error->message);
 		g_clear_error (&error);
 	}
 
@@ -5471,7 +6193,7 @@ nm_device_start_ip_check (NMDevice *self)
 	timeout = nm_setting_connection_get_gateway_ping_timeout (s_con);
 
 	if (timeout) {
-		if (priv->ip4_state == IP_DONE) {
+		if (priv->ip4_config && priv->ip4_state == IP_DONE) {
 			guint gw = 0;
 
 			ping_binary = "/usr/bin/ping";
@@ -5524,9 +6246,9 @@ nm_device_is_up (NMDevice *self)
 }
 
 static gboolean
-is_up (NMDevice *device)
+is_up (NMDevice *self)
 {
-	int ifindex = nm_device_get_ip_ifindex (device);
+	int ifindex = nm_device_get_ip_ifindex (self);
 
 	return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE;
 }
@@ -5539,7 +6261,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	nm_log_dbg (LOGD_HW, "(%s): bringing up device.", nm_device_get_iface (self));
+	_LOGD (LOGD_HW, "bringing up device.");
 
 	if (NM_DEVICE_GET_CLASS (self)->bring_up) {
 		if (!NM_DEVICE_GET_CLASS (self)->bring_up (self, no_firmware))
@@ -5561,9 +6283,9 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 	if (!device_is_up) {
 		if (block)
-			nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self));
+			_LOGW (LOGD_HW, "device not up after timeout!");
 		else
-			nm_log_dbg (LOGD_HW, "(%s): device not up immediately", nm_device_get_iface (self));
+			_LOGD (LOGD_HW, "device not up immediately");
 		return FALSE;
 	}
 
@@ -5589,18 +6311,18 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 }
 
 static void
-check_carrier (NMDevice *device)
+check_carrier (NMDevice *self)
 {
-	int ifindex = nm_device_get_ip_ifindex (device);
+	int ifindex = nm_device_get_ip_ifindex (self);
 
-	if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
-		nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex));
+	if (!device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
+		nm_device_set_carrier (self, nm_platform_link_is_connected (ifindex));
 }
 
 static gboolean
-bring_up (NMDevice *device, gboolean *no_firmware)
+bring_up (NMDevice *self, gboolean *no_firmware)
 {
-	int ifindex = nm_device_get_ip_ifindex (device);
+	int ifindex = nm_device_get_ip_ifindex (self);
 	gboolean result;
 
 	if (ifindex <= 0) {
@@ -5614,8 +6336,8 @@ bring_up (NMDevice *device, gboolean *no_firmware)
 		*no_firmware = nm_platform_get_error () == NM_PLATFORM_ERROR_NO_FIRMWARE;
 
 	/* Store carrier immediately. */
-	if (result && device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT))
-		check_carrier (device);
+	if (result && device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT))
+		check_carrier (self);
 
 	return result;
 }
@@ -5627,7 +6349,7 @@ nm_device_take_down (NMDevice *self, gboolean block)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	nm_log_dbg (LOGD_HW, "(%s): taking down device.", nm_device_get_iface (self));
+	_LOGD (LOGD_HW, "taking down device.");
 
 	if (NM_DEVICE_GET_CLASS (self)->take_down) {
 		if (!NM_DEVICE_GET_CLASS (self)->take_down (self))
@@ -5649,22 +6371,22 @@ nm_device_take_down (NMDevice *self, gboolean block)
 
 	if (device_is_up) {
 		if (block)
-			nm_log_warn (LOGD_HW, "(%s): device not down after timeout!", nm_device_get_iface (self));
+			_LOGW (LOGD_HW, "device not down after timeout!");
 		else
-			nm_log_dbg (LOGD_HW, "(%s): device not down immediately", nm_device_get_iface (self));
+			_LOGD (LOGD_HW, "device not down immediately");
 	}
 }
 
 static gboolean
-take_down (NMDevice *device)
+take_down (NMDevice *self)
 {
-	int ifindex = nm_device_get_ip_ifindex (device);
+	int ifindex = nm_device_get_ip_ifindex (self);
 
 	if (ifindex > 0)
 		return nm_platform_link_set_down (ifindex);
 
 	/* devices without ifindex are always up. */
-	nm_log_dbg (LOGD_HW, "(%s): cannot take down device without ifindex", nm_device_get_iface (device));
+	_LOGD (LOGD_HW, "cannot take down device without ifindex");
 	return FALSE;
 }
 
@@ -5689,11 +6411,11 @@ nm_device_get_firmware_missing (NMDevice *self)
 }
 
 static NMIP4Config *
-find_ip4_lease_config (NMDevice *device,
+find_ip4_lease_config (NMDevice *self,
                        NMConnection *connection,
                        NMIP4Config *ext_ip4_config)
 {
-	const char *ip_iface = nm_device_get_ip_iface (device);
+	const char *ip_iface = nm_device_get_ip_iface (self);
 	GSList *leases, *liter;
 	NMIP4Config *found = NULL;
 
@@ -5703,7 +6425,8 @@ find_ip4_lease_config (NMDevice *device,
 	leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (),
 	                                               ip_iface,
 	                                               nm_connection_get_uuid (connection),
-	                                               FALSE);
+	                                               FALSE,
+	                                               nm_device_get_ip4_route_metric (self));
 	for (liter = leases; liter && !found; liter = liter->next) {
 		NMIP4Config *lease_config = liter->data;
 		const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0);
@@ -5722,13 +6445,13 @@ find_ip4_lease_config (NMDevice *device,
 }
 
 static void
-capture_lease_config (NMDevice *device,
+capture_lease_config (NMDevice *self,
                       NMIP4Config *ext_ip4_config,
                       NMIP4Config **out_ip4_config,
                       NMIP6Config *ext_ip6_config,
                       NMIP6Config **out_ip6_config)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const GSList *connections, *citer;
 	guint i;
 	gboolean dhcp_used = FALSE;
@@ -5767,13 +6490,13 @@ capture_lease_config (NMDevice *device,
 		NMConnection *candidate = citer->data;
 		const char *method;
 
-		if (!nm_device_check_connection_compatible (device, candidate))
+		if (!nm_device_check_connection_compatible (self, candidate))
 			continue;
 
 		/* IPv4 leases */
 		method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
 		if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
-			*out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config);
+			*out_ip4_config = find_ip4_lease_config (self, candidate, ext_ip4_config);
 			if (*out_ip4_config)
 				return;
 		}
@@ -5805,7 +6528,8 @@ update_ip_config (NMDevice *self, gboolean initial)
 	/* IPv4 */
 	g_clear_object (&priv->ext_ip4_config);
 	priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf);
-
+	priv->ext_ip4_config_had_any_addresses = (   priv->ext_ip4_config
+	                                          && nm_ip4_config_get_num_addresses (priv->ext_ip4_config) > 0);
 	if (priv->ext_ip4_config) {
 		if (initial) {
 			g_clear_object (&priv->dev_ip4_config);
@@ -5815,6 +6539,8 @@ update_ip_config (NMDevice *self, gboolean initial)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config);
 		if (priv->vpn4_config)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config);
+		if (priv->wwan_ip4_config)
+			nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config);
 
 		ip4_config_merge_and_apply (self, NULL, FALSE, NULL);
 	}
@@ -5822,16 +6548,20 @@ update_ip_config (NMDevice *self, gboolean initial)
 	/* IPv6 */
 	g_clear_object (&priv->ext_ip6_config);
 	priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	priv->ext_ip6_config_had_any_addresses = (   priv->ext_ip6_config
+	                                          && nm_ip6_config_get_num_addresses (priv->ext_ip6_config) > 0);
 	if (priv->ext_ip6_config) {
 
 		/* Check this before modifying ext_ip6_config */
 		linklocal6_just_completed = priv->linklocal6_timeout_id &&
-		                            linklocal6_config_is_ready (priv->ext_ip6_config);
+		                            have_ip6_address (priv->ext_ip6_config, TRUE);
 
 		if (priv->ac_ip6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config);
 		if (priv->dhcp6_ip6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config);
+		if (priv->wwan_ip6_config)
+			nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config);
 		if (priv->vpn6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config);
 
@@ -5847,9 +6577,9 @@ update_ip_config (NMDevice *self, gboolean initial)
 }
 
 void
-nm_device_capture_initial_config (NMDevice *dev)
+nm_device_capture_initial_config (NMDevice *self)
 {
-	update_ip_config (dev, TRUE);
+	update_ip_config (self, TRUE);
 }
 
 static gboolean
@@ -5864,22 +6594,33 @@ queued_ip_config_change (gpointer user_data)
 
 	priv->queued_ip_config_id = 0;
 	update_ip_config (self, FALSE);
+
+	/* If no IPv6 link-local address exists but other addresses do then we
+	 * must add the LL address to remain conformant with RFC 3513 chapter 2.1
+	 * ("Addressing Model"): "All interfaces are required to have at least
+	 * one link-local unicast address".
+	 */
+	if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config))
+		check_and_add_ipv6ll_addr (self);
+
 	return FALSE;
 }
 
 static void
-device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data)
+device_ip_changed (NMPlatform *platform,
+                   int ifindex,
+                   gpointer platform_object,
+                   NMPlatformSignalChangeType change_type,
+                   NMPlatformReason reason,
+                   NMDevice *self)
 {
-	NMDevice *self = user_data;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (nm_device_get_ip_ifindex (self) == ifindex) {
-		NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
 		if (!priv->queued_ip_config_id)
 			priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self);
 
-		nm_log_dbg (LOGD_DEVICE, "(%s): queued IP config change",
-		            nm_device_get_iface (self));
+		_LOGD (LOGD_DEVICE, "queued IP config change");
 	}
 }
 
@@ -5889,8 +6630,7 @@ nm_device_queued_ip_config_change_clear (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->queued_ip_config_id) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued IP config change",
-		            nm_device_get_iface (self));
+		_LOGD (LOGD_DEVICE, "clearing queued IP config change");
 		g_source_remove (priv->queued_ip_config_id);
 		priv->queued_ip_config_id = 0;
 	}
@@ -5898,19 +6638,19 @@ nm_device_queued_ip_config_change_clear (NMDevice *self)
 
 /**
  * nm_device_get_managed():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  *
  * Returns: %TRUE if the device is managed
  */
 gboolean
-nm_device_get_managed (NMDevice *device)
+nm_device_get_managed (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 	gboolean managed;
 
-	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Return the composite of all managed flags.  However, if the device
 	 * is a default-unmanaged device, and would be managed except for the
@@ -5926,30 +6666,30 @@ nm_device_get_managed (NMDevice *device)
 
 /**
  * nm_device_get_unmanaged_flag():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  *
  * Returns: %TRUE if the device is unmanaged for @flag.
  */
 gboolean
-nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag)
+nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag)
 {
-	return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag;
+	return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags, flag);
 }
 
 /**
  * nm_device_get_default_unmanaged():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  *
  * Returns: %TRUE if the device is by default unmanaged
  */
 static gboolean
-nm_device_get_default_unmanaged (NMDevice *device)
+nm_device_get_default_unmanaged (NMDevice *self)
 {
-	return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT);
+	return nm_device_get_unmanaged_flag (self, NM_UNMANAGED_DEFAULT);
 }
 
 void
-nm_device_set_unmanaged (NMDevice *device,
+nm_device_set_unmanaged (NMDevice *self,
                          NMUnmanagedFlags flag,
                          gboolean unmanaged,
                          NMDeviceStateReason reason)
@@ -5957,42 +6697,40 @@ nm_device_set_unmanaged (NMDevice *device,
 	NMDevicePrivate *priv;
 	gboolean was_managed, now_managed;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 	g_return_if_fail (flag <= NM_UNMANAGED_LAST);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	was_managed = nm_device_get_managed (device);
+	was_managed = nm_device_get_managed (self);
 	if (unmanaged)
 		priv->unmanaged_flags |= flag;
 	else
 		priv->unmanaged_flags &= ~flag;
-	now_managed = nm_device_get_managed (device);
+	now_managed = nm_device_get_managed (self);
 
 	if (was_managed != now_managed) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): now %s",
-			        nm_device_get_iface (device),
-			        unmanaged ? "unmanaged" : "managed");
+		_LOGD (LOGD_DEVICE, "now %s", unmanaged ? "unmanaged" : "managed");
 
-		g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_MANAGED);
 
 		if (unmanaged)
-			nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason);
+			nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason);
 		else
-			nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason);
+			nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason);
 	}
 }
 
 void
-nm_device_set_unmanaged_quitting (NMDevice *device)
+nm_device_set_unmanaged_quitting (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* It's OK to block here because we're quitting */
-	if (nm_device_is_activating (device) || priv->state == NM_DEVICE_STATE_ACTIVATED)
-		_set_state_full (device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE);
+	if (nm_device_is_activating (self) || priv->state == NM_DEVICE_STATE_ACTIVATED)
+		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE);
 
-	nm_device_set_unmanaged (device,
+	nm_device_set_unmanaged (self,
 	                         NM_UNMANAGED_INTERNAL,
 	                         TRUE,
 	                         NM_DEVICE_STATE_REASON_REMOVED);
@@ -6000,26 +6738,26 @@ nm_device_set_unmanaged_quitting (NMDevice *device)
 
 /**
  * nm_device_set_initial_unmanaged_flag():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  * @flag: an #NMUnmanagedFlag
  * @unmanaged: %TRUE or %FALSE to set or clear @flag
  *
- * Like nm_device_set_unmanaged() but must be set before the device is exported
- * and does not trigger state changes.  Should only be used when initializing
- * a device.
+ * Like nm_device_set_unmanaged(), but must be set before the device is
+ * initialized by nm_device_finish_init(), and does not trigger state changes.
+ * Should only be used when initializing a device.
  */
 void
-nm_device_set_initial_unmanaged_flag (NMDevice *device,
+nm_device_set_initial_unmanaged_flag (NMDevice *self,
                                       NMUnmanagedFlags flag,
                                       gboolean unmanaged)
 {
 	NMDevicePrivate *priv;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 	g_return_if_fail (flag <= NM_UNMANAGED_LAST);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	g_return_if_fail (priv->path == NULL);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->initialized == FALSE);
 
 	if (unmanaged)
 		priv->unmanaged_flags |= flag;
@@ -6028,60 +6766,54 @@ nm_device_set_initial_unmanaged_flag (NMDevice *device,
 }
 
 void
-nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
+nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout)
 {
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 
-	NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout;
+	NM_DEVICE_GET_PRIVATE (self)->dhcp_timeout = timeout;
 }
 
 void
-nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr)
+nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr)
 {
 	NMDevicePrivate *priv;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
-
-	priv = NM_DEVICE_GET_PRIVATE (device);
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (!addr || nm_utils_hwaddr_valid (addr, ETH_ALEN));
 
-	if (priv->dhcp_anycast_address) {
-		g_byte_array_free (priv->dhcp_anycast_address, TRUE);
-		priv->dhcp_anycast_address = NULL;
-	}
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (addr) {
-		priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN);
-	}
+	g_free (priv->dhcp_anycast_address);
+	priv->dhcp_anycast_address = g_strdup (addr);
 }
 
 /**
  * nm_device_connection_is_available():
- * @device: the #NMDevice
+ * @self: the #NMDevice
  * @connection: the #NMConnection to check for availability
  * @allow_device_override: set to %TRUE to let the device do specific checks
  *
- * Check if @connection is available to be activated on @device.  Normally this
- * only checks if the connection is in @device's AvailableConnections property.
+ * Check if @connection is available to be activated on @self.  Normally this
+ * only checks if the connection is in @self's AvailableConnections property.
  * If @allow_device_override is %TRUE then the device is asked to do specific
  * checks that may bypass the AvailableConnections property.
  *
- * Returns: %TRUE if @connection can be activated on @device
+ * Returns: %TRUE if @connection can be activated on @self
  */
 gboolean
-nm_device_connection_is_available (NMDevice *device,
+nm_device_connection_is_available (NMDevice *self,
                                    NMConnection *connection,
                                    gboolean allow_device_override)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean available = FALSE;
 
-	if (nm_device_get_default_unmanaged (device) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) {
+	if (nm_device_get_default_unmanaged (self) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) {
 		/* default-unmanaged  devices in UNMANAGED state have no available connections
 		 * so we must manually check whether the connection is available here.
 		 */
-		if (   nm_device_check_connection_compatible (device, connection)
-		    && NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, NULL))
+		if (   nm_device_check_connection_compatible (self, connection)
+		    && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL))
 			return TRUE;
 	}
 
@@ -6092,32 +6824,33 @@ nm_device_connection_is_available (NMDevice *device,
 		 * activating but the network isn't available let the device recheck
 		 * availability.
 		 */
-		if (   nm_device_check_connection_compatible (device, connection)
-		    && NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden)
-			available = NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden (device, connection);
+		if (   nm_device_check_connection_compatible (self, connection)
+		    && NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden)
+			available = NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden (self, connection);
 	}
 
 	return available;
 }
 
 static void
-_signal_available_connections_changed (NMDevice *device)
+_signal_available_connections_changed (NMDevice *self)
 {
-	g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_AVAILABLE_CONNECTIONS);
 }
 
 static void
-_clear_available_connections (NMDevice *device, gboolean do_signal)
+_clear_available_connections (NMDevice *self, gboolean do_signal)
 {
-	g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections);
+	g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (self)->available_connections);
 	if (do_signal == TRUE)
-		_signal_available_connections_changed (device);
+		_signal_available_connections_changed (self);
 }
 
 static gboolean
 _try_add_available_connection (NMDevice *self, NMConnection *connection)
 {
-	if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED)
+	if (   nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED
+	    && !nm_device_get_default_unmanaged (self))
 		return FALSE;
 
 	if (nm_device_check_connection_compatible (self, connection)) {
@@ -6132,98 +6865,49 @@ _try_add_available_connection (NMDevice *self, NMConnection *connection)
 }
 
 static gboolean
-_del_available_connection (NMDevice *device, NMConnection *connection)
+_del_available_connection (NMDevice *self, NMConnection *connection)
 {
-	return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection);
+	return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (self)->available_connections, connection);
 }
 
 static gboolean
-connection_requires_carrier (NMConnection *connection)
-{
-	NMSettingIP4Config *s_ip4;
-	NMSettingIP6Config *s_ip6;
-	const char *method;
-	gboolean ip4_carrier_wanted = FALSE, ip6_carrier_wanted = FALSE;
-	gboolean ip4_used = FALSE, ip6_used = FALSE;
-
-	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
-	if (   strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0
-	    && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0) {
-		ip4_carrier_wanted = TRUE;
-
-		/* If IPv4 wants a carrier and cannot fail, the whole connection
-		 * requires a carrier regardless of the IPv6 method.
-		 */
-		s_ip4 = nm_connection_get_setting_ip4_config (connection);
-		if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4))
-			return TRUE;
-	}
-	ip4_used = (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0);
-
-	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0
-	    && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0) {
-		ip6_carrier_wanted = TRUE;
-
-		/* If IPv6 wants a carrier and cannot fail, the whole connection
-		 * requires a carrier regardless of the IPv4 method.
-		 */
-		s_ip6 = nm_connection_get_setting_ip6_config (connection);
-		if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6))
-			return TRUE;
-	}
-	ip6_used = (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0);
-
-	/* If an IP version wants a carrier and and the other IP version isn't
-	 * used, the connection requires carrier since it will just fail without one.
-	 */
-	if (ip4_carrier_wanted && !ip6_used)
-		return TRUE;
-	if (ip6_carrier_wanted && !ip4_used)
-		return TRUE;
-
-	/* If both want a carrier, the whole connection wants a carrier */
-	return ip4_carrier_wanted && ip6_carrier_wanted;
-}
-
-static gboolean
-check_connection_available (NMDevice *device,
+check_connection_available (NMDevice *self,
                             NMConnection *connection,
                             const char *specific_object)
 {
 	/* Connections which require a network connection are not available when
 	 * the device has no carrier, even with ignore-carrer=TRUE.
 	 */
-	if (NM_DEVICE_GET_PRIVATE (device)->carrier == FALSE)
+	if (NM_DEVICE_GET_PRIVATE (self)->carrier == FALSE)
 		return connection_requires_carrier (connection) ? FALSE : TRUE;
 
 	return TRUE;
 }
 
 void
-nm_device_recheck_available_connections (NMDevice *device)
+nm_device_recheck_available_connections (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 	const GSList *connections, *iter;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 
-	priv = NM_DEVICE_GET_PRIVATE(device);
+	priv = NM_DEVICE_GET_PRIVATE(self);
 
 	if (priv->con_provider) {
-		_clear_available_connections (device, FALSE);
+		_clear_available_connections (self, FALSE);
 
 		connections = nm_connection_provider_get_connections (priv->con_provider);
 		for (iter = connections; iter; iter = g_slist_next (iter))
-			_try_add_available_connection (device, NM_CONNECTION (iter->data));
+			_try_add_available_connection (self, NM_CONNECTION (iter->data));
 
-		_signal_available_connections_changed (device);
+		_signal_available_connections_changed (self);
 	}
 }
 
 /**
  * nm_device_get_available_connections:
- * @device: the #NMDevice
+ * @self: the #NMDevice
  * @specific_object: a specific object path if any
  *
  * Returns a list of connections available to activate on the device, taking
@@ -6233,9 +6917,9 @@ nm_device_recheck_available_connections (NMDevice *device)
  * Returns: caller-owned #GPtrArray of #NMConnections
  */
 GPtrArray *
-nm_device_get_available_connections (NMDevice *device, const char *specific_object)
+nm_device_get_available_connections (NMDevice *self, const char *specific_object)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	GHashTableIter iter;
 	guint num_available;
 	NMConnection *connection = NULL;
@@ -6250,7 +6934,7 @@ nm_device_get_available_connections (NMDevice *device, const char *specific_obje
 			 * compatible with it.
 			 */
 			if (   !specific_object
-			    || NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, specific_object))
+			    || NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, specific_object))
 				g_ptr_array_add (array, connection);
 		}
 	}
@@ -6286,14 +6970,14 @@ cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpoin
 }
 
 gboolean
-nm_device_supports_vlans (NMDevice *device)
+nm_device_supports_vlans (NMDevice *self)
 {
-	return nm_platform_link_supports_vlans (nm_device_get_ifindex (device));
+	return nm_platform_link_supports_vlans (nm_device_get_ifindex (self));
 }
 
 /**
  * nm_device_add_pending_action():
- * @device: the #NMDevice to add the pending action to
+ * @self: the #NMDevice to add the pending action to
  * @action: a static string that identifies the action
  * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending.
  * Otherwise, ignore duplicate scheduling of the same action silently.
@@ -6305,9 +6989,9 @@ nm_device_supports_vlans (NMDevice *device)
  * a second time.
  */
 gboolean
-nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending)
+nm_device_add_pending_action (NMDevice *self, const char *action, gboolean assert_not_yet_pending)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	GSList *iter;
 	guint count = 0;
 
@@ -6317,16 +7001,12 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass
 	for (iter = priv->pending_actions; iter; iter = iter->next) {
 		if (!strcmp (action, iter->data)) {
 			if (assert_not_yet_pending) {
-				nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending",
-				             nm_device_get_iface (device),
-				             count + g_slist_length (iter),
-				             action);
+				_LOGW (LOGD_DEVICE, "add_pending_action (%d): '%s' already pending",
+				       count + g_slist_length (iter), action);
 				g_return_val_if_reached (FALSE);
 			} else {
-				nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)",
-				            nm_device_get_iface (device),
-				            count + g_slist_length (iter),
-				            action);
+				_LOGD (LOGD_DEVICE, "add_pending_action (%d): '%s' already pending (expected)",
+				       count + g_slist_length (iter), action);
 			}
 			return FALSE;
 		}
@@ -6336,20 +7016,17 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass
 	priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action));
 	count++;
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'",
-	            nm_device_get_iface (device),
-	            count,
-	            action);
+	_LOGD (LOGD_DEVICE, "add_pending_action (%d): '%s'", count, action);
 
 	if (count == 1)
-		g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION);
 
 	return TRUE;
 }
 
 /**
  * nm_device_remove_pending_action():
- * @device: the #NMDevice to remove the pending action from
+ * @self: the #NMDevice to remove the pending action from
  * @action: a static string that identifies the action
  * @assert_is_pending: if %TRUE, assert that the @action is pending.
  * If %FALSE, don't do anything if the current action is not pending and
@@ -6360,9 +7037,9 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass
  * Returns: whether the @action was pending and is now removed.
  */
 gboolean
-nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending)
+nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean assert_is_pending)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	GSList *iter;
 	guint count = 0;
 
@@ -6370,38 +7047,31 @@ nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean
 
 	for (iter = priv->pending_actions; iter; iter = iter->next) {
 		if (!strcmp (action, iter->data)) {
-			nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'",
-			            nm_device_get_iface (device),
-			            count + g_slist_length (iter->next), /* length excluding 'iter' */
-			            action);
+			_LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s'",
+			       count + g_slist_length (iter->next), /* length excluding 'iter' */
+			       action);
 			g_free (iter->data);
 			priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter);
 			if (priv->pending_actions == NULL)
-				g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION);
+				g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION);
 			return TRUE;
 		}
 		count++;
 	}
 
 	if (assert_is_pending) {
-		nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending",
-		             nm_device_get_iface (device),
-		             count,
-		             action);
+		_LOGW (LOGD_DEVICE, "remove_pending_action (%d): '%s' not pending", count, action);
 		g_return_val_if_reached (FALSE);
-	} else {
-		nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)",
-		             nm_device_get_iface (device),
-		             count,
-		             action);
-	}
+	} else
+		_LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s' not pending (expected)", count, action);
+
 	return FALSE;
 }
 
 gboolean
-nm_device_has_pending_action (NMDevice *device)
+nm_device_has_pending_action (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	return !!priv->pending_actions;
 }
@@ -6409,25 +7079,41 @@ nm_device_has_pending_action (NMDevice *device)
 /***********************************************************/
 
 static void
+_cleanup_ip_pre (NMDevice *self, gboolean deconfigure)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->ip4_state = priv->ip6_state = IP_NONE;
+	nm_device_queued_ip_config_change_clear (self);
+
+	dhcp4_cleanup (self, deconfigure, FALSE);
+	arp_cleanup (self);
+	dhcp6_cleanup (self, deconfigure, FALSE);
+	linklocal6_cleanup (self);
+	addrconf6_cleanup (self);
+	dnsmasq_cleanup (self);
+	aipd_cleanup (self);
+}
+
+static void
 _cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
 
 	/* Clean up when device was deactivated during call to firewall */
-	if (priv->fw_manager) {
-		NMConnection *connection;
-
-		if (priv->fw_call) {
-			nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call);
-			priv->fw_call = NULL;
-		}
+	if (priv->fw_call) {
+		nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call);
+		priv->fw_call = NULL;
+	}
 
-		connection = nm_device_get_connection (self);
-		if (deconfigure && connection) {
-			nm_firewall_manager_remove_from_zone (priv->fw_manager,
-			                                      nm_device_get_ip_iface (self),
-			                                      NULL);
-		}
+	connection = nm_device_get_connection (self);
+	if (   deconfigure
+	    && connection
+	    && !nm_device_uses_assumed_connection (self)) {
+		nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (),
+		                                      nm_device_get_ip_iface (self),
+		                                      NULL);
 	}
 
 	ip_check_gw_ping_cleanup (self);
@@ -6438,17 +7124,8 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure)
 
 	/* Clear any queued transitions */
 	nm_device_queued_state_clear (self);
-	nm_device_queued_ip_config_change_clear (self);
 
-	priv->ip4_state = priv->ip6_state = IP_NONE;
-
-	dhcp4_cleanup (self, deconfigure, FALSE);
-	arp_cleanup (self);
-	dhcp6_cleanup (self, deconfigure, FALSE);
-	linklocal6_cleanup (self);
-	addrconf6_cleanup (self);
-	dnsmasq_cleanup (self);
-	aipd_cleanup (self);
+	_cleanup_ip_pre (self, deconfigure);
 }
 
 static void
@@ -6457,20 +7134,33 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
 
+	priv->default_route.v4_has = FALSE;
+	priv->default_route.v4_is_assumed = TRUE;
+	priv->default_route.v6_has = FALSE;
+	priv->default_route.v6_is_assumed = TRUE;
+
+	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
+	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface; the caller must flush routes and addresses explicitly.
 	 */
-	nm_device_set_ip4_config (self, NULL, TRUE, &ignored);
+	nm_device_set_ip4_config (self, NULL, 0, TRUE, &ignored);
 	nm_device_set_ip6_config (self, NULL, TRUE, &ignored);
 	g_clear_object (&priv->dev_ip4_config);
 	g_clear_object (&priv->ext_ip4_config);
+	g_clear_object (&priv->wwan_ip4_config);
 	g_clear_object (&priv->vpn4_config);
 	g_clear_object (&priv->ip4_config);
 	g_clear_object (&priv->ac_ip6_config);
 	g_clear_object (&priv->ext_ip6_config);
 	g_clear_object (&priv->vpn6_config);
+	g_clear_object (&priv->wwan_ip6_config);
 	g_clear_object (&priv->ip6_config);
 
+	priv->ext_ip4_config_had_any_addresses = FALSE;
+	priv->ext_ip6_config_had_any_addresses = FALSE;
+
 	clear_act_request (self);
 
 	/* Clear legacy IPv4 address property */
@@ -6507,13 +7197,10 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) {
-		nm_log_info (LOGD_DEVICE, "(%s): preparing device",
-		             nm_device_get_iface (self));
-	} else {
-		nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]",
-		             nm_device_get_iface (self), reason_to_string (reason), reason);
-	}
+	if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED)
+		_LOGI (LOGD_DEVICE, "preparing device");
+	else
+		_LOGI (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason);
 
 	/* Save whether or not we tried IPv6 for later */
 	priv = NM_DEVICE_GET_PRIVATE (self);
@@ -6521,7 +7208,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
 	_cleanup_generic_pre (self, TRUE);
 
 	/* Turn off kernel IPv6 */
-	nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
+	set_disable_ipv6 (self, "1");
 	nm_device_ipv6_sysctl_set (self, "accept_ra", "0");
 	nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
 
@@ -6547,6 +7234,170 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
 	_cleanup_generic_post (self, TRUE);
 }
 
+static char *
+bin2hexstr (const char *bytes, gsize len)
+{
+	GString *str;
+	int i;
+
+	g_return_val_if_fail (bytes != NULL, NULL);
+	g_return_val_if_fail (len > 0, NULL);
+
+	str = g_string_sized_new (len * 2 + 1);
+	for (i = 0; i < len; i++) {
+		if (str->len)
+			g_string_append_c (str, ':');
+		g_string_append_printf (str, "%02x", (guint8) bytes[i]);
+	}
+	return g_string_free (str, FALSE);
+}
+
+static char *
+find_dhcp4_address (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint i, n;
+
+	if (!priv->ip4_config)
+		return NULL;
+
+	n = nm_ip4_config_get_num_addresses (priv->ip4_config);
+	for (i = 0; i < n; i++) {
+		const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
+
+		if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
+			return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
+	}
+	return NULL;
+}
+
+void
+nm_device_spawn_iface_helper (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean configured = FALSE;
+	NMConnection *connection;
+	GError *error = NULL;
+	const char *method;
+	GPtrArray *argv;
+	gs_free char *dhcp4_address = NULL;
+
+	if (priv->state != NM_DEVICE_STATE_ACTIVATED)
+		return;
+	if (!nm_device_can_assume_connections (self))
+		return;
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+
+	argv = g_ptr_array_sized_new (10);
+	g_ptr_array_set_free_func (argv, g_free);
+
+	g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper"));
+	g_ptr_array_add (argv, g_strdup ("--ifname"));
+	g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self)));
+	g_ptr_array_add (argv, g_strdup ("--uuid"));
+	g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
+
+	dhcp4_address = find_dhcp4_address (self);
+
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (   priv->ip4_config
+	    && priv->ip4_state == IP_DONE
+	    && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
+	    && priv->dhcp4_client
+	    && dhcp4_address) {
+		NMSettingIPConfig *s_ip4;
+		GBytes *client_id;
+		char *hex_client_id;
+		const char *hostname;
+
+		s_ip4 = nm_connection_get_setting_ip4_config (connection);
+		g_assert (s_ip4);
+
+		g_ptr_array_add (argv, g_strdup ("--priority4"));
+		g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip4_route_metric (self)));
+
+		g_ptr_array_add (argv, g_strdup ("--dhcp4"));
+		g_ptr_array_add (argv, g_strdup (dhcp4_address));
+		if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
+			g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
+
+		client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
+		if (client_id) {
+			g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
+			hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
+			                            g_bytes_get_size (client_id));
+			g_ptr_array_add (argv, hex_client_id);
+		}
+
+		hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
+		if (client_id) {
+			g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
+			g_ptr_array_add (argv, g_strdup (hostname));
+		}
+
+		configured = TRUE;
+	}
+
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	if (   priv->ip6_config
+	    && priv->ip6_state == IP_DONE
+	    && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
+	    && priv->rdisc
+	    && priv->ac_ip6_config) {
+		NMSettingIPConfig *s_ip6;
+		char *hex_iid;
+		NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
+
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		g_assert (s_ip6);
+
+		g_ptr_array_add (argv, g_strdup ("--priority6"));
+		g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip6_route_metric (self)));
+
+		g_ptr_array_add (argv, g_strdup ("--slaac"));
+
+		if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE)
+			g_ptr_array_add (argv, g_strdup ("--slaac-required"));
+
+		g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
+		g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
+
+		if (nm_device_get_ip_iface_identifier (self, &iid)) {
+			g_ptr_array_add (argv, g_strdup ("--iid"));
+			hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
+			g_ptr_array_add (argv, hex_iid);
+		}
+
+		configured = TRUE;
+	}
+
+	if (configured) {
+		GPid pid;
+
+		g_ptr_array_add (argv, NULL);
+
+		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) {
+			char *tmp;
+
+			tmp = g_strjoinv (" ", (char **) argv->pdata);
+			_LOGD (LOGD_DEVICE, "running '%s'", tmp);
+			g_free (tmp);
+		}
+
+		if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+		                   G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) {
+			_LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid);
+		} else {
+			_LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message);
+			g_error_free (error);
+		}
+	}
+
+	g_ptr_array_unref (argv);
+}
+
 /***********************************************************/
 
 static gboolean
@@ -6558,22 +7409,33 @@ ip_config_valid (NMDeviceState state)
 }
 
 static void
-notify_ip_properties (NMDevice *device)
+notify_ip_properties (NMDevice *self)
+{
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+}
+
+static void
+ip6_managed_setup (NMDevice *self)
 {
-	g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE);
-	g_object_notify (G_OBJECT (device), NM_DEVICE_IP4_CONFIG);
-	g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
-	g_object_notify (G_OBJECT (device), NM_DEVICE_IP6_CONFIG);
-	g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
+	set_nm_ipv6ll (self, TRUE);
+	set_disable_ipv6 (self, "1");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0");
+	nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0");
+	nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0");
 }
 
 static void
-_set_state_full (NMDevice *device,
+_set_state_full (NMDevice *self,
                  NMDeviceState state,
                  NMDeviceStateReason reason,
                  gboolean quitting)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceState old_state;
 	NMActRequest *req;
 	gboolean no_firmware = FALSE;
@@ -6583,7 +7445,7 @@ _set_state_full (NMDevice *device,
 	g_warn_if_fail (priv->in_state_changed == FALSE);
 	priv->in_state_changed = TRUE;
 
-	g_return_if_fail (NM_IS_DEVICE (device));
+	g_return_if_fail (NM_IS_DEVICE (self));
 
 	/* Do nothing if state isn't changing, but as a special case allow
 	 * re-setting UNAVAILABLE if the device is missing firmware so that we
@@ -6599,78 +7461,96 @@ _set_state_full (NMDevice *device,
 	priv->state = state;
 	priv->state_reason = reason;
 
-	nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]",
-	             nm_device_get_iface (device),
-	             state_to_string (old_state),
-	             state_to_string (state),
-	             reason_to_string (reason),
-	             old_state,
-	             state,
-	             reason);
+	_LOGI (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]",
+	       state_to_string (old_state),
+	       state_to_string (state),
+	       reason_to_string (reason),
+	       old_state,
+	       state,
+	       reason);
 
 	/* Clear any queued transitions */
-	nm_device_queued_state_clear (device);
+	nm_device_queued_state_clear (self);
 
-	dispatcher_cleanup (device);
+	dispatcher_cleanup (self);
 
 	/* Cache the activation request for the dispatcher */
 	req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
 
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
-		_clear_available_connections (device, TRUE);
+		_clear_available_connections (self, TRUE);
 		g_clear_object (&priv->queued_act_request);
 	}
 
 	/* Update the available connections list when a device first becomes available */
-	if (   state >= NM_DEVICE_STATE_DISCONNECTED
-	    && old_state < NM_DEVICE_STATE_DISCONNECTED)
-		nm_device_recheck_available_connections (device);
+	if (   (state >= NM_DEVICE_STATE_DISCONNECTED && old_state < NM_DEVICE_STATE_DISCONNECTED)
+	    || nm_device_get_default_unmanaged (self))
+		nm_device_recheck_available_connections (self);
 
 	/* Handle the new state here; but anything that could trigger
 	 * another state change should be done below.
 	 */
 	switch (state) {
 	case NM_DEVICE_STATE_UNMANAGED:
-		nm_device_set_firmware_missing (device, FALSE);
+		nm_device_set_firmware_missing (self, FALSE);
 		if (old_state > NM_DEVICE_STATE_UNMANAGED) {
 			/* Clean up if the device is now unmanaged but was activated */
-			if (nm_device_get_act_request (device))
-				nm_device_cleanup (device, reason);
-			nm_device_take_down (device, TRUE);
-			restore_ip6_properties (device);
+			if (nm_device_get_act_request (self))
+				nm_device_cleanup (self, reason);
+			nm_device_take_down (self, TRUE);
+			set_nm_ipv6ll (self, FALSE);
+			restore_ip6_properties (self);
 		}
 		break;
 	case NM_DEVICE_STATE_UNAVAILABLE:
 		if (old_state == NM_DEVICE_STATE_UNMANAGED) {
-			save_ip6_properties (device);
-			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
-				nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
-				nm_device_ipv6_sysctl_set (device, "accept_ra_defrtr", "0");
-				nm_device_ipv6_sysctl_set (device, "accept_ra_pinfo", "0");
-				nm_device_ipv6_sysctl_set (device, "accept_ra_rtr_pref", "0");
-				nm_device_ipv6_sysctl_set (device, "use_tempaddr", "0");
-			}
+			save_ip6_properties (self);
+			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
+				ip6_managed_setup (self);
 		}
 
-		if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
-			if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware)
-				nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device));
-			nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE);
+		if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+			if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
+				if (!nm_device_bring_up (self, TRUE, &no_firmware) && no_firmware)
+					_LOGW (LOGD_HW, "firmware may be missing.");
+				nm_device_set_firmware_missing (self, no_firmware ? TRUE : FALSE);
+			}
+
+			/* Ensure the device gets deactivated in response to stuff like
+			 * carrier changes or rfkill.  But don't deactivate devices that are
+			 * about to assume a connection since that defeats the purpose of
+			 * assuming the device's existing connection.
+			 *
+			 * Note that we "deactivate" the device even when coming from
+			 * UNMANAGED, to ensure that it's in a clean state.
+			 */
+			nm_device_cleanup (self, reason);
 		}
-		/* Ensure the device gets deactivated in response to stuff like
-		 * carrier changes or rfkill.  But don't deactivate devices that are
-		 * about to assume a connection since that defeats the purpose of
-		 * assuming the device's existing connection.
-		 *
-		 * Note that we "deactivate" the device even when coming from
-		 * UNMANAGED, to ensure that it's in a clean state.
-		 */
-		if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
-			nm_device_cleanup (device, reason);
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
-		if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
-			nm_device_cleanup (device, reason);
+		if (old_state > NM_DEVICE_STATE_DISCONNECTED) {
+			/* Ensure devices that previously assumed a connection now have
+			 * userspace IPv6LL enabled.
+			 */
+			set_nm_ipv6ll (self, TRUE);
+
+			nm_device_cleanup (self, reason);
+		} else if (old_state < NM_DEVICE_STATE_DISCONNECTED) {
+			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+				/* Ensure IPv6 is set up as it may not have been done when
+				 * entering the UNAVAILABLE state depending on the reason.
+				 */
+				ip6_managed_setup (self);
+			}
+		}
+		break;
+	case NM_DEVICE_STATE_NEED_AUTH:
+		if (old_state > NM_DEVICE_STATE_NEED_AUTH) {
+			/* Clean up any half-done IP operations if the device's layer2
+			 * finds out it needs authentication during IP config.
+			 */
+			_cleanup_ip_pre (self, TRUE);
+		}
 		break;
 	default:
 		break;
@@ -6679,11 +7559,11 @@ _set_state_full (NMDevice *device,
 	/* Reset autoconnect flag when the device is activating or connected. */
 	if (   state >= NM_DEVICE_STATE_PREPARE
 	    && state <= NM_DEVICE_STATE_ACTIVATED)
-		priv->autoconnect = TRUE;
+		nm_device_set_autoconnect (self, TRUE);
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_STATE);
-	g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON);
-	g_signal_emit_by_name (device, "state-changed", state, old_state, reason);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_STATE);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_STATE_REASON);
+	g_signal_emit_by_name (self, "state-changed", state, old_state, reason);
 
 	/* Post-process the event after internal notification */
 
@@ -6695,35 +7575,33 @@ _set_state_full (NMDevice *device,
 		 * we can't change states again from the state handler for a variety of
 		 * reasons.
 		 */
-		if (nm_device_is_available (device)) {
-			nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
-			            nm_device_get_iface (device));
-			nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+		if (nm_device_is_available (self)) {
+			_LOGD (LOGD_DEVICE, "device is available, will transition to DISCONNECTED");
+			nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		} else {
-			if (old_state == NM_DEVICE_STATE_UNMANAGED) {
-				nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
-				            nm_device_get_iface (device));
-			} else if (   old_state > NM_DEVICE_STATE_UNAVAILABLE
-			           && nm_device_get_default_unmanaged (device))
-				nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
+			if (old_state == NM_DEVICE_STATE_UNMANAGED)
+				_LOGD (LOGD_DEVICE, "device not yet available for transition to DISCONNECTED");
+			else if (   old_state > NM_DEVICE_STATE_UNAVAILABLE
+			         && nm_device_get_default_unmanaged (self))
+				nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
 		}
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
 		if (quitting) {
 			nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
 			                         nm_act_request_get_connection (req),
-			                         device);
+			                         self);
 		} else {
 			priv->dispatcher.post_state = NM_DEVICE_STATE_DISCONNECTED;
 			priv->dispatcher.post_state_reason = reason;
 			if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN,
 			                         nm_act_request_get_connection (req),
-			                         device,
+			                         self,
 			                         dispatcher_complete_proceed_state,
-			                         device,
+			                         self,
 			                         &priv->dispatcher.call_id)) {
 				/* Just proceed on errors */
-				dispatcher_complete_proceed_state (0, device);
+				dispatcher_complete_proceed_state (0, self);
 			}
 		}
 		break;
@@ -6733,26 +7611,32 @@ _set_state_full (NMDevice *device,
 
 			queued_req = priv->queued_act_request;
 			priv->queued_act_request = NULL;
-			_device_activate (device, queued_req);
+			_device_activate (self, queued_req);
 			g_object_unref (queued_req);
 		} else if (   old_state > NM_DEVICE_STATE_DISCONNECTED
-		           && nm_device_get_default_unmanaged (device))
-			nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
+		           && nm_device_get_default_unmanaged (self))
+			nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
-		nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.",
-		             nm_device_get_iface (device));
-		nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL, NULL);
+		_LOGI (LOGD_DEVICE, "Activation: successful, device activated.");
+		nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL);
 		break;
 	case NM_DEVICE_STATE_FAILED:
-		connection = nm_device_get_connection (device);
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s) failed for connection '%s'",
-		             nm_device_get_iface (device),
-		             connection ? nm_connection_get_id (connection) : "<unknown>");
+		if (nm_device_uses_assumed_connection (self)) {
+			/* Avoid tearing down assumed connection, assume it's connected */
+			nm_device_queue_state (self,
+			                       NM_DEVICE_STATE_ACTIVATED,
+			                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+			break;
+		}
+
+		connection = nm_device_get_connection (self);
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: failed for connection '%s'",
+		       connection ? nm_connection_get_id (connection) : "<unknown>");
 
 		/* Notify any slaves of the unexpected failure */
-		nm_device_master_release_slaves (device);
+		nm_device_master_release_slaves (self);
 
 		/* If the connection doesn't yet have a timestamp, set it to zero so that
 		 * we can distinguish between connections we've tried to activate and have
@@ -6769,41 +7653,40 @@ _set_state_full (NMDevice *device,
 		 * immediately because we can't change states again from the state
 		 * handler for a variety of reasons.
 		 */
-		nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
+		nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		break;
 	case NM_DEVICE_STATE_IP_CHECK:
-		nm_device_start_ip_check (device);
+		nm_device_start_ip_check (self);
 
 		/* IP-related properties are only valid when the device has IP configuration;
 		 * now that it does, ensure their change notifications are emitted.
 		 */
-		notify_ip_properties (device);
+		notify_ip_properties (self);
 		break;
 	case NM_DEVICE_STATE_SECONDARIES:
-		ip_check_gw_ping_cleanup (device);
-		nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state",
-		            nm_device_get_iface (device));
+		ip_check_gw_ping_cleanup (self);
+		_LOGD (LOGD_DEVICE, "device entered SECONDARIES state");
 		break;
 	default:
 		break;
 	}
 
 	if (state > NM_DEVICE_STATE_DISCONNECTED)
-		delete_on_deactivate_unschedule (device);
+		delete_on_deactivate_unschedule (self);
 
 	if (   (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING)
 	    && (state != NM_DEVICE_STATE_DEACTIVATING)) {
 		if (quitting)
-			nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device);
+			nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self);
 		else
-			nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL, NULL);
+			nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self, NULL, NULL, NULL);
 	}
 
 	/* IP-related properties are only valid when the device has IP configuration.
 	 * If it no longer does, ensure their change notifications are emitted.
 	 */
 	if (ip_config_valid (old_state) && !ip_config_valid (state))
-	    notify_ip_properties (device);
+	    notify_ip_properties (self);
 
 	/* Dispose of the cached activation request */
 	if (req)
@@ -6813,11 +7696,11 @@ _set_state_full (NMDevice *device,
 }
 
 void
-nm_device_state_changed (NMDevice *device,
+nm_device_state_changed (NMDevice *self,
                          NMDeviceState state,
                          NMDeviceStateReason reason)
 {
-	_set_state_full (device, state, reason, FALSE);
+	_set_state_full (self, state, reason, FALSE);
 }
 
 static gboolean
@@ -6829,10 +7712,9 @@ queued_set_state (gpointer user_data)
 	NMDeviceStateReason new_reason;
 
 	if (priv->queued_state.id) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)",
-			        nm_device_get_iface (self),
-			        state_to_string (priv->queued_state.state),
-			        priv->queued_state.id);
+		_LOGD (LOGD_DEVICE, "running queued state change to %s (id %d)",
+		       state_to_string (priv->queued_state.state),
+		       priv->queued_state.id);
 
 		/* Clear queued state struct before triggering state change, since
 		 * the state change may queue another state.
@@ -6871,10 +7753,9 @@ nm_device_queue_state (NMDevice *self,
 
 	/* We should only ever have one delayed state transition at a time */
 	if (priv->queued_state.id) {
-		nm_log_warn (LOGD_DEVICE, "(%s): overwriting previously queued state change to %s (%s)",
-		             nm_device_get_iface (self),
-		             state_to_string (priv->queued_state.state),
-		             reason_to_string (priv->queued_state.reason));
+		_LOGW (LOGD_DEVICE, "overwriting previously queued state change to %s (%s)",
+		       state_to_string (priv->queued_state.state),
+		       reason_to_string (priv->queued_state.reason));
 		nm_device_queued_state_clear (self);
 	}
 
@@ -6882,9 +7763,9 @@ nm_device_queue_state (NMDevice *self,
 	priv->queued_state.reason = reason;
 	priv->queued_state.id = g_idle_add (queued_set_state, self);
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s due to %s (id %d)",
-	            nm_device_get_iface (self), state_to_string (state), reason_to_string (reason),
-	            priv->queued_state.id);
+	_LOGD (LOGD_DEVICE, "queued state change to %s due to %s (id %d)",
+	       state_to_string (state), reason_to_string (reason),
+	       priv->queued_state.id);
 }
 
 NMDeviceState
@@ -6905,8 +7786,8 @@ nm_device_queued_state_clear (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->queued_state.id) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
-		            nm_device_get_iface (self), priv->queued_state.id);
+		_LOGD (LOGD_DEVICE, "clearing queued state transition (id %d)",
+		       priv->queued_state.id);
 		g_source_remove (priv->queued_state.id);
 		nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE);
 	}
@@ -6914,152 +7795,112 @@ nm_device_queued_state_clear (NMDevice *self)
 }
 
 NMDeviceState
-nm_device_get_state (NMDevice *device)
+nm_device_get_state (NMDevice *self)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN);
 
-	return NM_DEVICE_GET_PRIVATE (device)->state;
+	return NM_DEVICE_GET_PRIVATE (self)->state;
 }
 
 /***********************************************************/
 /* NMConfigDevice interface related stuff */
 
-static guint
-nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
-{
-	return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent);
-}
-
-const guint8 *
-nm_device_get_hw_address (NMDevice *dev, guint *out_len)
+const char *
+nm_device_get_hw_address (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 
-	g_return_val_if_fail (NM_IS_DEVICE (dev), NULL);
-	priv = NM_DEVICE_GET_PRIVATE (dev);
-
-	if (out_len)
-		*out_len = priv->hw_addr_len;
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->hw_addr_len == 0)
-		return NULL;
-	else
-		return priv->hw_addr;
+	return priv->hw_addr_len ? priv->hw_addr : NULL;
 }
 
-gboolean
-nm_device_update_hw_address (NMDevice *dev)
+static void
+nm_device_update_hw_address (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
-	gboolean changed = FALSE, permanent = FALSE;
-
-	priv->hw_addr_len = nm_device_get_hw_address_length (dev, &permanent);
-
-	/* If the address can't be changed, don't bother trying */
-	if (permanent)
-		return FALSE;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (self);
+	const guint8 *hwaddr;
+	gsize hwaddrlen = 0;
 
-	if (priv->hw_addr_len) {
-		int ifindex = nm_device_get_ip_ifindex (dev);
-		gsize addrlen;
-		const guint8 *binaddr;
+	if (ifindex <= 0)
+		return;
 
-		g_return_val_if_fail (ifindex > 0, FALSE);
+	hwaddr = nm_platform_link_get_address (ifindex, &hwaddrlen);
 
-		binaddr = nm_platform_link_get_address (ifindex, &addrlen);
+	if (hwaddrlen) {
+		if (!priv->hw_addr || !nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) {
+			g_free (priv->hw_addr);
+			priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen);
 
-		if (addrlen != priv->hw_addr_len) {
-			nm_log_err (LOGD_HW | LOGD_DEVICE,
-			            "(%s): hardware address is wrong length (got %zd, expected %d)",
-			            nm_device_get_iface (dev), addrlen, priv->hw_addr_len);
-		} else {
-			changed = !!memcmp (priv->hw_addr, binaddr, addrlen);
-			if (changed) {
-				char *addrstr = nm_utils_hwaddr_ntoa_len (binaddr, priv->hw_addr_len);
-
-				memcpy (priv->hw_addr, binaddr, addrlen);
-				nm_log_dbg (LOGD_HW | LOGD_DEVICE,
-				            "(%s): hardware address is %s",
-				            nm_device_get_iface (dev), addrstr);
-				g_free (addrstr);
-				g_object_notify (G_OBJECT (dev), NM_DEVICE_HW_ADDRESS);
-			}
+			_LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr);
+			g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
 		}
 	} else {
-		int i;
-
-		/* hw_addr_len is now 0; see if hw_addr was already empty */
-		for (i = 0; i < sizeof (priv->hw_addr) && !changed; i++) {
-			if (priv->hw_addr[i])
-				changed = TRUE;
-		}
-		if (changed) {
-			memset (priv->hw_addr, 0, sizeof (priv->hw_addr));
-			nm_log_dbg (LOGD_HW | LOGD_DEVICE,
-			            "(%s): previous hardware address is no longer valid",
-			            nm_device_get_iface (dev));
-			g_object_notify (G_OBJECT (dev), NM_DEVICE_HW_ADDRESS);
+		/* Invalid or no hardware address */
+		if (priv->hw_addr_len != 0) {
+			g_clear_pointer (&priv->hw_addr, g_free);
+			_LOGD (LOGD_HW | LOGD_DEVICE,
+			       "previous hardware address is no longer valid");
+			g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
 		}
 	}
-
-	return changed;
+	priv->hw_addr_len = hwaddrlen;
 }
 
 gboolean
-nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
+nm_device_set_hw_addr (NMDevice *self, const char *addr,
                        const char *detail, guint64 hw_log_domain)
 {
-	const char *iface;
-	char *mac_str = NULL;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean success = FALSE;
-	guint len;
-	const guint8 *cur_addr = nm_device_get_hw_address (device, &len);
+	const char *cur_addr = nm_device_get_hw_address (self);
+	guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX];
 
 	g_return_val_if_fail (addr != NULL, FALSE);
 
-	iface = nm_device_get_iface (device);
-
 	/* Do nothing if current MAC is same */
-	if (cur_addr && !memcmp (cur_addr, addr, len)) {
-		nm_log_dbg (LOGD_DEVICE | hw_log_domain, "(%s): no MAC address change needed", iface);
+	if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) {
+		_LOGD (LOGD_DEVICE | hw_log_domain, "no MAC address change needed");
 		return TRUE;
 	}
-
-	mac_str = nm_utils_hwaddr_ntoa_len (addr, len);
+	if (!nm_utils_hwaddr_aton (addr, addr_bytes, priv->hw_addr_len)) {
+		_LOGW (LOGD_DEVICE | hw_log_domain, "invalid MAC address %s", addr);
+		return FALSE;
+	}
 
 	/* Can't change MAC address while device is up */
-	nm_device_take_down (device, FALSE);
+	nm_device_take_down (self, FALSE);
 
-	success = nm_platform_link_set_address (nm_device_get_ip_ifindex (device), addr, len);
+	success = nm_platform_link_set_address (nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len);
 	if (success) {
 		/* MAC address succesfully changed; update the current MAC to match */
-		nm_device_update_hw_address (device);
-		cur_addr = nm_device_get_hw_address (device, NULL);
-		if (memcmp (cur_addr, addr, len) == 0) {
-			nm_log_info (LOGD_DEVICE | hw_log_domain, "(%s): %s MAC address to %s",
-			             iface, detail, mac_str);
+		nm_device_update_hw_address (self);
+		cur_addr = nm_device_get_hw_address (self);
+		if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) {
+			_LOGI (LOGD_DEVICE | hw_log_domain, "%s MAC address to %s",
+			       detail, addr);
 		} else {
-			nm_log_warn (LOGD_DEVICE | hw_log_domain, "(%s): new MAC address %s "
-			             "not successfully set",
-			             iface, mac_str);
+			_LOGW (LOGD_DEVICE | hw_log_domain,
+			       "new MAC address %s not successfully set", addr);
 			success = FALSE;
 		}
 	} else {
-		nm_log_warn (LOGD_DEVICE | hw_log_domain, "(%s): failed to %s MAC address to %s",
-		             iface, detail, mac_str);
+		_LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s",
+		       detail, addr);
 	}
-	nm_device_bring_up (device, TRUE, NULL);
-	g_free (mac_str);
+	nm_device_bring_up (self, TRUE, NULL);
 
 	return success;
 }
 
 /**
  * nm_device_spec_match_list:
- * @device: an #NMDevice
+ * @self: an #NMDevice
  * @specs: (element-type utf8): a list of device specs
  *
- * Checks if @device matches any of the specifications in @specs. The
+ * Checks if @self matches any of the specifications in @specs. The
  * currently-supported spec types are:
  *
  *     "mac:00:11:22:33:44:55" - matches a device with the given
@@ -7073,52 +7914,37 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr,
  *
  *     "*" - matches any device
  *
- * Returns: #TRUE if @device matches one of the specs in @specs
+ * Returns: #TRUE if @self matches one of the specs in @specs
  */
 gboolean
-nm_device_spec_match_list (NMDevice *device, const GSList *specs)
+nm_device_spec_match_list (NMDevice *self, const GSList *specs)
 {
-	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	if (!specs)
 		return FALSE;
 
-	return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs);
+	return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs);
 }
 
 static gboolean
-spec_match_list (NMDevice *device, const GSList *specs)
+spec_match_list (NMDevice *self, const GSList *specs)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	char *hwaddr_str;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean matched = FALSE;
 
 	if (nm_match_spec_string (specs, "*"))
 		return TRUE;
 
-	if (priv->hw_addr_len) {
-		hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len);
-		matched = nm_match_spec_hwaddr (specs, hwaddr_str);
-		g_free (hwaddr_str);
-	}
+	if (priv->hw_addr_len)
+		matched = nm_match_spec_hwaddr (specs, priv->hw_addr);
 
 	if (!matched)
-		matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device));
+		matched = nm_match_spec_interface_name (specs, nm_device_get_iface (self));
 
 	return matched;
 }
 
-static guint
-get_hw_address_length (NMDevice *dev, gboolean *out_permanent)
-{
-	size_t len;
-
-	if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len))
-		return len;
-	else
-		return 0;
-}
-
 /***********************************************************/
 
 #define DEFAULT_AUTOCONNECT TRUE
@@ -7138,13 +7964,9 @@ nm_device_init (NMDevice *self)
 	priv->unmanaged_flags = NM_UNMANAGED_INTERNAL;
 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
 	priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
-}
 
-static void
-nm_device_config_device_interface_init (NMConfigDeviceInterface *iface)
-{
-	iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list;
-	iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address;
+	priv->default_route.v4_is_assumed = TRUE;
+	priv->default_route.v6_is_assumed = TRUE;
 }
 
 /*
@@ -7152,7 +7974,7 @@ nm_device_config_device_interface_init (NMConfigDeviceInterface *iface)
  * Returns driver and firmware versions to 'driver_version and' 'firmware_version'
  */
 static gboolean
-device_get_driver_info (const char *iface, char **driver_version, char **firmware_version)
+device_get_driver_info (NMDevice *self, const char *iface, char **driver_version, char **firmware_version)
 {
 	struct ethtool_drvinfo drvinfo;
 	struct ifreq req;
@@ -7160,7 +7982,7 @@ device_get_driver_info (const char *iface, char **driver_version, char **firmwar
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		nm_log_warn (LOGD_HW, "couldn't open control socket.");
+		_LOGW (LOGD_HW, "couldn't open control socket.");
 		return FALSE;
 	}
 
@@ -7173,8 +7995,8 @@ device_get_driver_info (const char *iface, char **driver_version, char **firmwar
 
 	errno = 0;
 	if (ioctl (fd, SIOCETHTOOL, &req) < 0) {
-		nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
-		            iface, errno);
+		_LOGD (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d",
+		       iface, errno);
 		close (fd);
 		return FALSE;
 	}
@@ -7193,7 +8015,7 @@ constructor (GType type,
              GObjectConstructParam *construct_params)
 {
 	GObject *object;
-	NMDevice *dev;
+	NMDevice *self;
 	NMDevicePrivate *priv;
 	NMPlatform *platform;
 	static guint32 id = 0;
@@ -7204,11 +8026,13 @@ constructor (GType type,
 	if (!object)
 		return NULL;
 
-	dev = NM_DEVICE (object);
-	priv = NM_DEVICE_GET_PRIVATE (dev);
+	self = NM_DEVICE (object);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	_LOGD (LOGD_DEVICE, "constructor(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex);
 
 	if (!priv->iface) {
-		nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring");
+		_LOGE (LOGD_DEVICE, "No device interface provided, ignoring");
 		goto error;
 	}
 
@@ -7217,52 +8041,60 @@ constructor (GType type,
 		priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
 	}
 
-	if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities)
-		priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
+	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
+		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
-	priv->fw_manager = nm_firewall_manager_get ();
+	if (priv->ifindex <= 0 && !device_has_capability (self, NM_DEVICE_CAP_IS_NON_KERNEL))
+		_LOGW (LOGD_HW, "failed to look up interface index");
 
-	device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version);
+	device_get_driver_info (self, priv->iface, &priv->driver_version, &priv->firmware_version);
 
 	/* Watch for external IP config changes */
 	platform = nm_platform_get ();
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev);
-	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self);
+	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
+
+	if (nm_platform_check_support_user_ipv6ll ()) {
+		int ip_ifindex = nm_device_get_ip_ifindex (self);
+
+		if (ip_ifindex > 0)
+			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (ip_ifindex);
+	}
 
 	return object;
 
 error:
-	g_object_unref (dev);
+	g_object_unref (self);
 	return NULL;
 }
 
 static void
 constructed (GObject *object)
 {
-	NMDevice *dev = NM_DEVICE (object);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int master;
 
-	nm_device_update_hw_address (dev);
+	nm_device_update_hw_address (self);
 
-	if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
-		NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
+	if (NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address)
+		NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address (self);
 
-	if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
-		NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
+	if (NM_DEVICE_GET_CLASS (self)->update_initial_hw_address)
+		NM_DEVICE_GET_CLASS (self)->update_initial_hw_address (self);
 
 	/* Have to call update_initial_hw_address() before calling get_ignore_carrier() */
-	if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) {
-		priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev));
-
-		check_carrier (dev);
-		nm_log_info (LOGD_HW,
-		             "(%s): carrier is %s%s",
-		             nm_device_get_iface (NM_DEVICE (dev)),
-		             priv->carrier ? "ON" : "OFF",
-		             priv->ignore_carrier ? " (but ignored)" : "");
+	if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
+		priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), self);
+
+		check_carrier (self);
+		_LOGI (LOGD_HW,
+		       "carrier is %s%s",
+		       priv->carrier ? "ON" : "OFF",
+		       priv->ignore_carrier ? " (but ignored)" : "");
 	} else {
 		/* Fake online link when carrier detection is not available. */
 		priv->carrier = TRUE;
@@ -7271,27 +8103,42 @@ constructed (GObject *object)
 	if (priv->ifindex > 0) {
 		priv->is_software = nm_platform_link_is_software (priv->ifindex);
 		priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
+		priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
 	}
+	/* Indicate software device in capabilities. */
+	if (priv->is_software)
+		priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
 
-	if (priv->ifindex > 0)
-		priv->mtu = nm_platform_link_get_mtu (priv->ifindex);
+	/* Enslave ourselves */
+	master = nm_platform_link_get_master (priv->ifindex);
+	if (master)
+		device_set_master (self, master);
 
 	priv->con_provider = nm_connection_provider_get ();
 	g_assert (priv->con_provider);
 	g_signal_connect (priv->con_provider,
 	                  NM_CP_SIGNAL_CONNECTION_ADDED,
 	                  G_CALLBACK (cp_connection_added),
-	                  dev);
+	                  self);
 
 	g_signal_connect (priv->con_provider,
 	                  NM_CP_SIGNAL_CONNECTION_REMOVED,
 	                  G_CALLBACK (cp_connection_removed),
-	                  dev);
+	                  self);
 
 	g_signal_connect (priv->con_provider,
 	                  NM_CP_SIGNAL_CONNECTION_UPDATED,
 	                  G_CALLBACK (cp_connection_updated),
-	                  dev);
+	                  self);
+
+	/* Update default-unmanaged device available connections immediately,
+	 * since they don't transition from UNMANAGED (and thus the state handler
+	 * doesn't run and update them) until something external happens.
+	 */
+	if (nm_device_get_default_unmanaged (self)) {
+		nm_device_set_autoconnect (self, FALSE);
+		nm_device_recheck_available_connections (self);
+	}
 
 	G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
 }
@@ -7303,6 +8150,8 @@ dispose (GObject *object)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatform *platform;
 
+	_LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self));
+
 	dispatcher_cleanup (self);
 
 	_cleanup_generic_pre (self, FALSE);
@@ -7310,6 +8159,9 @@ dispose (GObject *object)
 	g_warn_if_fail (priv->slaves == NULL);
 	g_assert (priv->master_ready_id == 0);
 
+	/* Let the kernel manage IPv6LL again */
+	set_nm_ipv6ll (self, FALSE);
+
 	_cleanup_generic_post (self, FALSE);
 
 	g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref);
@@ -7342,8 +8194,6 @@ dispose (GObject *object)
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self);
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
 
-	g_clear_object (&priv->fw_manager);
-
 	G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
 }
 
@@ -7353,6 +8203,9 @@ finalize (GObject *object)
 	NMDevice *self = NM_DEVICE (object);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	_LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self));
+
+	g_free (priv->hw_addr);
 	g_slist_free_full (priv->pending_actions, g_free);
 	g_clear_pointer (&priv->physical_port_id, g_free);
 	g_free (priv->udi);
@@ -7363,8 +8216,7 @@ finalize (GObject *object)
 	g_free (priv->driver_version);
 	g_free (priv->firmware_version);
 	g_free (priv->type_desc);
-	if (priv->dhcp_anycast_address)
-		g_byte_array_free (priv->dhcp_anycast_address, TRUE);
+	g_free (priv->dhcp_anycast_address);
 
 	G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
 }
@@ -7373,9 +8225,11 @@ static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatformLink *platform_device;
-	const char *hw_addr;
+	const char *hw_addr, *p;
+	guint count;
  
 	switch (prop_id) {
 	case PROP_PLATFORM_DEVICE:
@@ -7386,6 +8240,7 @@ set_property (GObject *object, guint prop_id,
 			g_free (priv->iface);
 			priv->iface = g_strdup (platform_device->name);
 			priv->ifindex = platform_device->ifindex;
+			priv->up = platform_device->up;
 			g_free (priv->driver);
 			priv->driver = g_strdup (platform_device->driver);
 		}
@@ -7399,18 +8254,10 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IFACE:
 		if (g_value_get_string (value)) {
 			g_free (priv->iface);
-			priv->ifindex = 0;
 			priv->iface = g_value_dup_string (value);
-
-			/* Only look up the ifindex if it appears to be an actual kernel
-			 * interface name.  eg Bluetooth devices won't have one until we know
-			 * the IP interface.
-			 */
-			if (priv->iface && !strchr (priv->iface, ':')) {
-				priv->ifindex = nm_platform_link_get_ifindex (priv->iface);
-				if (priv->ifindex <= 0)
-					nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface);
-			}
+			priv->ifindex = nm_platform_link_get_ifindex (priv->iface);
+			if (priv->ifindex > 0)
+				priv->up = nm_platform_link_is_up (priv->ifindex);
 		}
 		break;
 	case PROP_DRIVER:
@@ -7434,7 +8281,7 @@ set_property (GObject *object, guint prop_id,
 		priv->ip4_address = g_value_get_uint (value);
 		break;
 	case PROP_AUTOCONNECT:
-		priv->autoconnect = g_value_get_boolean (value);
+		nm_device_set_autoconnect (self, g_value_get_boolean (value));
 		break;
 	case PROP_FIRMWARE_MISSING:
 		priv->firmware_missing = g_value_get_boolean (value);
@@ -7454,19 +8301,30 @@ set_property (GObject *object, guint prop_id,
 		priv->is_master = g_value_get_boolean (value);
 		break;
 	case PROP_HW_ADDRESS:
-		priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL);
-
-		hw_addr = g_value_get_string (value);
-		if (!hw_addr)
-			break;
-		if (priv->hw_addr_len == 0) {
-			g_warn_if_fail (*hw_addr == '\0');
+		/* construct only */
+		p = hw_addr = g_value_get_string (value);
+
+		/* Hardware address length is the number of ':' plus 1 */
+		count = 1;
+		while (p && *p) {
+			if (*p++ == ':')
+				count++;
+		}
+		if (count < ETH_ALEN || count > NM_UTILS_HWADDR_LEN_MAX) {
+			if (hw_addr && *hw_addr) {
+				_LOGW (LOGD_DEVICE, "ignoring hardware address '%s' with unexpected length %d",
+				       hw_addr, count);
+			}
 			break;
 		}
 
-		if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) {
-			g_warning ("Could not parse hw-address '%s'", hw_addr);
-			memset (priv->hw_addr, 0, sizeof (priv->hw_addr));
+		priv->hw_addr_len = count;
+		g_free (priv->hw_addr);
+		if (nm_utils_hwaddr_valid (hw_addr, priv->hw_addr_len))
+			priv->hw_addr = g_strdup (hw_addr);
+		else {
+			_LOGW (LOGD_DEVICE, "could not parse hw-address '%s'", hw_addr);
+			priv->hw_addr = NULL;
 		}
 		break;
 	default:
@@ -7596,10 +8454,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_object (value, priv->master);
 		break;
 	case PROP_HW_ADDRESS:
-		if (priv->hw_addr_len)
-			g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len));
-		else
-			g_value_set_string (value, NULL);
+		g_value_set_string (value, priv->hw_addr);
 		break;
 	case PROP_HAS_PENDING_ACTION:
 		g_value_set_boolean (value, nm_device_has_pending_action (self));
@@ -7644,257 +8499,226 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->bring_up = bring_up;
 	klass->take_down = take_down;
 	klass->carrier_changed = carrier_changed;
-	klass->get_hw_address_length = get_hw_address_length;
+	klass->get_ip_iface_identifier = get_ip_iface_identifier;
 
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_PLATFORM_DEVICE,
-		 g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE,
-		                       "Platform Device",
-		                       "NMPlatform device object",
-		                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE, "", "",
+		                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_UDI,
-		 g_param_spec_string (NM_DEVICE_UDI,
-		                      "UDI",
-		                      "Unique Device Identifier",
+		 g_param_spec_string (NM_DEVICE_UDI, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IFACE,
-		 g_param_spec_string (NM_DEVICE_IFACE,
-		                      "Interface",
-		                      "Interface",
+		 g_param_spec_string (NM_DEVICE_IFACE, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP_IFACE,
-		 g_param_spec_string (NM_DEVICE_IP_IFACE,
-		                      "IP Interface",
-		                      "IP Interface",
+		 g_param_spec_string (NM_DEVICE_IP_IFACE, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DRIVER,
-		 g_param_spec_string (NM_DEVICE_DRIVER,
-		                      "Driver",
-		                      "Driver",
+		 g_param_spec_string (NM_DEVICE_DRIVER, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DRIVER_VERSION,
-		 g_param_spec_string (NM_DEVICE_DRIVER_VERSION,
-		                      "Driver Version",
-		                      "Driver Version",
+		 g_param_spec_string (NM_DEVICE_DRIVER_VERSION, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_FIRMWARE_VERSION,
-		 g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION,
-		                      "Firmware Version",
-		                      "Firmware Version",
+		 g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CAPABILITIES,
-		 g_param_spec_uint (NM_DEVICE_CAPABILITIES,
-		                    "Capabilities",
-		                    "Capabilities",
+		 g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "",
 		                    0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CARRIER,
-		 g_param_spec_boolean (NM_DEVICE_CARRIER,
-		                       "Carrier",
-		                       "Carrier",
+		 g_param_spec_boolean (NM_DEVICE_CARRIER, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_DEVICE_MTU,
-		                    "MTU",
-		                    "MTU",
+		 g_param_spec_uint (NM_DEVICE_MTU, "", "",
 		                    0, G_MAXUINT32, 1500,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP4_ADDRESS,
-		 g_param_spec_uint (NM_DEVICE_IP4_ADDRESS,
-		                    "IP4 address",
-		                    "IP4 address",
+		 g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, "", "",
 		                    0, G_MAXUINT32, 0, /* FIXME */
-		                    G_PARAM_READWRITE));
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP4_CONFIG,
-		 g_param_spec_boxed (NM_DEVICE_IP4_CONFIG,
-		                     "IP4 Config",
-		                     "IP4 Config",
+		 g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE));
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DHCP4_CONFIG,
-		 g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG,
-		                     "DHCP4 Config",
-		                     "DHCP4 Config",
+		 g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE));
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP6_CONFIG,
-		 g_param_spec_boxed (NM_DEVICE_IP6_CONFIG,
-		                     "IP6 Config",
-		                     "IP6 Config",
+		 g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE));
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DHCP6_CONFIG,
-		 g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG,
-		                     "DHCP6 Config",
-		                     "DHCP6 Config",
+		 g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE));
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STATE,
-		 g_param_spec_uint (NM_DEVICE_STATE,
-		                    "State",
-		                    "State",
+		 g_param_spec_uint (NM_DEVICE_STATE, "", "",
 		                    0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STATE_REASON,
-		 g_param_spec_boxed (NM_DEVICE_STATE_REASON,
-		                     "StateReason",
-		                     "StateReason",
+		 g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "",
 		                     DBUS_TYPE_STATE_REASON_STRUCT,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ACTIVE_CONNECTION,
-		 g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION,
-		                     "ActiveConnection",
-		                     "ActiveConnection",
+		 g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DEVICE_TYPE,
-		 g_param_spec_uint (NM_DEVICE_DEVICE_TYPE,
-		                    "DeviceType",
-		                    "DeviceType",
+		 g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "",
 		                    0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MANAGED,
-		 g_param_spec_boolean (NM_DEVICE_MANAGED,
-		                       "Managed",
-		                       "Managed",
+		 g_param_spec_boolean (NM_DEVICE_MANAGED, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_AUTOCONNECT,
-		 g_param_spec_boolean (NM_DEVICE_AUTOCONNECT,
-		                       "Autoconnect",
-		                       "Autoconnect",
+		 g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "",
 		                       DEFAULT_AUTOCONNECT,
-		                       G_PARAM_READWRITE));
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_FIRMWARE_MISSING,
-		 g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING,
-		                       "FirmwareMissing",
-		                       "Firmware missing",
+		 g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "",
 		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TYPE_DESC,
-		 g_param_spec_string (NM_DEVICE_TYPE_DESC,
-		                      "Type Description",
-		                      "Device type description",
+		 g_param_spec_string (NM_DEVICE_TYPE_DESC, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_RFKILL_TYPE,
-		 g_param_spec_uint (NM_DEVICE_RFKILL_TYPE,
-		                    "Rfkill Type",
-		                    "Type of rfkill switch (if any) supported by this device",
+		 g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, "", "",
 		                    RFKILL_TYPE_WLAN,
 		                    RFKILL_TYPE_MAX,
 		                    RFKILL_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IFINDEX,
-		 g_param_spec_int (NM_DEVICE_IFINDEX,
-		                   "Ifindex",
-		                   "Ifindex",
+		 g_param_spec_int (NM_DEVICE_IFINDEX, "", "",
 		                   0, G_MAXINT, 0,
-		                   G_PARAM_READABLE));
+		                   G_PARAM_READABLE |
+		                   G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_AVAILABLE_CONNECTIONS,
-		 g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS,
-		                     "AvailableConnections",
-		                     "AvailableConnections",
+		 g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PHYSICAL_PORT_ID,
-		 g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID,
-		                      "PhysicalPortId",
-		                      "PhysicalPortId",
+		 g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IS_MASTER,
-		 g_param_spec_boolean (NM_DEVICE_IS_MASTER,
-		                       "IsMaster",
-		                       "IsMaster",
+		 g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "",
 		                       FALSE,
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MASTER,
-		 g_param_spec_object (NM_DEVICE_MASTER,
-		                      "Master",
-		                      "Master",
+		 g_param_spec_object (NM_DEVICE_MASTER, "", "",
 		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_HW_ADDRESS,
-		 g_param_spec_string (NM_DEVICE_HW_ADDRESS,
-		                      "Hardware Address",
-		                      "Hardware address",
+		 g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_HAS_PENDING_ACTION,
-		 g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION,
-		                       "Has pending action",
-		                       "Has pending action",
+		 g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE));
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[STATE_CHANGED] =
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index f74486e6..b3855c74 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -19,24 +19,18 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef NM_DEVICE_H
-#define NM_DEVICE_H
+#ifndef __NETWORKMANAGER_DEVICE_H__
+#define __NETWORKMANAGER_DEVICE_H__
 
 #include <glib-object.h>
 #include <dbus/dbus-glib.h>
 #include <netinet/in.h>
 
-#include "NetworkManager.h"
+#include "nm-dbus-interface.h"
 #include "nm-types.h"
-#include "nm-activation-request.h"
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
-#include "nm-dhcp4-config.h"
-#include "nm-dhcp6-config.h"
 #include "nm-connection.h"
 #include "nm-rfkill-manager.h"
-#include "nm-connection-provider.h"
-#include "nm-platform.h"
+#include "NetworkManagerUtils.h"
 
 /* Properties */
 #define NM_DEVICE_UDI              "udi"
@@ -62,12 +56,13 @@
 #define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections"
 #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
 #define NM_DEVICE_MTU              "mtu"
-#define NM_DEVICE_TYPE_DESC        "type-desc"    /* Internal only */
-#define NM_DEVICE_RFKILL_TYPE      "rfkill-type"  /* Internal only */
-#define NM_DEVICE_IFINDEX          "ifindex"      /* Internal only */
-#define NM_DEVICE_IS_MASTER        "is-master"    /* Internal only */
-#define NM_DEVICE_MASTER           "master"       /* Internal only */
-#define NM_DEVICE_HW_ADDRESS       "hw-address"   /* Internal only */
+#define NM_DEVICE_HW_ADDRESS       "hw-address"
+
+#define NM_DEVICE_TYPE_DESC        "type-desc"      /* Internal only */
+#define NM_DEVICE_RFKILL_TYPE      "rfkill-type"    /* Internal only */
+#define NM_DEVICE_IFINDEX          "ifindex"        /* Internal only */
+#define NM_DEVICE_IS_MASTER        "is-master"      /* Internal only */
+#define NM_DEVICE_MASTER           "master"         /* Internal only */
 #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */
 
 /* Internal signals */
@@ -90,12 +85,6 @@ G_BEGIN_DECLS
 
 typedef enum NMActStageReturn NMActStageReturn;
 
-typedef enum {
-	NM_DEVICE_ERROR_CONNECTION_ACTIVATING = 0, /*< nick=ConnectionActivating >*/
-	NM_DEVICE_ERROR_CONNECTION_INVALID,        /*< nick=ConnectionInvalid >*/
-	NM_DEVICE_ERROR_NOT_ACTIVE,                /*< nick=NotActive >*/
-} NMDeviceError;
-
 struct _NMDevice {
 	GObject parent;
 };
@@ -120,10 +109,10 @@ typedef struct {
 	/* Carrier state (IFF_LOWER_UP) */
 	void            (*carrier_changed) (NMDevice *, gboolean carrier);
 
-	void        (* update_hw_address) (NMDevice *self);
 	void        (* update_permanent_hw_address) (NMDevice *self);
 	void        (* update_initial_hw_address) (NMDevice *self);
-	guint       (* get_hw_address_length) (NMDevice *self, gboolean *out_permanent);
+
+	gboolean    (* get_ip_iface_identifier) (NMDevice *self, NMUtilsIPv6IfaceId *out_iid);
 
 	guint32		(* get_generic_capabilities)	(NMDevice *self);
 
@@ -183,7 +172,7 @@ typedef struct {
 
 	/* Called right before IP config is set; use for setting MTU etc */
 	void                (* ip4_config_pre_commit) (NMDevice *self, NMIP4Config *config);
-	void                (* ip6_config_pre_commit) (NMDevice *self);
+	void                (* ip6_config_pre_commit) (NMDevice *self, NMIP6Config *config);
 
 	void			(* deactivate)			(NMDevice *self);
 
@@ -192,6 +181,11 @@ typedef struct {
 	/* Update the connection with currently configured L2 settings */
 	void            (* update_connection) (NMDevice *device, NMConnection *connection);
 
+	gboolean (*master_update_slave_connection) (NMDevice *self,
+	                                            NMDevice *slave,
+	                                            NMConnection *connection,
+	                                            GError **error);
+
 	gboolean        (* enslave_slave) (NMDevice *self,
 	                                   NMDevice *slave,
 	                                   NMConnection *connection,
@@ -207,6 +201,8 @@ typedef struct {
 	gboolean        (* component_added) (NMDevice *self, GObject *component);
 
 	gboolean        (* owns_iface) (NMDevice *self, const char *iface);
+
+	NMConnection *  (* new_default_connection) (NMDevice *self);
 } NMDeviceClass;
 
 
@@ -220,6 +216,8 @@ GType nm_device_get_type (void);
 const char *    nm_device_get_path (NMDevice *dev);
 void            nm_device_dbus_export   (NMDevice *device);
 
+void            nm_device_finish_init   (NMDevice *device);
+
 const char *	nm_device_get_udi		(NMDevice *dev);
 const char *	nm_device_get_iface		(NMDevice *dev);
 int             nm_device_get_ifindex	(NMDevice *dev);
@@ -232,11 +230,13 @@ const char *	nm_device_get_type_desc (NMDevice *dev);
 NMDeviceType	nm_device_get_device_type	(NMDevice *dev);
 
 int			nm_device_get_priority (NMDevice *dev);
+guint32     nm_device_get_ip4_route_metric (NMDevice *dev);
+guint32     nm_device_get_ip6_route_metric (NMDevice *dev);
 
-const guint8 *  nm_device_get_hw_address (NMDevice *dev, guint *out_len);
+const char *    nm_device_get_hw_address   (NMDevice *dev);
 
-NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *dev);
-NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *dev);
+NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev);
+NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *dev);
 
 NMIP4Config *	nm_device_get_ip4_config	(NMDevice *dev);
 void            nm_device_set_vpn4_config   (NMDevice *dev, NMIP4Config *config);
@@ -255,14 +255,21 @@ NMDevice *      nm_device_get_master        (NMDevice *dev);
 NMActRequest *	nm_device_get_act_request	(NMDevice *dev);
 NMConnection *  nm_device_get_connection	(NMDevice *dev);
 
+void            nm_device_removed        (NMDevice *dev);
+
 gboolean        nm_device_is_available   (NMDevice *dev);
 gboolean        nm_device_has_carrier    (NMDevice *dev);
 
-NMConnection * nm_device_generate_connection (NMDevice *device);
+NMConnection * nm_device_generate_connection (NMDevice *self, NMDevice *master);
 
-NMConnection * nm_device_get_best_auto_connection (NMDevice *dev,
-                                                   GSList *connections,
-                                                   char **specific_object);
+gboolean nm_device_master_update_slave_connection (NMDevice *master,
+                                                   NMDevice *slave,
+                                                   NMConnection *connection,
+                                                   GError **error);
+
+gboolean nm_device_can_auto_connect (NMDevice *self,
+                                     NMConnection *connection,
+                                     char **specific_object);
 
 gboolean nm_device_complete_connection (NMDevice *device,
                                         NMConnection *connection,
@@ -272,6 +279,8 @@ gboolean nm_device_complete_connection (NMDevice *device,
 
 gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
 
+gboolean nm_device_uses_assumed_connection (NMDevice *device);
+
 gboolean nm_device_can_assume_active_connection (NMDevice *device);
 
 gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
@@ -294,12 +303,16 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device);
  * @NM_UNMANAGED_INTERNAL: %TRUE when unmanaged by internal decision (ie,
  *   because NM is sleeping or not managed for some other reason)
  * @NM_UNMANAGED_USER: %TRUE when unmanaged by user decision (via unmanaged-specs)
+ * @NM_UNMANAGED_PARENT: %TRUE when unmanaged due to parent device being unmanaged
+ * @NM_UNMANAGED_EXTERNAL_DOWN: %TRUE when unmanaged because !IFF_UP and not created by NM
  */
 typedef enum {
-	NM_UNMANAGED_NONE     = 0x00,
-	NM_UNMANAGED_DEFAULT  = 0x01,
-	NM_UNMANAGED_INTERNAL = 0x02,
-	NM_UNMANAGED_USER     = 0x04,
+	NM_UNMANAGED_NONE          = 0x00,
+	NM_UNMANAGED_DEFAULT       = 0x01,
+	NM_UNMANAGED_INTERNAL      = 0x02,
+	NM_UNMANAGED_USER          = 0x04,
+	NM_UNMANAGED_PARENT        = 0x08,
+	NM_UNMANAGED_EXTERNAL_DOWN = 0x10,
 
 	/* Boundary value */
 	__NM_UNMANAGED_LAST,
@@ -355,6 +368,13 @@ gboolean nm_device_notify_component_added (NMDevice *device, GObject *component)
 
 gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
 
+NMConnection *nm_device_new_default_connection (NMDevice *self);
+
+const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed);
+const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed);
+
+void nm_device_spawn_iface_helper (NMDevice *self);
+
 G_END_DECLS
 
 /* For testing only */
diff --git a/src/devices/team/Makefile.am b/src/devices/team/Makefile.am
new file mode 100644
index 00000000..6fea9b3f
--- /dev/null
+++ b/src/devices/team/Makefile.am
@@ -0,0 +1,68 @@
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+AM_CPPFLAGS = \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/devices \
+	-I${top_srcdir}/src/platform \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
+	-DG_LOG_DOMAIN=\""NetworkManager-team"\" \
+	-DNETWORKMANAGER_COMPILATION \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(DBUS_CFLAGS)
+
+if WITH_TEAMDCTL
+AM_CPPFLAGS += ${LIBTEAMDCTL_CFLAGS}
+endif
+
+GLIB_GENERATED = nm-team-enum-types.h nm-team-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_team_enum_types_sources = $(srcdir)/nm-device-team.h
+
+glue_sources = \
+	nm-device-team-glue.h
+
+%-glue.h: $(top_srcdir)/introspection/%.xml
+	$(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources)
+
+pkglib_LTLIBRARIES = libnm-device-plugin-team.la
+
+SYMBOL_VIS_FILE=$(srcdir)/exports.ver
+
+libnm_device_plugin_team_la_SOURCES = \
+	nm-device-team.c \
+	nm-device-team.h \
+	nm-team-factory.c \
+	nm-team-factory.h \
+	\
+	$(BUILT_SOURCES)
+
+libnm_device_plugin_team_la_LDFLAGS = \
+	-module -avoid-version \
+	-Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_team_la_LIBADD = \
+	$(DBUS_LIBS) \
+	$(GUDEV_LIBS)
+
+if WITH_TEAMDCTL
+libnm_device_plugin_team_la_LIBADD += $(LIBTEAMDCTL_LIBS)
+endif
+
+CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+
+if ENABLE_TESTS
+
+check-local:
+	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-team.so $(SYMBOL_VIS_FILE)
+
+endif
+
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
new file mode 100644
index 00000000..4ee30e66
--- /dev/null
+++ b/src/devices/team/Makefile.in
@@ -0,0 +1,845 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@WITH_TEAMDCTL_TRUE@am__append_1 = ${LIBTEAMDCTL_CFLAGS}
+@WITH_TEAMDCTL_TRUE@am__append_2 = $(LIBTEAMDCTL_LIBS)
+subdir = src/devices/team
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(pkglibdir)"
+LTLIBRARIES = $(pkglib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@WITH_TEAMDCTL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+libnm_device_plugin_team_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+am__objects_1 = nm-team-enum-types.lo
+am__objects_2 =
+am__objects_3 = $(am__objects_1) $(am__objects_2)
+am_libnm_device_plugin_team_la_OBJECTS = nm-device-team.lo \
+	nm-team-factory.lo $(am__objects_3)
+libnm_device_plugin_team_la_OBJECTS =  \
+	$(am_libnm_device_plugin_team_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libnm_device_plugin_team_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) $(libnm_device_plugin_team_la_LDFLAGS) \
+	$(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libnm_device_plugin_team_la_SOURCES)
+DIST_SOURCES = $(libnm_device_plugin_team_la_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_DIR = @UDEV_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+AM_CPPFLAGS = -I${top_srcdir}/src -I${top_builddir}/src \
+	-I${top_srcdir}/src/devices -I${top_srcdir}/src/platform \
+	-I${top_srcdir}/include -I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
+	-DG_LOG_DOMAIN=\""NetworkManager-team"\" \
+	-DNETWORKMANAGER_COMPILATION \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(DBUS_CFLAGS) \
+	$(am__append_1)
+GLIB_GENERATED = nm-team-enum-types.h nm-team-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_team_enum_types_sources = $(srcdir)/nm-device-team.h
+glue_sources = \
+	nm-device-team-glue.h
+
+BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources)
+pkglib_LTLIBRARIES = libnm-device-plugin-team.la
+SYMBOL_VIS_FILE = $(srcdir)/exports.ver
+libnm_device_plugin_team_la_SOURCES = \
+	nm-device-team.c \
+	nm-device-team.h \
+	nm-team-factory.c \
+	nm-team-factory.h \
+	\
+	$(BUILT_SOURCES)
+
+libnm_device_plugin_team_la_LDFLAGS = \
+	-module -avoid-version \
+	-Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_team_la_LIBADD = $(DBUS_LIBS) $(GUDEV_LIBS) \
+	$(am__append_2)
+CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/team/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/devices/team/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \
+	}
+
+uninstall-pkglibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
+	done
+
+clean-pkglibLTLIBRARIES:
+	-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
+	@list='$(pkglib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libnm-device-plugin-team.la: $(libnm_device_plugin_team_la_OBJECTS) $(libnm_device_plugin_team_la_DEPENDENCIES) $(EXTRA_libnm_device_plugin_team_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libnm_device_plugin_team_la_LINK) -rpath $(pkglibdir) $(libnm_device_plugin_team_la_OBJECTS) $(libnm_device_plugin_team_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-team.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-team-enum-types.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-team-factory.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+@ENABLE_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(pkglibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-pkglibLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pkglibLTLIBRARIES
+
+.MAKE: all check check-am install install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \
+	clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	cscopelist-am ctags ctags-am distclean distclean-compile \
+	distclean-generic distclean-libtool distclean-tags distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-pkglibLTLIBRARIES install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-pkglibLTLIBRARIES
+
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+%-glue.h: $(top_srcdir)/introspection/%.xml
+	$(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $<
+
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-team.so $(SYMBOL_VIS_FILE)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/devices/team/exports.ver b/src/devices/team/exports.ver
new file mode 100644
index 00000000..24cd848c
--- /dev/null
+++ b/src/devices/team/exports.ver
@@ -0,0 +1,6 @@
+{
+global:
+	nm_device_factory_create;
+local:
+	*;
+};
diff --git a/src/devices/team/nm-device-team-glue.h b/src/devices/team/nm-device-team-glue.h
new file mode 100644
index 00000000..405975c8
--- /dev/null
+++ b/src/devices/team/nm-device-team-glue.h
@@ -0,0 +1,73 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_nm_device_team_MARSHAL_H__
+#define __dbus_glib_marshal_nm_device_team_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_nm_device_team_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_nm_device_team_methods[] = {
+};
+
+const DBusGObjectInfo dbus_glib_nm_device_team_object_info = {  1,
+  dbus_glib_nm_device_team_methods,
+  0,
+"\0",
+"org.freedesktop.NetworkManager.Device.Team\0PropertiesChanged\0\0",
+"org.freedesktop.NetworkManager.Device.Team\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.Team\0Carrier\0carrier\0read\0org.freedesktop.NetworkManager.Device.Team\0Slaves\0slaves\0read\0\0"
+};
+
diff --git a/src/devices/nm-device-team.c b/src/devices/team/nm-device-team.c
index f3b25e3b..16f1fcc2 100644
--- a/src/devices/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -27,38 +27,34 @@
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
-#include <netinet/ether.h>
-#if WITH_TEAMDCTL
 #include <teamdctl.h>
-#endif
 #include <stdlib.h>
 
 #include "nm-device-team.h"
 #include "nm-logging.h"
-#include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-platform.h"
 #include "nm-dbus-glib-types.h"
 #include "nm-dbus-manager.h"
 #include "nm-enum-types.h"
+#include "nm-team-enum-types.h"
 #include "nm-posix-signals.h"
+#include "nm-core-internal.h"
 
 #include "nm-device-team-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceTeam);
 
 G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate))
 
-#define NM_TEAM_ERROR (nm_team_error_quark ())
-
-static gboolean teamd_start (NMDevice *dev, NMSettingTeam *s_team);
+static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
 
 typedef struct {
-#if WITH_TEAMDCTL
 	struct teamdctl *tdc;
-#endif
 	GPid teamd_pid;
 	guint teamd_process_watch;
 	guint teamd_timeout;
@@ -74,28 +70,17 @@ enum {
 
 /******************************************************************/
 
-static GQuark
-nm_team_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-team-error");
-	return quark;
-}
-
-/******************************************************************/
-
 static guint32
-get_generic_capabilities (NMDevice *dev)
+get_generic_capabilities (NMDevice *device)
 {
 	return NM_DEVICE_CAP_CARRIER_DETECT;
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *device)
 {
-	if (NM_DEVICE_GET_CLASS (dev)->is_up)
-		return NM_DEVICE_GET_CLASS (dev)->is_up (dev);
+	if (NM_DEVICE_GET_CLASS (device)->is_up)
+		return NM_DEVICE_GET_CLASS (device)->is_up (device);
 	return FALSE;
 }
 
@@ -124,7 +109,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	/* Team connections must specify the virtual interface name */
-	iface = nm_connection_get_virtual_iface_name (connection);
+	iface = nm_connection_get_interface_name (connection);
 	if (!iface || strcmp (nm_device_get_iface (device), iface))
 		return FALSE;
 
@@ -140,17 +125,14 @@ complete_connection (NMDevice *device,
                      const GSList *existing_connections,
                      GError **error)
 {
-	NMSettingTeam *s_team, *tmp;
-	guint32 i = 0;
-	char *name;
-	const GSList *iter;
-	gboolean found;
+	NMSettingTeam *s_team;
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_TEAM_SETTING_NAME,
 	                           existing_connections,
-	                           _("Team connection %d"),
 	                           NULL,
+	                           _("Team connection"),
+	                           "team",
 	                           TRUE);
 
 	s_team = nm_connection_get_setting_team (connection);
@@ -159,41 +141,13 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_team));
 	}
 
-	/* Grab the first name that doesn't exist in either our connections
-	 * or a device on the system.
-	 */
-	while (i < 500 && !nm_setting_team_get_interface_name (s_team)) {
-		name = g_strdup_printf ("team%u", i);
-		/* check interface names */
-		if (!nm_platform_link_exists (name)) {
-			/* check existing team connections */
-			for (iter = existing_connections, found = FALSE; iter; iter = g_slist_next (iter)) {
-				NMConnection *candidate = iter->data;
-
-				tmp = nm_connection_get_setting_team (candidate);
-				if (tmp && nm_connection_is_type (candidate, NM_SETTING_TEAM_SETTING_NAME)) {
-					if (g_strcmp0 (nm_setting_team_get_interface_name (tmp), name) == 0) {
-						found = TRUE;
-						break;
-					}
-				}
-			}
-
-			if (!found)
-				g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_INTERFACE_NAME, name, NULL);
-		}
-
-		g_free (name);
-		i++;
-	}
-
 	return TRUE;
 }
 
-#if WITH_TEAMDCTL
 static gboolean
-ensure_teamd_connection (NMDevice *self)
+ensure_teamd_connection (NMDevice *device)
 {
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	int err;
 
@@ -202,32 +156,28 @@ ensure_teamd_connection (NMDevice *self)
 
 	priv->tdc = teamdctl_alloc ();
 	g_assert (priv->tdc);
-	err = teamdctl_connect (priv->tdc, nm_device_get_iface (self), NULL, NULL);
+	err = teamdctl_connect (priv->tdc, nm_device_get_iface (device), NULL, NULL);
 	if (err != 0) {
-		nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd (err=%d)",
-		            nm_device_get_iface (self), err);
+		_LOGE (LOGD_TEAM, "failed to connect to teamd (err=%d)", err);
 		teamdctl_free (priv->tdc);
 		priv->tdc = NULL;
 	}
 
 	return !!priv->tdc;
 }
-#endif
 
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMSettingTeam *s_team = nm_connection_get_setting_team (connection);
-	const char *iface = nm_device_get_iface (device);
 
 	if (!s_team) {
 		s_team = (NMSettingTeam *) nm_setting_team_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_team);
-		g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_INTERFACE_NAME, iface, NULL);
 	}
 	g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, NULL, NULL);
 
-#if WITH_TEAMDCTL
 	teamd_start (device, s_team);
 	if (NM_DEVICE_TEAM_GET_PRIVATE (device)->teamd_pid > 0 && ensure_teamd_connection (device)) {
 		const char *config = NULL;
@@ -238,51 +188,59 @@ update_connection (NMDevice *device, NMConnection *connection)
 		if (err == 0)
 			g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, config, NULL);
 		else
-			nm_log_err (LOGD_TEAM, "(%s): failed to read teamd config (err=%d)", iface, err);
+			_LOGE (LOGD_TEAM, "failed to read teamd config (err=%d)", err);
 	}
-#endif
 }
 
 /******************************************************************/
 
-gboolean
-nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection)
+static gboolean
+master_update_slave_connection (NMDevice *self,
+                                   NMDevice *slave,
+                                   NMConnection *connection,
+                                   GError **error)
 {
 	NMSettingTeamPort *s_port;
-	const char *iface = nm_device_get_iface (slave);
 	char *port_config = NULL;
-	gboolean with_teamdctl = FALSE;
 	int err = 0;
-#if WITH_TEAMDCTL
-	const char *master_iface;
-	int master_ifindex;
 	struct teamdctl *tdc;
 	const char *team_port_config = NULL;
-#endif
-
-	g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
-
-#if WITH_TEAMDCTL
-	master_ifindex = nm_platform_link_get_master (nm_device_get_ifindex (slave));
-	g_assert (master_ifindex > 0);
-	master_iface = nm_platform_link_get_name (master_ifindex);
-	g_assert (master_iface);
+	const char *iface = nm_device_get_iface (self);
+	const char *iface_slave = nm_device_get_iface (slave);
 
 	tdc = teamdctl_alloc ();
-	g_assert (tdc);
-	err = teamdctl_connect (tdc, master_iface, NULL, NULL);
+	if (!tdc) {
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_FAILED,
+		             "update slave connection for slave '%s' failed to connect to teamd for master %s (out of memory?)",
+		             iface_slave, iface);
+		g_return_val_if_reached (FALSE);
+	}
+
+	err = teamdctl_connect (tdc, iface, NULL, NULL);
 	if (err) {
-		nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd for master %s (err=%d)",
-		            iface, master_iface, err);
 		teamdctl_free (tdc);
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_FAILED,
+		             "update slave connection for slave '%s' failed to connect to teamd for master %s (err=%d)",
+		             iface_slave, iface, err);
 		return FALSE;
 	}
-	err = teamdctl_port_config_get_raw_direct (tdc, iface, (char **)&team_port_config);
+
+	err = teamdctl_port_config_get_raw_direct (tdc, iface_slave, (char **)&team_port_config);
 	port_config = g_strdup (team_port_config);
 	teamdctl_free (tdc);
-	with_teamdctl = TRUE;
-#endif
+	if (err) {
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_FAILED,
+		             "update slave connection for slave '%s' failed to get configuration from teamd master %s (err=%d)",
+		             iface_slave, iface, err);
+		g_free (port_config);
+		return FALSE;
+	}
 
 	s_port = nm_connection_get_setting_team_port (connection);
 	if (!s_port) {
@@ -293,56 +251,19 @@ nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection)
 	g_object_set (G_OBJECT (s_port), NM_SETTING_TEAM_PORT_CONFIG, port_config, NULL);
 	g_free (port_config);
 
-	if (!with_teamdctl || err != 0) {
-		if (!with_teamdctl)
-			nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration "
-			                       " (compiled without libteamdctl support)", iface);
-		else
-			nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration (err=%d)",
-			            iface, err);
-		return FALSE;
-	}
-
+	g_object_set (nm_connection_get_setting_connection (connection),
+	              NM_SETTING_CONNECTION_MASTER, iface,
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
+	              NULL);
 	return TRUE;
 }
 
 /******************************************************************/
 
-static gboolean
-ensure_killed (gpointer data)
-{
-	int pid = GPOINTER_TO_INT (data);
-
-	if (kill (pid, 0) == 0)
-		kill (pid, SIGKILL);
-
-	/* ensure the child is reaped */
-	nm_log_dbg (LOGD_TEAM, "waiting for teamd pid %d to exit", pid);
-	waitpid (pid, NULL, 0);
-	nm_log_dbg (LOGD_TEAM, "teamd pid %d cleaned up", pid);
-
-	return FALSE;
-}
-
 static void
-service_kill (int pid)
+teamd_timeout_remove (NMDevice *device)
 {
-	if (kill (pid, SIGTERM) == 0)
-		g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (pid));
-	else {
-		kill (pid, SIGKILL);
-
-		/* ensure the child is reaped */
-		nm_log_dbg (LOGD_TEAM, "waiting for teamd pid %d to exit", pid);
-		waitpid (pid, NULL, 0);
-		nm_log_dbg (LOGD_TEAM, "teamd pid %d cleaned up", pid);
-	}
-}
-
-static void
-teamd_timeout_remove (NMDevice *dev)
-{
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
 	if (priv->teamd_timeout) {
 		g_source_remove (priv->teamd_timeout);
@@ -351,9 +272,9 @@ teamd_timeout_remove (NMDevice *dev)
 }
 
 static void
-teamd_cleanup (NMDevice *dev, gboolean device_state_failed)
+teamd_cleanup (NMDevice *device, gboolean device_state_failed)
 {
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
 	if (priv->teamd_dbus_watch) {
 		g_bus_unwatch_name (priv->teamd_dbus_watch);
@@ -366,37 +287,36 @@ teamd_cleanup (NMDevice *dev, gboolean device_state_failed)
 	}
 
 	if (priv->teamd_pid > 0) {
-		service_kill (priv->teamd_pid);
+		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL);
 		priv->teamd_pid = 0;
 	}
 
-#if WITH_TEAMDCTL
 	if (priv->tdc) {
 		teamdctl_disconnect (priv->tdc);
 		teamdctl_free (priv->tdc);
 		priv->tdc = NULL;
 	}
-#endif
 
-	teamd_timeout_remove (dev);
+	teamd_timeout_remove (device);
 
 	if (device_state_failed) {
-		if (nm_device_is_activating (dev) ||
-		    (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED))
-			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+		if (nm_device_is_activating (device) ||
+		    (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 	}
 }
 
 static gboolean
 teamd_timeout_cb (gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
 	g_return_val_if_fail (priv->teamd_timeout, FALSE);
 
-	nm_log_info (LOGD_TEAM, "(%s): teamd timed out.", nm_device_get_iface (dev));
-	teamd_cleanup (dev, TRUE);
+	_LOGI (LOGD_TEAM, "teamd timed out.");
+	teamd_cleanup (device, TRUE);
 
 	return FALSE;
 }
@@ -407,20 +327,24 @@ teamd_dbus_appeared (GDBusConnection *connection,
                      const gchar *name_owner,
                      gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	gboolean success;
 
 	g_return_if_fail (priv->teamd_dbus_watch);
 
-	nm_log_info (LOGD_TEAM, "(%s): teamd appeared on D-Bus", nm_device_get_iface (dev));
-	teamd_timeout_remove (dev);
-#if WITH_TEAMDCTL
-	if (!ensure_teamd_connection (dev)) {
-		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	_LOGI (LOGD_TEAM, "teamd appeared on D-Bus");
+	teamd_timeout_remove (device);
+
+	success = ensure_teamd_connection (device);
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) {
+		if (success)
+			nm_device_activate_schedule_stage2_device_config (device);
+		else if (!nm_device_uses_assumed_connection (device))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 		return;
 	}
-#endif
-	nm_device_activate_schedule_stage2_device_config (dev);
 }
 
 static void
@@ -428,8 +352,9 @@ teamd_dbus_vanished (GDBusConnection *connection,
                      const gchar *name,
                      gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
 
 	g_return_if_fail (priv->teamd_dbus_watch);
 
@@ -441,26 +366,27 @@ teamd_dbus_vanished (GDBusConnection *connection,
 		 * Note that g_bus_watch_name is guaranteed to alternate vanished/appeared signals,
 		 * so we won't hit this condition again (because the next signal is either 'appeared'
 		 * or 'timeout'). */
-		nm_log_dbg (LOGD_TEAM, "(%s): teamd vanished from D-Bus (ignored)", nm_device_get_iface (dev));
+		_LOGD (LOGD_TEAM, "teamd vanished from D-Bus (ignored)");
 		return;
 	}
 
-	nm_log_info (LOGD_TEAM, "(%s): teamd vanished from D-Bus", nm_device_get_iface (dev));
-	teamd_cleanup (dev, TRUE);
+	_LOGI (LOGD_TEAM, "teamd vanished from D-Bus");
+	teamd_cleanup (device, TRUE);
 }
 
 static void
 teamd_process_watch_cb (GPid pid, gint status, gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
 
 	g_return_if_fail (priv->teamd_process_watch);
 
-	nm_log_info (LOGD_TEAM, "(%s): teamd died", nm_device_get_iface (dev));
+	_LOGI (LOGD_TEAM, "teamd died with status %d", status);
 	priv->teamd_process_watch = 0;
 	priv->teamd_pid = 0;
-	teamd_cleanup (dev, TRUE);
+	teamd_cleanup (device, TRUE);
 }
 
 static void
@@ -481,18 +407,14 @@ teamd_child_setup (gpointer user_data G_GNUC_UNUSED)
 }
 
 static gboolean
-teamd_start (NMDevice *dev, NMSettingTeam *s_team)
+teamd_start (NMDevice *device, NMSettingTeam *s_team)
 {
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
-	const char *iface = nm_device_get_ip_iface (dev);
-	char *tmp_str;
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	const char *iface = nm_device_get_ip_iface (device);
+	char *tmp_str = NULL;
 	const char *config;
-	const char **teamd_binary = NULL;
-	static const char *teamd_paths[] = {
-		"/usr/bin/teamd",
-		"/usr/local/bin/teamd",
-		NULL
-	};
+	const char *teamd_binary;
 	GPtrArray *argv;
 	GError *error = NULL;
 	gboolean ret;
@@ -501,51 +423,42 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team)
 	if (priv->teamd_dbus_watch ||
 	    priv->teamd_process_watch ||
 	    priv->teamd_pid > 0 ||
-#if WITH_TEAMDCTL
 	    priv->tdc ||
-#endif
 	    priv->teamd_timeout)
 	{
 		/* Just return if teamd_start() was already called */
 		return TRUE;
 	}
 
-	teamd_binary = teamd_paths;
-	while (*teamd_binary != NULL) {
-		if (g_file_test (*teamd_binary, G_FILE_TEST_EXISTS))
-			break;
-		teamd_binary++;
-	}
-
-	if (!*teamd_binary) {
-		nm_log_warn (LOGD_TEAM,
-		             "Activation (%s) failed to start teamd: teamd binary not found",
-		             iface);
+	teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
+	if (!teamd_binary) {
+		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: teamd binary not found");
 		return FALSE;
 	}
 
 	/* Kill teamd for same named device first if it is there */
 	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) *teamd_binary);
+	g_ptr_array_add (argv, (gpointer) teamd_binary);
 	g_ptr_array_add (argv, (gpointer) "-k");
 	g_ptr_array_add (argv, (gpointer) "-t");
 	g_ptr_array_add (argv, (gpointer) iface);
 	g_ptr_array_add (argv, NULL);
 
-	tmp_str = g_strjoinv (" ", (gchar **) argv->pdata);
-	nm_log_dbg (LOGD_TEAM, "running: %s", tmp_str);
-	g_free (tmp_str);
+	_LOGD (LOGD_TEAM, "running: %s",
+	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	g_clear_pointer (&tmp_str, g_free);
 
 	ret = g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, &status, &error);
 	g_ptr_array_free (argv, TRUE);
 
 	/* Start teamd now */
 	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) *teamd_binary);
+	g_ptr_array_add (argv, (gpointer) teamd_binary);
 	g_ptr_array_add (argv, (gpointer) "-o");
 	g_ptr_array_add (argv, (gpointer) "-n");
 	g_ptr_array_add (argv, (gpointer) "-U");
 	g_ptr_array_add (argv, (gpointer) "-D");
+	g_ptr_array_add (argv, (gpointer) "-N");
 	g_ptr_array_add (argv, (gpointer) "-t");
 	g_ptr_array_add (argv, (gpointer) iface);
 
@@ -559,12 +472,12 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team)
 		g_ptr_array_add (argv, (gpointer) "-gg");
 	g_ptr_array_add (argv, NULL);
 
-	tmp_str = g_strjoinv (" ", (gchar **) argv->pdata);
-	nm_log_dbg (LOGD_TEAM, "running: %s", tmp_str);
-	g_free (tmp_str);
+	_LOGD (LOGD_TEAM, "running: %s",
+	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	g_clear_pointer (&tmp_str, g_free);
 
 	/* Start a timeout for teamd to appear at D-Bus */
-	priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, dev);
+	priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
 
 	/* Register D-Bus name watcher */
 	tmp_str = g_strdup_printf ("org.libteam.teamd.%s", iface);
@@ -573,7 +486,7 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team)
 	                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
 	                                           teamd_dbus_appeared,
 	                                           teamd_dbus_vanished,
-	                                           dev,
+	                                           device,
 	                                           NULL);
 	g_free (tmp_str);
 
@@ -581,41 +494,36 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team)
 	                    &teamd_child_setup, NULL, &priv->teamd_pid, &error);
 	g_ptr_array_free (argv, TRUE);
 	if (!ret) {
-		nm_log_warn (LOGD_TEAM,
-		             "Activation (%s) failed to start teamd: %s",
-		             iface, error->message);
+		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
 		g_clear_error (&error);
-		teamd_cleanup (dev, FALSE);
+		teamd_cleanup (device, FALSE);
 		return FALSE;
 	}
 
 	/* Monitor the child process so we know when it dies */
 	priv->teamd_process_watch = g_child_watch_add (priv->teamd_pid,
 	                                               teamd_process_watch_cb,
-	                                               dev);
+	                                               device);
 
-	nm_log_info (LOGD_TEAM,
-	             "Activation (%s) started teamd...", iface);
+	_LOGI (LOGD_TEAM, "Activation: (team) started teamd...");
 	return TRUE;
 }
 
 static void
-teamd_stop (NMDevice *dev)
+teamd_stop (NMDevice *device)
 {
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (dev);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
-	if (priv->teamd_pid > 0) {
-		nm_log_info (LOGD_TEAM, "Deactivation (%s) stopping teamd...",
-		             nm_device_get_ip_iface (dev));
-	} else {
-		nm_log_dbg (LOGD_TEAM, "Deactivation (%s) stopping teamd (not started)...",
-		            nm_device_get_ip_iface (dev));
-	}
-	teamd_cleanup (dev, FALSE);
+	if (priv->teamd_pid > 0)
+		_LOGI (LOGD_TEAM, "Deactivation: stopping teamd...");
+	else
+		_LOGD (LOGD_TEAM, "Deactivation: stopping teamd (not started)...");
+	teamd_cleanup (device, FALSE);
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	NMConnection *connection;
@@ -623,13 +531,13 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, reason);
 	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
-		connection = nm_device_get_connection (dev);
+		connection = nm_device_get_connection (device);
 		g_assert (connection);
 		s_team = nm_connection_get_setting_team (connection);
 		g_assert (s_team);
-		if (teamd_start (dev, s_team))
+		if (teamd_start (device, s_team))
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 		else
 			ret = NM_ACT_STAGE_RETURN_FAILURE;
@@ -638,9 +546,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 }
 
 static void
-deactivate (NMDevice *dev)
+deactivate (NMDevice *device)
 {
-	teamd_stop (dev);
+	teamd_stop (device);
 }
 
 static gboolean
@@ -649,11 +557,9 @@ enslave_slave (NMDevice *device,
                NMConnection *connection,
                gboolean configure)
 {
-#if WITH_TEAMDCTL
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
-#endif
 	gboolean success = TRUE, no_firmware = FALSE;
-	const char *iface = nm_device_get_ip_iface (device);
 	const char *slave_iface = nm_device_get_ip_iface (slave);
 	NMSettingTeamPort *s_team_port;
 
@@ -667,10 +573,9 @@ enslave_slave (NMDevice *device,
 			const char *config = nm_setting_team_port_get_config (s_team_port);
 
 			if (config) {
-#if WITH_TEAMDCTL
 				if (!priv->tdc) {
-					nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd",
-					             iface, slave_iface);
+					_LOGW (LOGD_TEAM, "enslaved team port %s config not changed, not connected to teamd",
+					       slave_iface);
 				} else {
 					int err;
 					char *sanitized_config;
@@ -679,15 +584,11 @@ enslave_slave (NMDevice *device,
 					err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, sanitized_config);
 					g_free (sanitized_config);
 					if (err != 0) {
-						nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s (err=%d)",
-						            iface, slave_iface, err);
+						_LOGE (LOGD_TEAM, "failed to update config for port %s (err=%d)",
+						       slave_iface, err);
 						return FALSE;
 					}
 				}
-#else
-				nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support",
-				             iface, slave_iface);
-#endif
 			}
 		}
 		success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
@@ -697,9 +598,9 @@ enslave_slave (NMDevice *device,
 		if (!success)
 			return FALSE;
 
-		nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface);
+		_LOGI (LOGD_TEAM, "enslaved team port %s", slave_iface);
 	} else
-		nm_log_info (LOGD_TEAM, "(%s): team port %s was enslaved", iface, slave_iface);
+		_LOGI (LOGD_TEAM, "team port %s was enslaved", slave_iface);
 
 	g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES);
 
@@ -711,26 +612,19 @@ release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	gboolean success = TRUE, no_firmware = FALSE;
 
 	if (configure) {
 		success = nm_platform_link_release (nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
-		if (success) {
-			nm_log_info (LOGD_TEAM, "(%s): released team port %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
-		} else {
-			nm_log_warn (LOGD_TEAM, "(%s): failed to release team port %s",
-			             nm_device_get_ip_iface (device),
-			             nm_device_get_ip_iface (slave));
-		}
-	} else {
-		nm_log_info (LOGD_TEAM, "(%s): team port %s was released",
-		             nm_device_get_ip_iface (device),
-		             nm_device_get_ip_iface (slave));
-	}
+		if (success)
+			_LOGI (LOGD_TEAM, "released team port %s", nm_device_get_ip_iface (slave));
+		else
+			_LOGW (LOGD_TEAM, "failed to release team port %s", nm_device_get_ip_iface (slave));
+	} else
+		_LOGI (LOGD_TEAM, "team port %s was released", nm_device_get_ip_iface (slave));
 
 	if (success)
 		g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES);
@@ -740,10 +634,9 @@ release_slave (NMDevice *device,
 		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
 		 * other state is noticed by the now-released port.
 		 */
-		if (!nm_device_bring_up (slave, TRUE, &no_firmware)) {
-			nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.",
-			             nm_device_get_iface (slave));
-		}
+		if (!nm_device_bring_up (slave, TRUE, &no_firmware))
+			_LOGW (LOGD_TEAM, "released team port %s could not be brought up",
+			       nm_device_get_ip_iface (slave));
 	}
 
 	return success;
@@ -766,18 +659,21 @@ nm_device_team_new (NMPlatformLink *platform_device)
 }
 
 NMDevice *
-nm_device_team_new_for_connection (NMConnection *connection)
+nm_device_team_new_for_connection (NMConnection *connection, GError **error)
 {
 	const char *iface;
 
 	g_return_val_if_fail (connection != NULL, NULL);
 
-	iface = nm_connection_get_virtual_iface_name (connection);
+	iface = nm_connection_get_interface_name (connection);
 	g_return_val_if_fail (iface != NULL, NULL);
 
 	if (   !nm_platform_team_add (iface)
 	    && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
-		nm_log_warn (LOGD_DEVICE | LOGD_TEAM, "(%s): failed to create team master interface for '%s': %s",
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_CREATION_FAILED,
+		             "failed to create team master interface '%s' for connection '%s': %s",
 		             iface, nm_connection_get_id (connection),
 		             nm_platform_get_error_msg ());
 		return NULL;
@@ -793,16 +689,6 @@ nm_device_team_new_for_connection (NMConnection *connection)
 }
 
 static void
-constructed (GObject *object)
-{
-	G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object);
-
-	nm_log_dbg (LOGD_HW | LOGD_TEAM, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (NM_DEVICE (object)),
-	            nm_device_get_ifindex (NM_DEVICE (object)));
-}
-
-static void
 nm_device_team_init (NMDeviceTeam * self)
 {
 }
@@ -860,7 +746,6 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->connection_type = NM_SETTING_TEAM_SETTING_NAME;
 
 	/* virtual methods */
-	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
@@ -871,6 +756,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->check_connection_available = check_connection_available;
 	parent_class->complete_connection = complete_connection;
 	parent_class->update_connection = update_connection;
+	parent_class->master_update_slave_connection = master_update_slave_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->deactivate = deactivate;
@@ -880,15 +766,12 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES,
-		                     "Slaves",
-		                     "Slaves",
+		 g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_team_object_info);
-
-	dbus_g_error_domain_register (NM_TEAM_ERROR, NULL, NM_TYPE_TEAM_ERROR);
 }
diff --git a/src/devices/nm-device-team.h b/src/devices/team/nm-device-team.h
index fe1275c6..26ba5737 100644
--- a/src/devices/nm-device-team.h
+++ b/src/devices/team/nm-device-team.h
@@ -18,8 +18,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#ifndef NM_DEVICE_TEAM_H
-#define NM_DEVICE_TEAM_H
+#ifndef __NETWORKMANAGER_DEVICE_TEAM_H__
+#define __NETWORKMANAGER_DEVICE_TEAM_H__
 
 #include <glib-object.h>
 
@@ -34,12 +34,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_TEAM))
 #define NM_DEVICE_TEAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass))
 
-typedef enum {
-	NM_TEAM_ERROR_CONNECTION_NOT_TEAM = 0, /*< nick=ConnectionNotTeam >*/
-	NM_TEAM_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_TEAM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMTeamError;
-
 #define NM_DEVICE_TEAM_SLAVES "slaves"
 
 typedef struct {
@@ -55,9 +49,7 @@ typedef struct {
 GType nm_device_team_get_type (void);
 
 NMDevice *nm_device_team_new (NMPlatformLink *platform_device);
-NMDevice *nm_device_team_new_for_connection (NMConnection *connection);
-
-gboolean nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection);
+NMDevice *nm_device_team_new_for_connection (NMConnection *connection, GError **error);
 
 G_END_DECLS
 
diff --git a/src/devices/team/nm-team-enum-types.c b/src/devices/team/nm-team-enum-types.c
new file mode 100644
index 00000000..5ac3494d
--- /dev/null
+++ b/src/devices/team/nm-team-enum-types.c
@@ -0,0 +1,12 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#include "nm-team-enum-types.h"
+
+#include "nm-device-team.h"
+
+
+
+
diff --git a/src/devices/team/nm-team-enum-types.h b/src/devices/team/nm-team-enum-types.h
new file mode 100644
index 00000000..136cadc2
--- /dev/null
+++ b/src/devices/team/nm-team-enum-types.h
@@ -0,0 +1,17 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#ifndef __NM_TEAM_ENUM_TYPES_H__
+#define __NM_TEAM_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+G_END_DECLS
+
+#endif /* __NM_TEAM_ENUM_TYPES_H__ */
+
+
+
diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c
new file mode 100644
index 00000000..cb887cac
--- /dev/null
+++ b/src/devices/team/nm-team-factory.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gmodule.h>
+
+#include "nm-device-factory.h"
+#include "nm-team-factory.h"
+#include "nm-device-team.h"
+#include "nm-logging.h"
+#include "nm-platform.h"
+#include "nm-core-internal.h"
+
+static GType nm_team_factory_get_type (void);
+
+static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+
+G_DEFINE_TYPE_EXTENDED (NMTeamFactory, nm_team_factory, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
+
+/************************************************************************/
+
+G_MODULE_EXPORT NMDeviceFactory *
+nm_device_factory_create (GError **error)
+{
+	return (NMDeviceFactory *) g_object_new (NM_TYPE_TEAM_FACTORY, NULL);
+}
+
+/************************************************************************/
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_TEAM)
+		 return nm_device_team_new (plink);
+	return NULL;
+}
+
+static NMDevice *
+create_virtual_device_for_connection (NMDeviceFactory *factory,
+                                      NMConnection *connection,
+                                      NMDevice *parent,
+                                      GError **error)
+{
+	if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME))
+		return nm_device_team_new_for_connection (connection, error);
+	return NULL;
+}
+
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_TEAM;
+}
+
+/************************************************************************/
+
+static void
+nm_team_factory_init (NMTeamFactory *self)
+{
+}
+
+static void
+device_factory_interface_init (NMDeviceFactory *factory_iface)
+{
+	factory_iface->new_link = new_link;
+	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->get_device_type = get_device_type;
+}
+
+static void
+nm_team_factory_class_init (NMTeamFactoryClass *klass)
+{
+}
diff --git a/src/devices/team/nm-team-factory.h b/src/devices/team/nm-team-factory.h
new file mode 100644
index 00000000..2f02ac57
--- /dev/null
+++ b/src/devices/team/nm-team-factory.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_TEAM_FACTORY_H__
+#define __NETWORKMANAGER_TEAM_FACTORY_H__
+
+#include <glib-object.h>
+
+#define NM_TYPE_TEAM_FACTORY (nm_team_factory_get_type ())
+#define NM_TEAM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TEAM_FACTORY, NMTeamFactory))
+
+typedef struct {
+	GObject parent;
+} NMTeamFactory;
+
+typedef struct {
+	GObjectClass parent;
+} NMTeamFactoryClass;
+
+#endif /* __NETWORKMANAGER_TEAM_FACTORY_H__ */
diff --git a/src/devices/wifi/Makefile.am b/src/devices/wifi/Makefile.am
index 5f3ce286..230c00ac 100644
--- a/src/devices/wifi/Makefile.am
+++ b/src/devices/wifi/Makefile.am
@@ -7,21 +7,17 @@ SUBDIRS=. tests
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/supplicant-manager \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
+	$(DBUS_CFLAGS)
 
 GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
index cbf6826b..175dd900 100644
--- a/src/devices/wifi/Makefile.in
+++ b/src/devices/wifi/Makefile.in
@@ -261,6 +261,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -384,6 +386,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -403,6 +406,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -429,7 +433,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -512,21 +516,17 @@ SUBDIRS = . tests
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/supplicant-manager \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
+	$(DBUS_CFLAGS)
 
 GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
diff --git a/src/devices/wifi/exports.ver b/src/devices/wifi/exports.ver
index d2c45124..24cd848c 100644
--- a/src/devices/wifi/exports.ver
+++ b/src/devices/wifi/exports.ver
@@ -1,7 +1,6 @@
 {
 global:
 	nm_device_factory_create;
-	nm_device_factory_get_device_type;
 local:
 	*;
 };
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 85214a70..0f5aa164 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -25,18 +25,16 @@
  */
 
 #include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
-#include <net/ethernet.h>
-#include <netinet/ether.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <signal.h>
 #include <unistd.h>
-#include <linux/if.h>
 #include <sys/ioctl.h>
 #include <errno.h>
 
@@ -53,14 +51,18 @@
 #include "nm-manager.h"
 #include "nm-enum-types.h"
 #include "nm-dbus-manager.h"
+#include "nm-platform.h"
 #include "nm-wifi-enum-types.h"
 
 /* This is a bug; but we can't really change API now... */
-#include "NetworkManagerVPN.h"
+#include "nm-vpn-dbus-interface.h"
 
 
 #include "nm-device-olpc-mesh-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceOlpcMesh);
+
 G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate))
@@ -80,19 +82,6 @@ struct _NMDeviceOlpcMeshPrivate {
 
 /*******************************************************************/
 
-static GQuark
-nm_olpc_mesh_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-mesh-error");
-	return quark;
-}
-
-#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ())
-
-/*******************************************************************/
-
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
@@ -149,19 +138,17 @@ complete_connection (NMDevice *device,
 	}
 
 	if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) {
-		const guint8 anycast[ETH_ALEN] = { 0xC0, 0x27, 0xC0, 0x27, 0xC0, 0x27 };
+		const char *anycast = "c0:27:c0:27:c0:27";
 
-		tmp = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (tmp, anycast, sizeof (anycast));
-		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, tmp, NULL);
-		g_byte_array_free (tmp, TRUE);
+		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, anycast, NULL);
 
 	}
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_OLPC_MESH_SETTING_NAME,
 	                           existing_connections,
-	                           _("Mesh %d"),
+	                           NULL,
+	                           _("Mesh"),
 	                           NULL,
 	                           FALSE); /* No IPv6 by default */
 
@@ -171,28 +158,27 @@ complete_connection (NMDevice *device,
 /****************************************************************************/
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev);
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device);
 	NMActStageReturn ret;
 	gboolean scanning;
 
-	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (device, reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
 	/* disconnect companion device, if it is connected */
 	if (nm_device_get_act_request (NM_DEVICE (priv->companion))) {
-		nm_log_info (LOGD_OLPC, "(%s): disconnecting companion device %s",
-		             nm_device_get_iface (dev),
-		             nm_device_get_iface (priv->companion));
+		_LOGI (LOGD_OLPC, "disconnecting companion device %s",
+		       nm_device_get_iface (priv->companion));
 		/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
 		nm_device_state_changed (NM_DEVICE (priv->companion),
 		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
-		nm_log_info (LOGD_OLPC, "(%s): companion %s disconnected",
-		             nm_device_get_iface (dev),
-		             nm_device_get_iface (priv->companion));
+		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+		_LOGI (LOGD_OLPC, "companion %s disconnected",
+		       nm_device_get_iface (priv->companion));
 	}
 
 
@@ -218,16 +204,16 @@ _mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 	NMConnection *connection;
 	NMSettingOlpcMesh *s_mesh;
 	guint32 channel;
-	const GByteArray *anycast_addr_array;
-	guint8 *anycast_addr = NULL;
+	GBytes *ssid;
+	const char *anycast_addr;
 
-	connection = nm_device_get_connection (dev);
+	connection = nm_device_get_connection (device);
 	g_assert (connection);
 
 	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
@@ -236,25 +222,25 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 	channel = nm_setting_olpc_mesh_get_channel (s_mesh);
 	if (channel != 0)
 		_mesh_set_channel (self, channel);
-	nm_platform_mesh_set_ssid (nm_device_get_ifindex (dev),
-	                           nm_setting_olpc_mesh_get_ssid (s_mesh));
 
-	anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh);
-	if (anycast_addr_array)
-		anycast_addr = anycast_addr_array->data;
+	ssid = nm_setting_olpc_mesh_get_ssid (s_mesh);
+	nm_platform_mesh_set_ssid (nm_device_get_ifindex (device),
+	                           g_bytes_get_data (ssid, NULL),
+	                           g_bytes_get_size (ssid));
+
+	anycast_addr = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh);
+	nm_device_set_dhcp_anycast_address (device, anycast_addr);
 
-	nm_device_set_dhcp_anycast_address (dev, anycast_addr);
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *device)
 {
-	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 
 	if (!NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->companion) {
-		nm_log_dbg (LOGD_WIFI, "(%s): not available because companion not found",
-		            nm_device_get_iface (dev));
+		_LOGD (LOGD_WIFI, "not available because companion not found");
 		return FALSE;
 	}
 
@@ -310,12 +296,11 @@ companion_state_changed_cb (NMDeviceWifi *companion,
 	    || state > NM_DEVICE_STATE_ACTIVATED)
 		return;
 
-	nm_log_dbg (LOGD_OLPC, "(%s): disconnecting mesh due to companion connectivity",
-	            nm_device_get_iface (NM_DEVICE (self)));
+	_LOGD (LOGD_OLPC, "disconnecting mesh due to companion connectivity");
 	/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
 	nm_device_state_changed (NM_DEVICE (self),
 	                         NM_DEVICE_STATE_DISCONNECTED,
-	                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+	                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
 }
 
 static gboolean
@@ -343,24 +328,21 @@ static gboolean
 check_companion (NMDeviceOlpcMesh *self, NMDevice *other)
 {
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
-	const guint8 *my_addr, *their_addr;
-	guint their_addr_len;
+	const char *my_addr, *their_addr;
 
 	if (!NM_IS_DEVICE_WIFI (other))
 		return FALSE;
 
-	my_addr = nm_device_get_hw_address (NM_DEVICE (self), NULL);
-	their_addr = nm_device_get_hw_address (other, &their_addr_len);
-	if (   (their_addr_len != ETH_ALEN)
-	    || (memcmp (my_addr, their_addr, ETH_ALEN) != 0))
+	my_addr = nm_device_get_hw_address (NM_DEVICE (self));
+	their_addr = nm_device_get_hw_address (other);
+	if (!nm_utils_hwaddr_matches (my_addr, -1, their_addr, -1))
 		return FALSE;
 
 	g_assert (priv->companion == NULL);
 	priv->companion = g_object_ref (other);
 
-	nm_log_info (LOGD_OLPC, "(%s): found companion WiFi device %s",
-	             nm_device_get_iface (NM_DEVICE (self)),
-	             nm_device_get_iface (other));
+	_LOGI (LOGD_OLPC, "found companion WiFi device %s",
+	       nm_device_get_iface (other));
 
 	g_signal_connect (G_OBJECT (other), "state-changed",
 	                  G_CALLBACK (companion_state_changed_cb), self);
@@ -471,13 +453,8 @@ constructor (GType type,
 
 	self = NM_DEVICE_OLPC_MESH (object);
 
-	nm_log_dbg (LOGD_HW | LOGD_OLPC, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            nm_device_get_ifindex (NM_DEVICE (self)));
-
 	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) {
-		nm_log_warn (LOGD_HW | LOGD_OLPC, "(%s): failed to initialize WiFi driver",
-		             nm_device_get_iface (NM_DEVICE (self)));
+		_LOGW (LOGD_HW | LOGD_OLPC, "failed to initialize WiFi driver");
 		g_object_unref (object);
 		return NULL;
 	}
@@ -562,24 +539,20 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_COMPANION,
-		 g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION,
-		                     "Companion device",
-		                     "Companion device object path",
+		 g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION, "", "",
 		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ACTIVE_CHANNEL,
-		 g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL,
-		                   "Active channel",
-		                   "Active channel",
-		                   0, G_MAXUINT32, 0,
-		                   G_PARAM_READABLE));
+		 g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_olpc_mesh_object_info);
-
-	dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, NM_TYPE_OLPC_MESH_ERROR);
 }
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h
index c25dd8e5..67d83fb0 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.h
+++ b/src/devices/wifi/nm-device-olpc-mesh.h
@@ -25,11 +25,10 @@
  * (C) Copyright 2009 One Laptop per Child
  */
 
-#ifndef NM_DEVICE_OLPC_MESH_H
-#define NM_DEVICE_OLPC_MESH_H
+#ifndef __NETWORKMANAGER_DEVICE_OLPC_MESH_H__
+#define __NETWORKMANAGER_DEVICE_OLPC_MESH_H__
 
 #include <glib-object.h>
-#include <dbus/dbus.h>
 
 #include "nm-device.h"
 
@@ -42,13 +41,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_OLPC_MESH))
 #define NM_DEVICE_OLPC_MESH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
 
-typedef enum
-{
-	NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, /*< nick=ConnectionNotMesh >*/
-	NM_OLPC_MESH_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMOlpcMeshError;
-
 #define NM_DEVICE_OLPC_MESH_COMPANION      "companion"
 #define NM_DEVICE_OLPC_MESH_BITRATE        "bitrate"
 #define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel"
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 95173cf4..dd7754b3 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -19,12 +19,13 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
-#include <net/ethernet.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <signal.h>
@@ -32,7 +33,6 @@
 #include <linux/sockios.h>
 #include <linux/ethtool.h>
 #include <sys/ioctl.h>
-#include <netinet/ether.h>
 #include <errno.h>
 
 #include "nm-glib-compat.h"
@@ -52,9 +52,10 @@
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-ip4-config.h"
+#include "nm-ip4-config.h"
 #include "nm-setting-ip6-config.h"
 #include "nm-platform.h"
-#include "nm-manager-auth.h"
+#include "nm-auth-utils.h"
 #include "nm-settings-connection.h"
 #include "nm-enum-types.h"
 #include "nm-dbus-glib-types.h"
@@ -76,6 +77,8 @@ static void impl_device_request_scan (NMDeviceWifi *device,
 
 #include "nm-device-wifi-glue.h"
 
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceWifi);
 
 /* All of these are in seconds */
 #define SCAN_INTERVAL_MIN 3
@@ -115,8 +118,8 @@ static guint signals[LAST_SIGNAL] = { 0 };
 struct _NMDeviceWifiPrivate {
 	gboolean          disposed;
 
-	guint8            perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
-	guint8            initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
+	char *            perm_hw_addr;    /* Permanent MAC address */
+	char *            initial_hw_addr; /* Initial MAC address (as seen when NM starts) */
 
 	gint8             invalid_strength_counter;
 
@@ -192,19 +195,6 @@ static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
 
 /*****************************************************************/
 
-#define NM_WIFI_ERROR (nm_wifi_error_quark ())
-
-static GQuark
-nm_wifi_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-wifi-error");
-	return quark;
-}
-
-/*****************************************************************/
-
 static GObject*
 constructor (GType type,
              guint n_construct_params,
@@ -223,22 +213,15 @@ constructor (GType type,
 	self = NM_DEVICE_WIFI (object);
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            nm_device_get_ifindex (NM_DEVICE (self)));
-
 	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)),
 	                                        &priv->capabilities)) {
-		nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver",
-		             nm_device_get_iface (NM_DEVICE (self)));
+		_LOGW (LOGD_HW | LOGD_WIFI, "failed to initialize WiFi driver");
 		g_object_unref (object);
 		return NULL;
 	}
 
-	if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) {
-		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode",
-		             nm_device_get_iface (NM_DEVICE (self)));
-	}
+	if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP)
+		_LOGI (LOGD_HW | LOGD_WIFI, "driver supports Access Point (AP) mode");
 
 	/* Connect to the supplicant manager */
 	priv->sup_mgr = nm_supplicant_manager_get ();
@@ -260,13 +243,12 @@ supplicant_interface_acquire (NMDeviceWifi *self)
 	                                                   nm_device_get_iface (NM_DEVICE (self)),
 	                                                   TRUE);
 	if (priv->sup_iface == NULL) {
-		nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.",
-		            nm_device_get_iface (NM_DEVICE (self)));
+		_LOGE (LOGD_WIFI, "Couldn't initialize supplicant interface");
 		return FALSE;
 	}
 
 	if (nm_supplicant_interface_get_state (priv->sup_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
-		nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant", TRUE);
+		nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant", FALSE);
 
 	g_signal_connect (priv->sup_iface,
 	                  NM_SUPPLICANT_INTERFACE_STATE,
@@ -309,9 +291,8 @@ supplicant_interface_release (NMDeviceWifi *self)
 
 	/* Reset the scan interval to be pretty frequent when disconnected */
 	priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP;
-	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            priv->scan_interval);
+	_LOGD (LOGD_WIFI_SCAN, "reset scanning interval to %d seconds",
+	       priv->scan_interval);
 
 	if (priv->scanlist_cull_id) {
 		g_source_remove (priv->scanlist_cull_id);
@@ -370,9 +351,8 @@ find_active_ap (NMDeviceWifi *self,
                 gboolean match_hidden)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	const char *iface = nm_device_get_iface (NM_DEVICE (self));
 	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
-	struct ether_addr bssid;
+	guint8 bssid[ETH_ALEN];
 	GByteArray *ssid;
 	GSList *iter;
 	int i = 0;
@@ -382,22 +362,18 @@ find_active_ap (NMDeviceWifi *self,
 	NM80211Mode devmode;
 	guint32 devfreq;
 
-	nm_platform_wifi_get_bssid (ifindex, &bssid);
-	nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
-	            iface,
-	            bssid.ether_addr_octet[0], bssid.ether_addr_octet[1],
-	            bssid.ether_addr_octet[2], bssid.ether_addr_octet[3],
-	            bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]);
+	nm_platform_wifi_get_bssid (ifindex, bssid);
+	_LOGD (LOGD_WIFI, "active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
+	       bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
 
-	if (!nm_ethernet_address_is_valid (&bssid))
+	if (!nm_ethernet_address_is_valid (bssid, ETH_ALEN))
 		return NULL;
 
 	ssid = nm_platform_wifi_get_ssid (ifindex);
-	nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s",
-	            iface,
-	            ssid ? "'" : "",
-	            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-	            ssid ? "'" : "");
+	_LOGD (LOGD_WIFI, "active SSID: %s%s%s",
+	       ssid ? "'" : "",
+	       ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+	       ssid ? "'" : "");
 
 	devmode = nm_platform_wifi_get_mode (ifindex);
 	devfreq = nm_platform_wifi_get_frequency (ifindex);
@@ -407,50 +383,54 @@ find_active_ap (NMDeviceWifi *self,
 	 * and therefore it won't get matched the first time around.
 	 */
 	while (i++ < (match_hidden ? 2 : 1)) {
-		nm_log_dbg (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
+		_LOGD (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
 
 		/* Find this SSID + BSSID in the device's AP list */
 		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
 			NMAccessPoint *ap = NM_AP (iter->data);
-			const struct ether_addr	*ap_bssid = nm_ap_get_address (ap);
+			const char *ap_bssid = nm_ap_get_address (ap);
 			const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
 			NM80211Mode apmode;
 			guint32 apfreq;
 
-			nm_log_dbg (LOGD_WIFI, "    AP: %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
-			            ap_ssid ? "'" : "",
-			            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
-			            ap_ssid ? "'" : "",
-			            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
-			            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
-			            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+			_LOGD (LOGD_WIFI, "    AP: %s%s%s  %s",
+			       ap_ssid ? "'" : "",
+			       ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+			       ap_ssid ? "'" : "",
+			       str_if_set (ap_bssid, "(none)"));
 
 			if (ap == ignore_ap) {
-				nm_log_dbg (LOGD_WIFI, "      ignored");
+				_LOGD (LOGD_WIFI, "      ignored");
 				continue;
 			}
 
-			if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) {
-				nm_log_dbg (LOGD_WIFI, "      BSSID mismatch");
+			if (!nm_utils_hwaddr_matches (bssid, ETH_ALEN, ap_bssid, -1)) {
+				_LOGD (LOGD_WIFI, "      BSSID mismatch");
 				continue;
 			}
 
-			if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) {
-				nm_log_dbg (LOGD_WIFI, "      SSID mismatch");
-				continue;
+			if (i == 0) {
+				if (   (ssid && !ap_ssid)
+				    || (ap_ssid && !ssid)
+				    || (ssid && ap_ssid && !nm_utils_same_ssid (ssid->data, ssid->len,
+				                                                ap_ssid->data, ap_ssid->len,
+				                                                TRUE))) {
+					_LOGD (LOGD_WIFI, "      SSID mismatch");
+					continue;
+				}
 			}
 
 			apmode = nm_ap_get_mode (ap);
 			if (devmode != apmode) {
-				nm_log_dbg (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
-				            devmode, apmode);
+				_LOGD (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
+				       devmode, apmode);
 				continue;
 			}
 
 			apfreq = nm_ap_get_freq (ap);
 			if (devfreq != apfreq) {
-				nm_log_dbg (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
-				            devfreq, apfreq);
+				_LOGD (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
+				       devfreq, apfreq);
 
 				if (match_nofreq == NULL)
 					match_nofreq = ap;
@@ -463,7 +443,7 @@ find_active_ap (NMDeviceWifi *self,
 			}
 
 			// FIXME: handle security settings here too
-			nm_log_dbg (LOGD_WIFI, "      matched");
+			_LOGD (LOGD_WIFI, "      matched");
 			active_ap = ap;
 			goto done;
 		}
@@ -482,22 +462,19 @@ find_active_ap (NMDeviceWifi *self,
 	 * we can't match the AP based on frequency at all, just give up.
 	 */
 	if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) {
-		const struct ether_addr	*ap_bssid = nm_ap_get_address (match_nofreq);
 		const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
 
-		nm_log_dbg (LOGD_WIFI, "    matched %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
-		            ap_ssid ? "'" : "",
-		            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
-		            ap_ssid ? "'" : "",
-		            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
-		            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
-		            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+		_LOGD (LOGD_WIFI, "    matched %s%s%s  %s",
+		       ap_ssid ? "'" : "",
+		       ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+		       ap_ssid ? "'" : "",
+		       str_if_set (nm_ap_get_address (match_nofreq), "(none)"));
 
 		active_ap = match_nofreq;
 		goto done;
 	}
 
-	nm_log_dbg (LOGD_WIFI, "  No matching AP found.");
+	_LOGD (LOGD_WIFI, "  No matching AP found.");
 
 done:
 	if (ssid)
@@ -612,15 +589,17 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 	 * current AP with it, if the current AP is adhoc.
 	 */
 	if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) {
-		struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+		guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
 
-		nm_platform_wifi_get_bssid (ifindex, &bssid);
+		nm_platform_wifi_get_bssid (ifindex, bssid);
 		/* 0x02 means "locally administered" and should be OR-ed into
 		 * the first byte of IBSS BSSIDs.
 		 */
-		if (   (bssid.ether_addr_octet[0] & 0x02)
-		    && nm_ethernet_address_is_valid (&bssid))
-			nm_ap_set_address (priv->current_ap, &bssid);
+		if ((bssid[0] & 0x02) && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
+			char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
+			nm_ap_set_address (priv->current_ap, bssid_str);
+			g_free (bssid_str);
+		}
 	}
 
 	new_ap = find_active_ap (self, ignore_ap, FALSE);
@@ -636,32 +615,26 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 	}
 
 	if (new_ap != priv->current_ap) {
-		const struct ether_addr *new_bssid = NULL;
+		const char *new_bssid = NULL;
 		const GByteArray *new_ssid = NULL;
-		const struct ether_addr *old_bssid = NULL;
+		const char *old_bssid = NULL;
 		const GByteArray *old_ssid = NULL;
-		char *old_addr = NULL, *new_addr = NULL;
 
 		if (new_ap) {
 			new_bssid = nm_ap_get_address (new_ap);
-			new_addr = nm_utils_hwaddr_ntoa (new_bssid, ARPHRD_ETHER);
 			new_ssid = nm_ap_get_ssid (new_ap);
 		}
 
 		if (priv->current_ap) {
 			old_bssid = nm_ap_get_address (priv->current_ap);
-			old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER);
 			old_ssid = nm_ap_get_ssid (priv->current_ap);
 		}
 
-		nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)",
-		             nm_device_get_iface (NM_DEVICE (self)),
-		             old_addr ? old_addr : "(none)",
-		             old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
-		             new_addr ? new_addr : "(none)",
-		             new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
-		g_free (old_addr);
-		g_free (new_addr);
+		_LOGI (LOGD_WIFI, "roamed from BSSID %s (%s) to %s (%s)",
+		       old_bssid ? old_bssid : "(none)",
+		       old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
+		       new_bssid ? new_bssid : "(none)",
+		       new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
 
 		set_current_ap (self, new_ap, TRUE, FALSE);
 	}
@@ -734,15 +707,15 @@ remove_all_aps (NMDeviceWifi *self)
 }
 
 static void
-deactivate (NMDevice *dev)
+deactivate (NMDevice *device)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (dev);
+	int ifindex = nm_device_get_ifindex (device);
 	NMConnection *connection;
 	NM80211Mode old_mode = priv->mode;
 
-	connection = nm_device_get_connection (dev);
+	connection = nm_device_get_connection (device);
 	if (connection) {
 		/* Clear wireless secrets tries when deactivating */
 		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
@@ -768,7 +741,8 @@ deactivate (NMDevice *dev)
 	nm_platform_wifi_indicate_addressing_running (ifindex, FALSE);
 
 	/* Reset MAC address back to initial address */
-	nm_device_set_hw_addr (dev, priv->initial_hw_addr, "reset", LOGD_WIFI);
+	if (priv->initial_hw_addr)
+		nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_WIFI);
 
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
@@ -828,8 +802,9 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
 	NMSettingWireless *s_wireless;
-	const GByteArray *mac;
-	const GSList *mac_blacklist, *mac_blacklist_iter;
+	const char *mac;
+	const char * const *mac_blacklist;
+	int i;
 	const char *mode;
 
 	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection))
@@ -846,21 +821,18 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	mac = nm_setting_wireless_get_mac_address (s_wireless);
-	if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
+	if (mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1))
 		return FALSE;
 
 	/* Check for MAC address blacklist */
 	mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
-	for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
-	     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
-		struct ether_addr addr;
-
-		if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+	for (i = 0; mac_blacklist[i]; i++) {
+		if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
 			g_warn_if_reached ();
-			continue;
+			return FALSE;
 		}
 
-		if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0)
+		if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1))
 			return FALSE;
 	}
 
@@ -998,10 +970,12 @@ complete_connection (NMDevice *device,
 	NMSettingWireless *s_wifi;
 	NMSettingWirelessSecurity *s_wsec;
 	NMSetting8021x *s_8021x;
-	const GByteArray *setting_mac;
-	char *format, *str_ssid = NULL;
+	const char *setting_mac;
+	char *str_ssid = NULL;
 	NMAccessPoint *ap = NULL;
 	const GByteArray *ssid = NULL;
+	GByteArray *tmp_ssid = NULL;
+	GBytes *setting_ssid = NULL;
 	GSList *iter;
 	gboolean hidden = FALSE;
 
@@ -1013,17 +987,17 @@ complete_connection (NMDevice *device,
 		/* If not given a specific object, we need at minimum an SSID */
 		if (!s_wifi) {
 			g_set_error_literal (error,
-			                     NM_WIFI_ERROR,
-			                     NM_WIFI_ERROR_CONNECTION_INVALID,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 			                     "A 'wireless' setting is required if no AP path was given.");
 			return FALSE;
 		}
 
-		ssid = nm_setting_wireless_get_ssid (s_wifi);
-		if (!ssid || !ssid->len) {
+		setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
+		if (!setting_ssid || g_bytes_get_size (setting_ssid) == 0) {
 			g_set_error_literal (error,
-			                     NM_WIFI_ERROR,
-			                     NM_WIFI_ERROR_CONNECTION_INVALID,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 			                     "A 'wireless' setting with a valid SSID is required if no AP path was given.");
 			return FALSE;
 		}
@@ -1041,17 +1015,7 @@ complete_connection (NMDevice *device,
 		 * if the network isn't broadcasting the SSID for example.
 		 */
 		if (!ap) {
-			GSList *settings = NULL;
-			gboolean valid;
-
-			settings = g_slist_prepend (settings, s_wifi);
-			if (s_wsec)
-				settings = g_slist_prepend (settings, s_wsec);
-			if (s_8021x)
-				settings = g_slist_prepend (settings, s_8021x);
-			valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error);
-			g_slist_free (settings);
-			if (!valid)
+			if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error))
 				return FALSE;
 
 			hidden = TRUE;
@@ -1060,8 +1024,8 @@ complete_connection (NMDevice *device,
 		ap = get_ap_by_path (self, specific_object);
 		if (!ap) {
 			g_set_error (error,
-			             NM_WIFI_ERROR,
-			             NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
 			             "The access point %s was not in the scan list.",
 			             specific_object);
 			return FALSE;
@@ -1074,37 +1038,48 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
 	}
 
-	if (ap) {
+	if (ap)
 		ssid = nm_ap_get_ssid (ap);
-
-		if (ssid == NULL) {
-			/* The AP must be hidden.  Connecting to a WiFi AP requires the SSID
-			 * as part of the initial handshake, so check the connection details
-			 * for the SSID.  The AP object will still be used for encryption
-			 * settings and such.
-			 */
-			ssid = nm_setting_wireless_get_ssid (s_wifi);
+	if (ssid == NULL) {
+		/* The AP must be hidden.  Connecting to a WiFi AP requires the SSID
+		 * as part of the initial handshake, so check the connection details
+		 * for the SSID.  The AP object will still be used for encryption
+		 * settings and such.
+		 */
+		setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
+		if (setting_ssid) {
+			ssid = tmp_ssid = g_byte_array_new ();
+			g_byte_array_append (tmp_ssid,
+			                     g_bytes_get_data (setting_ssid, NULL),
+			                     g_bytes_get_size (setting_ssid));
 		}
+	}
 
-		if (ssid == NULL) {
-			/* If there's no SSID on the AP itself, and no SSID in the
-			 * connection data, then we cannot connect at all.  Return an error.
-			 */
-			g_set_error_literal (error,
-			                     NM_WIFI_ERROR,
-			                     NM_WIFI_ERROR_CONNECTION_INVALID,
-			                     "A 'wireless' setting with a valid SSID is required for hidden access points.");
-			return FALSE;
-		}
+	if (ssid == NULL) {
+		/* If there's no SSID on the AP itself, and no SSID in the
+		 * connection data, then we cannot connect at all.  Return an error.
+		 */
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     ap
+		                         ? "A 'wireless' setting with a valid SSID is required for hidden access points."
+		                         : "Cannot create 'wireless' setting due to missing SSID.");
+		return FALSE;
+	}
 
+	if (ap) {
 		/* If the SSID is a well-known SSID, lock the connection to the AP's
 		 * specific BSSID so NM doesn't autoconnect to some random wifi net.
 		 */
 		if (!nm_ap_complete_connection (ap,
 		                                connection,
 		                                is_manf_default_ssid (ssid),
-		                                error))
+		                                error)) {
+			if (tmp_ssid)
+				g_byte_array_unref (tmp_ssid);
 			return FALSE;
+		}
 	}
 
 	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
@@ -1113,24 +1088,27 @@ complete_connection (NMDevice *device,
 	 */
 	if (is_adhoc_wpa (connection)) {
 		g_set_error_literal (error,
-		                     NM_SETTING_WIRELESS_ERROR,
-		                     NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
-		                     "WPA Ad-Hoc disabled due to kernel bugs");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_SETTING,
+		                     _("WPA Ad-Hoc disabled due to kernel bugs"));
+		g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+		if (tmp_ssid)
+			g_byte_array_unref (tmp_ssid);
 		return FALSE;
 	}
 
-	g_assert (ssid);
-	str_ssid = nm_utils_ssid_to_utf8 (ssid);
-	format = g_strdup_printf ("%s %%d", str_ssid);
+	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
 
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_WIRELESS_SETTING_NAME,
 	                           existing_connections,
-	                           format,
 	                           str_ssid,
+	                           str_ssid,
+	                           NULL,
 	                           TRUE);
 	g_free (str_ssid);
-	g_free (format);
+	if (tmp_ssid)
+		g_byte_array_unref (tmp_ssid);
 
 	if (hidden)
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
@@ -1138,26 +1116,26 @@ complete_connection (NMDevice *device,
 	setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
 	if (setting_mac) {
 		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) {
-			g_set_error (error,
-			             NM_SETTING_WIRELESS_ERROR,
-			             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
-			             NM_SETTING_WIRELESS_MAC_ADDRESS);
+		if (!nm_utils_hwaddr_matches (setting_mac, -1, priv->perm_hw_addr, -1)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("connection does not match device"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
 			return FALSE;
 		}
 	} else {
-		GByteArray *mac;
-		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+		guint8 perm_hw_addr[ETH_ALEN];
 
 		/* Lock the connection to this device by default if it uses a
 		 * permanent MAC address (ie not a 'locally administered' one)
 		 */
-		if (   !(priv->perm_hw_addr[0] & 0x02)
-		    && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) {
-			mac = g_byte_array_sized_new (ETH_ALEN);
-			g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN);
-			g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
-			g_byte_array_free (mac, TRUE);
+		nm_utils_hwaddr_aton (priv->perm_hw_addr, perm_hw_addr, ETH_ALEN);
+		if (   !(perm_hw_addr[0] & 0x02)
+		    && !nm_utils_hwaddr_matches (perm_hw_addr, ETH_ALEN, NULL, ETH_ALEN)) {
+			g_object_set (G_OBJECT (s_wifi),
+			              NM_SETTING_WIRELESS_MAC_ADDRESS, priv->perm_hw_addr,
+			              NULL);
 		}
 	}
 
@@ -1165,31 +1143,26 @@ complete_connection (NMDevice *device,
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *device)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMSupplicantInterface *sup_iface;
 	guint32 state;
 
 	if (!priv->enabled) {
-		nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled",
-		            nm_device_get_iface (dev));
+		_LOGD (LOGD_WIFI, "not available because not enabled");
 		return FALSE;
 	}
 
-	sup_iface = priv->sup_iface;
-	if (!sup_iface) {
-		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running",
-		            nm_device_get_iface (dev));
+	if (!priv->sup_iface) {
+		_LOGD (LOGD_WIFI, "not available because supplicant not running");
 		return FALSE;
 	}
 
-	state = nm_supplicant_interface_get_state (sup_iface);
+	state = nm_supplicant_interface_get_state (priv->sup_iface);
 	if (   state < NM_SUPPLICANT_INTERFACE_STATE_READY
 	    || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) {
-		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready",
-		            nm_device_get_iface (dev));
+		_LOGD (LOGD_WIFI, "not available because supplicant interface not ready");
 		return FALSE;
 	}
 
@@ -1197,17 +1170,17 @@ is_available (NMDevice *dev)
 }
 
 static gboolean
-can_auto_connect (NMDevice *dev,
+can_auto_connect (NMDevice *device,
                   NMConnection *connection,
                   char **specific_object)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	GSList *ap_iter;
 	const char *method = NULL;
 	guint64 timestamp = 0;
 
-	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (dev, connection, specific_object))
+	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (device, connection, specific_object))
 		return FALSE;
 
 	/* Don't autoconnect to networks that have been tried at least once
@@ -1246,12 +1219,12 @@ ap_list_dump (NMDeviceWifi *self)
 
 	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
 
-	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:");
+	_LOGD (LOGD_WIFI_SCAN, "Current AP list:");
 	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) {
 		NMAccessPoint * ap = NM_AP (elt->data);
 		nm_ap_dump (ap, "List AP: ");
 	}
-	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done");
+	_LOGD (LOGD_WIFI_SCAN, "Current AP list: done");
 }
 
 static gboolean
@@ -1301,8 +1274,8 @@ request_scan_cb (NMDevice *device,
 	}
 
 	if (!check_scanning_allowed (self)) {
-		local = g_error_new_literal (NM_WIFI_ERROR,
-		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		local = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ALLOWED,
 		                             "Scanning not allowed at this time");
 		dbus_g_method_return_error (context, local);
 		g_error_free (local);
@@ -1328,23 +1301,23 @@ impl_device_request_scan (NMDeviceWifi *self,
 	    || !priv->sup_iface
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
-		error = g_error_new_literal (NM_WIFI_ERROR,
-		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ALLOWED,
 		                             "Scanning not allowed while unavailable or activating");
 		goto error;
 	}
 
 	if (nm_supplicant_interface_get_scanning (priv->sup_iface)) {
-		error = g_error_new_literal (NM_WIFI_ERROR,
-		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ALLOWED,
 		                             "Scanning not allowed while already scanning");
 		goto error;
 	}
 
 	last_scan = nm_supplicant_interface_get_last_scan_time (priv->sup_iface);
 	if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) {
-		error = g_error_new_literal (NM_WIFI_ERROR,
-		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ALLOWED,
 		                             "Scanning not allowed immediately following previous scan");
 		goto error;
 	}
@@ -1413,7 +1386,6 @@ scanning_allowed (NMDeviceWifi *self)
 	if (connection) {
 		NMSettingWireless *s_wifi;
 		const char *ip4_method = NULL;
-		const GByteArray *bssid;
 
 		/* Don't scan when a shared connection is active; it makes drivers mad */
 		ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
@@ -1427,8 +1399,7 @@ scanning_allowed (NMDeviceWifi *self)
 		 */
 		s_wifi = nm_connection_get_setting_wireless (connection);
 		g_assert (s_wifi);
-		bssid = nm_setting_wireless_get_bssid (s_wifi);
-		if (bssid && bssid->len == ETH_ALEN)
+		if (nm_setting_wireless_get_bssid (s_wifi))
 			return FALSE;
 	}
 
@@ -1500,20 +1471,25 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	                                                           hidden_filter_func,
 	                                                           NULL);
 	if (connections && connections->data) {
-		ssids = g_ptr_array_sized_new (max_scan_ssids - 1);
-		g_ptr_array_add (ssids, nullssid);  /* Add wildcard SSID */
+		ssids = g_ptr_array_new_full (max_scan_ssids - 1, (GDestroyNotify) g_byte_array_unref);
+		g_ptr_array_add (ssids, g_byte_array_ref (nullssid));  /* Add wildcard SSID */
 	}
 
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *connection = iter->data;
 		NMSettingWireless *s_wifi;
-		const GByteArray *ssid;
+		GBytes *ssid;
+		GByteArray *ssid_array;
 
 		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection);
 		g_assert (s_wifi);
 		ssid = nm_setting_wireless_get_ssid (s_wifi);
 		g_assert (ssid);
-		g_ptr_array_add (ssids, (gpointer) ssid);
+		ssid_array = g_byte_array_new ();
+		g_byte_array_append (ssid_array,
+		                     g_bytes_get_data (ssid, NULL),
+		                     g_bytes_get_size (ssid));
+		g_ptr_array_add (ssids, ssid_array);
 	}
 	g_slist_free (connections);
 
@@ -1534,27 +1510,25 @@ request_wireless_scan (gpointer user_data)
 	}
 
 	if (check_scanning_allowed (self)) {
-		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested",
-		            nm_device_get_iface (NM_DEVICE (self)));
+		_LOGD (LOGD_WIFI_SCAN, "scanning requested");
 
 		ssids = build_hidden_probe_list (self);
 
 		if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) {
 			if (ssids) {
+				const GByteArray *ssid;
 				guint i;
 				char *foo;
 
 				for (i = 0; i < ssids->len; i++) {
-					foo = nm_utils_ssid_to_utf8 (g_ptr_array_index (ssids, i));
-					nm_log_dbg (LOGD_WIFI_SCAN, "(%s): (%d) probe scanning SSID '%s'",
-					            nm_device_get_iface (NM_DEVICE (self)),
+					ssid = g_ptr_array_index (ssids, i);
+					foo = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
+					_LOGD (LOGD_WIFI_SCAN, "(%d) probe scanning SSID '%s'",
 					            i, foo ? foo : "<hidden>");
 					g_free (foo);
 				}
-			} else {
-				nm_log_dbg (LOGD_WIFI_SCAN, "(%s): no SSIDs to probe scan",
-				            nm_device_get_iface (NM_DEVICE (self)));
-			}
+			} else
+				_LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan");
 		}
 
 		if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
@@ -1564,14 +1538,10 @@ request_wireless_scan (gpointer user_data)
 			nm_device_add_pending_action (NM_DEVICE (self), "scan", TRUE);
 		}
 
-		if (ssids) {
-			/* Elements owned by the connections, so we don't free them here */
-			g_ptr_array_free (ssids, TRUE);
-		}
-	} else {
-		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time",
-		            nm_device_get_iface (NM_DEVICE (self)));
-	}
+		if (ssids)
+			g_ptr_array_unref (ssids);
+	} else
+		_LOGD (LOGD_WIFI_SCAN, "scan requested but not allowed at this time");
 
 	priv->pending_scan_id = 0;
 	schedule_scan (self, backoff);
@@ -1622,11 +1592,8 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff)
 			priv->scan_interval = 5;
 		}
 
-		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)",
-		            nm_device_get_iface (NM_DEVICE (self)),
-		            next_scan,
-		            priv->scan_interval);
-
+		_LOGD (LOGD_WIFI_SCAN, "scheduled scan in %d seconds (interval now %d seconds)",
+		       next_scan, priv->scan_interval);
 	}
 }
 
@@ -1649,9 +1616,7 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            success ? "successful" : "failed");
+	_LOGD (LOGD_WIFI_SCAN, "scan %s", success ? "successful" : "failed");
 
 	schedule_scan (self, success);
 
@@ -1674,7 +1639,7 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
 static void
 try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
 {
-	const struct ether_addr *bssid;
+	const char *bssid;
 	const GSList *connections, *iter;
 
 	g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
@@ -1692,16 +1657,17 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
 		s_wifi = nm_connection_get_setting_wireless (connection);
 		if (s_wifi) {
 			if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) {
-				nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
+				GBytes *ssid = nm_setting_wireless_get_ssid (s_wifi);
+
+				nm_ap_set_ssid (ap,
+				                g_bytes_get_data (ssid, NULL),
+				                g_bytes_get_size (ssid));
 				break;
 			}
 		}
 	}
 }
 
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
-
 /*
  * merge_scanned_ap
  *
@@ -1721,7 +1687,7 @@ merge_scanned_ap (NMDeviceWifi *self,
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMAccessPoint *found_ap = NULL;
 	const GByteArray *ssid;
-	const struct ether_addr *bssid;
+	const char *bssid;
 	gboolean strict_match = TRUE;
 
 	/* Let the manager try to fill in the SSID from seen-bssids lists */
@@ -1734,16 +1700,13 @@ merge_scanned_ap (NMDeviceWifi *self,
 		ssid = nm_ap_get_ssid (merge_ap);
 		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
 			/* Yay, matched it, no longer treat as hidden */
-			nm_log_dbg (LOGD_WIFI_SCAN, "(%s): matched hidden AP " MAC_FMT " => '%s'",
-			            nm_device_get_iface (NM_DEVICE (self)),
-			            MAC_ARG (bssid->ether_addr_octet),
-			            nm_utils_escape_ssid (ssid->data, ssid->len));
+			_LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'",
+			       str_if_set (bssid, "(none)"), nm_utils_escape_ssid (ssid->data, ssid->len));
 			nm_ap_set_broadcast (merge_ap, FALSE);
 		} else {
 			/* Didn't have an entry for this AP in the database */
-			nm_log_dbg (LOGD_WIFI_SCAN, "(%s): failed to match hidden AP " MAC_FMT,
-			            nm_device_get_iface (NM_DEVICE (self)),
-			            MAC_ARG (bssid->ether_addr_octet));
+			_LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s",
+			       str_if_set (bssid, "(none)"));
 		}
 	}
 
@@ -1760,10 +1723,9 @@ merge_scanned_ap (NMDeviceWifi *self,
 	if (!found_ap)
 		found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
 	if (found_ap) {
-		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): merging AP '%s' " MAC_FMT " (%p) with existing (%p)",
-		            nm_device_get_iface (NM_DEVICE (self)),
+		_LOGD (LOGD_WIFI_SCAN, "merging AP '%s' %s (%p) with existing (%p)",
 		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-		            MAC_ARG (bssid->ether_addr_octet),
+		            str_if_set (bssid, "(none)"),
 		            merge_ap,
 		            found_ap);
 
@@ -1783,11 +1745,9 @@ merge_scanned_ap (NMDeviceWifi *self,
 		nm_ap_set_fake (found_ap, FALSE);
 	} else {
 		/* New entry in the list */
-		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): adding new AP '%s' " MAC_FMT " (%p)",
-		            nm_device_get_iface (NM_DEVICE (self)),
-		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-		            MAC_ARG (bssid->ether_addr_octet),
-		            merge_ap);
+		_LOGD (LOGD_WIFI_SCAN, "adding new AP '%s' %s (%p)",
+		       ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		       str_if_set (bssid, "(none)"), merge_ap);
 
 		g_object_ref (merge_ap);
 		priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
@@ -1809,8 +1769,7 @@ cull_scan_list (NMDeviceWifi *self)
 
 	priv->scanlist_cull_id = 0;
 
-	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs",
-	            nm_device_get_iface (NM_DEVICE (self)));
+	_LOGD (LOGD_WIFI_SCAN, "checking scan list for outdated APs");
 
 	/* Walk the access point list and remove any access points older than
 	 * three times the inactive scan interval.
@@ -1844,28 +1803,23 @@ cull_scan_list (NMDeviceWifi *self)
 	/* Remove outdated APs */
 	for (elt = outdated_list; elt; elt = g_slist_next (elt)) {
 		NMAccessPoint *outdated_ap = NM_AP (elt->data);
-		const struct ether_addr *bssid;
 		const GByteArray *ssid;
 
-		bssid = nm_ap_get_address (outdated_ap);
 		ssid = nm_ap_get_ssid (outdated_ap);
-		nm_log_dbg (LOGD_WIFI_SCAN,
-		            "   removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)",
-		            bssid->ether_addr_octet[0], bssid->ether_addr_octet[1],
-		            bssid->ether_addr_octet[2], bssid->ether_addr_octet[3],
-		            bssid->ether_addr_octet[4], bssid->ether_addr_octet[5],
-		            ssid ? "'" : "",
-		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-		            ssid ? "'" : "");
+		_LOGD (LOGD_WIFI_SCAN,
+		       "   removing %s (%s%s%s)",
+		       str_if_set (nm_ap_get_address (outdated_ap), "(none)"),
+		       ssid ? "'" : "",
+		       ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		       ssid ? "'" : "");
 
 		remove_access_point (self, outdated_ap);
 		removed++;
 	}
 	g_slist_free (outdated_list);
 
-	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            removed, total);
+	_LOGD (LOGD_WIFI_SCAN, "removed %d APs (of %d)",
+	       removed, total);
 
 	ap_list_dump (self);
 
@@ -1913,10 +1867,8 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
 		/* Add the AP to the device's AP list */
 		merge_scanned_ap (self, ap);
 		g_object_unref (ap);
-	} else {
-		nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received",
-		             nm_device_get_iface (NM_DEVICE (self)));
-	}
+	} else
+		_LOGW (LOGD_WIFI_SCAN, "invalid AP properties received");
 
 	/* Remove outdated access points */
 	schedule_scanlist_cull (self);
@@ -1998,19 +1950,20 @@ wifi_secrets_cb (NMActRequest *req,
                  GError *error,
                  gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 
-	g_return_if_fail (req == nm_device_get_act_request (dev));
-	g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH);
+	g_return_if_fail (req == nm_device_get_act_request (device));
+	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
 	g_return_if_fail (nm_act_request_get_connection (req) == connection);
 
 	if (error) {
-		nm_log_warn (LOGD_WIFI, "%s", error->message);
-		nm_device_state_changed (dev,
+		_LOGW (LOGD_WIFI, "%s", error->message);
+		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
 	} else
-		nm_device_activate_schedule_stage1_device_prepare (dev);
+		nm_device_activate_schedule_stage1_device_prepare (device);
 }
 
 /*
@@ -2022,11 +1975,11 @@ wifi_secrets_cb (NMActRequest *req,
 static gboolean
 link_timeout_cb (gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev));
+	_LOGW (LOGD_WIFI, "link timed out.");
 
 	priv->link_timeout_id = 0;
 
@@ -2034,7 +1987,7 @@ link_timeout_cb (gpointer user_data)
 	 * to reassociate within the timeout period, so the connection must
 	 * fail.
 	 */
-	if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+	if (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
 		return FALSE;
 
 	/* If the access point failed, and wasn't found by the supplicant when it
@@ -2045,7 +1998,7 @@ link_timeout_cb (gpointer user_data)
 	if (priv->ssid_found == FALSE && priv->current_ap)
 		set_current_ap (self, NULL, TRUE, TRUE);
 
-	nm_device_state_changed (dev,
+	nm_device_state_changed (device,
 	                         NM_DEVICE_STATE_FAILED,
 	                         priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT :
 	                                            NM_DEVICE_STATE_REASON_SSID_NOT_FOUND);
@@ -2080,10 +2033,11 @@ need_new_8021x_secrets (NMDeviceWifi *self,
 
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	if (s_8021x) {
-		nm_setting_get_secret_flags (NM_SETTING (s_8021x),
-		                             NM_SETTING_802_1X_PASSWORD,
-		                             &secret_flags,
-		                             NULL);
+		if (!nm_setting_get_secret_flags (NM_SETTING (s_8021x),
+		                                  NM_SETTING_802_1X_PASSWORD,
+		                                  &secret_flags,
+		                                  NULL))
+			g_assert_not_reached ();
 		if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
 			*setting_name = NM_SETTING_802_1X_SETTING_NAME;
 		return *setting_name ? TRUE : FALSE;
@@ -2091,10 +2045,11 @@ need_new_8021x_secrets (NMDeviceWifi *self,
 
 	s_wsec = nm_connection_get_setting_wireless_security (connection);
 	if (s_wsec) {
-		nm_setting_get_secret_flags (NM_SETTING (s_wsec),
-		                             NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
-		                             &secret_flags,
-		                             NULL);
+		if (!nm_setting_get_secret_flags (NM_SETTING (s_wsec),
+		                                  NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+		                                  &secret_flags,
+		                                  NULL))
+			g_assert_not_reached ();
 		if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
 			*setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
 		return *setting_name ? TRUE : FALSE;
@@ -2160,16 +2115,15 @@ handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
 
 		nm_connection_clear_secrets (connection);
 
-		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): disconnected during association,"
-		             " asking for new key.", nm_device_get_iface (device));
+		_LOGI (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) disconnected during association, asking for new key");
 
 		cleanup_association_attempt (self, TRUE);
 		nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 		nm_act_request_get_secrets (req,
 		                            setting_name,
-		                            NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION
-		                              | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW,
+		                            NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+		                              | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW,
 		                            NULL,
 		                            wifi_secrets_cb,
 		                            self);
@@ -2195,11 +2149,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	if (new_state == old_state)
 		return;
 
-	nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-	             "(%s): supplicant interface state: %s -> %s",
-	             nm_device_get_iface (device),
-	             nm_supplicant_interface_state_to_string (old_state),
-	             nm_supplicant_interface_state_to_string (new_state));
+	_LOGI (LOGD_DEVICE | LOGD_WIFI,
+	       "supplicant interface state: %s -> %s",
+	       nm_supplicant_interface_state_to_string (old_state),
+	       nm_supplicant_interface_state_to_string (new_state));
 
 	devstate = nm_device_get_state (device);
 	scanning = nm_supplicant_interface_get_scanning (iface);
@@ -2222,9 +2175,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
 		}
 
-		nm_log_dbg (LOGD_WIFI_SCAN,
-		            "(%s): supplicant ready, requesting initial scan",
-		            nm_device_get_iface (device));
+		_LOGD (LOGD_WIFI_SCAN, "supplicant ready, requesting initial scan");
 
 		/* Request a scan to get latest results */
 		cancel_pending_scan (self);
@@ -2243,7 +2194,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		if (devstate == NM_DEVICE_STATE_CONFIG) {
 			NMConnection *connection;
 			NMSettingWireless *s_wifi;
-			const GByteArray *ssid;
+			GBytes *ssid;
 
 			connection = nm_device_get_connection (NM_DEVICE (self));
 			g_return_if_fail (connection);
@@ -2254,13 +2205,12 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			ssid = nm_setting_wireless_get_ssid (s_wifi);
 			g_return_if_fail (ssid);
 
-			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-			             "Activation (%s/wireless) Stage 2 of 5 (Device Configure) "
-			             "successful.  %s '%s'.",
-			             nm_device_get_iface (device),
-			             priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" :
-			                 "Connected to wireless network",
-			             ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)");
+			_LOGI (LOGD_DEVICE | LOGD_WIFI,
+			       "Activation: (wifi) Stage 2 of 5 (Device Configure) successful.  %s '%s'.",
+			       priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" :
+			       "Connected to wireless network",
+			       ssid ? nm_utils_escape_ssid (g_bytes_get_data (ssid, NULL),
+			                                    g_bytes_get_size (ssid)) : "(none)");
 			nm_device_activate_schedule_stage3_ip_config_start (device);
 		} else if (devstate == NM_DEVICE_STATE_ACTIVATED)
 			periodic_update (self, NULL);
@@ -2326,9 +2276,9 @@ supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
 	NMDevice *device = NM_DEVICE (self);
 
 	if (nm_device_is_activating (device)) {
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): supplicant association failed: %s - %s",
-		             nm_device_get_iface (device), name, message);
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) supplicant association failed: %s - %s",
+		       name, message);
 
 		cleanup_association_attempt (self, TRUE);
 		nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
@@ -2356,9 +2306,7 @@ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
 	gboolean scanning;
 
 	scanning = nm_supplicant_interface_get_scanning (iface);
-	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s",
-	            nm_device_get_iface (NM_DEVICE (self)),
-	            scanning ? "scanning" : "idle");
+	_LOGD (LOGD_WIFI_SCAN, "now %s", scanning ? "scanning" : "idle");
 
 	g_object_notify (G_OBJECT (self), "scanning");
 
@@ -2397,16 +2345,16 @@ handle_auth_or_fail (NMDeviceWifi *self,
 	nm_connection_clear_secrets (connection);
 	setting_name = nm_connection_need_secrets (connection, NULL);
 	if (setting_name) {
-		NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+		NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
 		if (new_secrets)
-			flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+			flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 		nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self);
 
 		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else
-		nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+		_LOGW (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
 
 	return ret;
 }
@@ -2420,7 +2368,7 @@ handle_auth_or_fail (NMDeviceWifi *self,
 static gboolean
 supplicant_connection_timeout_cb (gpointer user_data)
 {
-	NMDevice *dev = NM_DEVICE (user_data);
+	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMActRequest *req;
@@ -2428,7 +2376,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 
 	cleanup_association_attempt (self, TRUE);
 
-	if (!nm_device_is_activating (dev))
+	if (!nm_device_is_activating (device))
 		return FALSE;
 
 	/* Timed out waiting for a successful connection to the AP; if the AP's
@@ -2437,7 +2385,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	 * information (passwords, pin codes, etc) are wrong.
 	 */
 
-	req = nm_device_get_act_request (dev);
+	req = nm_device_get_act_request (device);
 	g_assert (req);
 
 	connection = nm_act_request_get_connection (req);
@@ -2449,12 +2397,10 @@ supplicant_connection_timeout_cb (gpointer user_data)
 		 * (if any), so supplicant timeouts here are almost certainly the wifi
 		 * driver being really stupid.
 		 */
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): %s network creation took "
-		             "too long, failing activation.",
-		             nm_device_get_iface (dev),
-		             priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot");
-		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) %s network creation took too long, failing activation",
+		       priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot");
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
 		return FALSE;
 	}
@@ -2468,9 +2414,8 @@ supplicant_connection_timeout_cb (gpointer user_data)
 		/* Connection failed; either driver problems, the encryption key is
 		 * wrong, or the passwords or certificates were wrong.
 		 */
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): association took too long.",
-		             nm_device_get_iface (dev));
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) association took too long");
 
 		/* Ask for new secrets only if we've never activated this connection
 		 * before.  If we've connected before, don't bother the user with
@@ -2480,20 +2425,16 @@ supplicant_connection_timeout_cb (gpointer user_data)
 		if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp))
 			new_secrets = !timestamp;
 
-		if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) {
-			nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-			             "Activation (%s/wireless): asking for new secrets",
-			             nm_device_get_iface (dev));
-		} else {
-			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
+			_LOGW (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) asking for new secrets");
+		else {
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
 		}
 	} else {
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): association took too long, "
-		             "failing activation.",
-		             nm_device_get_iface (dev));
-		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) association took too long, failing activation");
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
 		                         priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT :
 		                                            NM_DEVICE_STATE_REASON_SSID_NOT_FOUND);
 	}
@@ -2523,13 +2464,13 @@ build_supplicant_config (NMDeviceWifi *self,
 	/* Warn if AP mode may not be supported */
 	if (   g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0
 	    && nm_supplicant_interface_get_ap_support (priv->sup_iface) == AP_SUPPORT_UNKNOWN) {
-		nm_log_warn (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
+		_LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
 	}
 
 	if (!nm_supplicant_config_add_setting_wireless (config,
 	                                                s_wireless,
 	                                                fixed_freq)) {
-		nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
+		_LOGE (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
 		goto error;
 	}
 
@@ -2544,13 +2485,12 @@ build_supplicant_config (NMDeviceWifi *self,
 		                                                         s_wireless_sec,
 		                                                         s_8021x,
 		                                                         con_uuid)) {
-			nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to "
-			            "supplicant config.");
+			_LOGE (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to supplicant config.");
 			goto error;
 		}
 	} else {
 		if (!nm_supplicant_config_add_no_security (config)) {
-			nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config.");
+			_LOGE (LOGD_WIFI, "Couldn't add unsecured option to supplicant config.");
 			goto error;
 		}
 	}
@@ -2565,23 +2505,25 @@ error:
 /****************************************************************************/
 
 static void
-update_permanent_hw_address (NMDevice *dev)
+update_permanent_hw_address (NMDevice *device)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	struct ifreq req;
 	struct ethtool_perm_addr *epaddr = NULL;
-	int fd, ret;
+	int fd, ret, errsv;
+
+	g_return_if_fail (priv->perm_hw_addr == NULL);
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
-		nm_log_err (LOGD_HW, "could not open control socket.");
+		_LOGE (LOGD_HW, "could not open control socket.");
 		return;
 	}
 
 	/* Get permanent MAC address */
 	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+	strncpy (req.ifr_name, nm_device_get_iface (device), IFNAMSIZ);
 
 	epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
 	epaddr->cmd = ETHTOOL_GPERMADDR;
@@ -2590,56 +2532,50 @@ update_permanent_hw_address (NMDevice *dev)
 
 	errno = 0;
 	ret = ioctl (fd, SIOCETHTOOL, &req);
-	if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
-		nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
-		            nm_device_get_iface (dev), errno);
+	errsv = errno;
+	if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) {
+		_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)",
+		       errsv);
 		/* Fall back to current address */
-		memcpy (epaddr->data, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
+		nm_utils_hwaddr_aton (nm_device_get_hw_address (device), epaddr->data, ETH_ALEN);
 	}
 
-	if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
-		memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
-		g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
-	}
+	priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN);
 
 	g_free (epaddr);
 	close (fd);
 }
 
 static void
-update_initial_hw_address (NMDevice *dev)
+update_initial_hw_address (NMDevice *device)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	char *mac_str;
 
 	/* This sets initial MAC address from current MAC address. It should only
 	 * be called from NMDevice constructor() to really get the initial address.
 	 */
-	memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
+	priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (device));
 
-	mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER);
-	nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
-	            nm_device_get_iface (dev), mac_str);
-	g_free (mac_str);
+	_LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr);
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMActStageReturn ret;
 	NMAccessPoint *ap = NULL;
 	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
-	const GByteArray *cloned_mac;
+	const char *cloned_mac;
 	GSList *iter;
 	const char *mode;
 	const char *ap_path;
 
-	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (device, reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
@@ -2670,15 +2606,15 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
 	 */
 	if (is_adhoc_wpa (connection)) {
-		nm_log_warn (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs");
+		_LOGW (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs");
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	/* Set spoof MAC to the interface */
 	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
-	if (cloned_mac && (cloned_mac->len == ETH_ALEN))
-		nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_WIFI);
+	if (cloned_mac)
+		nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_WIFI);
 
 	/* AP mode never uses a specific object or existing scanned AP */
 	if (priv->mode != NM_802_11_MODE_AP) {
@@ -2716,7 +2652,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA)
 		nm_ap_set_broadcast (ap, FALSE);
 	else if (nm_ap_is_hotspot (ap))
-		nm_ap_set_address (ap, (const struct ether_addr *) nm_device_get_hw_address (dev, NULL));
+		nm_ap_set_address (ap, nm_device_get_hw_address (device));
 
 	priv->ap_list = g_slist_prepend (priv->ap_list, ap);
 	nm_ap_export_to_dbus (ap);
@@ -2759,12 +2695,11 @@ ensure_hotspot_frequency (NMDeviceWifi *self,
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	const char *iface = nm_device_get_iface (dev);
 	NMSupplicantConfig *config = NULL;
 	NMActRequest *req;
 	NMAccessPoint *ap;
@@ -2776,7 +2711,7 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 
 	remove_supplicant_timeouts (self);
 
-	req = nm_device_get_act_request (dev);
+	req = nm_device_get_act_request (device);
 	g_assert (req);
 
 	ap = priv->current_ap;
@@ -2794,10 +2729,9 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 	/* If we need secrets, get them */
 	setting_name = nm_connection_need_secrets (connection, NULL);
 	if (setting_name) {
-		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): access point '%s' has security,"
-		             " but secrets are required.",
-		             iface, nm_connection_get_id (connection));
+		_LOGI (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) access point '%s' has security, but secrets are required.",
+		       nm_connection_get_id (connection));
 
 		ret = handle_auth_or_fail (self, req, FALSE);
 		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
@@ -2807,15 +2741,13 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 
 	/* have secrets, or no secrets required */
 	if (nm_connection_get_setting_wireless_security (connection)) {
-		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): connection '%s' has security"
-		             ", and secrets exist.  No new secrets needed.",
-		             iface, nm_connection_get_id (connection));
+		_LOGI (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) connection '%s' has security, and secrets exist.  No new secrets needed.",
+		       nm_connection_get_id (connection));
 	} else {
-		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): connection '%s' requires no "
-		             "security.  No secrets needed.",
-		             iface, nm_connection_get_id (connection));
+		_LOGI (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) connection '%s' requires no security.  No secrets needed.",
+		       nm_connection_get_id (connection));
 	}
 
 	priv->ssid_found = FALSE;
@@ -2830,9 +2762,8 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 	/* Build up the supplicant configuration */
 	config = build_supplicant_config (self, connection, nm_ap_get_freq (ap));
 	if (config == NULL) {
-		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): couldn't build wireless configuration.",
-		             iface);
+		_LOGE (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) couldn't build wireless configuration.");
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
 	}
@@ -2844,9 +2775,8 @@ act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
 	                  self);
 
 	if (!nm_supplicant_interface_set_config (priv->sup_iface, config)) {
-		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
-		            "Activation (%s/wireless): couldn't send wireless "
-		            "configuration to the supplicant.", iface);
+		_LOGE (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) couldn't send wireless configuration to the supplicant.");
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
 	}
@@ -2879,14 +2809,14 @@ act_stage3_ip4_config_start (NMDevice *device,
                              NMDeviceStateReason *reason)
 {
 	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
 	const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 
 	connection = nm_device_get_connection (device);
 	g_assert (connection);
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (s_ip4)
-		method = nm_setting_ip4_config_get_method (s_ip4);
+		method = nm_setting_ip_config_get_method (s_ip4);
 
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
@@ -2901,14 +2831,14 @@ act_stage3_ip6_config_start (NMDevice *device,
                              NMDeviceStateReason *reason)
 {
 	NMConnection *connection;
-	NMSettingIP6Config *s_ip6;
+	NMSettingIPConfig *s_ip6;
 	const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
 
 	connection = nm_device_get_connection (device);
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	if (s_ip6)
-		method = nm_setting_ip6_config_get_method (s_ip6);
+		method = nm_setting_ip_config_get_method (s_ip6);
 
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 ||
@@ -2933,7 +2863,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	/* MTU override */
 	mtu = nm_setting_wireless_get_mtu (s_wifi);
 	if (mtu)
-		nm_ip4_config_set_mtu (config, mtu);
+		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 }
 
 static gboolean
@@ -2984,17 +2914,14 @@ handle_ip_config_timeout (NMDeviceWifi *self,
 	 */
 	if (!may_fail && is_static_wep (connection)) {
 		/* Activation failed, we must have bad encryption key */
-		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
-		             "Activation (%s/wireless): could not get IP configuration for "
-		             "connection '%s'.",
-		             nm_device_get_iface (NM_DEVICE (self)),
-		             nm_connection_get_id (connection));
+		_LOGW (LOGD_DEVICE | LOGD_WIFI,
+		       "Activation: (wifi) could not get IP configuration for connection '%s'.",
+		       nm_connection_get_id (connection));
 
 		ret = handle_auth_or_fail (self, NULL, TRUE);
 		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
-			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
-			             "Activation (%s/wireless): asking for new secrets",
-			             nm_device_get_iface (NM_DEVICE (self)));
+			_LOGI (LOGD_DEVICE | LOGD_WIFI,
+			       "Activation: (wifi) asking for new secrets");
 		} else {
 			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
 		}
@@ -3008,60 +2935,60 @@ handle_ip_config_timeout (NMDeviceWifi *self,
 
 
 static NMActStageReturn
-act_stage4_ip4_config_timeout (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
+	NMSettingIPConfig *s_ip4;
 	gboolean may_fail = FALSE, chain_up = FALSE;
 	NMActStageReturn ret;
 
-	connection = nm_device_get_connection (dev);
+	connection = nm_device_get_connection (device);
 	g_assert (connection);
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-	may_fail = nm_setting_ip4_config_get_may_fail (s_ip4);
+	may_fail = nm_setting_ip_config_get_may_fail (s_ip4);
 
-	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, reason);
 	if (chain_up)
-		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, reason);
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (device, reason);
 
 	return ret;
 }
 
 static NMActStageReturn
-act_stage4_ip6_config_timeout (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMConnection *connection;
-	NMSettingIP6Config *s_ip6;
+	NMSettingIPConfig *s_ip6;
 	gboolean may_fail = FALSE, chain_up = FALSE;
 	NMActStageReturn ret;
 
-	connection = nm_device_get_connection (dev);
+	connection = nm_device_get_connection (device);
 	g_assert (connection);
 
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
-	may_fail = nm_setting_ip6_config_get_may_fail (s_ip6);
+	may_fail = nm_setting_ip_config_get_may_fail (s_ip6);
 
-	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, reason);
 	if (chain_up)
-		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, reason);
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (device, reason);
 
 	return ret;
 }
 
 static void
-activation_success_handler (NMDevice *dev)
+activation_success_handler (NMDevice *device)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (dev);
+	int ifindex = nm_device_get_ifindex (device);
 	NMAccessPoint *ap;
-	struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+	guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
 	NMAccessPoint *tmp_ap = NULL;
 	NMActRequest *req;
 	NMConnection *connection;
 
-	req = nm_device_get_act_request (dev);
+	req = nm_device_get_act_request (device);
 	g_assert (req);
 
 	connection = nm_act_request_get_connection (req);
@@ -3087,9 +3014,12 @@ activation_success_handler (NMDevice *dev)
 	 * But if activation was successful, the card will know the BSSID.  Grab
 	 * the BSSID off the card and fill in the BSSID of the activation AP.
 	 */
-	nm_platform_wifi_get_bssid (ifindex, &bssid);
-	if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap)))
-		nm_ap_set_address (ap, &bssid);
+	nm_platform_wifi_get_bssid (ifindex, bssid);
+	if (!nm_ap_get_address (ap)) {
+		char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
+		nm_ap_set_address (ap, bssid_str);
+		g_free (bssid_str);
+	}
 	if (!nm_ap_get_freq (ap))
 		nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex));
 	if (!nm_ap_get_max_bitrate (ap))
@@ -3103,9 +3033,11 @@ activation_success_handler (NMDevice *dev)
 		 * instead.
 		 */
 
-		/* If the better match was a hidden AP, update it's SSID */
-		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len))
-			nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap));
+		/* If the better match was a hidden AP, update its SSID */
+		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+			ssid = nm_ap_get_ssid (ap);
+			nm_ap_set_ssid (tmp_ap, ssid->data, ssid->len);
+		}
 
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
 		                                          nm_ap_get_dbus_path (tmp_ap));
@@ -3130,18 +3062,18 @@ done:
 }
 
 static void
-activation_failure_handler (NMDevice *dev)
+activation_failure_handler (NMDevice *device)
 {
 	NMConnection *connection;
 
-	connection = nm_device_get_connection (dev);
+	connection = nm_device_get_connection (device);
 	g_assert (connection);
 
 	/* Clear wireless secrets tries on failure */
 	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
 
 	/* Clear any critical protocol notification in the wifi stack */
-	nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (dev), FALSE);
+	nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE);
 }
 
 static void
@@ -3225,15 +3157,12 @@ set_enabled (NMDevice *device, gboolean enabled)
 
 	priv->enabled = enabled;
 
-	nm_log_dbg (LOGD_WIFI, "(%s): device now %s",
-	            nm_device_get_iface (NM_DEVICE (device)),
-	            enabled ? "enabled" : "disabled");
+	_LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" : "disabled");
 
 	state = nm_device_get_state (NM_DEVICE (self));
 	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
-		nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state",
-		            enabled ? "enable" : "disable",
-		            nm_device_get_iface (NM_DEVICE (device)));
+		_LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED state",
+		       enabled ? "enable" : "disable");
 		return;
 	}
 
@@ -3241,11 +3170,10 @@ set_enabled (NMDevice *device, gboolean enabled)
 		gboolean no_firmware = FALSE;
 
 		if (state != NM_DEVICE_STATE_UNAVAILABLE)
-			nm_log_warn (LOGD_CORE, "not in expected unavailable state!");
+			_LOGW (LOGD_CORE, "not in expected unavailable state!");
 
 		if (!nm_device_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) {
-			nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up",
-			            nm_device_get_iface (NM_DEVICE (device)));
+			_LOGD (LOGD_WIFI, "enable blocked by failure to bring device up");
 
 			if (no_firmware)
 				nm_device_set_firmware_missing (NM_DEVICE (device), TRUE);
@@ -3261,8 +3189,7 @@ set_enabled (NMDevice *device, gboolean enabled)
 			supplicant_interface_release (self);
 		supplicant_interface_acquire (self);
 
-		nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state",
-		            nm_device_get_iface (NM_DEVICE (device)));
+		_LOGD (LOGD_WIFI, "enable waiting on supplicant state");
 	} else {
 		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_UNAVAILABLE,
@@ -3321,6 +3248,18 @@ dispose (GObject *object)
 }
 
 static void
+finalize (GObject *object)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	g_free (priv->perm_hw_addr);
+	g_free (priv->initial_hw_addr);
+
+	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
+}
+
+static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
@@ -3331,7 +3270,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PERM_HW_ADDRESS:
-		g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER));
+		g_value_set_string (value, priv->perm_hw_addr);
 		break;
 	case PROP_MODE:
 		g_value_set_uint (value, priv->mode);
@@ -3387,6 +3326,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
+	object_class->finalize = finalize;
 
 	parent_class->bring_up = bring_up;
 	parent_class->update_permanent_hw_address = update_permanent_hw_address;
@@ -3413,57 +3353,56 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	klass->scanning_allowed = scanning_allowed;
 
 	/* Properties */
-	g_object_class_install_property (object_class, PROP_PERM_HW_ADDRESS,
-		g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS,
-		                     "Permanent MAC Address",
-		                     "Permanent hardware MAC address",
-		                     NULL,
-		                     G_PARAM_READABLE));
-
-	g_object_class_install_property (object_class, PROP_MODE,
-		g_param_spec_uint (NM_DEVICE_WIFI_MODE,
-		                   "Mode",
-		                   "Mode",
-		                   NM_802_11_MODE_UNKNOWN,
-		                   NM_802_11_MODE_AP,
-		                   NM_802_11_MODE_INFRA,
-		                   G_PARAM_READABLE));
-
-	g_object_class_install_property (object_class, PROP_BITRATE,
-		g_param_spec_uint (NM_DEVICE_WIFI_BITRATE,
-		                   "Bitrate",
-		                   "Bitrate",
-		                   0, G_MAXUINT32, 0,
-		                   G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_PERM_HW_ADDRESS,
+		 g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_MODE,
+		 g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "",
+		                    NM_802_11_MODE_UNKNOWN,
+		                    NM_802_11_MODE_AP,
+		                    NM_802_11_MODE_INFRA,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_BITRATE,
+		 g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, "", "",
+		                    0, G_MAXUINT32, 0,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ACCESS_POINTS,
-		 g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS,
-		                     "Access points",
-		                     "Access points",
+		 g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
-
-	g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT,
-		g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT,
-		                    "Active access point",
-		                    "Currently active access point",
-		                    DBUS_TYPE_G_OBJECT_PATH,
-		                    G_PARAM_READABLE));
-
-	g_object_class_install_property (object_class, PROP_CAPABILITIES,
-		g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES,
-		                   "Wireless Capabilities",
-		                   "Wireless Capabilities",
-		                   0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
-		                   G_PARAM_READABLE));
-
-	g_object_class_install_property (object_class, PROP_SCANNING,
-		g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING,
-		                   "Scanning",
-		                   "Scanning",
-		                   FALSE,
-		                   G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_ACCESS_POINT,
+		 g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, "", "",
+		                    0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_SCANNING,
+		 g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[ACCESS_POINT_ADDED] =
@@ -3495,8 +3434,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_wifi_object_info);
-
-	dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR);
 }
 
 
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
index f0a1beac..bcba91da 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -19,12 +19,10 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef NM_DEVICE_WIFI_H
-#define NM_DEVICE_WIFI_H
+#ifndef __NETWORKMANAGER_DEVICE_WIFI_H__
+#define __NETWORKMANAGER_DEVICE_WIFI_H__
 
 #include <glib-object.h>
-#include <dbus/dbus.h>
-#include <net/ethernet.h>
 
 #include "nm-device.h"
 #include "nm-wifi-ap.h"
@@ -40,16 +38,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIFI))
 #define NM_DEVICE_WIFI_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
 
-typedef enum {
-	NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, /*< nick=ConnectionNotWireless >*/
-	NM_WIFI_ERROR_CONNECTION_INVALID,          /*< nick=ConnectionInvalid >*/
-	NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,     /*< nick=ConnectionIncompatible >*/
-	NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND,      /*< nick=AccessPointNotFound >*/
-	NM_WIFI_ERROR_SCAN_NOT_ALLOWED,            /*< nick=ScanNotAllowed >*/
-	NM_WIFI_ERROR_AP_MODE_UNSUPPORTED,         /*< nick=ApModeUnsupported >*/
-	NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED,      /*< nick=AdhocModeUnsupported >*/
-} NMWifiError;
-
 #define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address"
 #define NM_DEVICE_WIFI_MODE                "mode"
 #define NM_DEVICE_WIFI_BITRATE             "bitrate"
diff --git a/src/devices/wifi/nm-wifi-ap-utils.c b/src/devices/wifi/nm-wifi-ap-utils.c
index 9b03cbd4..9ac7052e 100644
--- a/src/devices/wifi/nm-wifi-ap-utils.c
+++ b/src/devices/wifi/nm-wifi-ap-utils.c
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*-*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /*
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,11 +18,15 @@
  * (C) Copyright 2011 Red Hat, Inc.
  */
 
-#include <config.h>
+#include "config.h"
+
 #include <string.h>
 #include <stdlib.h>
 
+#include <glib/gi18n.h>
+
 #include "nm-wifi-ap-utils.h"
+#include "nm-utils.h"
 
 static gboolean
 verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error)
@@ -35,9 +39,10 @@ verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **erro
 	    || nm_setting_wireless_security_get_wep_key_type (s_wsec)) {
 		/* Dynamic WEP cannot have any WEP keys set */
 		g_set_error (error,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		             "%s is incompatible with static WEP keys", tag);
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_SETTING,
+		             _("%s is incompatible with static WEP keys"), tag);
+		g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
 		return FALSE;
 	}
 
@@ -68,17 +73,21 @@ verify_leap (NMSettingWirelessSecurity *s_wsec,
 			/* LEAP authentication requires at least a LEAP username */
 			if (!leap_username) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
-				                     "LEAP requires a LEAP username");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+				                     _("LEAP authentication requires a LEAP username"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
 				return FALSE;
 			}
 		} else if (leap_username) {
 			/* Leap username requires 'leap' auth */
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "LEAP requires 'leap' authentication");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("LEAP username requires 'leap' authentication"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
 			return FALSE;
 		}
 	}
@@ -87,9 +96,11 @@ verify_leap (NMSettingWirelessSecurity *s_wsec,
 		if (key_mgmt && strcmp (key_mgmt, "ieee8021x")) {
 			/* LEAP requires ieee8021x key management */
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X,
-			                     "LEAP requires IEEE 802.1x key management");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("LEAP authentication requires IEEE 802.1x key management"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 			return FALSE;
 		}
 	}
@@ -105,9 +116,10 @@ verify_leap (NMSettingWirelessSecurity *s_wsec,
 
 		if (adhoc) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "LEAP incompatible with Ad-Hoc mode");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("LEAP authentication is incompatible with Ad-Hoc mode"));
+			g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
 			return FALSE;
 		}
 
@@ -116,9 +128,10 @@ verify_leap (NMSettingWirelessSecurity *s_wsec,
 
 		if (s_8021x) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
-			                     "LEAP incompatible with 802.1x setting");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("LEAP authentication is incompatible with 802.1x setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 			return FALSE;
 		}
 	}
@@ -137,17 +150,23 @@ verify_no_wpa (NMSettingWirelessSecurity *s_wsec,
 	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
 	if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) {
 		g_set_error (error,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		             "%s incompatible with any WPA key management", tag);
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("a connection using '%s' authentication cannot use WPA key management"),
+		             tag);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 		return FALSE;
 	}
 
 	if (nm_setting_wireless_security_get_num_protos (s_wsec)) {
 		g_set_error (error,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		             "%s incompatible with any 'proto' setting", tag);
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("a connection using '%s' authentication cannot specific WPA protocols"),
+		             tag);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_PROTO);
 		return FALSE;
 	}
 
@@ -158,9 +177,12 @@ verify_no_wpa (NMSettingWirelessSecurity *s_wsec,
 		pw = nm_setting_wireless_security_get_pairwise (s_wsec, i);
 		if (!strcmp (pw, "tkip") || !strcmp (pw, "ccmp")) {
 			g_set_error (error,
-			             NM_SETTING_WIRELESS_SECURITY_ERROR,
-			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			             "%s is incompatible with WPA pairwise ciphers", tag);
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("a connection using '%s' authentication cannot specific WPA ciphers"),
+			             tag);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
 			return FALSE;
 		}
 	}
@@ -172,18 +194,24 @@ verify_no_wpa (NMSettingWirelessSecurity *s_wsec,
 		gr = nm_setting_wireless_security_get_group (s_wsec, i);
 		if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) {
 			g_set_error (error,
-			             NM_SETTING_WIRELESS_SECURITY_ERROR,
-			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			             "%s is incompatible with WPA group ciphers", tag);
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("a connection using '%s' authentication cannot specific WPA ciphers"),
+			             tag);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_GROUP);
 			return FALSE;
 		}
 	}
 
 	if (nm_setting_wireless_security_get_psk (s_wsec)) {
 		g_set_error (error,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR,
-		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		             "%s is incompatible with a WPA Pre-Shared Key", tag);
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("a connection using '%s' authentication cannot specific a WPA password"),
+		             tag);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                NM_SETTING_WIRELESS_SECURITY_PSK);
 		return FALSE;
 	}
 
@@ -209,18 +237,21 @@ verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec,
 			if (!s_8021x) {
 				/* 802.1x key management requires an 802.1x setting */
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "Dynamic WEP requires an 802.1x setting");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_MISSING_SETTING,
+				                     _("Dynamic WEP requires an 802.1x setting"));
+				g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 				return FALSE;
 			}
 
 			if (auth_alg && strcmp (auth_alg, "open")) {
 				/* 802.1x key management must use "open" authentication */
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "Dynamic WEP requires 'open' authentication");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("Dynamic WEP requires 'open' authentication"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 				return FALSE;
 			}
 
@@ -231,9 +262,11 @@ verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec,
 			if (s_8021x) {
 				/* 802.1x setting requires 802.1x key management */
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "Dynamic WEP requires 'ieee8021x' key management");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("Dynamic WEP requires 'ieee8021x' key management"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 				return FALSE;
 			}
 		}
@@ -242,9 +275,11 @@ verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec,
 		if (auth_alg && strcmp (auth_alg, "open")) {
 			/* 802.1x key management must use "open" authentication */
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "Dynamic WEP requires 'open' authentication");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("Dynamic WEP requires 'open' authentication"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 			return FALSE;
 		}
 
@@ -274,18 +309,21 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 		if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) {
 			if (s_8021x) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA-PSK incompatible with 802.1x");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_SETTING,
+				                     _("WPA-PSK authentication is incompatible with 802.1x"));
+				g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 				return FALSE;
 			}
 
 			if (auth_alg && strcmp (auth_alg, "open")) {
 				/* WPA must use "open" authentication */
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA-PSK requires 'open' authentication");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA-PSK requires 'open' authentication"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 				return FALSE;
 			}
 		}
@@ -293,9 +331,11 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 		if (!strcmp (key_mgmt, "wpa-none")) {
 			if (!adhoc) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA Ad-Hoc requires an Ad-Hoc mode AP");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA Ad-Hoc authentication requires an Ad-Hoc mode AP"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME,
+				                NM_SETTING_WIRELESS_MODE);
 				return FALSE;
 			}
 
@@ -304,9 +344,11 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 			tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL;
 			if (n > 1 || !tmp || strcmp (tmp, "wpa")) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA Ad-Hoc requires 'wpa' proto");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA Ad-Hoc authentication requires 'wpa' protocol"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_PROTO);
 				return FALSE;
 			}
 
@@ -314,9 +356,11 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 			tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL;
 			if (n > 1 || g_strcmp0 (tmp, "none")) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA Ad-Hoc requires 'none' pairwise cipher");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA Ad-Hoc authentication requires 'none' pairwise cipher"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
 				return FALSE;
 			}
 
@@ -324,9 +368,11 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 			tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL;
 			if (n > 1 || !tmp || strcmp (tmp, "tkip")) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA Ad-Hoc requires 'tkip' group cipher");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA Ad-Hoc requires 'tkip' group cipher"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_GROUP);
 				return FALSE;
 			}
 		}
@@ -336,9 +382,11 @@ verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
 			if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
 			    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "AP does not support PSK but setting requires it");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("Access point does not support PSK but setting requires it"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 				return FALSE;
 			}
 		}
@@ -364,27 +412,31 @@ verify_wpa_eap (NMSettingWirelessSecurity *s_wsec,
 		if (!strcmp (key_mgmt, "wpa-eap")) {
 			if (!s_8021x) {
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA-EAP requires an 802.1x setting");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_MISSING_SETTING,
+				                     _("WPA-EAP authentication requires an 802.1x setting"));
+				g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 				return FALSE;
 			}
 
 			if (auth_alg && strcmp (auth_alg, "open")) {
 				/* WPA must use "open" authentication */
 				g_set_error_literal (error,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-				                     "WPA-EAP requires 'open' authentication");
+				                     NM_CONNECTION_ERROR,
+				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+				                     _("WPA-EAP requires 'open' authentication"));
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+				                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 				return FALSE;
 			}
 
 			is_wpa_eap = TRUE;
 		} else if (s_8021x) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "Setting requires 802.1x but does not use 'wpa-eap' key management");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("802.1x setting requires 'wpa-eap' key management"));
+			g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 			return FALSE;
 		}
 	}
@@ -394,9 +446,10 @@ verify_wpa_eap (NMSettingWirelessSecurity *s_wsec,
 		if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
 		    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "AP does not support 802.1x but setting requires it");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("Access point does not support 802.1x but setting requires it"));
+			g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 			return FALSE;
 		}
 	}
@@ -421,41 +474,50 @@ verify_adhoc (NMSettingWirelessSecurity *s_wsec,
 	if (adhoc) {
 		if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "AP mode is Ad-Hoc but setting requires Infrastructure security");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("Access point mode is Ad-Hoc but setting requires Infrastructure security"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 			return FALSE;
 		}
 
 		if (s_8021x) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "Ad-Hoc mode incompatible with 802.1x security");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("Ad-Hoc mode is incompatible with 802.1x security"));
+			g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 			return FALSE;
 		}
 
 		if (leap_username) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "Ad-Hoc mode incompatible with LEAP security");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("Ad-Hoc mode is incompatible with LEAP security"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 			return FALSE;
 		}
 
 		if (auth_alg && strcmp (auth_alg, "open")) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "Ad-Hoc mode requires 'open' authentication");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("Ad-Hoc mode requires 'open' authentication"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 			return FALSE;
 		}
 	} else {
 		if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "AP mode is Infrastructure but setting requires Ad-Hoc security");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("Access point mode is Infrastructure but setting requires Ad-Hoc security"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+			                NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 			return FALSE;
 		}
 	}
@@ -465,7 +527,7 @@ verify_adhoc (NMSettingWirelessSecurity *s_wsec,
 
 gboolean
 nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
-                                 const guint8 ap_bssid[ETH_ALEN],
+                                 const char *bssid,
                                  NM80211Mode ap_mode,
                                  guint32 ap_flags,
                                  guint32 ap_wpa_flags,
@@ -477,7 +539,7 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 	NMSettingWireless *s_wifi;
 	NMSettingWirelessSecurity *s_wsec;
 	NMSetting8021x *s_8021x;
-	const GByteArray *ssid;
+	GBytes *ssid, *ap_ssid_bytes;
 	const char *mode, *key_mgmt, *auth_alg, *leap_username;
 	gboolean adhoc = FALSE;
 
@@ -487,26 +549,23 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 
 	/* Fill in missing SSID */
+	ap_ssid_bytes = g_bytes_new (ap_ssid->data, ap_ssid->len);
 	ssid = nm_setting_wireless_get_ssid (s_wifi);
 	if (!ssid)
-		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL);
-	else if (   ssid->len != ap_ssid->len
-	         || memcmp (ssid->data, ap_ssid->data, ssid->len)) {
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid_bytes, NULL);
+	else if (!g_bytes_equal (ssid, ap_ssid_bytes)) {
 		g_set_error_literal (error,
-		                     NM_SETTING_WIRELESS_ERROR,
-		                     NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
-		                     "Setting SSID did not match AP SSID");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("connection does not match access point"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID);
+		g_bytes_unref (ap_ssid_bytes);
 		return FALSE;
 	}
+	g_bytes_unref (ap_ssid_bytes);
 
-	if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi)) {
-		GByteArray *bssid;
-
-		bssid = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (bssid, ap_bssid, ETH_ALEN);
+	if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi))
 		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL);
-		g_byte_array_free (bssid, TRUE);
-	}
 
 	/* And mode */
 	mode = nm_setting_wireless_get_mode (s_wifi);
@@ -526,9 +585,10 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 
 		if (valid == FALSE) {
 			g_set_error (error,
-			             NM_SETTING_WIRELESS_ERROR,
-			             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
-			             NM_SETTING_WIRELESS_MODE);
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("connection does not match access point"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MODE);
 			return FALSE;
 		}
 	} else {
@@ -549,9 +609,13 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 		/* Make sure the connection doesn't specify security */
 		if (s_wsec || s_8021x) {
 			g_set_error_literal (error,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-			                     "AP is unencrypted but setting specifies security");
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_SETTING,
+			                     _("Access point is unencrypted but setting specifies security"));
+			if (s_wsec)
+				g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+			else
+				g_prefix_error (error, "%s: ", NM_SETTING_802_1X_SETTING_NAME);
 			return FALSE;
 		}
 		return TRUE;
@@ -634,18 +698,20 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 	/* Ensure key management is valid for WPA */
 	if ((key_mgmt && !strcmp (key_mgmt, "ieee8021x")) || leap_username) {
 		g_set_error_literal (error,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		                     "WPA incompatible with non-EAP (original) LEAP or Dynamic WEP");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("WPA authentication is incompatible with non-EAP (original) LEAP or Dynamic WEP"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
 		return FALSE;
 	}
 
 	/* 'shared' auth incompatible with any type of WPA */
 	if (auth_alg && strcmp (auth_alg, "open")) {
 		g_set_error_literal (error,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		                     "WPA incompatible with Shared Key authentication");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("WPA authentication is incompatible with Shared Key authentication"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
 		return FALSE;
 	}
 
@@ -689,9 +755,9 @@ nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
 		 */
 	} else {
 		g_set_error_literal (error,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
-		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
-		                     "Failed to determine AP security information");
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_FAILED,
+		                     _("Failed to determine AP security information"));
 		return FALSE;
 	}
 
diff --git a/src/devices/wifi/nm-wifi-ap-utils.h b/src/devices/wifi/nm-wifi-ap-utils.h
index 992b839d..31c12cd7 100644
--- a/src/devices/wifi/nm-wifi-ap-utils.h
+++ b/src/devices/wifi/nm-wifi-ap-utils.h
@@ -18,19 +18,18 @@
  * (C) Copyright 2011 Red Hat, Inc.
  */
 
-#ifndef NM_WIFI_AP_UTILS_H
-#define NM_WIFI_AP_UTILS_H
+#ifndef __NETWORKMANAGER_WIFI_AP_UTILS_H__
+#define __NETWORKMANAGER_WIFI_AP_UTILS_H__
 
-#include <net/ethernet.h>
 
-#include <NetworkManager.h>
+#include <nm-dbus-interface.h>
 #include <nm-connection.h>
 #include <nm-setting-wireless.h>
 #include <nm-setting-wireless-security.h>
 #include <nm-setting-8021x.h>
 
 gboolean nm_ap_utils_complete_connection (const GByteArray *ssid,
-                                          const guint8 bssid[ETH_ALEN],
+                                          const char *bssid,
                                           NM80211Mode mode,
                                           guint32 flags,
                                           guint32 wpa_flags,
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index 363be2e3..ee07cf40 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -19,9 +19,10 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include <stdlib.h>
-#include <netinet/ether.h>
 
 #include "nm-wifi-ap.h"
 #include "nm-wifi-ap-utils.h"
@@ -45,7 +46,7 @@ typedef struct
 
 	/* Scanned or cached values */
 	GByteArray *	ssid;
-	struct ether_addr	address;
+	char *          address;
 	NM80211Mode		mode;
 	gint8			strength;
 	guint32			freq;		/* Frequency in MHz; ie 2412 (== 2.412 GHz) */
@@ -102,6 +103,7 @@ finalize (GObject *object)
 	g_free (priv->supplicant_path);
 	if (priv->ssid)
 		g_byte_array_free (priv->ssid, TRUE);
+	g_free (priv->address);
 
 	G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object);
 }
@@ -111,6 +113,7 @@ set_property (GObject *object, guint prop_id,
 		    const GValue *value, GParamSpec *pspec)
 {
 	NMAccessPoint *ap = NM_AP (object);
+	GByteArray *ssid;
 
 	switch (prop_id) {
 	case PROP_FLAGS:
@@ -123,7 +126,11 @@ set_property (GObject *object, guint prop_id,
 		nm_ap_set_rsn_flags (ap, g_value_get_uint (value));
 		break;
 	case PROP_SSID:
-		nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value));
+		ssid = g_value_get_boxed (value);
+		if (ssid)
+			nm_ap_set_ssid (ap, ssid->data, ssid->len);
+		else
+			nm_ap_set_ssid (ap, NULL, 0);
 		break;
 	case PROP_FREQUENCY:
 		nm_ap_set_freq (ap, g_value_get_uint (value));
@@ -176,7 +183,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->freq);
 		break;
 	case PROP_HW_ADDRESS:
-		g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->address, ARPHRD_ETHER));
+		g_value_set_string (value, priv->address);
 		break;
 	case PROP_MODE:
 		g_value_set_uint (value, priv->mode);
@@ -219,81 +226,72 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_FLAGS,
-		 g_param_spec_uint (NM_AP_FLAGS,
-							"Flags",
-							"Flags",
-							NM_802_11_AP_FLAGS_NONE,
-							NM_802_11_AP_FLAGS_PRIVACY,
-							NM_802_11_AP_FLAGS_NONE,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_FLAGS, "", "",
+		                    NM_802_11_AP_FLAGS_NONE,
+		                    NM_802_11_AP_FLAGS_PRIVACY,
+		                    NM_802_11_AP_FLAGS_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_WPA_FLAGS,
-		 g_param_spec_uint (NM_AP_WPA_FLAGS,
-							"WPA Flags",
-							"WPA Flags",
-							NM_802_11_AP_SEC_NONE,
-							all_sec_flags,
-							NM_802_11_AP_SEC_NONE,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_WPA_FLAGS, "", "",
+		                    NM_802_11_AP_SEC_NONE,
+		                    all_sec_flags,
+		                    NM_802_11_AP_SEC_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_RSN_FLAGS,
-		 g_param_spec_uint (NM_AP_RSN_FLAGS,
-							"RSN Flags",
-							"RSN Flags",
-							NM_802_11_AP_SEC_NONE,
-							all_sec_flags,
-							NM_802_11_AP_SEC_NONE,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_RSN_FLAGS, "", "",
+		                    NM_802_11_AP_SEC_NONE,
+		                    all_sec_flags,
+		                    NM_802_11_AP_SEC_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SSID,
-	     g_param_spec_boxed (NM_AP_SSID,
-	                         "SSID",
-	                         "SSID",
+	     g_param_spec_boxed (NM_AP_SSID, "", "",
 	                         DBUS_TYPE_G_UCHAR_ARRAY,
-	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_FREQUENCY,
-		 g_param_spec_uint (NM_AP_FREQUENCY,
-							"Frequency",
-							"Frequency",
-							0, 10000, 0,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_FREQUENCY, "", "",
+		                    0, 10000, 0,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_HW_ADDRESS,
-		 g_param_spec_string (NM_AP_HW_ADDRESS,
-							  "MAC Address",
-							  "Hardware MAC address",
-							  NULL,
-							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_string (NM_AP_HW_ADDRESS, "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 	
 	g_object_class_install_property
 		(object_class, PROP_MODE,
-		 g_param_spec_uint (NM_AP_MODE,
-						   "Mode",
-						   "Mode",
-						   NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
-						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_MODE, "", "",
+		                    NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MAX_BITRATE,
-		 g_param_spec_uint (NM_AP_MAX_BITRATE,
-							"Max Bitrate",
-							"Max Bitrate",
-							0, G_MAXUINT16, 0,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_uint (NM_AP_MAX_BITRATE, "", "",
+		                    0, G_MAXUINT16, 0,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STRENGTH,
-		 g_param_spec_char (NM_AP_STRENGTH,
-							"Strength",
-							"Strength",
-							G_MININT8, G_MAXINT8, 0,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_char (NM_AP_STRENGTH, "", "",
+		                    G_MININT8, G_MAXINT8, 0,
+		                    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 (ap_class),
@@ -402,7 +400,6 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
 
 		if (!strcmp (key, "SSID")) {
 			guint32 len = MIN (32, array->len);
-			GByteArray *ssid;
 
 			/* Stupid ieee80211 layer uses <hidden> */
 			if (((len == 8) || (len == 9))
@@ -412,18 +409,15 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
 			if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len))
 				return;
 
-			ssid = g_byte_array_sized_new (len);
-			g_byte_array_append (ssid, (const guint8 *) array->data, len);
-			nm_ap_set_ssid (ap, ssid);
-			g_byte_array_free (ssid, TRUE);
+			nm_ap_set_ssid (ap, (const guint8 *) array->data, len);
 		} else if (!strcmp (key, "BSSID")) {
-			struct ether_addr addr;
+			char *addr;
 
 			if (array->len != ETH_ALEN)
 				return;
-			memset (&addr, 0, sizeof (struct ether_addr));
-			memcpy (&addr, array->data, ETH_ALEN);
-			nm_ap_set_address (ap, &addr);
+			addr = nm_utils_hwaddr_ntoa (array->data, array->len);
+			nm_ap_set_address (ap, addr);
+			g_free (addr);
 		} else if (!strcmp (key, "Rates")) {
 			guint32 maxrate = 0;
 			int i;
@@ -483,7 +477,7 @@ NMAccessPoint *
 nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties)
 {
 	NMAccessPoint *ap;
-	const struct ether_addr * addr;
+	const char *addr;
 	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 
@@ -498,8 +492,8 @@ nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties)
 
 	/* ignore APs with invalid BSSIDs */
 	addr = nm_ap_get_address (ap);
-	if (   !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN))
-	    || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) {
+	if (   nm_utils_hwaddr_matches (addr, -1, bad_bssid1, ETH_ALEN)
+	    || nm_utils_hwaddr_matches (addr, -1, bad_bssid2, ETH_ALEN)) {
 		g_object_unref (ap);
 		return NULL;
 	}
@@ -597,7 +591,7 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 	NMAccessPoint *ap;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
-	const GByteArray *ssid;
+	GBytes *ssid;
 	const char *mode, *band, *key_mgmt;
 	guint32 channel;
 	NM80211ApSecurityFlags flags;
@@ -610,11 +604,11 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 
 	ssid = nm_setting_wireless_get_ssid (s_wireless);
 	g_return_val_if_fail (ssid != NULL, NULL);
-	g_return_val_if_fail (ssid->len > 0, NULL);
+	g_return_val_if_fail (g_bytes_get_size (ssid) > 0, NULL);
 
 	ap = nm_ap_new ();
 	nm_ap_set_fake (ap, TRUE);
-	nm_ap_set_ssid (ap, ssid);
+	nm_ap_set_ssid (ap, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
 
 	// FIXME: bssid too?
 
@@ -720,10 +714,6 @@ error:
 	return NULL;
 }
 
-
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
-
 void
 nm_ap_dump (NMAccessPoint *ap, const char *prefix)
 {
@@ -737,7 +727,7 @@ nm_ap_dump (NMAccessPoint *ap, const char *prefix)
 	            prefix,
 	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
 	            ap);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    BSSID     " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet));
+	nm_log_dbg (LOGD_WIFI_SCAN, "    BSSID     %s", str_if_set (priv->address, "(none)"));
 	nm_log_dbg (LOGD_WIFI_SCAN, "    mode      %d", priv->mode);
 	nm_log_dbg (LOGD_WIFI_SCAN, "    flags     0x%X", priv->flags);
 	nm_log_dbg (LOGD_WIFI_SCAN, "    wpa flags 0x%X", priv->wpa_flags);
@@ -786,20 +776,18 @@ const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap)
 }
 
 void
-nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid)
+nm_ap_set_ssid (NMAccessPoint *ap, const guint8 *ssid, gsize len)
 {
 	NMAccessPointPrivate *priv;
 
 	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (ssid == NULL || len > 0);
 
 	priv = NM_AP_GET_PRIVATE (ap);
 
-	if (ssid == priv->ssid)
-		return;
-
 	/* same SSID */
-	if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) {
-		if (!memcmp (ssid->data, priv->ssid->data, ssid->len))
+	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
+		if (!memcmp (ssid, priv->ssid->data, len))
 			return;
 	}
 
@@ -809,14 +797,8 @@ nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid)
 	}
 
 	if (ssid) {
-		/* Should never get zero-length SSIDs */
-		g_warn_if_fail (ssid->len > 0);
-
-		if (ssid->len) {
-			priv->ssid = g_byte_array_sized_new (ssid->len);
-			priv->ssid->len = ssid->len;
-			memcpy (priv->ssid->data, ssid->data, ssid->len);
-		}
+		priv->ssid = g_byte_array_new ();
+		g_byte_array_append (priv->ssid, ssid, len);
 	}
 
 	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
@@ -897,24 +879,28 @@ nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
  * Get/set functions for address
  *
  */
-const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap)
+const char *
+nm_ap_get_address (const NMAccessPoint *ap)
 {
 	g_return_val_if_fail (NM_IS_AP (ap), NULL);
 
-	return &NM_AP_GET_PRIVATE (ap)->address;
+	return NM_AP_GET_PRIVATE (ap)->address;
 }
 
-void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr)
+void
+nm_ap_set_address (NMAccessPoint *ap, const char *addr)
 {
 	NMAccessPointPrivate *priv;
 
 	g_return_if_fail (NM_IS_AP (ap));
 	g_return_if_fail (addr != NULL);
+	g_return_if_fail (nm_utils_hwaddr_valid (addr, ETH_ALEN));
 
 	priv = NM_AP_GET_PRIVATE (ap);
 
-	if (memcmp (addr, &priv->address, sizeof (priv->address))) {
-		memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr));
+	if (!priv->address || !nm_utils_hwaddr_matches (addr, -1, priv->address, -1)) {
+		g_free (priv->address);
+		priv->address = g_strdup (addr);
 		g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS);
 	}
 }
@@ -1118,9 +1104,10 @@ nm_ap_check_compatible (NMAccessPoint *self,
 	NMAccessPointPrivate *priv;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
+	GBytes *ssid;
 	const char *mode;
 	const char *band;
-	const GByteArray *bssid;
+	const char *bssid;
 	guint32 channel;
 
 	g_return_val_if_fail (NM_IS_AP (self), FALSE);
@@ -1132,11 +1119,19 @@ nm_ap_check_compatible (NMAccessPoint *self,
 	if (s_wireless == NULL)
 		return FALSE;
 	
-	if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE))
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	if (   (ssid && !priv->ssid)
+	    || (priv->ssid && !ssid))
+		return FALSE;
+
+	if (   ssid && priv->ssid &&
+	    !nm_utils_same_ssid (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid),
+	                         priv->ssid->data, priv->ssid->len,
+	                         TRUE))
 		return FALSE;
 
 	bssid = nm_setting_wireless_get_bssid (s_wireless);
-	if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN))
+	if (bssid && (!priv->address || !nm_utils_hwaddr_matches (bssid, -1, priv->address, -1)))
 		return FALSE;
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
@@ -1190,7 +1185,7 @@ nm_ap_complete_connection (NMAccessPoint *self,
 	g_return_val_if_fail (connection != NULL, FALSE);
 
 	return nm_ap_utils_complete_connection (priv->ssid,
-	                                        priv->address.ether_addr_octet,
+	                                        priv->address,
 	                                        priv->mode,
 	                                        priv->flags,
 	                                        priv->wpa_flags,
@@ -1233,27 +1228,29 @@ nm_ap_match_in_list (NMAccessPoint *find_ap,
 	for (iter = ap_list; iter; iter = g_slist_next (iter)) {
 		NMAccessPoint * list_ap = NM_AP (iter->data);
 		const GByteArray * list_ssid = nm_ap_get_ssid (list_ap);
-		const struct ether_addr * list_addr = nm_ap_get_address (list_ap);
+		const char * list_addr = nm_ap_get_address (list_ap);
 
 		const GByteArray * find_ssid = nm_ap_get_ssid (find_ap);
-		const struct ether_addr * find_addr = nm_ap_get_address (find_ap);
+		const char * find_addr = nm_ap_get_address (find_ap);
 
 		/* SSID match; if both APs are hiding their SSIDs,
 		 * let matching continue on BSSID and other properties
 		 */
 		if (   (!list_ssid && find_ssid)
-		    || (list_ssid && !find_ssid)
-		    || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE))
+		    || (list_ssid && !find_ssid))
+			continue;
+		if (   list_ssid
+		    && find_ssid
+		    && !nm_utils_same_ssid (list_ssid->data, list_ssid->len,
+		                            find_ssid->data, find_ssid->len,
+		                            TRUE))
 			continue;
 
 		/* BSSID match */
-		if (   (strict_match || nm_ethernet_address_is_valid (find_addr))
-		    && nm_ethernet_address_is_valid (list_addr)
-		    && memcmp (list_addr->ether_addr_octet, 
-		               find_addr->ether_addr_octet,
-		               ETH_ALEN) != 0) {
+		if (   (strict_match || nm_ethernet_address_is_valid (find_addr, -1))
+		    && nm_ethernet_address_is_valid (list_addr, -1)
+		    && !nm_utils_hwaddr_matches (list_addr, -1, find_addr, -1))
 			continue;
-		}
 
 		/* mode match */
 		if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap))
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index f51fa078..0abb28fc 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -19,12 +19,12 @@
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef NM_ACCESS_POINT_H
-#define NM_ACCESS_POINT_H
+#ifndef __NETWORKMANAGER_ACCESS_POINT_H__
+#define __NETWORKMANAGER_ACCESS_POINT_H__
 
 #include <glib.h>
 #include <glib-object.h>
-#include "NetworkManager.h"
+#include "nm-dbus-interface.h"
 #include "nm-connection.h"
 
 #define NM_TYPE_AP            (nm_ap_get_type ())
@@ -60,62 +60,62 @@ NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
 NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
 void            nm_ap_export_to_dbus    (NMAccessPoint *ap);
 
-const char *		nm_ap_get_dbus_path (NMAccessPoint *ap);
+const char *nm_ap_get_dbus_path (NMAccessPoint *ap);
 
-const char *        nm_ap_get_supplicant_path (NMAccessPoint *ap);
-void                nm_ap_set_supplicant_path (NMAccessPoint *ap,
-                                               const char *path);
+const char *nm_ap_get_supplicant_path (NMAccessPoint *ap);
+void        nm_ap_set_supplicant_path (NMAccessPoint *ap,
+                                       const char *path);
 
-const GByteArray *	nm_ap_get_ssid (const NMAccessPoint * ap);
-void				nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid);
+const GByteArray *nm_ap_get_ssid (const NMAccessPoint * ap);
+void              nm_ap_set_ssid (NMAccessPoint * ap, const guint8 * ssid, gsize len);
 
-NM80211ApFlags         nm_ap_get_flags     (NMAccessPoint *ap);
-void                   nm_ap_set_flags     (NMAccessPoint *ap, NM80211ApFlags flags);
+NM80211ApFlags nm_ap_get_flags (NMAccessPoint *ap);
+void           nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags);
 
 NM80211ApSecurityFlags nm_ap_get_wpa_flags (NMAccessPoint *ap);
 void                   nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags);
 
-NM80211ApSecurityFlags nm_ap_get_rsn_flags	(NMAccessPoint *ap);
-void                   nm_ap_set_rsn_flags  (NMAccessPoint *ap, NM80211ApSecurityFlags flags);
+NM80211ApSecurityFlags nm_ap_get_rsn_flags (NMAccessPoint *ap);
+void                   nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags);
 
-const struct ether_addr * nm_ap_get_address	(const NMAccessPoint *ap);
-void				nm_ap_set_address		(NMAccessPoint *ap, const struct ether_addr *addr);
+const char *nm_ap_get_address (const NMAccessPoint *ap);
+void        nm_ap_set_address (NMAccessPoint *ap, const char *addr);
 
-NM80211Mode			nm_ap_get_mode			(NMAccessPoint *ap);
-void				nm_ap_set_mode			(NMAccessPoint *ap, const NM80211Mode mode);
+NM80211Mode nm_ap_get_mode (NMAccessPoint *ap);
+void        nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode);
 
-gboolean            nm_ap_is_hotspot        (NMAccessPoint *ap);
+gboolean nm_ap_is_hotspot (NMAccessPoint *ap);
 
-gint8			nm_ap_get_strength		(NMAccessPoint *ap);
-void				nm_ap_set_strength		(NMAccessPoint *ap, gint8 strength);
+gint8 nm_ap_get_strength (NMAccessPoint *ap);
+void  nm_ap_set_strength (NMAccessPoint *ap, gint8 strength);
 
-guint32			nm_ap_get_freq			(NMAccessPoint *ap);
-void				nm_ap_set_freq			(NMAccessPoint *ap, guint32 freq);
+guint32 nm_ap_get_freq (NMAccessPoint *ap);
+void    nm_ap_set_freq (NMAccessPoint *ap, guint32 freq);
 
-guint32			nm_ap_get_max_bitrate			(NMAccessPoint *ap);
-void				nm_ap_set_max_bitrate		(NMAccessPoint *ap, guint32 bitrate);
+guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap);
+void    nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate);
 
-gboolean			nm_ap_get_fake	(const NMAccessPoint *ap);
-void				nm_ap_set_fake	(NMAccessPoint *ap, gboolean fake);
+gboolean nm_ap_get_fake (const NMAccessPoint *ap);
+void     nm_ap_set_fake (NMAccessPoint *ap, gboolean fake);
 
-gboolean			nm_ap_get_broadcast		(NMAccessPoint *ap);
-void				nm_ap_set_broadcast		(NMAccessPoint *ap, gboolean broadcast);
+gboolean nm_ap_get_broadcast (NMAccessPoint *ap);
+void     nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast);
 
-gint32                  nm_ap_get_last_seen     (const NMAccessPoint *ap);
-void                    nm_ap_set_last_seen     (NMAccessPoint *ap, gint32 last_seen);
+gint32   nm_ap_get_last_seen (const NMAccessPoint *ap);
+void     nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen);
 
-gboolean			nm_ap_check_compatible (NMAccessPoint *self,
-                                            NMConnection *connection);
+gboolean nm_ap_check_compatible (NMAccessPoint *self,
+                                 NMConnection *connection);
 
-gboolean            nm_ap_complete_connection (NMAccessPoint *self,
-                                               NMConnection *connection,
-                                               gboolean lock_bssid,
-                                               GError **error);
+gboolean nm_ap_complete_connection (NMAccessPoint *self,
+                                    NMConnection *connection,
+                                    gboolean lock_bssid,
+                                    GError **error);
 
 NMAccessPoint *     nm_ap_match_in_list (NMAccessPoint *find_ap,
                                          GSList *ap_list,
                                          gboolean strict_match);
 
-void				nm_ap_dump (NMAccessPoint *ap, const char *prefix);
+void                nm_ap_dump (NMAccessPoint *ap, const char *prefix);
 
-#endif /* NM_ACCESS_POINT_H */
+#endif /* __NETWORKMANAGER_ACCESS_POINT_H__ */
diff --git a/src/devices/wifi/nm-wifi-enum-types.c b/src/devices/wifi/nm-wifi-enum-types.c
index e914cfd6..7cce51cd 100644
--- a/src/devices/wifi/nm-wifi-enum-types.c
+++ b/src/devices/wifi/nm-wifi-enum-types.c
@@ -9,50 +9,6 @@
 #include "nm-wifi-ap.h" 
 #include "nm-device-olpc-mesh.h"
 
-GType
-nm_wifi_error_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, "NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS", "ConnectionNotWireless" },
-        { NM_WIFI_ERROR_CONNECTION_INVALID, "NM_WIFI_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
-        { NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
-        { NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND", "AccessPointNotFound" },
-        { NM_WIFI_ERROR_SCAN_NOT_ALLOWED, "NM_WIFI_ERROR_SCAN_NOT_ALLOWED", "ScanNotAllowed" },
-        { NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, "NM_WIFI_ERROR_AP_MODE_UNSUPPORTED", "ApModeUnsupported" },
-        { NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, "NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED", "AdhocModeUnsupported" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMWifiError"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-GType
-nm_olpc_mesh_error_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, "NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH", "ConnectionNotMesh" },
-        { NM_OLPC_MESH_ERROR_CONNECTION_INVALID, "NM_OLPC_MESH_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
-        { NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, "NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMOlpcMeshError"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
 
 
 
diff --git a/src/devices/wifi/nm-wifi-enum-types.h b/src/devices/wifi/nm-wifi-enum-types.h
index d4dca5bf..7fc64cd1 100644
--- a/src/devices/wifi/nm-wifi-enum-types.h
+++ b/src/devices/wifi/nm-wifi-enum-types.h
@@ -9,10 +9,6 @@
 #include <glib-object.h>
 
 G_BEGIN_DECLS
-GType nm_wifi_error_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ())
-GType nm_olpc_mesh_error_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_OLPC_MESH_ERROR (nm_olpc_mesh_error_get_type ())
 G_END_DECLS
 
 #endif /* __NM_WIFI_ENUM_TYPES_H__ */
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 02ad93f4..4093c612 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -18,12 +18,15 @@
  * Copyright (C) 2011 - 2014 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <gmodule.h>
 
 #include "nm-device-factory.h"
 #include "nm-device-wifi.h"
 #include "nm-device-olpc-mesh.h"
 #include "nm-settings-connection.h"
+#include "nm-platform.h"
 
 #define NM_TYPE_WIFI_FACTORY (nm_wifi_factory_get_type ())
 #define NM_WIFI_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_FACTORY, NMWifiFactory))
@@ -45,20 +48,12 @@ G_DEFINE_TYPE_EXTENDED (NMWifiFactory, nm_wifi_factory, G_TYPE_OBJECT, 0,
 
 /**************************************************************************/
 
-#define PLUGIN_TYPE NM_DEVICE_TYPE_WIFI
-
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_WIFI_FACTORY, NULL);
 }
 
-G_MODULE_EXPORT NMDeviceType
-nm_device_factory_get_device_type (void)
-{
-	return PLUGIN_TYPE;
-}
-
 /**************************************************************************/
 
 static NMDevice *
@@ -71,10 +66,17 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 	return NULL;
 }
 
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_WIFI;
+}
+
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
 	factory_iface->new_link = new_link;
+	factory_iface->get_device_type = get_device_type;
 }
 
 static void
diff --git a/src/devices/wifi/tests/Makefile.am b/src/devices/wifi/tests/Makefile.am
index 2667c5f1..09c6401f 100644
--- a/src/devices/wifi/tests/Makefile.am
+++ b/src/devices/wifi/tests/Makefile.am
@@ -1,14 +1,13 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
-	-I$(top_srcdir)/libnm-util \
-	-I$(top_builddir)/libnm-util \
-	-I$(top_srcdir)/src/platform \
-	-I$(top_srcdir)/src/logging \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	-I${top_srcdir}/src/platform \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/devices/wifi \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	$(DBUS_CFLAGS)
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
index c0e75eed..a8ae2e9d 100644
--- a/src/devices/wifi/tests/Makefile.in
+++ b/src/devices/wifi/tests/Makefile.in
@@ -205,6 +205,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -328,6 +330,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -347,6 +350,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -373,7 +377,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -454,15 +458,14 @@ with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
-	-I$(top_srcdir)/libnm-util \
-	-I$(top_builddir)/libnm-util \
-	-I$(top_srcdir)/src/platform \
-	-I$(top_srcdir)/src/logging \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	-I${top_srcdir}/src/platform \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/devices/wifi \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	$(DBUS_CFLAGS)
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
index 36d58f2a..fb335b89 100644
--- a/src/devices/wifi/tests/test-wifi-ap-utils.c
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -18,16 +18,15 @@
  *
  */
 
+#include "config.h"
+
 #include <glib.h>
 #include <string.h>
 
 #include "nm-wifi-ap-utils.h"
 #include "nm-dbus-glib-types.h"
 
-#include "nm-setting-connection.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-setting-8021x.h"
+#include "nm-core-internal.h"
 
 #define DEBUG 1
 
@@ -66,7 +65,7 @@
 
 static gboolean
 complete_connection (const char *ssid,
-                     const guint8 bssid[ETH_ALEN],
+                     const char *bssid,
                      NM80211Mode mode,
                      guint32 flags,
                      guint32 wpa_flags,
@@ -113,7 +112,7 @@ set_items (NMSetting *setting, const KeyData *items)
 {
 	const KeyData *item;
 	GParamSpec *pspec;
-	GByteArray *tmp;
+	GBytes *tmp;
 
 	for (item = items; item && item->key; item++) {
 		g_assert (item->key);
@@ -137,12 +136,11 @@ set_items (NMSetting *setting, const KeyData *items)
 
 			g_assert (item->str == NULL);
 			g_object_set (G_OBJECT (setting), item->key, foo, NULL);
-		} else if (pspec->value_type == DBUS_TYPE_G_UCHAR_ARRAY) {
+		} else if (pspec->value_type == G_TYPE_BYTES) {
 			g_assert (item->str);
-			tmp = g_byte_array_sized_new (strlen (item->str));
-			g_byte_array_append (tmp, (const guint8 *) item->str, strlen (item->str));
+			tmp = g_bytes_new (item->str, strlen (item->str));
 			g_object_set (G_OBJECT (setting), item->key, tmp, NULL);
-			g_byte_array_free (tmp, TRUE);
+			g_bytes_unref (tmp);
 		} else {
 			/* Special types, check based on property name */
 			if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PROTO))
@@ -217,31 +215,26 @@ fill_8021x (NMConnection *connection, const KeyData items[])
 
 static NMConnection *
 create_basic (const char *ssid,
-              const guint8 *bssid,
+              const char *bssid,
               NM80211Mode mode)
 {
 	NMConnection *connection;
 	NMSettingWireless *s_wifi = NULL;
-	GByteArray *tmp;
+	GBytes *tmp;
 
-	connection = nm_connection_new ();
+	connection = nm_simple_connection_new ();
 
 	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
 
 	/* SSID */
-	tmp = g_byte_array_sized_new (strlen (ssid));
-	g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid));
+	tmp = g_bytes_new (ssid, strlen (ssid));
 	g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, tmp, NULL);
-	g_byte_array_free (tmp, TRUE);
+	g_bytes_unref (tmp);
 
 	/* BSSID */
-	if (bssid) {
-		tmp = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (tmp, bssid, ETH_ALEN);
-		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, tmp, NULL);
-		g_byte_array_free (tmp, TRUE);
-	}
+	if (bssid)
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL);
 
 	if (mode == NM_802_11_MODE_INFRA)
 		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL);
@@ -259,12 +252,12 @@ static void
 test_lock_bssid (void)
 {
 	NMConnection *src, *expected;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *ssid = "blahblah";
 	gboolean success;
 	GError *error = NULL;
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	success = complete_connection (ssid, bssid,
 	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
 	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
@@ -283,7 +276,7 @@ static void
 test_open_ap_empty_connection (void)
 {
 	NMConnection *src, *expected;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *ssid = "blahblah";
 	gboolean success;
 	GError *error = NULL;
@@ -292,7 +285,7 @@ test_open_ap_empty_connection (void)
 	 * SSID and Infra modes of the given AP details.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	success = complete_connection (ssid, bssid,
 	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
 	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
@@ -311,7 +304,7 @@ static void
 test_open_ap_leap_connection_1 (gconstpointer add_wifi)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "Bill Smith", 0 }, { NULL } };
 	gboolean success;
 	GError *error = NULL;
@@ -321,7 +314,7 @@ test_open_ap_leap_connection_1 (gconstpointer add_wifi)
 	 * the AP to have the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	if (add_wifi)
 		fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
@@ -332,7 +325,7 @@ test_open_ap_leap_connection_1 (gconstpointer add_wifi)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING);
 
 	g_object_unref (src);
 }
@@ -343,7 +336,7 @@ static void
 test_open_ap_leap_connection_2 (void)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, { NULL } };
 	gboolean success;
 	GError *error = NULL;
@@ -352,7 +345,7 @@ test_open_ap_leap_connection_2 (void)
 	 * WEP or LEAP) is rejected when completion is attempted with an open AP.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 
@@ -362,7 +355,7 @@ test_open_ap_leap_connection_2 (void)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING);
 
 	g_object_unref (src);
 }
@@ -373,7 +366,7 @@ static void
 test_open_ap_wep_connection (gconstpointer add_wifi)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "11111111111111111111111111", 0 },
 	    { NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, NULL, 0 },
@@ -386,7 +379,7 @@ test_open_ap_wep_connection (gconstpointer add_wifi)
 	 * attempted with an open AP.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	if (add_wifi)
 		fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
@@ -396,7 +389,7 @@ test_open_ap_wep_connection (gconstpointer add_wifi)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING);
 
 	g_object_unref (src);
 }
@@ -410,11 +403,12 @@ test_ap_wpa_psk_connection_base (const char *key_mgmt,
                                  guint32 wpa_flags,
                                  guint32 rsn_flags,
                                  gboolean add_wifi,
+                                 guint error_code,
                                  NMConnection *expected)
 {
 	NMConnection *src;
 	const char *ssid = "blahblah";
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData exp_wifi[] = {
 		{ NM_SETTING_WIRELESS_SSID, ssid, 0 },
 		{ NM_SETTING_WIRELESS_MODE, "infrastructure", 0 },
@@ -427,7 +421,7 @@ test_ap_wpa_psk_connection_base (const char *key_mgmt,
 	gboolean success;
 	GError *error = NULL;
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	if (add_wifi)
 		fill_wifi_empty (src);
 	fill_wsec (src, both_wsec);
@@ -438,7 +432,7 @@ test_ap_wpa_psk_connection_base (const char *key_mgmt,
 		fill_wifi (expected, exp_wifi);
 		fill_wsec (expected, both_wsec);
 	}
-	COMPARE (src, expected, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, expected, success, error, NM_CONNECTION_ERROR, error_code);
 
 	g_object_unref (src);
 }
@@ -454,7 +448,9 @@ test_open_ap_wpa_psk_connection_1 (void)
 	                                 NM_802_11_AP_FLAGS_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_SETTING,
+	                                 NULL);
 }
 
 static void
@@ -468,7 +464,9 @@ test_open_ap_wpa_psk_connection_2 (void)
 	                                 NM_802_11_AP_FLAGS_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 TRUE, NULL);
+	                                 TRUE,
+	                                 NM_CONNECTION_ERROR_INVALID_SETTING,
+	                                 NULL);
 }
 
 static void
@@ -481,7 +479,9 @@ test_open_ap_wpa_psk_connection_3 (void)
 	                                 NM_802_11_AP_FLAGS_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_SETTING,
+	                                 NULL);
 }
 
 static void
@@ -495,7 +495,9 @@ test_open_ap_wpa_psk_connection_4 (void)
 	                                 NM_802_11_AP_FLAGS_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_SETTING,
+	                                 NULL);
 }
 
 static void
@@ -508,7 +510,9 @@ test_open_ap_wpa_psk_connection_5 (void)
 	                                 NM_802_11_AP_FLAGS_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_SETTING,
+	                                 NULL);
 }
 
 /*******************************************/
@@ -520,11 +524,10 @@ test_ap_wpa_eap_connection_base (const char *key_mgmt,
                                  guint32 wpa_flags,
                                  guint32 rsn_flags,
                                  gboolean add_wifi,
-                                 guint error_domain,
                                  guint error_code)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_empty[] = { { NULL } };
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 },
@@ -533,7 +536,7 @@ test_ap_wpa_eap_connection_base (const char *key_mgmt,
 	gboolean success;
 	GError *error = NULL;
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	if (add_wifi)
 		fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
@@ -542,7 +545,7 @@ test_ap_wpa_eap_connection_base (const char *key_mgmt,
 	                               flags, wpa_flags, rsn_flags,
 	                               FALSE, src, &error);
 	/* Failure expected */
-	COMPARE (src, NULL, success, error, error_domain, error_code);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, error_code);
 
 	g_object_unref (src);
 }
@@ -619,37 +622,22 @@ rsn_flags_for_idx (guint32 idx)
 }
 
 static guint32
-error_domain_for_idx (guint32 idx, guint num)
-{
-	if (idx == IDX_OPEN)
-		return NM_SETTING_WIRELESS_SECURITY_ERROR;
-	else if (idx == IDX_PRIV) {
-		if (num <= 3)
-			return NM_SETTING_802_1X_ERROR;
-		else
-			return NM_SETTING_WIRELESS_SECURITY_ERROR;
-	} else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
-	           || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP
-	           || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP)
-		return NM_SETTING_WIRELESS_SECURITY_ERROR;
-	else
-		g_assert_not_reached ();
-}
-
-static guint32
 error_code_for_idx (guint32 idx, guint num)
 {
 	if (idx == IDX_OPEN)
-		return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
+		return NM_CONNECTION_ERROR_INVALID_SETTING;
 	else if (idx == IDX_PRIV) {
 		if (num <= 3)
-			return NM_SETTING_802_1X_ERROR_MISSING_PROPERTY;
+			return NM_CONNECTION_ERROR_MISSING_PROPERTY;
 		else
-			return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
-	} else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
+			return NM_CONNECTION_ERROR_INVALID_PROPERTY;
+	} else if (   idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
 	           || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP
 	           || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP)
-		return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
+		if (num == 4)
+			return NM_CONNECTION_ERROR_INVALID_PROPERTY;
+		else
+			return NM_CONNECTION_ERROR_INVALID_SETTING;
 	else
 		g_assert_not_reached ();
 }
@@ -664,7 +652,6 @@ test_ap_wpa_eap_connection_1 (gconstpointer data)
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
 	                                 FALSE,
-	                                 error_domain_for_idx (idx, 1),
 	                                 error_code_for_idx (idx, 1));
 }
 
@@ -678,7 +665,6 @@ test_ap_wpa_eap_connection_2 (gconstpointer data)
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
 	                                 TRUE,
-	                                 error_domain_for_idx (idx, 2),
 	                                 error_code_for_idx (idx, 2));
 }
 
@@ -692,7 +678,6 @@ test_ap_wpa_eap_connection_3 (gconstpointer data)
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
 	                                 FALSE,
-	                                 error_domain_for_idx (idx, 3),
 	                                 error_code_for_idx (idx, 3));
 }
 
@@ -706,7 +691,6 @@ test_ap_wpa_eap_connection_4 (gconstpointer data)
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
 	                                 FALSE,
-	                                 error_domain_for_idx (idx, 4),
 	                                 error_code_for_idx (idx, 4));
 }
 
@@ -720,7 +704,6 @@ test_ap_wpa_eap_connection_5 (gconstpointer data)
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
 	                                 FALSE,
-	                                 error_domain_for_idx (idx, 5),
 	                                 error_code_for_idx (idx, 5));
 }
 
@@ -730,7 +713,7 @@ static void
 test_priv_ap_empty_connection (void)
 {
 	NMConnection *src, *expected;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *ssid = "blahblah";
 	const KeyData exp_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", 0 },
@@ -742,7 +725,7 @@ test_priv_ap_empty_connection (void)
 	 * connection when completed with an AP with the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	success = complete_connection (ssid, bssid,
 	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
 	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
@@ -765,7 +748,7 @@ test_priv_ap_leap_connection_1 (gconstpointer add_wifi)
 {
 	NMConnection *src, *expected;
 	const char *ssid = "blahblah";
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *leap_username = "Bill Smith";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
@@ -784,7 +767,7 @@ test_priv_ap_leap_connection_1 (gconstpointer add_wifi)
 	 * with an AP with the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	if (add_wifi)
 		fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
@@ -810,7 +793,7 @@ static void
 test_priv_ap_leap_connection_2 (void)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 },
@@ -823,7 +806,7 @@ test_priv_ap_leap_connection_2 (void)
 	 * with an AP with the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	success = complete_connection ("blahblah", bssid,
@@ -832,7 +815,7 @@ test_priv_ap_leap_connection_2 (void)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure here, we need a LEAP username */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY);
 
 	g_object_unref (src);
 }
@@ -844,7 +827,7 @@ test_priv_ap_dynamic_wep_1 (void)
 {
 	NMConnection *src, *expected;
 	const char *ssid = "blahblah";
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
@@ -866,7 +849,7 @@ test_priv_ap_dynamic_wep_1 (void)
 	 * Dynamic WEP connection when completed with an AP with the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	fill_8021x (src, both_8021x);
@@ -892,7 +875,7 @@ test_priv_ap_dynamic_wep_2 (void)
 {
 	NMConnection *src, *expected;
 	const char *ssid = "blahblah";
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
 	    { NULL } };
@@ -913,7 +896,7 @@ test_priv_ap_dynamic_wep_2 (void)
 	 * WEP connection when completed with an AP with the Privacy bit set.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	fill_8021x (src, both_8021x);
@@ -938,7 +921,7 @@ static void
 test_priv_ap_dynamic_wep_3 (void)
 {
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", 0 },
 	    { NULL } };
@@ -954,7 +937,7 @@ test_priv_ap_dynamic_wep_3 (void)
 	 * setting is rejected, as 802.1x is incompatible with 'shared' auth.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	fill_8021x (src, src_8021x);
@@ -964,7 +947,7 @@ test_priv_ap_dynamic_wep_3 (void)
 	                               FALSE,
 	                               src, &error);
 	/* Expect failure; shared is not compatible with dynamic WEP */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
 
 	g_object_unref (src);
 }
@@ -982,7 +965,9 @@ test_priv_ap_wpa_psk_connection_1 (void)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 static void
@@ -996,7 +981,9 @@ test_priv_ap_wpa_psk_connection_2 (void)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 TRUE, NULL);
+	                                 TRUE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 static void
@@ -1011,7 +998,9 @@ test_priv_ap_wpa_psk_connection_3 (void)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 static void
@@ -1026,7 +1015,9 @@ test_priv_ap_wpa_psk_connection_4 (void)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 static void
@@ -1041,7 +1032,9 @@ test_priv_ap_wpa_psk_connection_5 (void)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 NM_802_11_AP_SEC_NONE,
 	                                 NM_802_11_AP_SEC_NONE,
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 /*******************************************/
@@ -1051,7 +1044,7 @@ test_wpa_ap_empty_connection (gconstpointer data)
 {
 	guint idx = GPOINTER_TO_UINT (data);
 	NMConnection *src, *expected;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *ssid = "blahblah";
 	const KeyData exp_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
@@ -1065,7 +1058,7 @@ test_wpa_ap_empty_connection (gconstpointer data)
 	 * or RSN flags.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	success = complete_connection (ssid, bssid,
 	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
 	                               wpa_flags_for_idx (idx),
@@ -1089,7 +1082,7 @@ test_wpa_ap_leap_connection_1 (gconstpointer data)
 	guint idx = GPOINTER_TO_UINT (data);
 	NMConnection *src;
 	const char *ssid = "blahblah";
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const char *leap_username = "Bill Smith";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
@@ -1102,7 +1095,7 @@ test_wpa_ap_leap_connection_1 (gconstpointer data)
 	 * rejected since WPA APs (usually) do not support LEAP.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	success = complete_connection (ssid, bssid,
@@ -1112,7 +1105,7 @@ test_wpa_ap_leap_connection_1 (gconstpointer data)
 	                               FALSE,
 	                               src, &error);
 	/* Expect failure here; WPA APs don't support old-school LEAP */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
 
 	g_object_unref (src);
 }
@@ -1124,7 +1117,7 @@ test_wpa_ap_leap_connection_2 (gconstpointer data)
 {
 	guint idx = GPOINTER_TO_UINT (data);
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 },
@@ -1136,7 +1129,7 @@ test_wpa_ap_leap_connection_2 (gconstpointer data)
 	 * rejected since WPA APs (usually) do not support LEAP.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	success = complete_connection ("blahblah", bssid,
@@ -1146,7 +1139,7 @@ test_wpa_ap_leap_connection_2 (gconstpointer data)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure here, we need a LEAP username */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
 
 	g_object_unref (src);
 }
@@ -1158,7 +1151,7 @@ test_wpa_ap_dynamic_wep_connection (gconstpointer data)
 {
 	guint idx = GPOINTER_TO_UINT (data);
 	NMConnection *src;
-	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *bssid = "01:02:03:04:05:06";
 	const KeyData src_wsec[] = {
 	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
 	    { NULL } };
@@ -1169,7 +1162,7 @@ test_wpa_ap_dynamic_wep_connection (gconstpointer data)
 	 * rejected since WPA APs (usually) do not support Dynamic WEP.
 	 */
 
-	src = nm_connection_new ();
+	src = nm_simple_connection_new ();
 	fill_wifi_empty (src);
 	fill_wsec (src, src_wsec);
 	success = complete_connection ("blahblah", bssid,
@@ -1179,7 +1172,7 @@ test_wpa_ap_dynamic_wep_connection (gconstpointer data)
 	                               FALSE,
 	                               src, &error);
 	/* We expect failure here since Dynamic WEP is incompatible with WPA */
-	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+	COMPARE (src, NULL, success, error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
 
 	g_object_unref (src);
 }
@@ -1196,13 +1189,15 @@ test_wpa_ap_wpa_psk_connection_1 (gconstpointer data)
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
 	    { NULL } };
 
-	expected = nm_connection_new ();
+	expected = nm_simple_connection_new ();
 	fill_wsec (expected, exp_wsec);
 	test_ap_wpa_psk_connection_base (NULL, NULL,
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
-	                                 FALSE, expected);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 expected);
 	g_object_unref (expected);
 }
 
@@ -1216,13 +1211,15 @@ test_wpa_ap_wpa_psk_connection_2 (gconstpointer data)
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
 	    { NULL } };
 
-	expected = nm_connection_new ();
+	expected = nm_simple_connection_new ();
 	fill_wsec (expected, exp_wsec);
 	test_ap_wpa_psk_connection_base (NULL, NULL,
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
-	                                 TRUE, expected);
+	                                 TRUE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 expected);
 	g_object_unref (expected);
 }
 
@@ -1236,13 +1233,15 @@ test_wpa_ap_wpa_psk_connection_3 (gconstpointer data)
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
 	    { NULL } };
 
-	expected = nm_connection_new ();
+	expected = nm_simple_connection_new ();
 	fill_wsec (expected, exp_wsec);
 	test_ap_wpa_psk_connection_base (NULL, "open",
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
-	                                 FALSE, expected);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 expected);
 	g_object_unref (expected);
 }
 
@@ -1254,7 +1253,9 @@ test_wpa_ap_wpa_psk_connection_4 (gconstpointer data)
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
-	                                 FALSE, NULL);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 NULL);
 }
 
 static void
@@ -1267,13 +1268,15 @@ test_wpa_ap_wpa_psk_connection_5 (gconstpointer data)
 	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
 	    { NULL } };
 
-	expected = nm_connection_new ();
+	expected = nm_simple_connection_new ();
 	fill_wsec (expected, exp_wsec);
 	test_ap_wpa_psk_connection_base ("wpa-psk", "open",
 	                                 NM_802_11_AP_FLAGS_PRIVACY,
 	                                 wpa_flags_for_idx (idx),
 	                                 rsn_flags_for_idx (idx),
-	                                 FALSE, expected);
+	                                 FALSE,
+	                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                                 expected);
 	g_object_unref (expected);
 }
 
diff --git a/src/devices/wimax/Makefile.am b/src/devices/wimax/Makefile.am
index def8bf62..555b5ff2 100644
--- a/src/devices/wimax/Makefile.am
+++ b/src/devices/wimax/Makefile.am
@@ -1,20 +1,16 @@
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/platform \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wimax"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(IWMX_SDK_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
+	$(IWMX_SDK_CFLAGS)
 
 pkglib_LTLIBRARIES = libnm-device-plugin-wimax.la
 
diff --git a/src/devices/wimax/Makefile.in b/src/devices/wimax/Makefile.in
index ba16d277..f1ffb09c 100644
--- a/src/devices/wimax/Makefile.in
+++ b/src/devices/wimax/Makefile.in
@@ -215,6 +215,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -338,6 +340,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -357,6 +360,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -383,7 +387,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -465,20 +469,16 @@ with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/platform \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wimax"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
-	$(IWMX_SDK_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(GUDEV_CFLAGS)
+	$(IWMX_SDK_CFLAGS)
 
 pkglib_LTLIBRARIES = libnm-device-plugin-wimax.la
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
diff --git a/src/devices/wimax/exports.ver b/src/devices/wimax/exports.ver
index d2c45124..24cd848c 100644
--- a/src/devices/wimax/exports.ver
+++ b/src/devices/wimax/exports.ver
@@ -1,7 +1,6 @@
 {
 global:
 	nm_device_factory_create;
-	nm_device_factory_get_device_type;
 local:
 	*;
 };
diff --git a/src/devices/wimax/iwmxsdk.c b/src/devices/wimax/iwmxsdk.c
index d1ef7b68..1c2c6821 100644
--- a/src/devices/wimax/iwmxsdk.c
+++ b/src/devices/wimax/iwmxsdk.c
@@ -19,24 +19,22 @@
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "config.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/socket.h>
-#include <linux/if.h>
 
 #include <glib.h>
 
-#include <WiMaxType.h>
+#include "nm-wimax-util.h"
+
 #include <WiMaxAPI.h>
 #include <WiMaxAPIEx.h>
 
-#include "logging/nm-logging.h"
+#include "nm-logging.h"
 #include "iwmxsdk.h"
 
 static WIMAX_API_DEVICE_ID g_api;
diff --git a/src/devices/wimax/iwmxsdk.h b/src/devices/wimax/iwmxsdk.h
index 785ca8c2..c8e76696 100644
--- a/src/devices/wimax/iwmxsdk.h
+++ b/src/devices/wimax/iwmxsdk.h
@@ -19,10 +19,11 @@
  *
  */
 
-#ifndef IWMXSDK_H
-#define IWMXSDK_H
+#ifndef __IWMXSDK_H__
+#define __IWMXSDK_H__
+
+#include "nm-wimax-util.h"
 
-#include <wimax/WiMaxType.h>
 #include <wimax/WiMaxTypesEx.h>
 #include <wimax/WiMaxAPIEx.h>
 
@@ -108,4 +109,4 @@ int iwmx_sdk_get_networks(struct wmxsdk *wmxsdk);
 int iwmx_sdk_api_init(void);
 void iwmx_sdk_api_exit(void);
 
-#endif  /* IWMXSDK_H */
+#endif  /* __IWMXSDK_H__ */
diff --git a/src/devices/wimax/nm-device-wimax.c b/src/devices/wimax/nm-device-wimax.c
index 1bdda489..466cd979 100644
--- a/src/devices/wimax/nm-device-wimax.c
+++ b/src/devices/wimax/nm-device-wimax.c
@@ -19,14 +19,17 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
-#include <net/ethernet.h>
 #include <sys/socket.h>
-#include <linux/if.h>
-#include <netinet/ether.h>
+
+#include <glib/gi18n.h>
+
+#include "nm-wimax-util.h"
 
 #include <WiMaxAPI.h>
 #include <WiMaxAPIEx.h>
@@ -36,8 +39,10 @@
 #include "nm-logging.h"
 #include "nm-device-private.h"
 #include "NetworkManagerUtils.h"
+#include "nm-active-connection.h"
 #include "nm-dbus-manager.h"
 #include "nm-connection.h"
+#include "nm-platform.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wimax.h"
 #include "nm-utils.h"
@@ -110,19 +115,6 @@ typedef struct {
 
 /***********************************************************/
 
-#define NM_WIMAX_ERROR (nm_wimax_error_quark ())
-
-static GQuark
-nm_wimax_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-wimax-error");
-	return quark;
-}
-
-/***********************************************************/
-
 static gboolean
 impl_device_get_nsp_list (NMDeviceWimax *self, GPtrArray **nsps, GError **error)
 {
@@ -316,7 +308,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	NMSettingConnection *s_con;
 	NMSettingWimax *s_wimax;
 	const char *connection_type;
-	const GByteArray *mac;
+	const char *mac;
 
 	if (!NM_DEVICE_CLASS (nm_device_wimax_parent_class)->check_connection_compatible (device, connection))
 		return FALSE;
@@ -333,7 +325,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	mac = nm_setting_wimax_get_mac_address (s_wimax);
-	if (mac && memcmp (mac->data, nm_device_get_hw_address (device, NULL), ETH_ALEN))
+	if (mac && !nm_utils_hwaddr_matches (mac, -1, nm_device_get_hw_address (device), -1))
 		return FALSE;
 
 	return TRUE;
@@ -372,9 +364,8 @@ complete_connection (NMDevice *device,
 	NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
 	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
 	NMSettingWimax *s_wimax;
-	const GByteArray *setting_mac;
-	const guint8 *hw_address;
-	char *format;
+	const char *setting_mac;
+	const char *hw_address;
 	const char *nsp_name = NULL;
 	NMWimaxNsp *nsp = NULL;
 	GSList *iter;
@@ -385,8 +376,8 @@ complete_connection (NMDevice *device,
 		/* If not given a specific object, we need at minimum an NSP name */
 		if (!s_wimax) {
 			g_set_error_literal (error,
-			                     NM_WIMAX_ERROR,
-			                     NM_WIMAX_ERROR_CONNECTION_INVALID,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 			                     "A 'wimax' setting is required if no NSP path was given.");
 			return FALSE;
 		}
@@ -394,8 +385,8 @@ complete_connection (NMDevice *device,
 		nsp_name = nm_setting_wimax_get_network_name (s_wimax);
 		if (!nsp_name || !strlen (nsp_name)) {
 			g_set_error_literal (error,
-			                     NM_WIMAX_ERROR,
-			                     NM_WIMAX_ERROR_CONNECTION_INVALID,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 			                     "A 'wimax' setting with a valid network name is required if no NSP path was given.");
 			return FALSE;
 		}
@@ -422,8 +413,8 @@ complete_connection (NMDevice *device,
 
 		if (!nsp) {
 			g_set_error (error,
-			             NM_WIMAX_ERROR,
-			             NM_WIMAX_ERROR_NSP_NOT_FOUND,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
 			             "The NSP %s was not in the scan list.",
 			             specific_object);
 			return FALSE;
@@ -439,38 +430,31 @@ complete_connection (NMDevice *device,
 	}
 
 	g_assert (nsp_name);
-	format = g_strdup_printf ("%s %%d", nsp_name);
 	nm_utils_complete_generic (connection,
 	                           NM_SETTING_WIMAX_SETTING_NAME,
 	                           existing_connections,
-	                           format,
 	                           nsp_name,
+	                           nsp_name,
+	                           NULL,
 	                           TRUE);
-	g_free (format);
 	g_object_set (G_OBJECT (s_wimax), NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
 
 	setting_mac = nm_setting_wimax_get_mac_address (s_wimax);
-	hw_address = nm_device_get_hw_address (device, NULL);
+	hw_address = nm_device_get_hw_address (device);
 	if (setting_mac) {
 		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		if (memcmp (setting_mac->data, hw_address, ETH_ALEN)) {
-			g_set_error (error,
-				         NM_SETTING_WIMAX_ERROR,
-				         NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY,
-				         NM_SETTING_WIMAX_MAC_ADDRESS);
+		if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("connection does not match device"));
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_MAC_ADDRESS);
 			return FALSE;
 		}
 	} else {
-		GByteArray *mac;
-		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-
 		/* Lock the connection to this device by default */
-		if (memcmp (hw_address, null_mac, ETH_ALEN)) {
-			mac = g_byte_array_sized_new (ETH_ALEN);
-			g_byte_array_append (mac, hw_address, ETH_ALEN);
-			g_object_set (G_OBJECT (s_wimax), NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
-			g_byte_array_free (mac, TRUE);
-		}
+		if (!nm_utils_hwaddr_matches (hw_address, -1, NULL, ETH_ALEN))
+			g_object_set (G_OBJECT (s_wimax), NM_SETTING_WIMAX_MAC_ADDRESS, hw_address, NULL);
 	}
 
 	return TRUE;
@@ -1047,7 +1031,7 @@ set_link_status (NMDeviceWimax *self, WIMAX_API_LINK_STATUS_INFO_EX *link_status
 		conv_rssi = sdk_rssi_to_dbm (link_status->RSSI);
 		conv_cinr = sdk_cinr_to_db (link_status->CINR);
 		conv_tx_pow = sdk_tx_pow_to_dbm (link_status->txPWR);
-		new_bsid = nm_utils_hwaddr_ntoa_len (link_status->bsId, 6);
+		new_bsid = nm_utils_hwaddr_ntoa (link_status->bsId, 6);
 	}
 
 	if (priv->center_freq != center_freq) {
@@ -1363,58 +1347,52 @@ nm_device_wimax_class_init (NMDeviceWimaxClass *klass)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_NSPS,
-		 g_param_spec_boxed (NM_DEVICE_WIMAX_NSPS,
-		                     "Network access points",
-		                     "Network access points",
+		 g_param_spec_boxed (NM_DEVICE_WIMAX_NSPS, "", "",
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     G_PARAM_READABLE));
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
-	g_object_class_install_property (object_class, PROP_ACTIVE_NSP,
-		g_param_spec_boxed (NM_DEVICE_WIMAX_ACTIVE_NSP,
-		                    "Active NSP",
-		                    "Currently active NSP",
-		                    DBUS_TYPE_G_OBJECT_PATH,
-		                    G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_NSP,
+		 g_param_spec_boxed (NM_DEVICE_WIMAX_ACTIVE_NSP, "", "",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CENTER_FREQ,
-		 g_param_spec_uint (NM_DEVICE_WIMAX_CENTER_FREQUENCY,
-		                    "Center frequency",
-		                    "Center frequency",
+		 g_param_spec_uint (NM_DEVICE_WIMAX_CENTER_FREQUENCY, "", "",
 		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READABLE));
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_RSSI,
-		 g_param_spec_int (NM_DEVICE_WIMAX_RSSI,
-		                   "RSSI",
-		                   "RSSI",
+		 g_param_spec_int (NM_DEVICE_WIMAX_RSSI, "", "",
 		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READABLE));
+		                   G_PARAM_READABLE |
+		                   G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CINR,
-		 g_param_spec_int (NM_DEVICE_WIMAX_CINR,
-		                   "CINR",
-		                   "CINR",
+		 g_param_spec_int (NM_DEVICE_WIMAX_CINR, "", "",
 		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READABLE));
+		                   G_PARAM_READABLE |
+		                   G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_TX_POWER,
-		 g_param_spec_int (NM_DEVICE_WIMAX_TX_POWER,
-		                   "TX Power",
-		                   "TX Power",
+		 g_param_spec_int (NM_DEVICE_WIMAX_TX_POWER, "", "",
 		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READABLE));
+		                   G_PARAM_READABLE |
+		                   G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_BSID,
-		 g_param_spec_string (NM_DEVICE_WIMAX_BSID,
-		                      "BSID",
-		                      "BSID",
+		 g_param_spec_string (NM_DEVICE_WIMAX_BSID, "", "",
 		                      NULL,
-		                      G_PARAM_READABLE));
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	/* Signals */
 	signals[NSP_ADDED] =
@@ -1440,6 +1418,4 @@ nm_device_wimax_class_init (NMDeviceWimaxClass *klass)
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
 	                                        &dbus_glib_nm_device_wimax_object_info);
-
-	dbus_g_error_domain_register (NM_WIMAX_ERROR, NULL, NM_TYPE_WIMAX_ERROR);
 }
diff --git a/src/devices/wimax/nm-device-wimax.h b/src/devices/wimax/nm-device-wimax.h
index 8b252ee1..30b2481f 100644
--- a/src/devices/wimax/nm-device-wimax.h
+++ b/src/devices/wimax/nm-device-wimax.h
@@ -19,10 +19,9 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#ifndef NM_DEVICE_WIMAX_H
-#define NM_DEVICE_WIMAX_H
+#ifndef __NETWORKMANAGER_DEVICE_WIMAX_H__
+#define __NETWORKMANAGER_DEVICE_WIMAX_H__
 
-#include <net/ethernet.h>
 #include "nm-device.h"
 #include "nm-wimax-nsp.h"
 
@@ -35,14 +34,6 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIMAX))
 #define NM_DEVICE_WIMAX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass))
 
-typedef enum
-{
-	NM_WIMAX_ERROR_CONNECTION_NOT_WIMAX = 0, /*< nick=ConnectionNotWimax >*/
-	NM_WIMAX_ERROR_CONNECTION_INVALID,       /*< nick=ConnectionInvalid >*/
-	NM_WIMAX_ERROR_CONNECTION_INCOMPATIBLE,  /*< nick=ConnectionIncompatible >*/
-	NM_WIMAX_ERROR_NSP_NOT_FOUND,            /*< nick=NspNotFound >*/
-} NMWimaxError;
-
 #define NM_DEVICE_WIMAX_NSPS             "nsps"
 #define NM_DEVICE_WIMAX_ACTIVE_NSP       "active-nsp"
 #define NM_DEVICE_WIMAX_CENTER_FREQUENCY "center-frequency"
diff --git a/src/devices/wimax/nm-wimax-factory.c b/src/devices/wimax/nm-wimax-factory.c
index ca962e68..39e9a287 100644
--- a/src/devices/wimax/nm-wimax-factory.c
+++ b/src/devices/wimax/nm-wimax-factory.c
@@ -18,10 +18,13 @@
  * Copyright (C) 2011 - 2014 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <gmodule.h>
 
 #include "nm-device-factory.h"
 #include "nm-device-wimax.h"
+#include "nm-platform.h"
 
 #define NM_TYPE_WIMAX_FACTORY            (nm_wimax_factory_get_type ())
 #define NM_WIMAX_FACTORY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_WIMAX_FACTORY, NMWimaxFactory))
@@ -43,20 +46,12 @@ G_DEFINE_TYPE_EXTENDED (NMWimaxFactory, nm_wimax_factory, G_TYPE_OBJECT, 0,
 
 /**************************************************************************/
 
-#define PLUGIN_TYPE NM_DEVICE_TYPE_WIMAX
-
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_WIMAX_FACTORY, NULL);
 }
 
-G_MODULE_EXPORT NMDeviceType
-nm_device_factory_get_device_type (void)
-{
-	return PLUGIN_TYPE;
-}
-
 /**************************************************************************/
 
 static NMDevice *
@@ -71,10 +66,17 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
 	return (NMDevice *) nm_device_wimax_new (plink);
 }
 
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_WIMAX;
+}
+
 static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
 	factory_iface->new_link = new_link;
+	factory_iface->get_device_type = get_device_type;
 }
 
 static void
diff --git a/src/devices/wimax/nm-wimax-nsp.c b/src/devices/wimax/nm-wimax-nsp.c
index efe3b058..f4db63f0 100644
--- a/src/devices/wimax/nm-wimax-nsp.c
+++ b/src/devices/wimax/nm-wimax-nsp.c
@@ -19,8 +19,10 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
+#include "config.h"
+
 #include "nm-wimax-nsp.h"
-#include "NetworkManager.h"
+#include "nm-dbus-interface.h"
 #include "nm-dbus-manager.h"
 #include "nm-setting-wimax.h"
 #include "nm-wimax-nsp-glue.h"
@@ -210,31 +212,28 @@ nm_wimax_nsp_class_init (NMWimaxNspClass *klass)
 
 	g_object_class_install_property
 		(object_class, PROP_NAME,
-		 g_param_spec_string (NM_WIMAX_NSP_NAME,
-							  "Name",
-							  "Name",
-							  NULL,
-							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_string (NM_WIMAX_NSP_NAME, "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SIGNAL_QUALITY,
-		 g_param_spec_uint (NM_WIMAX_NSP_SIGNAL_QUALITY,
-							"SignalQuality",
-							"SignalQuality",
-							0,
-							100,
-							0,
-							G_PARAM_READWRITE));
+		 g_param_spec_uint (NM_WIMAX_NSP_SIGNAL_QUALITY, "", "",
+		                    0,
+		                    100,
+		                    0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_NETWORK_TYPE,
-		 g_param_spec_uint (NM_WIMAX_NSP_NETWORK_TYPE,
-		                    "NetworkType",
-		                    "NetworkType",
+		 g_param_spec_uint (NM_WIMAX_NSP_NETWORK_TYPE, "", "",
 		                    NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
 		                    NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER,
 		                    NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE));
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (klass),
diff --git a/src/devices/wimax/nm-wimax-nsp.h b/src/devices/wimax/nm-wimax-nsp.h
index a74b68a7..8d0a6a14 100644
--- a/src/devices/wimax/nm-wimax-nsp.h
+++ b/src/devices/wimax/nm-wimax-nsp.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#ifndef NM_WIMAX_NSP_H
-#define NM_WIMAX_NSP_H
+#ifndef __NETWORKMANAGER_WIMAX_NSP_H__
+#define __NETWORKMANAGER_WIMAX_NSP_H__
 
 #include <glib-object.h>
 #include "nm-wimax-types.h"
diff --git a/src/devices/wimax/nm-wimax-types.h b/src/devices/wimax/nm-wimax-types.h
index 8c807fd8..207d2477 100644
--- a/src/devices/wimax/nm-wimax-types.h
+++ b/src/devices/wimax/nm-wimax-types.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#ifndef NM_WIMAX_TYPES_H
-#define NM_WIMAX_TYPES_H
+#ifndef __NETWORKMANAGER_WIMAX_TYPES_H__
+#define __NETWORKMANAGER_WIMAX_TYPES_H__
 
 typedef enum {
 	NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
diff --git a/src/devices/wimax/nm-wimax-util.c b/src/devices/wimax/nm-wimax-util.c
index bca25a1d..0ae285ae 100644
--- a/src/devices/wimax/nm-wimax-util.c
+++ b/src/devices/wimax/nm-wimax-util.c
@@ -18,7 +18,8 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#include <WiMaxAPI.h>
+#include "config.h"
+
 #include "nm-wimax-util.h"
 #include "nm-utils.h"
 #include "iwmxsdk.h"
diff --git a/src/devices/wimax/nm-wimax-util.h b/src/devices/wimax/nm-wimax-util.h
index 71f5aa29..a1852d55 100644
--- a/src/devices/wimax/nm-wimax-util.h
+++ b/src/devices/wimax/nm-wimax-util.h
@@ -18,12 +18,17 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#ifndef NM_WIMAX_UTIL_H
-#define NM_WIMAX_UTIL_H
+#ifndef __NETWORKMANAGER_WIMAX_UTIL_H__
+#define __NETWORKMANAGER_WIMAX_UTIL_H__
 
 #include <glib.h>
 
+#include "nm-utils-internal.h"
+
+NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes")
 #include <WiMaxType.h>
+NM_PRAGMA_WARNING_REENABLE
+
 #include <WiMaxError.h>
 #include "nm-wimax-types.h"
 
diff --git a/src/devices/wwan/Makefile.am b/src/devices/wwan/Makefile.am
index 988f4679..7e5b190b 100644
--- a/src/devices/wwan/Makefile.am
+++ b/src/devices/wwan/Makefile.am
@@ -5,18 +5,16 @@ include $(GLIB_MAKEFILE)
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
 BUILT_SOURCES = $(null)
@@ -33,9 +31,8 @@ nm_modem_enum_types_sources = $(srcdir)/nm-modem.h
 BUILT_SOURCES += $(GLIB_GENERATED)
 
 libnm_wwan_la_SOURCES = \
-	nm-modem-old.c \
-	nm-modem-old.h \
-	nm-modem-old-types.h \
+	nm-modem-broadband.c \
+	nm-modem-broadband.h \
 	nm-modem-manager.c \
 	nm-modem-manager.h \
 	nm-modem.c \
@@ -43,12 +40,6 @@ libnm_wwan_la_SOURCES = \
 	\
 	$(GLIB_GENERATED)
 
-if WITH_MODEM_MANAGER_1
-libnm_wwan_la_SOURCES += \
-	nm-modem-broadband.c \
-	nm-modem-broadband.h
-endif
-
 WWAN_SYMBOL_VIS_FILE=$(srcdir)/wwan-exports.ver
 
 libnm_wwan_la_LDFLAGS = \
diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in
index ae33d1c8..18cbfdd3 100644
--- a/src/devices/wwan/Makefile.in
+++ b/src/devices/wwan/Makefile.in
@@ -78,13 +78,9 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-@WITH_MODEM_MANAGER_1_TRUE@am__append_1 = \
-@WITH_MODEM_MANAGER_1_TRUE@	nm-modem-broadband.c \
-@WITH_MODEM_MANAGER_1_TRUE@	nm-modem-broadband.h
-
 subdir = src/devices/wwan
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp README
+	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -151,15 +147,9 @@ libnm_device_plugin_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 	$(LDFLAGS) -o $@
 libnm_wwan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1)
-am__libnm_wwan_la_SOURCES_DIST = nm-modem-old.c nm-modem-old.h \
-	nm-modem-old-types.h nm-modem-manager.c nm-modem-manager.h \
-	nm-modem.c nm-modem.h nm-modem-enum-types.h \
-	nm-modem-enum-types.c nm-modem-broadband.c \
-	nm-modem-broadband.h
 am__objects_1 = nm-modem-enum-types.lo
-@WITH_MODEM_MANAGER_1_TRUE@am__objects_2 = nm-modem-broadband.lo
-am_libnm_wwan_la_OBJECTS = nm-modem-old.lo nm-modem-manager.lo \
-	nm-modem.lo $(am__objects_1) $(am__objects_2)
+am_libnm_wwan_la_OBJECTS = nm-modem-broadband.lo nm-modem-manager.lo \
+	nm-modem.lo $(am__objects_1)
 libnm_wwan_la_OBJECTS = $(am_libnm_wwan_la_OBJECTS)
 libnm_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -201,7 +191,7 @@ am__v_CCLD_1 =
 SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \
 	$(libnm_wwan_la_SOURCES)
 DIST_SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \
-	$(am__libnm_wwan_la_SOURCES_DIST)
+	$(libnm_wwan_la_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -236,6 +226,8 @@ AUTOCONF = @AUTOCONF@
 AUTOHEADER = @AUTOHEADER@
 AUTOMAKE = @AUTOMAKE@
 AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
@@ -359,6 +351,7 @@ NEWT_CFLAGS = @NEWT_CFLAGS@
 NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -378,6 +371,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -404,7 +398,7 @@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
+UDEV_DIR = @UDEV_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
@@ -486,18 +480,16 @@ with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
-	-I${top_srcdir}/src/logging \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_builddir}/include \
 	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
+	-I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
+	-DNETWORKMANAGER_COMPILATION \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(DBUS_CFLAGS) \
-	$(POLKIT_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
 BUILT_SOURCES = $(null) $(GLIB_GENERATED) nm-device-modem-glue.h
@@ -508,9 +500,16 @@ GLIB_GENERATED = nm-modem-enum-types.h nm-modem-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
 GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
 nm_modem_enum_types_sources = $(srcdir)/nm-modem.h
-libnm_wwan_la_SOURCES = nm-modem-old.c nm-modem-old.h \
-	nm-modem-old-types.h nm-modem-manager.c nm-modem-manager.h \
-	nm-modem.c nm-modem.h $(GLIB_GENERATED) $(am__append_1)
+libnm_wwan_la_SOURCES = \
+	nm-modem-broadband.c \
+	nm-modem-broadband.h \
+	nm-modem-manager.c \
+	nm-modem-manager.h \
+	nm-modem.c \
+	nm-modem.h \
+	\
+	$(GLIB_GENERATED)
+
 WWAN_SYMBOL_VIS_FILE = $(srcdir)/wwan-exports.ver
 libnm_wwan_la_LDFLAGS = \
 	-avoid-version \
@@ -624,7 +623,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-broadband.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-enum-types.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-manager.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-old.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wwan-factory.Plo@am__quote@
 
diff --git a/src/devices/wwan/README b/src/devices/wwan/README
deleted file mode 100644
index 4661c042..00000000
--- a/src/devices/wwan/README
+++ /dev/null
@@ -1,45 +0,0 @@
-
-ModemManager integration is organized as follows:
-
-
-Common source
-********************************************************************************
-
- * nm-modem.[h|c]:
-      Defines the basic `NMModem' object. The core NetworkManager implementation
-      will use this interface exclusively, regardless of the real final type of
-      the modem object.
-
- * nm-modem-manager.[h|c]:
-      Defines the `NMModemManager' object, which takes care of listening to
-      signals from the DBus interface notifying about added or removed modems.
-      It also takes care of creating proper `NMModem' objects from the
-      information retrieved from the DBus interface.
-
-
-ModemManager 0.7 integration
-********************************************************************************
-
- * nm-modem-broadband.[h|c]:
-      Defines the `NMModemBroadband' object, which is a subclass of `NMModem'.
-      This object handles both 3GPP and 3GPP2 modems exposed in the new
-      `ModemManager1' interface.
-
-
-ModemManager 0.4/0.5/0.6 integration
-********************************************************************************
-
- * nm-modem-old-types.h:
-      Defines helper types to use with the (old) ModemManager DBus API.
-
- * nm-modem-old.[h|c]:
-      Defines the `NMModemGeneric' object. All modem objects based on the old
-      ModemManager interface are subclasses of this one.
-
- * nm-modem-gsm.[h|c]:
-      Defines the `NMModemGsm' object, which is a subclass of `NMModemGeneric'.
-      This object handles 3GPP-specific (GSM, UMTS, HSPA, LTE) modems.
-
- * nm-modem-cdma.[h|c]:
-      Defines the `NMModemCdma' object, which is a subclass of `NMModemGeneric'.
-      This object handles 3GPP2-specific modems (CDMA, EV-DO).
diff --git a/src/devices/wwan/exports.ver b/src/devices/wwan/exports.ver
index d2c45124..24cd848c 100644
--- a/src/devices/wwan/exports.ver
+++ b/src/devices/wwan/exports.ver
@@ -1,7 +1,6 @@
 {
 global:
 	nm_device_factory_create;
-	nm_device_factory_get_device_type;
 local:
 	*;
 };
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 678f7795..f681e59c 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -20,20 +20,22 @@
 
 #include "config.h"
 
+#include <string.h>
 #include <glib.h>
 
 #include "nm-device-modem.h"
 #include "nm-modem.h"
-#include "nm-modem-old.h"
 #include "nm-device-private.h"
 #include "nm-rfkill-manager.h"
 #include "nm-logging.h"
 #include "nm-dbus-manager.h"
 #include "nm-settings-connection.h"
-
-#if WITH_MODEM_MANAGER_1
 #include "nm-modem-broadband.h"
-#endif
+#include "NetworkManagerUtils.h"
+#include "nm-core-internal.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceModem);
 
 G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
 
@@ -91,7 +93,8 @@ modem_prepare_result (NMModem *modem,
                       NMDeviceStateReason reason,
                       gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState state;
 
 	state = nm_device_get_state (device);
@@ -106,8 +109,7 @@ modem_prepare_result (NMModem *modem,
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
 			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL);
-			nm_log_info (LOGD_MB, "(%s): disabling autoconnect due to failed SIM PIN",
-			             nm_device_get_iface (device));
+			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
 		}
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
@@ -147,33 +149,110 @@ modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
 }
 
 static void
-modem_ip4_config_result (NMModem *self,
+modem_ip4_config_result (NMModem *modem,
                          NMIP4Config *config,
                          GError *error,
                          gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDevice *device = NM_DEVICE (self);
 
 	g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
 
 	if (error) {
-		nm_log_warn (LOGD_MB | LOGD_IP4, "retrieving IP4 configuration failed: (%d) %s",
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: (%d) %s",
+		       error->code, error->message ? error->message : "(unknown)");
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	} else
-		nm_device_activate_schedule_ip4_config_result (device, config);
+	} else {
+		nm_device_set_wwan_ip4_config (device, config);
+		nm_device_activate_schedule_ip4_config_result (device, NULL);
+	}
+}
+
+static void
+modem_ip6_config_result (NMModem *modem,
+                         NMIP6Config *config,
+                         gboolean do_slaac,
+                         GError *error,
+                         gpointer user_data)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDevice *device = NM_DEVICE (self);
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMIP6Config *ignored = NULL;
+	gboolean got_config = !!config;
+
+	g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE);
+
+	if (error) {
+		_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: (%d) %s",
+		       error->code, error->message ? error->message : "(unknown)");
+
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		return;
+	}
+
+	/* Re-enable IPv6 on the interface */
+	nm_device_ipv6_sysctl_set (device, "disable_ipv6", "0");
+
+	if (config)
+		nm_device_set_wwan_ip6_config (device, config);
+
+	if (do_slaac == FALSE) {
+		if (got_config)
+			nm_device_activate_schedule_ip6_config_result (device);
+		else {
+			_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses");
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		}
+		return;
+	}
+
+	/* Start SLAAC now that we have a link-local address from the modem */
+	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &reason);
+	g_assert (ignored == NULL);
+	switch (ret) {
+	case NM_ACT_STAGE_RETURN_FAILURE:
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	case NM_ACT_STAGE_RETURN_STOP:
+		/* all done */
+		nm_device_activate_schedule_ip6_config_result (device);
+		break;
+	case NM_ACT_STAGE_RETURN_POSTPONE:
+		/* let SLAAC run */
+		break;
+	default:
+		/* Should never get here since we've assured that the IPv6 method
+		 * will either be "auto" or "ignored" when starting IPv6 configuration.
+		 */
+		g_assert_not_reached ();
+	}
 }
 
 static void
 data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
+	const char *old = nm_device_get_ip_iface (self);
+	const char *new = nm_modem_get_data_port (modem);
+	gboolean changed = FALSE;
+
+	if (new && g_strcmp0 (new, old))
+		changed = TRUE;
 
 	/* We set the IP iface in the device as soon as we know it, so that we
 	 * properly ifup it if needed */
-	nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
+	nm_device_set_ip_iface (self, new);
+
+	/* Disable IPv6 immediately on the interface since NM handles IPv6
+	 * internally, and leaving it enabled could allow the kernel's IPv6
+	 * RA handling code to run before NM is ready.
+	 */
+	if (changed)
+		nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 }
 
 static void
@@ -258,6 +337,7 @@ device_state_changed (NMDevice *device,
                       NMDeviceState old_state,
                       NMDeviceStateReason reason)
 {
+	NMDeviceModem *self = NM_DEVICE_MODEM (device);
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 	NMConnection *connection = nm_device_get_connection (device);
 
@@ -266,9 +346,8 @@ device_state_changed (NMDevice *device,
 	if (new_state == NM_DEVICE_STATE_UNAVAILABLE &&
 	    old_state < NM_DEVICE_STATE_UNAVAILABLE) {
 		/* Log initial modem state */
-		nm_log_info (LOGD_MB, "(%s): modem state '%s'",
-		             nm_device_get_iface (device),
-		             nm_modem_state_to_string (nm_modem_get_state (priv->modem)));
+		_LOGI (LOGD_MB, "modem state '%s'",
+		       nm_modem_state_to_string (nm_modem_get_state (priv->modem)));
 	}
 
 	nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
@@ -294,10 +373,10 @@ device_state_changed (NMDevice *device,
 	}
 }
 
-static guint
-get_hw_address_length (NMDevice *device, gboolean *out_permanent)
+static guint32
+get_generic_capabilities (NMDevice *device)
 {
-	return 0;
+	return NM_DEVICE_CAP_IS_NON_KERNEL;
 }
 
 static gboolean
@@ -404,11 +483,24 @@ act_stage3_ip6_config_start (NMDevice *device,
                              NMDeviceStateReason *reason)
 {
 	return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
-	                                         device,
-	                                         NM_DEVICE_CLASS (nm_device_modem_parent_class),
+	                                         nm_device_get_act_request (device),
 	                                         reason);
 }
 
+static gboolean
+get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid)
+{
+	NMDeviceModem *self = NM_DEVICE_MODEM (device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
+	gboolean success;
+
+	g_return_val_if_fail (priv->modem, FALSE);
+	success = nm_modem_get_iid (priv->modem, out_iid);
+	if (!success)
+		success = NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_ip_iface_identifier (device, out_iid);
+	return success;
+}
+
 /*****************************************************************************/
 
 static gboolean
@@ -444,23 +536,22 @@ set_enabled (NMDevice *device, gboolean enabled)
 }
 
 static gboolean
-is_available (NMDevice *dev)
+is_available (NMDevice *device)
 {
-	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (dev);
+	NMDeviceModem *self = NM_DEVICE_MODEM (device);
+	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
 	NMModemState modem_state;
 
 	if (!priv->rf_enabled) {
-		nm_log_dbg (LOGD_MB, "(%s): not available because WWAN airplane mode is on",
-		            nm_device_get_iface (dev));
+		_LOGD (LOGD_MB, "not available because WWAN airplane mode is on");
 		return FALSE;
 	}
 
 	g_assert (priv->modem);
 	modem_state = nm_modem_get_state (priv->modem);
 	if (modem_state <= NM_MODEM_STATE_INITIALIZING) {
-		nm_log_dbg (LOGD_MB, "(%s): not available because modem is not ready (%s)",
-		            nm_device_get_iface (dev),
-		            nm_modem_state_to_string (modem_state));
+		_LOGD (LOGD_MB, "not available because modem is not ready (%s)",
+		       nm_modem_state_to_string (modem_state));
 		return FALSE;
 	}
 
@@ -496,8 +587,10 @@ nm_device_modem_new (NMModem *modem)
 
 	/* If the data port is known, set it as the IP interface immediately */
 	data_port = nm_modem_get_data_port (modem);
-	if (data_port)
+	if (data_port) {
 		nm_device_set_ip_iface (device, data_port);
+		nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+	}
 
 	return device;
 }
@@ -508,6 +601,18 @@ nm_device_modem_init (NMDeviceModem *self)
 }
 
 static void
+constructed (GObject *object)
+{
+	G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object);
+
+	/* DHCP is always done by the modem firmware, not by the network, and
+	 * by the time we get around to DHCP the firmware should already know
+	 * the IP addressing details.  So the DHCP timeout can be much shorter.
+	 */
+	nm_device_set_dhcp_timeout (NM_DEVICE (object), 15);
+}
+
+static void
 set_modem (NMDeviceModem *self, NMModem *modem)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
@@ -519,6 +624,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
 	g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
 	g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
+	g_signal_connect (modem, NM_MODEM_IP6_CONFIG_RESULT, G_CALLBACK (modem_ip6_config_result), self);
 	g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self);
 	g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
@@ -599,8 +705,9 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
+	object_class->constructed = constructed;
 
-	device_class->get_hw_address_length = get_hw_address_length;
+	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->check_connection_available = check_connection_available;
 	device_class->complete_connection = complete_connection;
@@ -614,31 +721,31 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	device_class->set_enabled = set_enabled;
 	device_class->owns_iface = owns_iface;
 	device_class->is_available = is_available;
+	device_class->get_ip_iface_identifier = get_ip_iface_identifier;
 
 	device_class->state_changed = device_state_changed;
 
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_MODEM,
-		 g_param_spec_object (NM_DEVICE_MODEM_MODEM,
-		                      "Modem",
-		                      "Modem",
+		 g_param_spec_object (NM_DEVICE_MODEM_MODEM, "", "",
 		                      NM_TYPE_MODEM,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (object_class, PROP_CAPABILITIES,
-		g_param_spec_uint (NM_DEVICE_MODEM_CAPABILITIES,
-		                   "Modem Capabilities",
-		                   "Modem Capabilities",
-		                   0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
-		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
-	g_object_class_install_property (object_class, PROP_CURRENT_CAPABILITIES,
-		g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES,
-		                   "Current modem Capabilities",
-		                   "Current modem Capabilities",
-		                   0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
-		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_MODEM_CAPABILITIES, "", "",
+		                    0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_CURRENT_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES, "", "",
+		                    0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
+		                    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 (mclass),
diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h
index ee6b2988..996195c4 100644
--- a/src/devices/wwan/nm-device-modem.h
+++ b/src/devices/wwan/nm-device-modem.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
-#ifndef NM_DEVICE_MODEM_H
-#define NM_DEVICE_MODEM_H
+#ifndef __NETWORKMANAGER_DEVICE_MODEM_H__
+#define __NETWORKMANAGER_DEVICE_MODEM_H__
 
 #include <glib.h>
 #include <glib-object.h>
@@ -51,4 +51,4 @@ GType nm_device_modem_get_type (void);
 
 NMDevice *nm_device_modem_new (NMModem *modem);
 
-#endif /* NM_DEVICE_MODEM_H */
+#endif /* __NETWORKMANAGER_DEVICE_MODEM_H__ */
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 807b8924..f8239b79 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -18,14 +18,19 @@
  * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
  */
 
+#include "config.h"
+
 #include <glib/gi18n.h>
 #include <string.h>
+#include <arpa/inet.h>
 #include <libmm-glib.h>
+
 #include "nm-modem-broadband.h"
-#include "nm-setting-connection.h"
+#include "nm-core-internal.h"
 #include "nm-logging.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
+#include "nm-platform.h"
 
 G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
 
@@ -156,13 +161,29 @@ ask_for_pin (NMModemBroadband *self)
 	                      NM_SETTING_GSM_PIN);
 }
 
+static NMModemIPMethod
+get_bearer_ip_method (MMBearerIpConfig *config)
+{
+	MMBearerIpMethod mm_method;
+
+	mm_method = mm_bearer_ip_config_get_method (config);
+	if (mm_method == MM_BEARER_IP_METHOD_PPP)
+		return NM_MODEM_IP_METHOD_PPP;
+	else if (mm_method == MM_BEARER_IP_METHOD_STATIC)
+		return NM_MODEM_IP_METHOD_STATIC;
+	else if (mm_method == MM_BEARER_IP_METHOD_DHCP)
+		return NM_MODEM_IP_METHOD_AUTO;
+	return NM_MODEM_IP_METHOD_UNKNOWN;
+}
+
 static void
 connect_ready (MMModemSimple *simple_iface,
                GAsyncResult *res,
                NMModemBroadband *self)
 {
 	GError *error = NULL;
-	guint ip_method;
+	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
+	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
 
 	g_clear_object (&self->priv->connect_properties);
 
@@ -195,35 +216,31 @@ connect_ready (MMModemSimple *simple_iface,
 
 	/* Grab IP configurations */
 	self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer);
-	self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
+	if (self->priv->ipv4_config)
+		ip4_method = get_bearer_ip_method (self->priv->ipv4_config);
 
-	switch (mm_bearer_ip_config_get_method (self->priv->ipv4_config)) {
-	case MM_BEARER_IP_METHOD_PPP:
-		ip_method = MM_MODEM_IP_METHOD_PPP;
-		break;
-	case MM_BEARER_IP_METHOD_STATIC:
-		ip_method = MM_MODEM_IP_METHOD_STATIC;
-		break;
-	case MM_BEARER_IP_METHOD_DHCP:
-		ip_method = MM_MODEM_IP_METHOD_DHCP;
-		break;
-	default:
-		error = g_error_new (NM_MODEM_ERROR,
-		                     NM_MODEM_ERROR_CONNECTION_INVALID,
-		                     "invalid IP config");
-		nm_log_warn (LOGD_MB, "(%s) failed to connect modem: %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             error->message);
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
+	self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
+	if (self->priv->ipv6_config)
+		ip6_method = get_bearer_ip_method (self->priv->ipv6_config);
+
+	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
+	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
+		nm_log_warn (LOGD_MB, "(%s) failed to connect modem: invalid bearer IP configuration",
+		             nm_modem_get_uid (NM_MODEM (self)));
+
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                             "invalid bearer IP configuration");
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, error);
 		g_error_free (error);
 		g_object_unref (self);
 		return;
 	}
 
-	/* IPv4 for now only */
 	g_object_set (self,
 	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (self->priv->bearer),
-	              NM_MODEM_IP_METHOD,  ip_method,
+	              NM_MODEM_IP4_METHOD, ip4_method,
+	              NM_MODEM_IP6_METHOD, ip6_method,
 	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer),
 	              NULL);
 
@@ -249,12 +266,15 @@ create_cdma_connect_properties (NMConnection *connection)
 }
 
 static MMSimpleConnectProperties *
-create_gsm_connect_properties (NMConnection *connection)
+create_gsm_connect_properties (NMModem *modem,
+                               NMConnection *connection,
+                               GError **error)
 {
 	NMSettingGsm *setting;
-	NMSettingPPP *s_ppp;
+	NMSettingPpp *s_ppp;
 	MMSimpleConnectProperties *properties;
 	const gchar *str;
+	NMModemIPType ip_type;
 
 	setting = nm_connection_get_setting_gsm (connection);
 	properties = mm_simple_connect_properties_new ();
@@ -264,9 +284,9 @@ create_gsm_connect_properties (NMConnection *connection)
 	if (str)
 		mm_simple_connect_properties_set_number (properties, str);
 
+	/* Blank APN ("") means the default subscription APN */
 	str = nm_setting_gsm_get_apn (setting);
-	if (str)
-		mm_simple_connect_properties_set_apn (properties, str);
+	mm_simple_connect_properties_set_apn (properties, str ? str : "");
 
 	str = nm_setting_gsm_get_network_id (setting);
 	if (str)
@@ -309,6 +329,22 @@ create_gsm_connect_properties (NMConnection *connection)
 		mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth);
 	}
 
+	/* Determine IP types to use when connecting */
+	ip_type = nm_modem_get_connection_ip_type (modem, connection, error);
+	if (ip_type == NM_MODEM_IP_TYPE_UNKNOWN) {
+		g_object_unref (properties);
+		return NULL;
+	}
+
+	if (ip_type == NM_MODEM_IP_TYPE_IPV4)
+		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4);
+	else if (ip_type == NM_MODEM_IP_TYPE_IPV6)
+		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV6);
+	else if (ip_type == NM_MODEM_IP_TYPE_IPV4V6)
+		mm_simple_connect_properties_set_ip_type (properties, MM_BEARER_IP_FAMILY_IPV4V6);
+	else
+		g_assert_not_reached ();
+
 	return properties;
 }
 
@@ -319,17 +355,29 @@ act_stage1_prepare (NMModem *_self,
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 	MMModemCapability caps;
+	GError *error = NULL;
 
 	g_clear_object (&self->priv->connect_properties);
 
 	caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
 	if (MODEM_CAPS_3GPP (caps))
-		self->priv->connect_properties = create_gsm_connect_properties (connection);
+		self->priv->connect_properties = create_gsm_connect_properties (_self, connection, &error);
 	else if (MODEM_CAPS_3GPP2 (caps))
 		self->priv->connect_properties = create_cdma_connect_properties (connection);
 	else {
 		nm_log_warn (LOGD_MB, "(%s) not a mobile broadband modem",
 					 nm_modem_get_uid (NM_MODEM (self)));
+		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	if (error) {
+		nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
+		             nm_modem_get_uid (NM_MODEM (self)),
+		             nm_connection_get_id (connection),
+		             error->message);
+		g_clear_error (&error);
+		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
@@ -400,14 +448,14 @@ complete_connection (NMModem *_self,
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 	MMModemCapability modem_caps;
-	NMSettingPPP *s_ppp;
+	NMSettingPpp *s_ppp;
 
 	modem_caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
 
 	/* PPP settings common to 3GPP and 3GPP2 */
 	s_ppp = nm_connection_get_setting_ppp (connection);
 	if (!s_ppp) {
-		s_ppp = (NMSettingPPP *) nm_setting_ppp_new ();
+		s_ppp = (NMSettingPpp *) nm_setting_ppp_new ();
 		g_object_set (G_OBJECT (s_ppp),
 		              NM_SETTING_PPP_LCP_ECHO_FAILURE, 5,
 		              NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30,
@@ -419,12 +467,13 @@ complete_connection (NMModem *_self,
 		NMSettingGsm *s_gsm;
 
 		s_gsm = nm_connection_get_setting_gsm (connection);
-		if (!s_gsm || !nm_setting_gsm_get_apn (s_gsm)) {
-			/* Need an APN at least */
+		if (!s_gsm) {
+			/* Need a GSM setting at least */
 			g_set_error_literal (error,
-			                     NM_SETTING_GSM_ERROR,
-			                     NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
-			                     NM_SETTING_GSM_APN);
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_MISSING_SETTING,
+			                     _("GSM mobile broadband connection requires a 'gsm' setting"));
+			g_prefix_error (error, "%s: ", NM_SETTING_GSM_SETTING_NAME);
 			return FALSE;
 		}
 
@@ -435,7 +484,8 @@ complete_connection (NMModem *_self,
 		nm_utils_complete_generic (connection,
 		                           NM_SETTING_GSM_SETTING_NAME,
 		                           existing_connections,
-		                           _("GSM connection %d"),
+		                           NULL,
+		                           _("GSM connection"),
 		                           NULL,
 		                           FALSE); /* No IPv6 yet by default */
 
@@ -457,7 +507,8 @@ complete_connection (NMModem *_self,
 		nm_utils_complete_generic (connection,
 		                           NM_SETTING_CDMA_SETTING_NAME,
 		                           existing_connections,
-		                           _("CDMA connection %d"),
+		                           NULL,
+		                           _("CDMA connection"),
 		                           NULL,
 		                           FALSE); /* No IPv6 yet by default */
 
@@ -465,8 +516,8 @@ complete_connection (NMModem *_self,
 	}
 
 	g_set_error (error,
-	             NM_MODEM_ERROR,
-	             NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
+	             NM_DEVICE_ERROR,
+	             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 	             "Device is not a mobile broadband modem");
 	return FALSE;
 }
@@ -590,11 +641,10 @@ set_mm_enabled (NMModem *_self,
 }
 
 /*****************************************************************************/
-/* IP method static */
+/* IPv4 method static */
 
 static gboolean
-ip_string_to_network_address (const gchar *str,
-                              guint32 *out)
+ip4_string_to_num (const gchar *str, guint32 *out)
 {
 	guint32 addr = 0;
 	gboolean success = FALSE;
@@ -609,7 +659,7 @@ ip_string_to_network_address (const gchar *str,
 }
 
 static gboolean
-static_stage3_done (NMModemBroadband *self)
+static_stage3_ip4_done (NMModemBroadband *self)
 {
 	GError *error = NULL;
 	NMIP4Config *config = NULL;
@@ -628,9 +678,9 @@ static_stage3_done (NMModemBroadband *self)
 
 	/* Fully fail if invalid IP address retrieved */
 	address_string = mm_bearer_ip_config_get_address (self->priv->ipv4_config);
-	if (!ip_string_to_network_address (address_string, &address_network)) {
-		error = g_error_new (NM_MODEM_ERROR,
-		                     NM_MODEM_ERROR_CONNECTION_INVALID,
+	if (!ip4_string_to_num (address_string, &address_network)) {
+		error = g_error_new (NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "(%s) retrieving IP4 configuration failed: invalid address given '%s'",
 		                     nm_modem_get_uid (NM_MODEM (self)),
 		                     address_string);
@@ -639,14 +689,14 @@ static_stage3_done (NMModemBroadband *self)
 
 	/* Missing gateway not a hard failure */
 	gw_string = mm_bearer_ip_config_get_gateway (self->priv->ipv4_config);
-	ip_string_to_network_address (gw_string, &gw);
+	ip4_string_to_num (gw_string, &gw);
 
 	config = nm_ip4_config_new ();
 
 	memset (&address, 0, sizeof (address));
 	address.address = address_network;
 	address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
-	address.source = NM_PLATFORM_SOURCE_WWAN;
+	address.source = NM_IP_CONFIG_SOURCE_WWAN;
 	nm_ip4_config_add_address (config, &address);
 
 	nm_log_info (LOGD_MB, "  address %s/%d", address_string, address.plen);
@@ -659,7 +709,7 @@ static_stage3_done (NMModemBroadband *self)
 	/* DNS servers */
 	dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config);
 	for (i = 0; dns[i]; i++) {
-		if (   ip_string_to_network_address (dns[i], &address_network)
+		if (   ip4_string_to_num (dns[i], &address_network)
 		    && address_network > 0) {
 			nm_ip4_config_add_nameserver (config, address_network);
 			nm_log_info (LOGD_MB, "  DNS %s", dns[i]);
@@ -681,7 +731,110 @@ static_stage3_ip4_config_start (NMModem *_self,
 
 	/* We schedule it in an idle just to follow the same logic as in the
 	 * generic modem implementation. */
-	g_idle_add ((GSourceFunc)static_stage3_done, self);
+	g_idle_add ((GSourceFunc) static_stage3_ip4_done, self);
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+/*****************************************************************************/
+/* IPv6 method static */
+
+static gboolean
+stage3_ip6_done (NMModemBroadband *self)
+{
+	GError *error = NULL;
+	NMIP6Config *config = NULL;
+	const gchar *address_string;
+	NMPlatformIP6Address address;
+	NMModemIPMethod ip_method;
+	const gchar **dns;
+	guint i;
+
+	g_assert (self->priv->ipv6_config);
+
+	memset (&address, 0, sizeof (address));
+
+	ip_method = get_bearer_ip_method (self->priv->ipv6_config);
+
+	address_string = mm_bearer_ip_config_get_address (self->priv->ipv6_config);
+	if (!address_string) {
+		/* DHCP/SLAAC is allowed to skip addresses; other methods require it */
+		if (ip_method != NM_MODEM_IP_METHOD_AUTO) {
+			error = g_error_new (NM_DEVICE_ERROR,
+				                 NM_DEVICE_ERROR_INVALID_CONNECTION,
+				                 "(%s) retrieving IPv6 configuration failed: no address given",
+				                 nm_modem_get_uid (NM_MODEM (self)));
+		}
+		goto out;
+	}
+
+	/* Fail if invalid IP address retrieved */
+	if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
+		error = g_error_new (NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "(%s) retrieving IPv6 configuration failed: invalid address given '%s'",
+		                     nm_modem_get_uid (NM_MODEM (self)),
+		                     address_string);
+		goto out;
+	}
+
+	nm_log_info (LOGD_MB, "(%s): IPv6 base configuration:",
+	             nm_modem_get_uid (NM_MODEM (self)));
+
+	config = nm_ip6_config_new ();
+
+	address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv6_config);
+	nm_ip6_config_add_address (config, &address);
+
+	nm_log_info (LOGD_MB, "  address %s/%d", address_string, address.plen);
+
+	address_string = mm_bearer_ip_config_get_gateway (self->priv->ipv6_config);
+	if (address_string) {
+		if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) {
+			error = g_error_new (NM_DEVICE_ERROR,
+				                 NM_DEVICE_ERROR_INVALID_CONNECTION,
+				                 "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
+				                 nm_modem_get_uid (NM_MODEM (self)),
+				                 address_string);
+			goto out;
+		}
+		nm_log_info (LOGD_MB, "  gateway %s", address_string);
+		nm_ip6_config_set_gateway (config, &address.address);
+	} else if (ip_method == NM_MODEM_IP_METHOD_STATIC) {
+		/* Gateway required for the 'static' method */
+		error = g_error_new (NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "(%s) retrieving IPv6 configuration failed: missing gateway",
+		                     nm_modem_get_uid (NM_MODEM (self)));
+		goto out;
+	}
+
+	/* DNS servers */
+	dns = mm_bearer_ip_config_get_dns (self->priv->ipv6_config);
+	for (i = 0; dns[i]; i++) {
+		struct in6_addr addr;
+
+		if (inet_pton (AF_INET6, dns[i], &addr)) {
+			nm_ip6_config_add_nameserver (config, &addr);
+			nm_log_info (LOGD_MB, "  DNS %s", dns[i]);
+		}
+	}
+
+out:
+	nm_modem_emit_ip6_config_result (NM_MODEM (self), config, error);
+	g_clear_object (&config);
+	g_clear_error (&error);
+	return FALSE;
+}
+
+static NMActStageReturn
+stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason)
+{
+	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+
+	/* We schedule it in an idle just to follow the same logic as in the
+	 * generic modem implementation. */
+	g_idle_add ((GSourceFunc) stage3_ip6_done, self);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -720,11 +873,15 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
 }
 
 static void
-disconnect (NMModem *self,
+disconnect (NMModem *modem,
             gboolean warn)
 {
+	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
 	SimpleDisconnectContext *ctx;
 
+	if (!self->priv->simple_iface)
+		return;
+
 	ctx = g_slice_new (SimpleDisconnectContext);
 	ctx->self = g_object_ref (self);
 
@@ -806,6 +963,21 @@ modem_state_changed (MMModem *modem,
 
 /*****************************************************************************/
 
+static NMModemIPType
+mm_ip_family_to_nm (MMBearerIpFamily family)
+{
+	NMModemIPType nm_type = NM_MODEM_IP_TYPE_UNKNOWN;
+
+	if (family & MM_BEARER_IP_FAMILY_IPV4)
+		nm_type |= NM_MODEM_IP_TYPE_IPV4;
+	if (family & MM_BEARER_IP_FAMILY_IPV6)
+		nm_type |= NM_MODEM_IP_TYPE_IPV6;
+	if (family & MM_BEARER_IP_FAMILY_IPV4V6)
+		nm_type |= MM_BEARER_IP_FAMILY_IPV4V6;
+
+	return nm_type;
+}
+
 NMModem *
 nm_modem_broadband_new (GObject *object, GError **error)
 {
@@ -830,6 +1002,7 @@ nm_modem_broadband_new (GObject *object, GError **error)
 	                      NM_MODEM_UID, mm_modem_get_primary_port (modem_iface),
 	                      NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
 	                      NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */
+	                      NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
 	                      NM_MODEM_STATE, mm_state_to_nm (mm_modem_get_state (modem_iface)),
 	                      NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
 	                      NM_MODEM_BROADBAND_MODEM, modem_object,
@@ -967,6 +1140,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
 
 	modem_class->get_capabilities = get_capabilities;
 	modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
+	modem_class->stage3_ip6_config_request = stage3_ip6_config_request;
 	modem_class->disconnect = disconnect;
 	modem_class->deactivate = deactivate;
 	modem_class->set_mm_enabled = set_mm_enabled;
@@ -979,9 +1153,8 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
 	/* Properties */
 	g_object_class_install_property
 		(object_class, PROP_MODEM,
-		 g_param_spec_object (NM_MODEM_BROADBAND_MODEM,
-		                      "Modem",
-		                      "Broadband modem object",
+		 g_param_spec_object (NM_MODEM_BROADBAND_MODEM, "", "",
 		                      MM_GDBUS_TYPE_OBJECT,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 }
diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h
index e12ca68c..2268bac4 100644
--- a/src/devices/wwan/nm-modem-broadband.h
+++ b/src/devices/wwan/nm-modem-broadband.h
@@ -18,10 +18,9 @@
  * Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org>
  */
 
-#ifndef NM_MODEM_BROADBAND_H
-#define NM_MODEM_BROADBAND_H
+#ifndef __NETWORKMANAGER_MODEM_BROADBAND_H__
+#define __NETWORKMANAGER_MODEM_BROADBAND_H__
 
-#include <dbus/dbus-glib.h>
 #include <glib-object.h>
 #include "nm-modem.h"
 
@@ -55,4 +54,4 @@ NMModem *nm_modem_broadband_new (GObject *object, GError **error);
 
 G_END_DECLS
 
-#endif /* NM_MODEM_BROADBAND_H */
+#endif /* __NETWORKMANAGER_MODEM_BROADBAND_H__ */
diff --git a/src/devices/wwan/nm-modem-enum-types.c b/src/devices/wwan/nm-modem-enum-types.c
index cf59ba2b..6fbb3b6d 100644
--- a/src/devices/wwan/nm-modem-enum-types.c
+++ b/src/devices/wwan/nm-modem-enum-types.c
@@ -8,22 +8,42 @@
 #include "nm-modem.h"
 
 GType
-nm_modem_error_get_type (void)
+nm_modem_ip_method_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
 
   if (g_once_init_enter (&g_define_type_id__volatile))
     {
       static const GEnumValue values[] = {
-        { NM_MODEM_ERROR_CONNECTION_NOT_GSM, "NM_MODEM_ERROR_CONNECTION_NOT_GSM", "ConnectionNotGsm" },
-        { NM_MODEM_ERROR_CONNECTION_NOT_CDMA, "NM_MODEM_ERROR_CONNECTION_NOT_CDMA", "ConnectionNotCdma" },
-        { NM_MODEM_ERROR_CONNECTION_INVALID, "NM_MODEM_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
-        { NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, "NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
-        { NM_MODEM_ERROR_INITIALIZATION_FAILED, "NM_MODEM_ERROR_INITIALIZATION_FAILED", "InitializationFailed" },
+        { NM_MODEM_IP_METHOD_UNKNOWN, "NM_MODEM_IP_METHOD_UNKNOWN", "unknown" },
+        { NM_MODEM_IP_METHOD_PPP, "NM_MODEM_IP_METHOD_PPP", "ppp" },
+        { NM_MODEM_IP_METHOD_STATIC, "NM_MODEM_IP_METHOD_STATIC", "static" },
+        { NM_MODEM_IP_METHOD_AUTO, "NM_MODEM_IP_METHOD_AUTO", "auto" },
         { 0, NULL, NULL }
       };
       GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMModemError"), values);
+        g_enum_register_static (g_intern_static_string ("NMModemIPMethod"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+GType
+nm_modem_ip_type_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { NM_MODEM_IP_TYPE_UNKNOWN, "NM_MODEM_IP_TYPE_UNKNOWN", "unknown" },
+        { NM_MODEM_IP_TYPE_IPV4, "NM_MODEM_IP_TYPE_IPV4", "ipv4" },
+        { NM_MODEM_IP_TYPE_IPV6, "NM_MODEM_IP_TYPE_IPV6", "ipv6" },
+        { NM_MODEM_IP_TYPE_IPV4V6, "NM_MODEM_IP_TYPE_IPV4V6", "ipv4v6" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("NMModemIPType"), values);
       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
     }
 
diff --git a/src/devices/wwan/nm-modem-enum-types.h b/src/devices/wwan/nm-modem-enum-types.h
index 81ccd8d2..ccbe6201 100644
--- a/src/devices/wwan/nm-modem-enum-types.h
+++ b/src/devices/wwan/nm-modem-enum-types.h
@@ -9,8 +9,10 @@
 #include <glib-object.h>
 
 G_BEGIN_DECLS
-GType nm_modem_error_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_MODEM_ERROR (nm_modem_error_get_type ())
+GType nm_modem_ip_method_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_MODEM_IP_METHOD (nm_modem_ip_method_get_type ())
+GType nm_modem_ip_type_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_MODEM_IP_TYPE (nm_modem_ip_type_get_type ())
 GType nm_modem_state_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_MODEM_STATE (nm_modem_state_get_type ())
 G_END_DECLS
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index c4811700..f0e56926 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -20,42 +20,28 @@
  * Copyright (C) 2009 Canonical Ltd.
  */
 
-#include <string.h>
 #include "config.h"
+
+#include <string.h>
+
+#include <libmm-glib.h>
+
 #include "nm-modem-manager.h"
 #include "nm-logging.h"
 #include "nm-modem.h"
-#include "nm-modem-old.h"
-#include "nm-dbus-manager.h"
-#include "nm-modem-old-types.h"
-#include "nm-dbus-glib-types.h"
-
-#if WITH_MODEM_MANAGER_1
-#include <libmm-glib.h>
 #include "nm-modem-broadband.h"
-#endif
 
 #define MODEM_POKE_INTERVAL 120
 
 G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT)
 
 struct _NMModemManagerPrivate {
-	/* ModemManager < 0.7 */
-	NMDBusManager *dbus_mgr;
-	DBusGProxy *proxy;
-	guint poke_id;
-
-#if WITH_MODEM_MANAGER_1
-	/* ModemManager >= 0.7 */
 	GDBusConnection *dbus_connection;
-	MMManager *modem_manager_1;
-	guint modem_manager_1_launch_id;
-	gboolean old_modem_manager_found;
-	gboolean new_modem_manager_found;
-	guint modem_manager_1_name_owner_changed_id;
-	guint modem_manager_1_object_added_id;
-	guint modem_manager_1_object_removed_id;
-#endif
+	MMManager *modem_manager;
+	guint mm_launch_id;
+	guint mm_name_owner_changed_id;
+	guint mm_object_added_id;
+	guint mm_object_removed_id;
 
 	/* Common */
 	GHashTable *modems;
@@ -85,191 +71,6 @@ handle_new_modem (NMModemManager *self, NMModem *modem)
 	g_signal_emit (self, signals[MODEM_ADDED], 0, modem);
 }
 
-/************************************************************************/
-/* Support for ModemManager < 0.7 */
-
-static void
-clear_modem_manager_support (NMModemManager *self)
-{
-	if (self->priv->poke_id) {
-		g_source_remove (self->priv->poke_id);
-		self->priv->poke_id = 0;
-	}
-
-	if (self->priv->proxy) {
-		g_object_unref (self->priv->proxy);
-		self->priv->proxy = NULL;
-	}
-}
-
-static void
-create_modem (NMModemManager *self, const char *path)
-{
-	DBusGProxy *proxy;
-	GError *error = NULL;
-	NMModem *modem = NULL;
-	GHashTable *properties;
-
-	if (g_hash_table_lookup (self->priv->modems, path)) {
-		nm_log_warn (LOGD_MB, "modem with path %s already exists, ignoring", path);
-		return;
-	}
-
-	proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr),
-	                                   MM_OLD_DBUS_SERVICE,
-	                                   path,
-	                                   DBUS_INTERFACE_PROPERTIES);
-	g_assert (proxy);
-	if (dbus_g_proxy_call_with_timeout (proxy, "GetAll", 15000, &error,
-	                                    G_TYPE_STRING, MM_OLD_DBUS_INTERFACE_MODEM,
-	                                    G_TYPE_INVALID,
-	                                    DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
-	                                    G_TYPE_INVALID)) {
-		/* Success, create the modem */
-		modem = nm_modem_old_new (path, properties, &error);
-		if (modem)
-			handle_new_modem (self, modem);
-		else {
-			nm_log_warn (LOGD_MB, "failed to create modem: %s",
-				         error ? error->message : "(unknown)");
-		}
-		g_hash_table_destroy (properties);
-	} else {
-		nm_log_warn (LOGD_MB, "could not get modem properties: %s %s",
-		             error ? dbus_g_error_get_name (error) : "(none)",
-		             error ? error->message : "(unknown)");
-	}
-
-	g_object_unref (proxy);
-	g_clear_error (&error);
-}
-
-static void
-modem_added (DBusGProxy *proxy, const char *path, gpointer user_data)
-{
-	create_modem (NM_MODEM_MANAGER (user_data), path);
-}
-
-static void
-modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
-{
-	NMModemManager *self = NM_MODEM_MANAGER (user_data);
-	NMModem *modem;
-
-	modem = (NMModem *) g_hash_table_lookup (self->priv->modems, path);
-	if (modem) {
-		nm_modem_emit_removed (modem);
-		g_hash_table_remove (self->priv->modems, path);
-	}
-}
-
-static void
-mm_poke_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
-{
-	GPtrArray *modems;
-	int i;
-
-	if (dbus_g_proxy_end_call (proxy, call, NULL,
-	                           DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &modems,
-	                           G_TYPE_INVALID)) {
-		/* Don't care about the returned value, just free it */
-		for (i = 0; i < modems->len; i++)
-			g_free ((char *) g_ptr_array_index (modems, i));
-		g_ptr_array_free (modems, TRUE);
-	}
-	g_object_unref (proxy);
-}
-
-static gboolean
-poke_modem_cb (gpointer user_data)
-{
-	NMModemManager *self = NM_MODEM_MANAGER (user_data);
-	DBusGConnection *g_connection;
-	DBusGProxy *proxy;
-
-	g_connection = nm_dbus_manager_get_connection (self->priv->dbus_mgr);
-	proxy = dbus_g_proxy_new_for_name (g_connection,
-	                                   MM_OLD_DBUS_SERVICE,
-	                                   MM_OLD_DBUS_PATH,
-	                                   MM_OLD_DBUS_INTERFACE);
-
-	nm_log_dbg (LOGD_MB, "Requesting to (re)launch modem-manager...");
-
-	dbus_g_proxy_begin_call_with_timeout (proxy,
-	                                      "EnumerateDevices",
-	                                      mm_poke_cb,
-	                                      NULL,
-	                                      NULL,
-	                                      5000,
-	                                      G_TYPE_INVALID);
-	return TRUE;
-}
-
-static void
-enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data)
-{
-	NMModemManager *manager = NM_MODEM_MANAGER (data);
-	GPtrArray *modems;
-	GError *error = NULL;
-
-	if (!dbus_g_proxy_end_call (proxy, call_id, &error,
-								dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems,
-								G_TYPE_INVALID)) {
-		nm_log_warn (LOGD_MB, "could not get modem list: %s", error->message);
-		g_error_free (error);
-	} else {
-		int i;
-
-		for (i = 0; i < modems->len; i++) {
-			char *path = (char *) g_ptr_array_index (modems, i);
-
-			create_modem (manager, path);
-			g_free (path);
-		}
-
-		g_ptr_array_free (modems, TRUE);
-	}
-}
-
-#if WITH_MODEM_MANAGER_1
-static void clear_modem_manager_1_support (NMModemManager *self);
-#endif
-
-static void
-modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices)
-{
-	if (self->priv->poke_id) {
-		g_source_remove (self->priv->poke_id);
-		self->priv->poke_id = 0;
-	}
-
-	nm_log_info (LOGD_MB, "modem-manager is now available");
-
-#if WITH_MODEM_MANAGER_1
-	self->priv->old_modem_manager_found = TRUE;
-	if (self->priv->new_modem_manager_found)
-		nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found");
-	else
-		clear_modem_manager_1_support (self);
-#endif
-
-	self->priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (self->priv->dbus_mgr),
-	                                               MM_OLD_DBUS_SERVICE, MM_OLD_DBUS_PATH, MM_OLD_DBUS_INTERFACE);
-
-	dbus_g_proxy_add_signal (self->priv->proxy, "DeviceAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceAdded",
-								 G_CALLBACK (modem_added), self,
-								 NULL);
-
-	dbus_g_proxy_add_signal (self->priv->proxy, "DeviceRemoved", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (self->priv->proxy, "DeviceRemoved",
-								 G_CALLBACK (modem_removed), self,
-								 NULL);
-
-	if (enumerate_devices)
-		dbus_g_proxy_begin_call (self->priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID);
-}
-
 static gboolean
 remove_one_modem (gpointer key, gpointer value, gpointer user_data)
 {
@@ -278,93 +79,34 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data)
 }
 
 static void
-modem_manager_disappeared (NMModemManager *self)
+modem_manager_clear_signals (NMModemManager *self)
 {
-	g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, self);
-
-	if (self->priv->proxy) {
-		g_object_unref (self->priv->proxy);
-		self->priv->proxy = NULL;
-	}
-
-	/* Try to activate the modem-manager */
-	nm_log_dbg (LOGD_MB, "trying to start the modem manager...");
-	poke_modem_cb (self);
-	self->priv->poke_id = g_timeout_add_seconds (MODEM_POKE_INTERVAL, poke_modem_cb, self);
-}
-
-static void
-nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr,
-									 const char *name,
-									 const char *old_owner,
-									 const char *new_owner,
-									 gpointer user_data)
-{
-	gboolean old_owner_good;
-	gboolean new_owner_good;
-
-	/* Can't handle the signal if its not from the modem service */
-	if (strcmp (MM_OLD_DBUS_SERVICE, name) != 0)
+	if (!self->priv->modem_manager)
 		return;
 
-	old_owner_good = (old_owner && strlen (old_owner));
-	new_owner_good = (new_owner && strlen (new_owner));
-
-	if (!old_owner_good && new_owner_good) {
-		modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE);
-	} else if (old_owner_good && !new_owner_good) {
-		nm_log_info (LOGD_MB, "the modem manager disappeared");
-		modem_manager_disappeared (NM_MODEM_MANAGER (user_data));
-	}
-}
-
-/************************************************************************/
-/* Support for ModemManager >= 0.7 */
-
-#if WITH_MODEM_MANAGER_1
-
-static void
-modem_manager_1_clear_signals (NMModemManager *self)
-{
-	if (!self->priv->modem_manager_1)
-		return;
-
-	if (self->priv->modem_manager_1_name_owner_changed_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager_1,
-		                                   self->priv->modem_manager_1_name_owner_changed_id))
-			g_signal_handler_disconnect (self->priv->modem_manager_1,
-			                             self->priv->modem_manager_1_name_owner_changed_id);
-		self->priv->modem_manager_1_name_owner_changed_id = 0;
-	}
-
-	if (self->priv->modem_manager_1_object_added_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager_1,
-		                                   self->priv->modem_manager_1_object_added_id))
-			g_signal_handler_disconnect (self->priv->modem_manager_1,
-			                             self->priv->modem_manager_1_object_added_id);
-		self->priv->modem_manager_1_object_added_id = 0;
+	if (self->priv->mm_name_owner_changed_id) {
+		if (g_signal_handler_is_connected (self->priv->modem_manager,
+		                                   self->priv->mm_name_owner_changed_id))
+			g_signal_handler_disconnect (self->priv->modem_manager,
+			                             self->priv->mm_name_owner_changed_id);
+		self->priv->mm_name_owner_changed_id = 0;
 	}
 
-	if (self->priv->modem_manager_1_object_removed_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager_1,
-		                                   self->priv->modem_manager_1_object_removed_id))
-			g_signal_handler_disconnect (self->priv->modem_manager_1,
-			                             self->priv->modem_manager_1_object_removed_id);
-		self->priv->modem_manager_1_object_removed_id = 0;
+	if (self->priv->mm_object_added_id) {
+		if (g_signal_handler_is_connected (self->priv->modem_manager,
+		                                   self->priv->mm_object_added_id))
+			g_signal_handler_disconnect (self->priv->modem_manager,
+			                             self->priv->mm_object_added_id);
+		self->priv->mm_object_added_id = 0;
 	}
-}
 
-static void
-clear_modem_manager_1_support (NMModemManager *self)
-{
-	if (self->priv->modem_manager_1_launch_id) {
-		g_source_remove (self->priv->modem_manager_1_launch_id);
-		self->priv->modem_manager_1_launch_id = 0;
+	if (self->priv->mm_object_removed_id) {
+		if (g_signal_handler_is_connected (self->priv->modem_manager,
+		                                   self->priv->mm_object_removed_id))
+			g_signal_handler_disconnect (self->priv->modem_manager,
+			                             self->priv->mm_object_removed_id);
+		self->priv->mm_object_removed_id = 0;
 	}
-
-	modem_manager_1_clear_signals (self);
-	g_clear_object (&self->priv->modem_manager_1);
-	g_clear_object (&self->priv->dbus_connection);
 }
 
 static void
@@ -426,49 +168,43 @@ modem_object_removed (MMManager *manager,
 }
 
 static void
-modem_manager_1_available (NMModemManager *self)
+modem_manager_available (NMModemManager *self)
 {
 	GList *modems, *l;
 
 	nm_log_info (LOGD_MB, "ModemManager available in the bus");
 
-	self->priv->new_modem_manager_found = TRUE;
-	if (self->priv->old_modem_manager_found)
-		nm_log_warn (LOGD_MB, "Both the old and the new ModemManager were found");
-	else
-		clear_modem_manager_support (self);
-
 	/* Update initial modems list */
-    modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->priv->modem_manager_1));
-    for (l = modems; l; l = g_list_next (l))
-	    modem_object_added (self->priv->modem_manager_1, MM_OBJECT (l->data), self);
-    g_list_free_full (modems, (GDestroyNotify) g_object_unref);
+	modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->priv->modem_manager));
+	for (l = modems; l; l = g_list_next (l))
+		modem_object_added (self->priv->modem_manager, MM_OBJECT (l->data), self);
+	g_list_free_full (modems, (GDestroyNotify) g_object_unref);
 }
 
-static void schedule_modem_manager_1_relaunch (NMModemManager *self,
-                                               guint n_seconds);
-static void ensure_client                     (NMModemManager *self);
+static void schedule_modem_manager_relaunch (NMModemManager *self,
+                                             guint n_seconds);
+static void ensure_client                   (NMModemManager *self);
 
 static void
-modem_manager_1_name_owner_changed (MMManager *modem_manager_1,
-                                    GParamSpec *pspec,
-                                    NMModemManager *self)
+modem_manager_name_owner_changed (MMManager *modem_manager,
+                                  GParamSpec *pspec,
+                                  NMModemManager *self)
 {
 	gchar *name_owner;
 
 	/* Quit poking, if any */
-	if (self->priv->modem_manager_1_launch_id) {
-		g_source_remove (self->priv->modem_manager_1_launch_id);
-		self->priv->modem_manager_1_launch_id = 0;
+	if (self->priv->mm_launch_id) {
+		g_source_remove (self->priv->mm_launch_id);
+		self->priv->mm_launch_id = 0;
 	}
 
-	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager_1));
+	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager));
 	if (!name_owner) {
 		nm_log_info (LOGD_MB, "ModemManager disappeared from bus");
 
 #if !HAVE_SYSTEMD
 		/* If not managed by systemd, schedule relaunch */
-		schedule_modem_manager_1_relaunch (self, 0);
+		schedule_modem_manager_relaunch (self, 0);
 #endif
 
 		return;
@@ -481,21 +217,21 @@ modem_manager_1_name_owner_changed (MMManager *modem_manager_1,
 	 * nor 'object-removed' if it was created while there was no ModemManager in
 	 * the bus. This hack avoids this issue until we get a GIO with the fix
 	 * included... */
-	modem_manager_1_clear_signals (self);
-	g_clear_object (&self->priv->modem_manager_1);
+	modem_manager_clear_signals (self);
+	g_clear_object (&self->priv->modem_manager);
 	ensure_client (self);
 
 	/* Whenever GDBusObjectManagerClient is fixed, we can just do the following:
-	 * modem_manager_1_available (self);
+	 * modem_manager_available (self);
 	 */
 }
 
 #if !HAVE_SYSTEMD
 
 static void
-modem_manager_1_poke_cb (GDBusConnection *connection,
-                         GAsyncResult *res,
-                         NMModemManager *self)
+modem_manager_poke_cb (GDBusConnection *connection,
+                       GAsyncResult *res,
+                       NMModemManager *self)
 {
 	GError *error = NULL;
 	GVariant *result;
@@ -514,7 +250,7 @@ modem_manager_1_poke_cb (GDBusConnection *connection,
 		g_error_free (error);
 
 		/* Setup timeout to relaunch */
-		schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
+		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
 	} else
 		g_variant_unref (result);
 
@@ -523,7 +259,7 @@ modem_manager_1_poke_cb (GDBusConnection *connection,
 }
 
 static void
-modem_manager_1_poke (NMModemManager *self)
+modem_manager_poke (NMModemManager *self)
 {
 	/* If there is no current owner right away, ensure we poke to get one */
 	g_dbus_connection_call (self->priv->dbus_connection,
@@ -536,28 +272,28 @@ modem_manager_1_poke (NMModemManager *self)
 	                        G_DBUS_CALL_FLAGS_NONE,
 	                        -1,
 	                        NULL, /* cancellable */
-	                        (GAsyncReadyCallback)modem_manager_1_poke_cb, /* callback */
+	                        (GAsyncReadyCallback)modem_manager_poke_cb, /* callback */
 	                        g_object_ref (self)); /* user_data */
 }
 
 #endif /* HAVE_SYSTEMD */
 
 static void
-modem_manager_1_check_name_owner (NMModemManager *self)
+modem_manager_check_name_owner (NMModemManager *self)
 {
 	gchar *name_owner;
 
-	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->priv->modem_manager_1));
+	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->priv->modem_manager));
 	if (name_owner) {
 		/* Available! */
-		modem_manager_1_available (self);
+		modem_manager_available (self);
 		g_free (name_owner);
 		return;
 	}
 
 #if !HAVE_SYSTEMD
 	/* If the lifecycle is not managed by systemd, poke */
-	modem_manager_1_poke (self);
+	modem_manager_poke (self);
 #endif
 }
 
@@ -570,38 +306,35 @@ manager_new_ready (GObject *source,
 
 	GError *error = NULL;
 
-	g_assert (!self->priv->modem_manager_1);
-	self->priv->modem_manager_1 = mm_manager_new_finish (res, &error);
-	if (!self->priv->modem_manager_1) {
+	g_assert (!self->priv->modem_manager);
+	self->priv->modem_manager = mm_manager_new_finish (res, &error);
+	if (!self->priv->modem_manager) {
 		/* We're not really supposed to get any error here. If we do get one,
 		 * though, just re-schedule the MMManager creation after some time.
 		 * During this period, name-owner changes won't be followed. */
 		nm_log_warn (LOGD_MB, "error creating ModemManager client: %s", error->message);
 		g_error_free (error);
 		/* Setup timeout to relaunch */
-		schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
-	} else if (self->priv->old_modem_manager_found) {
-		/* If we found the old MM, abort */
-		clear_modem_manager_1_support (self);
+		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
 	} else {
 		/* Setup signals in the GDBusObjectManagerClient */
-		self->priv->modem_manager_1_name_owner_changed_id =
-			g_signal_connect (self->priv->modem_manager_1,
+		self->priv->mm_name_owner_changed_id =
+			g_signal_connect (self->priv->modem_manager,
 			                  "notify::name-owner",
-			                  G_CALLBACK (modem_manager_1_name_owner_changed),
+			                  G_CALLBACK (modem_manager_name_owner_changed),
 			                  self);
-		self->priv->modem_manager_1_object_added_id =
-			g_signal_connect (self->priv->modem_manager_1,
+		self->priv->mm_object_added_id =
+			g_signal_connect (self->priv->modem_manager,
 			                  "object-added",
 			                  G_CALLBACK (modem_object_added),
 			                  self);
-		self->priv->modem_manager_1_object_removed_id =
-			g_signal_connect (self->priv->modem_manager_1,
+		self->priv->mm_object_removed_id =
+			g_signal_connect (self->priv->modem_manager,
 			                  "object-removed",
 			                  G_CALLBACK (modem_object_removed),
 			                  self);
 
-		modem_manager_1_check_name_owner (self);
+		modem_manager_check_name_owner (self);
 	}
 
 	/* Balance refcount */
@@ -616,7 +349,7 @@ ensure_client (NMModemManager *self)
 	/* Create the GDBusObjectManagerClient. We do not request to autostart, as
 	 * we don't really want the MMManager creation to fail. We can always poke
 	 * later on if we want to request the autostart */
-	if (!self->priv->modem_manager_1) {
+	if (!self->priv->modem_manager) {
 		mm_manager_new (self->priv->dbus_connection,
 		                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
 		                NULL,
@@ -626,7 +359,7 @@ ensure_client (NMModemManager *self)
 	}
 
 	/* If already available, recheck name owner! */
-	modem_manager_1_check_name_owner (self);
+	modem_manager_check_name_owner (self);
 }
 
 static void
@@ -643,10 +376,7 @@ bus_get_ready (GObject *source,
 		nm_log_warn (LOGD_CORE, "error getting bus connection: %s", error->message);
 		g_error_free (error);
 		/* Setup timeout to relaunch */
-		schedule_modem_manager_1_relaunch (self, MODEM_POKE_INTERVAL);
-	} else if (self->priv->old_modem_manager_found) {
-		/* If we found the old MM, abort */
-		clear_modem_manager_1_support (self);
+		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
 	} else {
 		/* Got the bus, ensure client */
 		ensure_client (self);
@@ -660,7 +390,7 @@ static gboolean
 ensure_bus (NMModemManager *self)
 {
 	/* Clear launch ID */
-	self->priv->modem_manager_1_launch_id = 0;
+	self->priv->mm_launch_id = 0;
 
 	if (!self->priv->dbus_connection)
 		g_bus_get (G_BUS_TYPE_SYSTEM,
@@ -675,20 +405,18 @@ ensure_bus (NMModemManager *self)
 }
 
 static void
-schedule_modem_manager_1_relaunch (NMModemManager *self,
-                                   guint n_seconds)
+schedule_modem_manager_relaunch (NMModemManager *self,
+                                 guint n_seconds)
 {
 	/* No need to pass an extra reference to self; timeout/idle will be
 	 * cancelled if the object gets disposed. */
 
 	if (n_seconds)
-		self->priv->modem_manager_1_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)ensure_bus, self);
+		self->priv->mm_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)ensure_bus, self);
 	else
-		self->priv->modem_manager_1_launch_id = g_idle_add ((GSourceFunc)ensure_bus, self);
+		self->priv->mm_launch_id = g_idle_add ((GSourceFunc)ensure_bus, self);
 }
 
-#endif /* WITH_MODEM_MANAGER_1 */
-
 /************************************************************************/
 
 static void
@@ -698,20 +426,7 @@ nm_modem_manager_init (NMModemManager *self)
 
 	self->priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
-	/* ModemManager < 0.7 */
-	self->priv->dbus_mgr = nm_dbus_manager_get ();
-	g_signal_connect (self->priv->dbus_mgr, NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-					  G_CALLBACK (nm_modem_manager_name_owner_changed),
-					  self);
-	if (nm_dbus_manager_name_has_owner (self->priv->dbus_mgr, MM_OLD_DBUS_SERVICE))
-		modem_manager_appeared (self, TRUE);
-	else
-		modem_manager_disappeared (self);
-
-#if WITH_MODEM_MANAGER_1
-	/* ModemManager >= 0.7 */
-	schedule_modem_manager_1_relaunch (self, 0);
-#endif
+	schedule_modem_manager_relaunch (self, 0);
 }
 
 static void
@@ -719,21 +434,20 @@ dispose (GObject *object)
 {
 	NMModemManager *self = NM_MODEM_MANAGER (object);
 
-	/* ModemManager < 0.7 */
-	clear_modem_manager_support (self);
+	if (self->priv->mm_launch_id) {
+		g_source_remove (self->priv->mm_launch_id);
+		self->priv->mm_launch_id = 0;
+	}
 
-#if WITH_MODEM_MANAGER_1
-	/* ModemManager >= 0.7 */
-	clear_modem_manager_1_support (self);
-#endif
+	modem_manager_clear_signals (self);
+	g_clear_object (&self->priv->modem_manager);
+	g_clear_object (&self->priv->dbus_connection);
 
 	if (self->priv->modems) {
 		g_hash_table_foreach_remove (self->priv->modems, remove_one_modem, object);
 		g_hash_table_destroy (self->priv->modems);
 	}
 
-	self->priv->dbus_mgr = NULL;
-
 	/* Chain up to the parent class */
 	G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object);
 }
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 3082bdb3..9e0db41e 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -20,8 +20,8 @@
  * Copyright (C) 2009 Canonical Ltd.
  */
 
-#ifndef NM_MODEM_MANAGER_H
-#define NM_MODEM_MANAGER_H
+#ifndef __NETWORKMANAGER_MODEM_MANAGER_H__
+#define __NETWORKMANAGER_MODEM_MANAGER_H__
 
 #include <glib-object.h>
 #include "nm-modem.h"
@@ -46,4 +46,4 @@ typedef struct {
 
 GType nm_modem_manager_get_type (void);
 
-#endif /* NM_MODEM_MANAGER_H */
+#endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */
diff --git a/src/devices/wwan/nm-modem-old-types.h b/src/devices/wwan/nm-modem-old-types.h
deleted file mode 100644
index 84065a68..00000000
--- a/src/devices/wwan/nm-modem-old-types.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#ifndef NM_MODEM_OLD_TYPES_H
-#define NM_MODEM_OLD_TYPES_H
-
-#define MM_OLD_DBUS_SERVICE                     "org.freedesktop.ModemManager"
-#define MM_OLD_DBUS_PATH                        "/org/freedesktop/ModemManager"
-#define MM_OLD_DBUS_INTERFACE                   "org.freedesktop.ModemManager"
-#define MM_OLD_DBUS_INTERFACE_MODEM             "org.freedesktop.ModemManager.Modem"
-#define MM_OLD_DBUS_INTERFACE_MODEM_SIMPLE      "org.freedesktop.ModemManager.Modem.Simple"
-#define MM_OLD_DBUS_INTERFACE_MODEM_CDMA        "org.freedesktop.ModemManager.Modem.Cdma"
-#define MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD    "org.freedesktop.ModemManager.Modem.Gsm.Card"
-#define MM_OLD_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network"
-
-#define MM_OLD_MODEM_TYPE_UNKNOWN  0
-#define MM_OLD_MODEM_TYPE_GSM      1
-#define MM_OLD_MODEM_TYPE_CDMA     2
-
-/* Errors */
-
-#define MM_OLD_MODEM_CONNECT_ERROR_NO_CARRIER  MM_OLD_DBUS_INTERFACE_MODEM ".NoCarrier"
-#define MM_OLD_MODEM_CONNECT_ERROR_NO_DIALTONE MM_OLD_DBUS_INTERFACE_MODEM ".NoDialtone"
-#define MM_OLD_MODEM_CONNECT_ERROR_BUSY        MM_OLD_DBUS_INTERFACE_MODEM ".Busy"
-#define MM_OLD_MODEM_CONNECT_ERROR_NO_ANSWER   MM_OLD_DBUS_INTERFACE_MODEM ".NoAnswer"
-
-#define MM_OLD_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm"
-
-#define MM_OLD_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_OLD_MODEM_ERROR ".NetworkNotAllowed"
-#define MM_OLD_MODEM_ERROR_NETWORK_TIMEOUT     MM_OLD_MODEM_ERROR ".NetworkTimeout"
-#define MM_OLD_MODEM_ERROR_NO_NETWORK          MM_OLD_MODEM_ERROR ".NoNetwork"
-#define MM_OLD_MODEM_ERROR_SIM_NOT_INSERTED    MM_OLD_MODEM_ERROR ".SimNotInserted"
-#define MM_OLD_MODEM_ERROR_SIM_PIN             MM_OLD_MODEM_ERROR ".SimPinRequired"
-#define MM_OLD_MODEM_ERROR_SIM_PUK             MM_OLD_MODEM_ERROR ".SimPukRequired"
-#define MM_OLD_MODEM_ERROR_SIM_WRONG           MM_OLD_MODEM_ERROR ".SimWrong"
-#define MM_OLD_MODEM_ERROR_WRONG_PASSWORD      MM_OLD_MODEM_ERROR ".IncorrectPassword"
-
-typedef enum {
-    MM_OLD_MODEM_STATE_UNKNOWN = 0,
-    MM_OLD_MODEM_STATE_DISABLED = 10,
-    MM_OLD_MODEM_STATE_DISABLING = 20,
-    MM_OLD_MODEM_STATE_ENABLING = 30,
-    MM_OLD_MODEM_STATE_ENABLED = 40,
-    MM_OLD_MODEM_STATE_SEARCHING = 50,
-    MM_OLD_MODEM_STATE_REGISTERED = 60,
-    MM_OLD_MODEM_STATE_DISCONNECTING = 70,
-    MM_OLD_MODEM_STATE_CONNECTING = 80,
-    MM_OLD_MODEM_STATE_CONNECTED = 90,
-
-    MM_OLD_MODEM_STATE_LAST = MM_OLD_MODEM_STATE_CONNECTED
-} MMOldModemState;
-
-#endif /* NM_MODEM_OLD_TYPES_H */
diff --git a/src/devices/wwan/nm-modem-old.c b/src/devices/wwan/nm-modem-old.c
deleted file mode 100644
index ce1a382e..00000000
--- a/src/devices/wwan/nm-modem-old.c
+++ /dev/null
@@ -1,1139 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2013 Red Hat, Inc.
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#include <string.h>
-#include <glib/gi18n.h>
-
-#include "nm-modem-old.h"
-#include "nm-dbus-manager.h"
-#include "nm-setting-connection.h"
-#include "nm-properties-changed-signal.h"
-#include "nm-modem-old-types.h"
-#include "nm-logging.h"
-#include "NetworkManagerUtils.h"
-#include "nm-device-private.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-glib-compat.h"
-
-G_DEFINE_TYPE (NMModemOld, nm_modem_old, NM_TYPE_MODEM)
-
-#define NM_MODEM_OLD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_OLD, NMModemOldPrivate))
-
-typedef struct {
-	DBusGProxy *proxy;
-	DBusGProxy *props_proxy;
-
-	MMOldModemState state;
-	NMDeviceModemCapabilities caps;
-	char *unlock_required;
-
-	DBusGProxyCall *call;
-	GHashTable *connect_properties;
-
-	guint32 pin_tries;
-	guint enable_delay_id;
-} NMModemOldPrivate;
-
-#define CAPS_3GPP (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE)
-
-/*****************************************************************************/
-
-typedef enum {
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA,
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA,
-
-    MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_LAST = MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA
-} MMModemDeprecatedMode;
-
-typedef enum {
-    MM_MODEM_GSM_ALLOWED_MODE_ANY          = 0,
-    MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1,
-    MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2,
-    MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY      = 3,
-    MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY      = 4,
-    MM_MODEM_GSM_ALLOWED_MODE_4G_PREFERRED = 5,
-    MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY      = 6,
-
-    MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY
-} MMModemGsmAllowedMode;
-
-typedef enum {
-	MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN  = 0x0000,
-    /* bits 0..4 order match Ericsson device bitmap */
-    MM_MODEM_GSM_ALLOWED_AUTH_NONE     = 0x0001,
-    MM_MODEM_GSM_ALLOWED_AUTH_PAP      = 0x0002,
-    MM_MODEM_GSM_ALLOWED_AUTH_CHAP     = 0x0004,
-    MM_MODEM_GSM_ALLOWED_AUTH_MSCHAP   = 0x0008,
-    MM_MODEM_GSM_ALLOWED_AUTH_MSCHAPV2 = 0x0010,
-    MM_MODEM_GSM_ALLOWED_AUTH_EAP      = 0x0020,
-
-    MM_MODEM_GSM_ALLOWED_AUTH_LAST = MM_MODEM_GSM_ALLOWED_AUTH_EAP
-} MMModemGsmAllowedAuth;
-
-static NMDeviceStateReason
-translate_mm_error (GError *error)
-{
-	NMDeviceStateReason reason;
-
-	if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_CARRIER))
-		reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_DIALTONE))
-		reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_BUSY))
-		reason = NM_DEVICE_STATE_REASON_MODEM_BUSY;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_CONNECT_ERROR_NO_ANSWER))
-		reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NETWORK_NOT_ALLOWED))
-		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NETWORK_TIMEOUT))
-		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_NO_NETWORK))
-		reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_NOT_INSERTED))
-		reason = NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN))
-		reason = NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PUK))
-		reason = NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_WRONG))
-		reason = NM_DEVICE_STATE_REASON_GSM_SIM_WRONG;
-	else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_WRONG_PASSWORD))
-		reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT;
-	else {
-		/* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */
-		nm_log_dbg (LOGD_MB, "unmapped dbus error detected: '%s'", dbus_g_error_get_name (error));
-		reason = NM_DEVICE_STATE_REASON_UNKNOWN;
-	}
-
-	/* FIXME: We have only GSM error messages here, and we have no idea which
-	   activation state failed. Reasons like:
-	   NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED,
-	   NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED,
-	   NM_DEVICE_STATE_REASON_GSM_APN_FAILED,
-	   NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,
-	   NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED
-	   are not used.
-	*/
-	return reason;
-}
-
-#define MAP_STATE(name) case MM_OLD_MODEM_STATE_##name: return NM_MODEM_STATE_##name;
-
-static NMModemState
-mm_state_to_nm (MMOldModemState mm_state, const char *unlock_required)
-{
-	if (unlock_required && *unlock_required)
-		return NM_MODEM_STATE_LOCKED;
-
-	switch (mm_state) {
-	MAP_STATE(UNKNOWN)
-	MAP_STATE(DISABLED)
-	MAP_STATE(DISABLING)
-	MAP_STATE(ENABLING)
-	MAP_STATE(ENABLED)
-	MAP_STATE(SEARCHING)
-	MAP_STATE(REGISTERED)
-	MAP_STATE(DISCONNECTING)
-	MAP_STATE(CONNECTING)
-	MAP_STATE(CONNECTED)
-	}
-	return NM_MODEM_STATE_UNKNOWN;
-};
-
-/*****************************************************************************/
-
-static DBusGProxy *
-nm_modem_old_get_proxy (NMModemOld *self, const char *interface)
-{
-
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	const char *current_iface;
-
-	g_return_val_if_fail (NM_IS_MODEM_OLD (self), NULL);
-
-	/* Default to the default interface. */
-	if (interface == NULL)
-		interface = MM_OLD_DBUS_INTERFACE_MODEM;
-
-	if (interface && !strcmp (interface, DBUS_INTERFACE_PROPERTIES))
-		return priv->props_proxy;
-
-	current_iface = dbus_g_proxy_get_interface (priv->proxy);
-	if (!current_iface || strcmp (current_iface, interface))
-		dbus_g_proxy_set_interface (priv->proxy, interface);
-
-	return priv->proxy;
-}
-
-/*****************************************************************************/
-/* Query/Update enabled state */
-
-static void
-set_mm_enabled_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
-	GError *error = NULL;
-
-	if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
-		nm_log_warn (LOGD_MB, "failed to enable/disable modem: (%d) %s",
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-		nm_modem_set_prev_state (NM_MODEM (user_data), "enable/disable failed");
-	}
-	/* Wait for the state change signal to indicate enabled state changed */
-}
-
-static void
-set_mm_enabled (NMModem *self, gboolean enabled)
-{
-	dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM),
-	                         "Enable", set_mm_enabled_done,
-	                         g_object_ref (self), g_object_unref,
-	                         G_TYPE_BOOLEAN, enabled,
-	                         G_TYPE_INVALID);
-}
-
-/*****************************************************************************/
-
-static void
-ask_for_pin (NMModemOld *self, gboolean always_ask)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	guint32 tries = 0;
-
-	g_return_if_fail (NM_IS_MODEM_OLD (self));
-
-	if (!always_ask)
-		tries = priv->pin_tries++;
-
-	nm_modem_get_secrets (NM_MODEM (self),
-	                      NM_SETTING_GSM_SETTING_NAME,
-	                      (tries || always_ask) ? TRUE : FALSE,
-	                      NM_SETTING_GSM_PIN);
-}
-
-static void
-stage1_prepare_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	GError *error = NULL;
-	gboolean asked = FALSE;
-
-	priv->call = NULL;
-
-	if (priv->connect_properties) {
-		g_hash_table_destroy (priv->connect_properties);
-		priv->connect_properties = NULL;
-	}
-
-	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
-	else {
-		if (priv->caps & CAPS_3GPP) {
-			if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN)) {
-				ask_for_pin (self, FALSE);
-				asked = TRUE;
-			} else if (dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_WRONG)) {
-				ask_for_pin (self, TRUE);
-				asked = TRUE;
-			}
-		}
-
-		if (!asked) {
-			nm_log_warn (LOGD_MB, "Mobile connection failed: (%d) %s",
-					     error ? error->code : -1,
-					     error && error->message ? error->message : "(unknown)");
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
-		}
-		g_error_free (error);
-	}
-}
-
-static void
-do_connect (NMModemOld *self)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	DBusGProxy *proxy;
-
-	proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM_SIMPLE);
-	priv->call = dbus_g_proxy_begin_call_with_timeout (proxy,
-	                                                   "Connect", stage1_prepare_done,
-	                                                   self, NULL, 120000,
-	                                                   DBUS_TYPE_G_MAP_OF_VARIANT, priv->connect_properties,
-	                                                   G_TYPE_INVALID);
-}
-
-static void stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data);
-
-/* do_enable() is used as a GSourceFunc, hence the gboolean return */
-static gboolean
-do_enable (NMModemOld *self)
-{
-	DBusGProxy *proxy;
-
-	g_return_val_if_fail (NM_IS_MODEM_OLD (self), FALSE);
-
-	NM_MODEM_OLD_GET_PRIVATE (self)->enable_delay_id = 0;
-	proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM);
-	dbus_g_proxy_begin_call_with_timeout (proxy,
-	                                      "Enable", stage1_enable_done,
-	                                      self, NULL, 20000,
-	                                      G_TYPE_BOOLEAN, TRUE,
-	                                      G_TYPE_INVALID);
-	return FALSE;
-}
-
-static void
-stage1_pin_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	NMDeviceStateReason reason;
-	GError *error = NULL;
-
-	if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
-		/* Success; try to enable the modem again.  Wait a few seconds to ensure
-		 * that ModemManager is ready for the enable right after the unlock.
-		 */
-		if (priv->enable_delay_id == 0)
-			priv->enable_delay_id = g_timeout_add_seconds (4, (GSourceFunc) do_enable, self);
-	} else {
-		nm_log_warn (LOGD_MB, "GSM PIN unlock failed: (%d) %s",
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-
-		/* try to translate the error reason */
-		reason = translate_mm_error (error);
-		if (reason == NM_DEVICE_STATE_REASON_UNKNOWN)
-			reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
-
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, reason);
-		g_error_free (error);
-	}
-}
-
-static void
-handle_enable_pin_required (NMModemOld *self)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	const char *pin = NULL;
-	GValue *value;
-	DBusGProxy *proxy;
-
-	g_assert (priv->caps & CAPS_3GPP);
-
-	/* See if we have a PIN already */
-	value = g_hash_table_lookup (priv->connect_properties, "pin");
-	if (value && G_VALUE_HOLDS_STRING (value))
-		pin = g_value_get_string (value);
-
-	/* If we do, send it */
-	if (pin) {
-		proxy = nm_modem_old_get_proxy (NM_MODEM_OLD (self), MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD);
-		dbus_g_proxy_begin_call_with_timeout (proxy,
-		                                      "SendPin", stage1_pin_done,
-		                                      self, NULL, 10000,
-		                                      G_TYPE_STRING, pin,
-		                                      G_TYPE_INVALID);
-	} else
-		ask_for_pin (self, FALSE);
-}
-
-static void
-stage1_enable_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	NMDeviceStateReason reason;
-	GError *error = NULL;
-
-	if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID))
-		do_connect (self);
-	else {
-		if ((priv->caps & CAPS_3GPP) && dbus_g_error_has_name (error, MM_OLD_MODEM_ERROR_SIM_PIN))
-			handle_enable_pin_required (self);
-		else {
-			nm_log_warn (LOGD_MB, "Modem enable failed: (%d) %s",
-				         error ? error->code : -1,
-				         error && error->message ? error->message : "(unknown)");
-
-			/* try to translate the error reason */
-			reason = translate_mm_error (error);
-			if (reason == NM_DEVICE_STATE_REASON_UNKNOWN)
-				reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, reason);
-		}
-
-		g_error_free (error);
-	}
-}
-
-static GHashTable *
-create_connect_properties (NMConnection *connection)
-{
-	NMSettingCdma *s_cdma;
-	NMSettingGsm *s_gsm;
-	NMSettingPPP *s_ppp;
-	GHashTable *properties;
-	const char *str;
-
-	properties = value_hash_create ();
-
-	s_cdma = nm_connection_get_setting_cdma (connection);
-	if (s_cdma) {
-		str = nm_setting_cdma_get_number (s_cdma);
-		if (str)
-			value_hash_add_str (properties, "number", str);
-		return properties;
-	}
-
-	s_gsm = nm_connection_get_setting_gsm (connection);
-	if (s_gsm) {
-		str = nm_setting_gsm_get_number (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "number", str);
-
-		str = nm_setting_gsm_get_apn (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "apn", str);
-
-		str = nm_setting_gsm_get_network_id (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "network_id", str);
-
-		str = nm_setting_gsm_get_pin (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "pin", str);
-
-		str = nm_setting_gsm_get_username (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "username", str);
-
-		str = nm_setting_gsm_get_password (s_gsm);
-		if (str)
-			value_hash_add_str (properties, "password", str);
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-		/* Add both old and new preferred modes */
-		switch (nm_setting_gsm_get_network_type (s_gsm)) {
-		case NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA:
-			value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY);
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY);
-			break;
-		case NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE:
-			value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY);
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY);
-			break;
-		case NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA:
-			value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED);
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED);
-			break;
-		case NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE:
-			value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED);
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED);
-			break;
-		case NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G:
-			/* deprecated modes not extended for 4G, so no need to set them here */
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_4G_PREFERRED);
-			break;
-		case NM_SETTING_GSM_NETWORK_TYPE_4G:
-			/* deprecated modes not extended for 4G, so no need to set them here */
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_4G_ONLY);
-			break;
-		default:
-			value_hash_add_uint (properties, "network_mode", MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY);
-			value_hash_add_uint (properties, "allowed_mode", MM_MODEM_GSM_ALLOWED_MODE_ANY);
-			break;
-		}
-G_GNUC_END_IGNORE_DEPRECATIONS
-
-		/* Roaming */
-		if (nm_setting_gsm_get_home_only (s_gsm))
-			value_hash_add_bool (properties, "home_only", TRUE);
-
-		/* For IpMethod == STATIC or DHCP */
-		s_ppp = nm_connection_get_setting_ppp (connection);
-		if (s_ppp) {
-			guint32 auth = MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN;
-
-			if (nm_setting_ppp_get_noauth (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_NONE;
-			if (!nm_setting_ppp_get_refuse_pap (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_PAP;
-			if (!nm_setting_ppp_get_refuse_chap (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_CHAP;
-			if (!nm_setting_ppp_get_refuse_mschap (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_MSCHAP;
-			if (!nm_setting_ppp_get_refuse_mschapv2 (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_MSCHAPV2;
-			if (!nm_setting_ppp_get_refuse_eap (s_ppp))
-				auth |= MM_MODEM_GSM_ALLOWED_AUTH_EAP;
-
-			if (auth != MM_MODEM_GSM_ALLOWED_AUTH_UNKNOWN)
-				value_hash_add_uint (properties, "allowed_auth", auth);
-		}
-
-		return properties;
-	}
-
-	g_hash_table_destroy (properties);
-	return NULL;
-}
-
-static NMActStageReturn
-act_stage1_prepare (NMModem *modem,
-                    NMConnection *connection,
-                    NMDeviceStateReason *reason)
-{
-	NMModemOld *self = NM_MODEM_OLD (modem);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-
-	if (priv->connect_properties)
-		g_hash_table_destroy (priv->connect_properties);
-	priv->connect_properties = create_connect_properties (connection);
-
-	if (nm_modem_get_state (modem) >= NM_MODEM_STATE_ENABLING)
-		do_connect (self);
-	else
-		do_enable (self);
-
-	return NM_ACT_STAGE_RETURN_POSTPONE;
-}
-
-/*****************************************************************************/
-/* IP method static */
-
-static char addr_to_string_buf[INET6_ADDRSTRLEN + 1];
-
-static const char *
-ip_address_to_string (guint32 numeric)
-{
-	guint32 temp_addr;
-
-	memset (&addr_to_string_buf, '\0', sizeof (addr_to_string_buf));
-	temp_addr = numeric;
-
-	if (inet_ntop (AF_INET, &temp_addr, addr_to_string_buf, INET_ADDRSTRLEN)) {
-		return addr_to_string_buf;
-	} else {
-		nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X",
-		             ntohl (temp_addr));
-		return NULL;
-	}
-}
-
-static void
-static_stage3_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	GValueArray *ret_array = NULL;
-	GError *error = NULL;
-	NMIP4Config *config = NULL;
-
-	priv->call = NULL;
-
-	/* Returned value array is (uuuu): [IP, DNS1, DNS2, DNS3], all in
-	 * network byte order.
-	 */
-	if (dbus_g_proxy_end_call (proxy, call, &error,
-	                           G_TYPE_VALUE_ARRAY, &ret_array,
-	                           G_TYPE_INVALID)) {
-		NMPlatformIP4Address address;
-		int i;
-
-		config = nm_ip4_config_new ();
-		memset (&address, 0, sizeof (address));
-
-		nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:",
-		             nm_modem_get_uid (NM_MODEM (self)));
-
-		/* IP address */
-		address.address = g_value_get_uint (g_value_array_get_nth (ret_array, 0));
-		address.plen = 32;
-		address.source = NM_PLATFORM_SOURCE_WWAN;
-		nm_ip4_config_add_address (config, &address);
-
-		nm_log_info (LOGD_MB, "  address %s/%d",
-		             ip_address_to_string (address.address),
-		             address.plen);
-
-		/* DNS servers */
-		for (i = 1; i < ret_array->n_values; i++) {
-			GValue *value = g_value_array_get_nth (ret_array, i);
-			guint32 tmp = g_value_get_uint (value);
-
-			if (tmp > 0) {
-				nm_ip4_config_add_nameserver (config, tmp);
-				nm_log_info (LOGD_MB, "  DNS %s", ip_address_to_string (tmp));
-			}
-		}
-		g_value_array_free (ret_array);
-	}
-
-	g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, config, error);
-	g_clear_error (&error);
-}
-
-static NMActStageReturn
-static_stage3_ip4_config_start (NMModem *self,
-                                NMActRequest *req,
-                                NMDeviceStateReason *reason)
-{
-	NMModemOldPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	priv = NM_MODEM_OLD_GET_PRIVATE (self);
-
-	priv->call = dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self),
-	                                                                  MM_OLD_DBUS_INTERFACE_MODEM),
-	                                      "GetIP4Config", static_stage3_done,
-	                                      self, NULL,
-	                                      G_TYPE_INVALID);
-
-	return NM_ACT_STAGE_RETURN_POSTPONE;
-}
-
-/*****************************************************************************/
-
-static void
-disconnect_done (DBusGProxy *proxy,
-                 DBusGProxyCall *call_id,
-                 gpointer user_data)
-{
-	GError *error = NULL;
-	gboolean warn = GPOINTER_TO_UINT (user_data);
-
-	if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID) && warn) {
-		nm_log_info (LOGD_MB, "disconnect failed: (%d) %s",
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-	}
-}
-
-static void
-disconnect (NMModem *self,
-            gboolean warn)
-{
-	dbus_g_proxy_begin_call (nm_modem_old_get_proxy (NM_MODEM_OLD (self),
-	                                                     MM_OLD_DBUS_INTERFACE_MODEM),
-	                         "Disconnect",
-	                         disconnect_done,
-	                         GUINT_TO_POINTER (warn),
-	                         NULL,
-	                         G_TYPE_INVALID);
-}
-
-/*****************************************************************************/
-
-static void
-deactivate (NMModem *self, NMDevice *device)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-
-	priv->pin_tries = 0;
-
-	if (priv->call) {
-		dbus_g_proxy_cancel_call (priv->proxy, priv->call);
-		priv->call = NULL;
-	}
-
-	if (priv->enable_delay_id) {
-		g_source_remove (priv->enable_delay_id);
-		priv->enable_delay_id = 0;
-	}
-
-	/* Chain up parent */
-	NM_MODEM_CLASS (nm_modem_old_parent_class)->deactivate (self, device);
-}
-
-/*****************************************************************************/
-
-static void
-modem_properties_changed (DBusGProxy *proxy,
-                          const char *interface,
-                          GHashTable *props,
-                          gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (self);
-	GValue *value;
-	gboolean update_state = FALSE;
-
-	if (strcmp (interface, MM_OLD_DBUS_INTERFACE_MODEM) &&
-	    strcmp (interface, MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD))
-		return;
-
-	value = g_hash_table_lookup (props, "IpMethod");
-	if (value && G_VALUE_HOLDS_UINT (value)) {
-		g_object_set (self,
-		              NM_MODEM_IP_METHOD, g_value_get_uint (value),
-		              NULL);
-	}
-
-	value = g_hash_table_lookup (props, "SimIdentifier");
-	if (value && G_VALUE_HOLDS_STRING (value)) {
-		const char *sim_id = g_value_get_string (value);
-
-		g_object_set (self,
-		              NM_MODEM_SIM_ID, (sim_id && *sim_id) ? sim_id : NULL,
-		              NULL);
-	}
-
-	value = g_hash_table_lookup (props, "UnlockRequired");
-	if (value && G_VALUE_HOLDS_STRING (value)) {
-		g_free (priv->unlock_required);
-		priv->unlock_required = g_value_dup_string (value);
-		update_state = TRUE;
-	}
-
-	value = g_hash_table_lookup (props, "State");
-	if (value && G_VALUE_HOLDS_UINT (value)) {
-		priv->state = g_value_get_uint (value);
-		update_state = TRUE;
-	}
-
-	if (update_state) {
-		nm_modem_set_state (NM_MODEM (self),
-		                    mm_state_to_nm (priv->state, priv->unlock_required),
-		                    NULL);
-	}
-}
-
-/*****************************************************************************/
-
-static gboolean
-check_connection_compatible (NMModem *modem, NMConnection *connection)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem);
-	NMSettingConnection *s_con;
-	gboolean valid_cdma = FALSE, valid_gsm = FALSE;
-	const char *ctype;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-	ctype = nm_setting_connection_get_connection_type (s_con);
-	g_assert (ctype);
-
-	/* Check for valid CDMA first */
-	if (strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME) == 0)
-		valid_cdma = !!nm_connection_get_setting_cdma (connection);
-
-	if (strcmp (ctype, NM_SETTING_GSM_SETTING_NAME) == 0)
-		valid_gsm = !!nm_connection_get_setting_gsm (connection);
-
-	/* Validate CDMA */
-	if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) {
-		if (valid_cdma)
-			return TRUE;
-
-		/* If the modem is only CDMA and the connection is not CDMA, error */
-		if ((priv->caps ^ NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) == 0)
-			return FALSE;
-	}
-
-	/* Validate 3GPP */
-	if (priv->caps & CAPS_3GPP)
-		return valid_gsm;
-
-	return FALSE;
-}
-
-/*****************************************************************************/
-
-static void
-complete_ppp_setting (NMConnection *connection)
-{
-	NMSettingPPP *s_ppp;
-
-	s_ppp = nm_connection_get_setting_ppp (connection);
-	if (!s_ppp) {
-		s_ppp = (NMSettingPPP *) nm_setting_ppp_new ();
-		g_object_set (G_OBJECT (s_ppp),
-		              NM_SETTING_PPP_LCP_ECHO_FAILURE, 5,
-		              NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30,
-		              NULL);
-		nm_connection_add_setting (connection, NM_SETTING (s_ppp));
-	}
-}
-
-static gboolean
-complete_connection_3gpp (NMConnection *connection,
-                          const GSList *existing_connections,
-                          GError **error)
-{
-	NMSettingGsm *s_gsm;
-
-	s_gsm = nm_connection_get_setting_gsm (connection);
-	if (!s_gsm || !nm_setting_gsm_get_apn (s_gsm)) {
-		/* Need an APN at least */
-		g_set_error_literal (error,
-		                     NM_SETTING_GSM_ERROR,
-		                     NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
-		                     NM_SETTING_GSM_APN);
-		return FALSE;
-	}
-
-	if (!nm_setting_gsm_get_number (s_gsm))
-		g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
-	complete_ppp_setting (connection);
-
-	nm_utils_complete_generic (connection,
-	                           NM_SETTING_GSM_SETTING_NAME,
-	                           existing_connections,
-	                           _("GSM connection %d"),
-	                           NULL,
-	                           FALSE); /* No IPv6 yet by default */
-	return TRUE;
-}
-
-static gboolean
-complete_connection_cdma (NMConnection *connection,
-                          const GSList *existing_connections,
-                          GError **error)
-{
-	NMSettingCdma *s_cdma;
-
-	s_cdma = nm_connection_get_setting_cdma (connection);
-	if (!s_cdma) {
-		s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
-		nm_connection_add_setting (connection, NM_SETTING (s_cdma));
-	}
-
-	if (!nm_setting_cdma_get_number (s_cdma))
-		g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL);
-
-	complete_ppp_setting (connection);
-
-	nm_utils_complete_generic (connection,
-	                           NM_SETTING_CDMA_SETTING_NAME,
-	                           existing_connections,
-	                           _("CDMA connection %d"),
-	                           NULL,
-	                           FALSE); /* No IPv6 yet by default */
-	return TRUE;
-}
-
-static gboolean
-complete_connection (NMModem *modem,
-                     NMConnection *connection,
-                     const GSList *existing_connections,
-                     GError **error)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem);
-
-	/* If the modem has LTE, complete as 3GPP */
-	if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_LTE)
-		return complete_connection_3gpp (connection, existing_connections, error);
-
-	/* Otherwise, prefer CDMA on the theory that if the modem has CDMA/EVDO
-	 * that's most likely what the user will be using.
-	 */
-	if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
-		return complete_connection_cdma (connection, existing_connections, error);
-
-	if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
-		return complete_connection_3gpp (connection, existing_connections, error);
-
-	g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE,
-	                     "Modem had no WWAN capabilities.");
-	return FALSE;
-}
-
-/*****************************************************************************/
-
-static gboolean
-get_user_pass (NMModem *modem,
-               NMConnection *connection,
-               const char **user,
-               const char **pass)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (modem);
-	NMSettingCdma *s_cdma;
-	NMSettingGsm *s_gsm;
-
-	if (priv->caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO) {
-		s_cdma = nm_connection_get_setting_cdma (connection);
-		if (s_cdma) {
-			if (user)
-				*user = nm_setting_cdma_get_username (s_cdma);
-			if (pass)
-				*pass = nm_setting_cdma_get_password (s_cdma);
-			return TRUE;
-		}
-	}
-
-	/* Fall back to GSM; will be used for CDMA devices on LTE networks too */
-	s_gsm = nm_connection_get_setting_gsm (connection);
-	if (s_gsm) {
-		if (user)
-			*user = nm_setting_gsm_get_username (s_gsm);
-		if (pass)
-			*pass = nm_setting_gsm_get_password (s_gsm);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-/*****************************************************************************/
-
-static void
-get_capabilities (NMModem *_self,
-                  NMDeviceModemCapabilities *modem_caps,
-                  NMDeviceModemCapabilities *current_caps)
-{
-	NMModemOld *self = NM_MODEM_OLD (_self);
-
-	*current_caps = *modem_caps = NM_MODEM_OLD_GET_PRIVATE (self)->caps;
-}
-
-/*****************************************************************************/
-
-NMModem *
-nm_modem_old_new (const char *path, GHashTable *properties, GError **error)
-{
-	NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
-	NMModemOld *self;
-	GHashTableIter iter;
-	const char *prop;
-	GValue *value;
-	const char *data_device = NULL;
-	const char *driver = NULL;
-	const char *master_device = NULL;
-	const char *unlock_required = NULL;
-	const char *device_id = NULL;
-	guint32 modem_type = MM_OLD_MODEM_TYPE_UNKNOWN;
-	guint32 ip_method = MM_MODEM_IP_METHOD_PPP;
-	guint32 ip_timeout = 0;
-	MMOldModemState state = MM_OLD_MODEM_STATE_UNKNOWN;
-
-	g_return_val_if_fail (path != NULL, NULL);
-	g_return_val_if_fail (properties != NULL, NULL);
-
-	g_hash_table_iter_init (&iter, properties);
-	while (g_hash_table_iter_next (&iter, (gpointer) &prop, (gpointer) &value)) {
-		if (g_strcmp0 (prop, "Type") == 0)
-			modem_type = g_value_get_uint (value);
-		else if (g_strcmp0 (prop, "MasterDevice") == 0)
-			master_device = g_value_get_string (value);
-		else if (g_strcmp0 (prop, "IpMethod") == 0)
-			ip_method = g_value_get_uint (value);
-		else if (g_strcmp0 (prop, "Device") == 0)
-			data_device = g_value_get_string (value);
-		else if (g_strcmp0 (prop, "Driver") == 0)
-			driver = g_value_get_string (value);
-		else if (g_strcmp0 (prop, "IpTimeout") == 0)
-			ip_timeout = g_value_get_uint (value);
-		else if (g_strcmp0 (prop, "State") == 0)
-			state = g_value_get_uint (value);
-		else if (g_strcmp0 (prop, "UnlockRequired") == 0)
-			unlock_required = g_value_get_string (value);
-		else if (g_strcmp0 (prop, "DeviceIdentifier") == 0)
-			device_id = g_value_get_string (value);
-	}
-
-	if (modem_type == MM_OLD_MODEM_TYPE_UNKNOWN) {
-		g_set_error (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED,
-		             "Unhandled modem type %d", modem_type);
-		return NULL;
-	}
-
-	if (!master_device || !strlen (master_device)) {
-		g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED,
-		                     "Failed to retrieve modem master device.");
-		return NULL;
-	}
-
-	if (!driver || !strlen (driver)) {
-		g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED,
-		                     "Failed to retrieve modem driver.");
-		return NULL;
-	}
-
-	if (!data_device || !strlen (data_device)) {
-		g_set_error_literal (error, NM_MODEM_ERROR, NM_MODEM_ERROR_INITIALIZATION_FAILED,
-		                     "Failed to retrieve modem data device.");
-		return NULL;
-	}
-
-	self = (NMModemOld *) g_object_new (NM_TYPE_MODEM_OLD,
-	                                    NM_MODEM_PATH, path,
-	                                    NM_MODEM_DRIVER, driver,
-	                                    NM_MODEM_UID, data_device,
-	                                    NM_MODEM_CONTROL_PORT, NULL,
-	                                    NM_MODEM_DATA_PORT, data_device,
-	                                    NM_MODEM_IP_METHOD, ip_method,
-	                                    NM_MODEM_IP_TIMEOUT, ip_timeout,
-	                                    NM_MODEM_DEVICE_ID, device_id,
-	                                    NM_MODEM_STATE, mm_state_to_nm (state, unlock_required),
-	                                    NULL);
-	if (self) {
-		if (modem_type == MM_OLD_MODEM_TYPE_CDMA)
-			caps |= NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
-		if (modem_type == MM_OLD_MODEM_TYPE_GSM)
-			caps |= NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
-
-		NM_MODEM_OLD_GET_PRIVATE (self)->caps = caps;
-		NM_MODEM_OLD_GET_PRIVATE (self)->state = state;
-		NM_MODEM_OLD_GET_PRIVATE (self)->unlock_required = g_strdup (unlock_required);
-	}
-
-	return (NMModem *) self;
-}
-
-static void
-nm_modem_old_init (NMModemOld *self)
-{
-}
-
-static void
-get_sim_id_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
-	NMModemOld *self = NM_MODEM_OLD (user_data);
-	GValue value = G_VALUE_INIT;
-
-	if (dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
-		if (G_VALUE_HOLDS_STRING (&value)) {
-			const char *sim_id = g_value_get_string (&value);
-
-			if (sim_id && *sim_id)
-				g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, sim_id, NULL);
-		}
-		g_value_unset (&value);
-	}
-}
-
-static GObject*
-constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
-{
-	GObject *object;
-	NMModemOldPrivate *priv;
-	DBusGConnection *bus;
-
-	object = G_OBJECT_CLASS (nm_modem_old_parent_class)->constructor (type, n_construct_params, construct_params);
-	if (!object)
-		return NULL;
-
-	priv = NM_MODEM_OLD_GET_PRIVATE (object);
-
-	bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
-	priv->proxy = dbus_g_proxy_new_for_name (bus,
-	                                         MM_OLD_DBUS_SERVICE,
-	                                         nm_modem_get_path (NM_MODEM (object)),
-	                                         MM_OLD_DBUS_INTERFACE_MODEM);
-
-	priv->props_proxy = dbus_g_proxy_new_for_name (bus,
-	                                               MM_OLD_DBUS_SERVICE,
-	                                               nm_modem_get_path (NM_MODEM (object)),
-	                                               DBUS_INTERFACE_PROPERTIES);
-	dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
-	                                   G_TYPE_NONE,
-	                                   G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
-	                                   G_TYPE_INVALID);
-	dbus_g_proxy_add_signal (priv->props_proxy, "MmPropertiesChanged",
-	                         G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
-	                         G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->props_proxy, "MmPropertiesChanged",
-	                             G_CALLBACK (modem_properties_changed),
-	                             object,
-	                             NULL);
-
-	/* Request the SIM ID */
-	dbus_g_proxy_begin_call (priv->props_proxy,
-	                         "Get",
-	                         get_sim_id_done,
-	                         g_object_ref (object), g_object_unref,
-	                         G_TYPE_STRING, MM_OLD_DBUS_INTERFACE_MODEM_GSM_CARD,
-	                         G_TYPE_STRING, "SimIdentifier",
-	                         G_TYPE_INVALID);
-
-	return object;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMModemOldPrivate *priv = NM_MODEM_OLD_GET_PRIVATE (object);
-
-	if (priv->proxy) {
-		g_object_unref (priv->proxy);
-		priv->proxy = NULL;
-	}
-
-	if (priv->props_proxy) {
-		g_object_unref (priv->props_proxy);
-		priv->props_proxy = NULL;
-	}
-
-	if (priv->connect_properties) {
-		g_hash_table_destroy (priv->connect_properties);
-		priv->connect_properties = NULL;
-	}
-
-	if (priv->enable_delay_id) {
-		g_source_remove (priv->enable_delay_id);
-		priv->enable_delay_id = 0;
-	}
-
-	g_free (priv->unlock_required);
-	priv->unlock_required = NULL;
-
-	G_OBJECT_CLASS (nm_modem_old_parent_class)->dispose (object);
-}
-
-static void
-nm_modem_old_class_init (NMModemOldClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMModemClass *modem_class = NM_MODEM_CLASS (klass);
-
-	g_type_class_add_private (object_class, sizeof (NMModemOldPrivate));
-
-	/* Virtual methods */
-	object_class->constructor = constructor;
-	object_class->dispose = dispose;
-
-	modem_class->get_capabilities = get_capabilities;
-	modem_class->get_user_pass = get_user_pass;
-	modem_class->complete_connection = complete_connection;
-	modem_class->check_connection_compatible = check_connection_compatible;
-	modem_class->act_stage1_prepare = act_stage1_prepare;
-	modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
-	modem_class->disconnect = disconnect;
-	modem_class->deactivate = deactivate;
-	modem_class->set_mm_enabled = set_mm_enabled;
-}
diff --git a/src/devices/wwan/nm-modem-old.h b/src/devices/wwan/nm-modem-old.h
deleted file mode 100644
index 65e3db2d..00000000
--- a/src/devices/wwan/nm-modem-old.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#ifndef NM_MODEM_OLD_H
-#define NM_MODEM_OLD_H
-
-#include <dbus/dbus-glib.h>
-#include <glib-object.h>
-#include "nm-modem.h"
-#include "nm-modem-old-types.h"
-
-G_BEGIN_DECLS
-
-#define NM_TYPE_MODEM_OLD            (nm_modem_old_get_type ())
-#define NM_MODEM_OLD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_OLD, NMModemOld))
-#define NM_MODEM_OLD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_MODEM_OLD, NMModemOldClass))
-#define NM_IS_MODEM_OLD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_OLD))
-#define NM_IS_MODEM_OLD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_MODEM_OLD))
-#define NM_MODEM_OLD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_MODEM_OLD, NMModemOldClass))
-
-typedef struct {
-	NMModem parent;
-} NMModemOld;
-
-typedef struct {
-	NMModemClass parent;
-} NMModemOldClass;
-
-GType nm_modem_old_get_type (void);
-
-NMModem *nm_modem_old_new (const char *path, GHashTable *properties, GError **error);
-
-G_END_DECLS
-
-#endif /* NM_MODEM_OLD_H */
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index fd3b4a36..ba1db8a4 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -19,6 +19,8 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include "nm-modem.h"
 #include "nm-platform.h"
@@ -42,11 +44,13 @@ enum {
 	PROP_PATH,
 	PROP_UID,
 	PROP_DRIVER,
-	PROP_IP_METHOD,
+	PROP_IP4_METHOD,
+	PROP_IP6_METHOD,
 	PROP_IP_TIMEOUT,
 	PROP_STATE,
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
+	PROP_IP_TYPES,
 
 	LAST_PROP
 };
@@ -58,11 +62,14 @@ typedef struct {
 	char *control_port;
 	char *data_port;
 	char *ppp_iface;
-	guint32 ip_method;
+	NMModemIPMethod ip4_method;
+	NMModemIPMethod ip6_method;
+	NMUtilsIPv6IfaceId iid;
 	NMModemState state;
 	NMModemState prev_state;  /* revert to this state if enable/disable fails */
 	char *device_id;
 	char *sim_id;
+	NMModemIPType ip_types;
 
 	NMPPPManager *ppp_manager;
 
@@ -82,6 +89,7 @@ enum {
 	PPP_FAILED,
 	PREPARE_RESULT,
 	IP4_CONFIG_RESULT,
+	IP6_CONFIG_RESULT,
 	AUTH_REQUESTED,
 	AUTH_RESULT,
 	REMOVED,
@@ -94,17 +102,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 
 /*****************************************************************************/
-
-GQuark
-nm_modem_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-modem-error");
-	return quark;
-}
-
-/*****************************************************************************/
 /* State/enabled/connected */
 
 static const char *state_table[] = {
@@ -126,7 +123,7 @@ static const char *state_table[] = {
 const char *
 nm_modem_state_to_string (NMModemState state)
 {
-	if (state >= 0 && state < G_N_ELEMENTS (state_table))
+	if ((gsize) state < G_N_ELEMENTS (state_table))
 		return state_table[state];
 	return NULL;
 }
@@ -218,6 +215,102 @@ nm_modem_emit_removed (NMModem *self)
 	g_signal_emit (self, signals[REMOVED], 0);
 }
 
+NMModemIPType
+nm_modem_get_supported_ip_types (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->ip_types;
+}
+
+/**
+ * nm_modem_get_connection_ip_type:
+ * @self: the #NMModem
+ * @connection: the #NMConnection to determine IP type to use
+ *
+ * Given a modem and a connection, determine which NMModemIpType to use
+ * when connecting.
+ *
+ * Returns: a single %NMModemIpType value
+ */
+NMModemIPType
+nm_modem_get_connection_ip_type (NMModem *self,
+                                 NMConnection *connection,
+                                 GError **error)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMSettingIPConfig *s_ip4, *s_ip6;
+	const char *method;
+	gboolean ip4 = TRUE, ip6 = TRUE;
+	gboolean ip4_may_fail = TRUE, ip6_may_fail = TRUE;
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4) {
+		method = nm_setting_ip_config_get_method (s_ip4);
+		if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+			ip4 = FALSE;
+		ip4_may_fail = nm_setting_ip_config_get_may_fail (s_ip4);
+	}
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (s_ip6) {
+		method = nm_setting_ip_config_get_method (s_ip6);
+		if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+			ip6 = FALSE;
+		ip6_may_fail = nm_setting_ip_config_get_may_fail (s_ip6);
+	}
+
+	if (ip4 && !ip6) {
+		if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV4)) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			                     "Connection requested IPv4 but IPv4 is "
+			                     "unsuported by the modem.");
+			return NM_MODEM_IP_TYPE_UNKNOWN;
+		}
+		return NM_MODEM_IP_TYPE_IPV4;
+	}
+
+	if (ip6 && !ip4) {
+		if (!(priv->ip_types & NM_MODEM_IP_TYPE_IPV6)) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			                     "Connection requested IPv6 but IPv6 is "
+			                     "unsuported by the modem.");
+			return NM_MODEM_IP_TYPE_UNKNOWN;
+		}
+		return NM_MODEM_IP_TYPE_IPV6;
+	}
+
+	if (ip4 && ip6) {
+		/* Modem supports dual-stack */
+		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6)
+			return NM_MODEM_IP_TYPE_IPV4V6;
+
+		/* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack;
+		 * if one method is marked "may-fail" then use the other.
+		 */
+		if (ip6_may_fail)
+			return NM_MODEM_IP_TYPE_IPV4;
+		else if (ip4_may_fail)
+			return NM_MODEM_IP_TYPE_IPV6;
+
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+		                     "Connection requested both IPv4 and IPv6 "
+		                     "but dual-stack addressing is unsupported "
+		                     "by the modem.");
+		return NM_MODEM_IP_TYPE_UNKNOWN;
+	}
+
+	g_set_error_literal (error,
+	                     NM_DEVICE_ERROR,
+	                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+	                     "Connection specified no IP configuration!");
+	return NM_MODEM_IP_TYPE_UNKNOWN;
+}
+
 /*****************************************************************************/
 /* IP method PPP */
 
@@ -237,13 +330,24 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
+set_data_port (NMModem *self, const char *new_data_port)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+
+	if (g_strcmp0 (priv->data_port, new_data_port) != 0) {
+		g_free (priv->data_port);
+		priv->data_port = g_strdup (new_data_port);
+		g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+	}
+}
+
+static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
 				const char *iface,
 				NMIP4Config *config,
 				gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	guint32 i, num;
 	guint32 bad_dns1 = htonl (0x0A0B0C0D);
 	guint32 good_dns1 = htonl (0x04020201);  /* GTE nameserver */
@@ -252,9 +356,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 	gboolean dns_workaround = FALSE;
 
 	/* Notify about the new data port to use */
-	g_free (priv->ppp_iface);
-	priv->ppp_iface = g_strdup (iface);
-	g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
+	set_data_port (self, iface);
 
 	/* Work around a PPP bug (#1732) which causes many mobile broadband
 	 * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
@@ -296,6 +398,23 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 }
 
 static void
+ppp_ip6_config (NMPPPManager *ppp_manager,
+                const char *iface,
+                const NMUtilsIPv6IfaceId *iid,
+                NMIP6Config *config,
+                gpointer user_data)
+{
+	NMModem *self = NM_MODEM (user_data);
+
+	/* Notify about the new data port to use */
+	set_data_port (self, iface);
+
+	NM_MODEM_GET_PRIVATE (self)->iid = *iid;
+
+	nm_modem_emit_ip6_config_result (self, config, NULL);
+}
+
+static void
 ppp_stats (NMPPPManager *ppp_manager,
 		   guint32 in_bytes,
 		   guint32 out_bytes,
@@ -313,20 +432,27 @@ ppp_stats (NMPPPManager *ppp_manager,
 }
 
 static NMActStageReturn
-ppp_stage3_ip4_config_start (NMModem *self,
-                             NMActRequest *req,
-                             NMDeviceStateReason *reason)
+ppp_stage3_ip_config_start (NMModem *self,
+                            NMActRequest *req,
+                            NMDeviceStateReason *reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	const char *ppp_name = NULL;
 	GError *error = NULL;
 	NMActStageReturn ret;
-	guint ip_timeout = 20;
+	guint ip_timeout = 30;
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
+	/* If we're already running PPP don't restart it; for example, if both
+	 * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the
+	 * same pppd for both v4 and v6.
+	 */
+	if (priv->ppp_manager)
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+
 	if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
 		NMConnection *connection = nm_act_request_get_connection (req);
 
@@ -336,7 +462,7 @@ ppp_stage3_ip4_config_start (NMModem *self,
 	}
 
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
-	 * use the default one (20s) */
+	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
 		nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
 		             priv->mm_ip_timeout);
@@ -351,6 +477,9 @@ ppp_stage3_ip4_config_start (NMModem *self,
 		g_signal_connect (priv->ppp_manager, "ip4-config",
 		                  G_CALLBACK (ppp_ip4_config),
 		                  self);
+		g_signal_connect (priv->ppp_manager, "ip6-config",
+		                  G_CALLBACK (ppp_ip6_config),
+		                  self);
 		g_signal_connect (priv->ppp_manager, "stats",
 		                  G_CALLBACK (ppp_stats),
 		                  self);
@@ -382,6 +511,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 {
 	NMModemPrivate *priv;
 	NMActRequest *req;
+	NMConnection *connection;
+	const char *method;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
@@ -391,21 +522,36 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 
 	req = nm_device_get_act_request (device);
 	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+	/* Only Disabled and Auto methods make sense for WWAN */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+		return NM_ACT_STAGE_RETURN_STOP;
+
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) {
+		nm_log_warn (LOGD_MB | LOGD_IP4,
+		             "(%s): unhandled WWAN IPv4 method '%s'; will fail",
+		             nm_modem_get_uid (self), method);
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	priv = NM_MODEM_GET_PRIVATE (self);
-	switch (priv->ip_method) {
-	case MM_MODEM_IP_METHOD_PPP:
-		ret = ppp_stage3_ip4_config_start (self, req, reason);
+	switch (priv->ip4_method) {
+	case NM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage3_ip_config_start (self, req, reason);
 		break;
-	case MM_MODEM_IP_METHOD_STATIC:
+	case NM_MODEM_IP_METHOD_STATIC:
 		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
 		break;
-	case MM_MODEM_IP_METHOD_DHCP:
+	case NM_MODEM_IP_METHOD_AUTO:
 		ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
 		break;
 	default:
-		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self));
+		ret = NM_ACT_STAGE_RETURN_STOP;
 		break;
 	}
 
@@ -423,8 +569,8 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 	 * not point-to-point) and IP config has a /32 prefix, then we assume that
 	 * ARP will be pointless and we turn it off.
 	 */
-	if (   priv->ip_method == MM_MODEM_IP_METHOD_STATIC
-	    || priv->ip_method == MM_MODEM_IP_METHOD_DHCP) {
+	if (   priv->ip4_method == NM_MODEM_IP_METHOD_STATIC
+	    || priv->ip4_method == NM_MODEM_IP_METHOD_AUTO) {
 		const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0);
 
 		g_assert (address);
@@ -435,15 +581,100 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 
 /*****************************************************************************/
 
+void
+nm_modem_emit_ip6_config_result (NMModem *self,
+                                 NMIP6Config *config,
+                                 GError *error)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	guint i, num;
+	gboolean do_slaac = TRUE;
+
+	if (error) {
+		g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, NULL, FALSE, error);
+		return;
+	}
+
+	if (config) {
+		/* If the IPv6 configuration only included a Link-Local address, then
+		 * we have to run SLAAC to get the full IPv6 configuration.
+		 */
+		num = nm_ip6_config_get_num_addresses (config);
+		g_assert (num > 0);
+		for (i = 0; i < num; i++) {
+			const NMPlatformIP6Address * addr = nm_ip6_config_get_address (config, i);
+
+			if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+				if (!priv->iid.id)
+					priv->iid.id = ((guint64 *)(&addr->address.s6_addr))[1];
+			} else
+				do_slaac = FALSE;
+		}
+	}
+	g_assert (config || do_slaac);
+
+	g_signal_emit (self, signals[IP6_CONFIG_RESULT], 0, config, do_slaac, NULL);
+}
+
+static NMActStageReturn
+stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *reason)
+{
+	*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+	return NM_ACT_STAGE_RETURN_FAILURE;
+}
+
 NMActStageReturn
 nm_modem_stage3_ip6_config_start (NMModem *self,
-                                  NMDevice *device,
-                                  NMDeviceClass *device_class,
+                                  NMActRequest *req,
                                   NMDeviceStateReason *reason)
 {
-	/* FIXME: We don't support IPv6 on modems quite yet... */
-	nm_device_activate_schedule_ip6_config_timeout (device);
-	return NM_ACT_STAGE_RETURN_POSTPONE;
+	NMModemPrivate *priv;
+	NMActStageReturn ret;
+	NMConnection *connection;
+	const char *method;
+
+	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+
+	/* Only Ignore and Auto methods make sense for WWAN */
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
+		return NM_ACT_STAGE_RETURN_STOP;
+
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) {
+		nm_log_warn (LOGD_MB | LOGD_IP6,
+		             "(%s): unhandled WWAN IPv6 method '%s'; will fail",
+		             nm_modem_get_uid (self), method);
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	priv = NM_MODEM_GET_PRIVATE (self);
+	switch (priv->ip6_method) {
+	case NM_MODEM_IP_METHOD_PPP:
+		ret = ppp_stage3_ip_config_start (self, req, reason);
+		break;
+	case NM_MODEM_IP_METHOD_STATIC:
+	case NM_MODEM_IP_METHOD_AUTO:
+		/* Both static and DHCP/Auto retrieve a base IP config from the modem
+		 * which in the static case is the full config, and the DHCP/Auto case
+		 * is just the IPv6LL address to use for SLAAC.
+		 */
+		ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason);
+		break;
+	default:
+		nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self));
+		ret = NM_ACT_STAGE_RETURN_STOP;
+		break;
+	}
+
+	return ret;
 }
 
 /*****************************************************************************/
@@ -486,12 +717,12 @@ nm_modem_get_secrets (NMModem *self,
                       const char *hint)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
 	cancel_get_secrets (self);
 
 	if (request_new)
-		flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 	priv->secrets_id = nm_act_request_get_secrets (priv->act_request,
 	                                               setting_name,
 	                                               flags,
@@ -524,7 +755,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	NMActStageReturn ret;
 	GPtrArray *hints = NULL;
 	const char *setting_name = NULL;
-	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 	NMConnection *connection;
 
 	if (priv->act_request)
@@ -543,7 +774,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 
 	/* Secrets required... */
 	if (priv->secrets_tries++)
-		flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 
 	priv->secrets_id = nm_act_request_get_secrets (req,
 	                                               setting_name,
@@ -633,22 +864,19 @@ deactivate (NMModem *self, NMDevice *device)
 		priv->ppp_manager = NULL;
 	}
 
-	switch (priv->ip_method) {
-	case MM_MODEM_IP_METHOD_PPP:
-		break;
-	case MM_MODEM_IP_METHOD_STATIC:
-	case MM_MODEM_IP_METHOD_DHCP:
+	if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
+	    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
+	    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
+	    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
 		ifindex = nm_device_get_ip_ifindex (device);
 		if (ifindex > 0) {
 			nm_platform_route_flush (ifindex);
 			nm_platform_address_flush (ifindex);
 			nm_platform_link_set_down (ifindex);
 		}
-		break;
-	default:
-		nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
-		break;
 	}
+	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
+	priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
 
 	g_free (priv->ppp_iface);
 	priv->ppp_iface = NULL;
@@ -772,6 +1000,15 @@ nm_modem_owns_port (NMModem *self, const char *iface)
 	return FALSE;
 }
 
+gboolean
+nm_modem_get_iid (NMModem *self, NMUtilsIPv6IfaceId *out_iid)
+{
+	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
+
+	*out_iid = NM_MODEM_GET_PRIVATE (self)->iid;
+	return TRUE;
+}
+
 /*****************************************************************************/
 
 void
@@ -846,8 +1083,11 @@ get_property (GObject *object, guint prop_id,
 	case PROP_UID:
 		g_value_set_string (value, priv->uid);
 		break;
-	case PROP_IP_METHOD:
-		g_value_set_uint (value, priv->ip_method);
+	case PROP_IP4_METHOD:
+		g_value_set_uint (value, priv->ip4_method);
+		break;
+	case PROP_IP6_METHOD:
+		g_value_set_uint (value, priv->ip6_method);
 		break;
 	case PROP_IP_TIMEOUT:
 		g_value_set_uint (value, priv->mm_ip_timeout);
@@ -861,6 +1101,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SIM_ID:
 		g_value_set_string (value, priv->sim_id);
 		break;
+	case PROP_IP_TYPES:
+		g_value_set_uint (value, priv->ip_types);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -892,8 +1135,11 @@ set_property (GObject *object, guint prop_id,
 		/* Construct only */
 		priv->uid = g_value_dup_string (value);
 		break;
-	case PROP_IP_METHOD:
-		priv->ip_method = g_value_get_uint (value);
+	case PROP_IP4_METHOD:
+		priv->ip4_method = g_value_get_uint (value);
+		break;
+	case PROP_IP6_METHOD:
+		priv->ip6_method = g_value_get_uint (value);
 		break;
 	case PROP_IP_TIMEOUT:
 		priv->mm_ip_timeout = g_value_get_uint (value);
@@ -909,6 +1155,9 @@ set_property (GObject *object, guint prop_id,
 		g_free (priv->sim_id);
 		priv->sim_id = g_value_dup_string (value);
 		break;
+	case PROP_IP_TYPES:
+		priv->ip_types = g_value_get_uint (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -959,92 +1208,100 @@ nm_modem_class_init (NMModemClass *klass)
 	object_class->finalize = finalize;
 
 	klass->act_stage1_prepare = act_stage1_prepare;
+	klass->stage3_ip6_config_request = stage3_ip6_config_request;
 	klass->deactivate = deactivate;
 
 	/* Properties */
 
 	g_object_class_install_property
 		(object_class, PROP_UID,
-		 g_param_spec_string (NM_MODEM_UID,
-		                      "UID",
-		                      "Modem unique ID",
+		 g_param_spec_string (NM_MODEM_UID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_PATH,
-		 g_param_spec_string (NM_MODEM_PATH,
-		                      "DBus path",
-		                      "DBus path",
+		 g_param_spec_string (NM_MODEM_PATH, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DRIVER,
-		 g_param_spec_string (NM_MODEM_DRIVER,
-		                      "Driver",
-		                      "Driver",
+		 g_param_spec_string (NM_MODEM_DRIVER, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_CONTROL_PORT,
-		 g_param_spec_string (NM_MODEM_CONTROL_PORT,
-		                      "Control port",
-		                      "The port controlling the modem",
+		 g_param_spec_string (NM_MODEM_CONTROL_PORT, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DATA_PORT,
-		 g_param_spec_string (NM_MODEM_DATA_PORT,
-		                      "Data port",
-		                      "The port to connect to",
+		 g_param_spec_string (NM_MODEM_DATA_PORT, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-		(object_class, PROP_IP_METHOD,
-		 g_param_spec_uint (NM_MODEM_IP_METHOD,
-		                    "IP method",
-		                    "IP method",
-		                    MM_MODEM_IP_METHOD_PPP,
-		                    MM_MODEM_IP_METHOD_DHCP,
-		                    MM_MODEM_IP_METHOD_PPP,
-		                    G_PARAM_READWRITE));
+		(object_class, PROP_IP4_METHOD,
+		 g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "",
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    NM_MODEM_IP_METHOD_AUTO,
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_IP6_METHOD,
+		 g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "",
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    NM_MODEM_IP_METHOD_AUTO,
+		                    NM_MODEM_IP_METHOD_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_IP_TIMEOUT,
-		 g_param_spec_uint (NM_MODEM_IP_TIMEOUT,
-		                    "IP timeout",
-		                    "IP timeout",
+		 g_param_spec_uint (NM_MODEM_IP_TIMEOUT, "", "",
 		                    0, 360, 20,
-		                    G_PARAM_READWRITE));
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STATE,
-		 g_param_spec_enum (NM_MODEM_STATE,
-		                   "State",
-		                   "State",
-		                   NM_TYPE_MODEM_STATE,
-		                   NM_MODEM_STATE_UNKNOWN,
-		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		 g_param_spec_enum (NM_MODEM_STATE, "", "",
+		                    NM_TYPE_MODEM_STATE,
+		                    NM_MODEM_STATE_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DEVICE_ID,
-		 g_param_spec_string (NM_MODEM_DEVICE_ID,
-		                      "DeviceId",
-		                      "Device ID",
+		 g_param_spec_string (NM_MODEM_DEVICE_ID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_SIM_ID,
-		 g_param_spec_string (NM_MODEM_SIM_ID,
-		                      "SimId",
-		                      "Sim ID",
+		 g_param_spec_string (NM_MODEM_SIM_ID, "", "",
 		                      NULL,
-		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_IP_TYPES,
+		 g_param_spec_uint (NM_MODEM_IP_TYPES,
+		                    "IP Types",
+		                    "Supported IP types",
+		                    0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
 	/* Signals */
 
@@ -1073,6 +1330,27 @@ nm_modem_class_init (NMModemClass *klass)
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER);
 
+	/**
+	 * NMModem::ip6-config-result:
+	 * @modem: the #NMModem  on which the signal is emitted
+	 * @config: the #NMIP6Config to apply to the modem's data port
+	 * @do_slaac: %TRUE if IPv6 SLAAC should be started
+	 * @error: a #GError if any error occurred during IP configuration
+	 *
+	 * This signal is emitted when IPv6 configuration has completed or failed.
+	 * If @error is set the configuration failed.  If @config is set, then
+	 * the details should be applied to the data port before any further
+	 * configuration (like SLAAC) is done.  @do_slaac indicates whether SLAAC
+	 * should be started after applying @config to the data port.
+	 */
+	signals[IP6_CONFIG_RESULT] =
+		g_signal_new (NM_MODEM_IP6_CONFIG_RESULT,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMModemClass, ip6_config_result),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+
 	signals[PREPARE_RESULT] =
 		g_signal_new (NM_MODEM_PREPARE_RESULT,
 		              G_OBJECT_CLASS_TYPE (object_class),
@@ -1112,8 +1390,4 @@ nm_modem_class_init (NMModemClass *klass)
 		              G_STRUCT_OFFSET (NMModemClass, state_changed),
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, NM_TYPE_MODEM_STATE, NM_TYPE_MODEM_STATE);
-
-	dbus_g_error_domain_register (NM_MODEM_ERROR,
-	                              NM_DBUS_INTERFACE_DEVICE_MODEM,
-	                              NM_TYPE_MODEM_ERROR);
 }
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index c992cf72..2a4d9173 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -19,10 +19,9 @@
  * Copyright (C) 2009 Novell, Inc.
  */
 
-#ifndef NM_MODEM_H
-#define NM_MODEM_H
+#ifndef __NETWORKMANAGER_MODEM_H__
+#define __NETWORKMANAGER_MODEM_H__
 
-#include <dbus/dbus-glib.h>
 #include <glib-object.h>
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-device.h"
@@ -42,33 +41,50 @@ G_BEGIN_DECLS
 #define NM_MODEM_DRIVER       "driver"
 #define NM_MODEM_CONTROL_PORT "control-port"
 #define NM_MODEM_DATA_PORT    "data-port"
-#define NM_MODEM_IP_METHOD    "ip-method"
+#define NM_MODEM_IP4_METHOD   "ip4-method"
+#define NM_MODEM_IP6_METHOD   "ip6-method"
 #define NM_MODEM_IP_TIMEOUT   "ip-timeout"
 #define NM_MODEM_STATE        "state"
 #define NM_MODEM_DEVICE_ID    "device-id"
 #define NM_MODEM_SIM_ID       "sim-id"
+#define NM_MODEM_IP_TYPES     "ip-types"   /* Supported IP types */
 
 /* Signals */
 #define NM_MODEM_PPP_STATS         "ppp-stats"
 #define NM_MODEM_PPP_FAILED        "ppp-failed"
 #define NM_MODEM_PREPARE_RESULT    "prepare-result"
 #define NM_MODEM_IP4_CONFIG_RESULT "ip4-config-result"
+#define NM_MODEM_IP6_CONFIG_RESULT "ip6-config-result"
 #define NM_MODEM_AUTH_REQUESTED    "auth-requested"
 #define NM_MODEM_AUTH_RESULT       "auth-result"
 #define NM_MODEM_REMOVED           "removed"
 #define NM_MODEM_STATE_CHANGED     "state-changed"
 
-#define MM_MODEM_IP_METHOD_PPP    0
-#define MM_MODEM_IP_METHOD_STATIC 1
-#define MM_MODEM_IP_METHOD_DHCP   2
-
 typedef enum {
-	NM_MODEM_ERROR_CONNECTION_NOT_GSM,      /*< nick=ConnectionNotGsm >*/
-	NM_MODEM_ERROR_CONNECTION_NOT_CDMA,     /*< nick=ConnectionNotCdma >*/
-	NM_MODEM_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
-	NM_MODEM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-	NM_MODEM_ERROR_INITIALIZATION_FAILED,   /*< nick=InitializationFailed >*/
-} NMModemError;
+	NM_MODEM_IP_METHOD_UNKNOWN = 0,
+	NM_MODEM_IP_METHOD_PPP,
+	NM_MODEM_IP_METHOD_STATIC,
+	NM_MODEM_IP_METHOD_AUTO,  /* DHCP and/or SLAAC */
+} NMModemIPMethod;
+
+/**
+ * NMModemIPType:
+ * @NM_MODEM_IP_TYPE_UNKNOWN: unknown or no IP support
+ * @NM_MODEM_IP_TYPE_IPV4: IPv4-only bearers are supported
+ * @NM_MODEM_IP_TYPE_IPV6: IPv6-only bearers are supported
+ * @NM_MODEM_IP_TYPE_IPV4V6: dual-stack IPv4 + IPv6 bearers are supported
+ *
+ * Indicates what IP protocols the modem supports for an IP bearer.  Any
+ * combination of flags is possible.  For example, (%NM_MODEM_IP_TYPE_IPV4 |
+ * %NM_MODEM_IP_TYPE_IPV6) indicates that the modem supports IPv4 and IPv6
+ * but not simultaneously on the same bearer.
+ */ 
+typedef enum {
+	NM_MODEM_IP_TYPE_UNKNOWN = 0x0,
+	NM_MODEM_IP_TYPE_IPV4 = 0x1,
+	NM_MODEM_IP_TYPE_IPV6 = 0x2,
+	NM_MODEM_IP_TYPE_IPV4V6 = 0x4
+} NMModemIPType;
 
 typedef enum {  /*< underscore_name=nm_modem_state >*/
 	NM_MODEM_STATE_UNKNOWN       = 0,
@@ -86,9 +102,6 @@ typedef enum {  /*< underscore_name=nm_modem_state >*/
 	NM_MODEM_STATE_CONNECTED     = 12,
 } NMModemState;
 
-#define NM_MODEM_ERROR (nm_modem_error_quark ())
-GQuark nm_modem_error_quark (void);
-
 
 typedef struct {
 	GObject parent;
@@ -122,6 +135,12 @@ typedef struct {
 	                                                    NMActRequest *req,
 	                                                    NMDeviceStateReason *reason);
 
+	/* Request the IP6 config; when the config returns the modem
+	 * subclass should emit the ip6_config_result signal.
+	 */
+	NMActStageReturn (*stage3_ip6_config_request) (NMModem *self,
+	                                               NMDeviceStateReason *reason);
+
 	void (*set_mm_enabled)                     (NMModem *self, gboolean enabled);
 
 	void (*disconnect)                         (NMModem *self, gboolean warn);
@@ -136,6 +155,10 @@ typedef struct {
 
 	void (*prepare_result)    (NMModem *self, gboolean success, NMDeviceStateReason reason);
 	void (*ip4_config_result) (NMModem *self, NMIP4Config *config, GError *error);
+	void (*ip6_config_result) (NMModem *self,
+	                           NMIP6Config *config,
+	                           gboolean do_slaac,
+	                           GError *error);
 
 	void (*auth_requested)    (NMModem *self);
 	void (*auth_result)       (NMModem *self, GError *error);
@@ -154,6 +177,7 @@ const char *nm_modem_get_uid          (NMModem *modem);
 const char *nm_modem_get_control_port (NMModem *modem);
 const char *nm_modem_get_data_port    (NMModem *modem);
 const char *nm_modem_get_driver       (NMModem *modem);
+gboolean    nm_modem_get_iid          (NMModem *modem, NMUtilsIPv6IfaceId *out_iid);
 
 gboolean    nm_modem_owns_port        (NMModem *modem, const char *iface);
 
@@ -182,8 +206,7 @@ NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
                                                    NMDeviceStateReason *reason);
 
 NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem,
-                                                   NMDevice *device,
-                                                   NMDeviceClass *device_class,
+                                                   NMActRequest *req,
                                                    NMDeviceStateReason *reason);
 
 void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config);
@@ -209,9 +232,20 @@ void          nm_modem_set_state (NMModem *self,
 void          nm_modem_set_prev_state (NMModem *self, const char *reason);
 const char *  nm_modem_state_to_string (NMModemState state);
 
+NMModemIPType nm_modem_get_supported_ip_types (NMModem *self);
+
 /* For the modem-manager only */
 void          nm_modem_emit_removed (NMModem *self);
 
+NMModemIPType nm_modem_get_connection_ip_type (NMModem *self,
+                                               NMConnection *connection,
+                                               GError **error);
+
+/* For subclasses */
+void nm_modem_emit_ip6_config_result (NMModem *self,
+                                      NMIP6Config *config,
+                                      GError *error);
+
 G_END_DECLS
 
-#endif /* NM_MODEM_H */
+#endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index b1e2307e..2b9c3d4d 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -18,10 +18,11 @@
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #include <string.h>
 #include <gmodule.h>
 
-#include "config.h"
 #include "nm-device-factory.h"
 #include "nm-wwan-factory.h"
 #include "nm-modem-manager.h"
@@ -43,20 +44,12 @@ typedef struct {
 
 /************************************************************************/
 
-#define PLUGIN_TYPE NM_DEVICE_TYPE_MODEM
-
 G_MODULE_EXPORT NMDeviceFactory *
 nm_device_factory_create (GError **error)
 {
 	return (NMDeviceFactory *) g_object_new (NM_TYPE_WWAN_FACTORY, NULL);
 }
 
-G_MODULE_EXPORT NMDeviceType
-nm_device_factory_get_device_type (void)
-{
-	return PLUGIN_TYPE;
-}
-
 /************************************************************************/
 
 static void
@@ -93,9 +86,17 @@ modem_added_cb (NMModemManager *manager,
 	g_object_unref (device);
 }
 
+
+static NMDeviceType
+get_device_type (NMDeviceFactory *factory)
+{
+	return NM_DEVICE_TYPE_MODEM;
+}
+
 static void
-nm_wwan_factory_init (NMWwanFactory *self)
+start (NMDeviceFactory *factory)
 {
+	NMWwanFactory *self = NM_WWAN_FACTORY (factory);
 	NMWwanFactoryPrivate *priv = NM_WWAN_FACTORY_GET_PRIVATE (self);
 
 	priv->mm = g_object_new (NM_TYPE_MODEM_MANAGER, NULL);
@@ -107,8 +108,15 @@ nm_wwan_factory_init (NMWwanFactory *self)
 }
 
 static void
+nm_wwan_factory_init (NMWwanFactory *self)
+{
+}
+
+static void
 device_factory_interface_init (NMDeviceFactory *factory_iface)
 {
+	factory_iface->get_device_type = get_device_type;
+	factory_iface->start = start;
 }
 
 static void
diff --git a/src/devices/wwan/nm-wwan-factory.h b/src/devices/wwan/nm-wwan-factory.h
index b7aee01f..c6c03f1f 100644
--- a/src/devices/wwan/nm-wwan-factory.h
+++ b/src/devices/wwan/nm-wwan-factory.h
@@ -18,8 +18,8 @@
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
-#ifndef NM_WWAN_FACTORY_H
-#define NM_WWAN_FACTORY_H
+#ifndef __NETWORKMANAGER_WWAN_FACTORY_H__
+#define __NETWORKMANAGER_WWAN_FACTORY_H__
 
 #include <glib-object.h>
 
@@ -34,4 +34,4 @@ typedef struct {
 	GObjectClass parent;
 } NMWwanFactoryClass;
 
-#endif /* NM_WWAN_FACTORY_H */
+#endif /* __NETWORKMANAGER_WWAN_FACTORY_H__ */
diff --git a/src/devices/wwan/wwan-exports.ver b/src/devices/wwan/wwan-exports.ver
index dc505d17..c23ab24b 100644
--- a/src/devices/wwan/wwan-exports.ver
+++ b/src/devices/wwan/wwan-exports.ver
@@ -6,11 +6,11 @@ global:
 	nm_modem_complete_connection;
 	nm_modem_deactivate;
 	nm_modem_device_state_changed;
-	nm_modem_error_quark;
 	nm_modem_get_capabilities;
 	nm_modem_get_control_port;
 	nm_modem_get_data_port;
 	nm_modem_get_driver;
+	nm_modem_get_iid;
 	nm_modem_get_path;
 	nm_modem_get_secrets;
 	nm_modem_get_state;