summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/Makefile.am1
-rw-r--r--src/devices/Makefile.in (renamed from src/devices/wimax/Makefile.in)446
-rw-r--r--src/devices/adsl/Makefile.am25
-rw-r--r--src/devices/adsl/Makefile.in62
-rw-r--r--src/devices/adsl/nm-adsl-enum-types.c12
-rw-r--r--src/devices/adsl/nm-adsl-enum-types.h17
-rw-r--r--src/devices/adsl/nm-atm-manager.c8
-rw-r--r--src/devices/adsl/nm-atm-manager.h3
-rw-r--r--src/devices/adsl/nm-device-adsl-glue.h73
-rw-r--r--src/devices/adsl/nm-device-adsl.c32
-rw-r--r--src/devices/adsl/nm-device-adsl.h3
-rw-r--r--src/devices/bluetooth/Makefile.am18
-rw-r--r--src/devices/bluetooth/Makefile.in39
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h2
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c48
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h6
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c28
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h3
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c95
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h3
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c173
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.h7
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c7
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h3
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c130
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h7
-rw-r--r--src/devices/bluetooth/nm-bt-enum-types.c2
-rw-r--r--src/devices/bluetooth/nm-bt-error.c2
-rw-r--r--src/devices/bluetooth/nm-device-bt-glue.h73
-rw-r--r--src/devices/bluetooth/nm-device-bt.c107
-rw-r--r--src/devices/bluetooth/nm-device-bt.h2
-rw-r--r--src/devices/nm-device-bond.c164
-rw-r--r--src/devices/nm-device-bond.h4
-rw-r--r--src/devices/nm-device-bridge.c195
-rw-r--r--src/devices/nm-device-bridge.h5
-rw-r--r--src/devices/nm-device-ethernet-utils.c3
-rw-r--r--src/devices/nm-device-ethernet-utils.h2
-rw-r--r--src/devices/nm-device-ethernet.c256
-rw-r--r--src/devices/nm-device-ethernet.h3
-rw-r--r--src/devices/nm-device-factory.c150
-rw-r--r--src/devices/nm-device-factory.h100
-rw-r--r--src/devices/nm-device-generic.c57
-rw-r--r--src/devices/nm-device-generic.h4
-rw-r--r--src/devices/nm-device-gre.c283
-rw-r--r--src/devices/nm-device-gre.h55
-rw-r--r--src/devices/nm-device-infiniband.c165
-rw-r--r--src/devices/nm-device-infiniband.h2
-rw-r--r--src/devices/nm-device-ip-tunnel.c1052
-rw-r--r--src/devices/nm-device-ip-tunnel.h60
-rw-r--r--src/devices/nm-device-logging.h2
-rw-r--r--src/devices/nm-device-macvlan.c653
-rw-r--r--src/devices/nm-device-macvlan.h9
-rw-r--r--src/devices/nm-device-private.h13
-rw-r--r--src/devices/nm-device-tun.c356
-rw-r--r--src/devices/nm-device-tun.h6
-rw-r--r--src/devices/nm-device-veth.c43
-rw-r--r--src/devices/nm-device-veth.h2
-rw-r--r--src/devices/nm-device-vlan.c538
-rw-r--r--src/devices/nm-device-vlan.h5
-rw-r--r--src/devices/nm-device-vxlan.c602
-rw-r--r--src/devices/nm-device-vxlan.h6
-rw-r--r--src/devices/nm-device.c3971
-rw-r--r--src/devices/nm-device.h205
-rw-r--r--src/devices/nm-lldp-listener.c648
-rw-r--r--src/devices/nm-lldp-listener.h57
-rw-r--r--src/devices/team/Makefile.am30
-rw-r--r--src/devices/team/Makefile.in73
-rw-r--r--src/devices/team/nm-device-team-glue.h73
-rw-r--r--src/devices/team/nm-device-team.c156
-rw-r--r--src/devices/team/nm-device-team.h18
-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.c26
-rw-r--r--src/devices/team/nm-team-factory.h2
-rw-r--r--src/devices/tests/Makefile.am42
-rw-r--r--src/devices/tests/Makefile.in1193
-rw-r--r--src/devices/tests/test-lldp.c221
-rw-r--r--src/devices/wifi/Makefile.am37
-rw-r--r--src/devices/wifi/Makefile.in80
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh-glue.h73
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c90
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.h4
-rw-r--r--src/devices/wifi/nm-device-wifi-glue.h167
-rw-r--r--src/devices/wifi/nm-device-wifi.c1379
-rw-r--r--src/devices/wifi/nm-device-wifi.h4
-rw-r--r--src/devices/wifi/nm-wifi-ap-utils.c3
-rw-r--r--src/devices/wifi/nm-wifi-ap.c1202
-rw-r--r--src/devices/wifi/nm-wifi-ap.h112
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.c14
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.h17
-rw-r--r--src/devices/wifi/nm-wifi-factory.c33
-rw-r--r--src/devices/wifi/tests/Makefile.am10
-rw-r--r--src/devices/wifi/tests/Makefile.in28
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c13
-rw-r--r--src/devices/wimax/Makefile.am60
-rw-r--r--src/devices/wimax/exports.ver6
-rw-r--r--src/devices/wimax/iwmxsdk.c1515
-rw-r--r--src/devices/wimax/iwmxsdk.h112
-rw-r--r--src/devices/wimax/nm-device-wimax.c1416
-rw-r--r--src/devices/wimax/nm-device-wimax.h64
-rw-r--r--src/devices/wimax/nm-wimax-factory.c91
-rw-r--r--src/devices/wimax/nm-wimax-nsp.c241
-rw-r--r--src/devices/wimax/nm-wimax-nsp.h63
-rw-r--r--src/devices/wimax/nm-wimax-types.h31
-rw-r--r--src/devices/wimax/nm-wimax-util.c83
-rw-r--r--src/devices/wimax/nm-wimax-util.h43
-rw-r--r--src/devices/wwan/Makefile.am24
-rw-r--r--src/devices/wwan/Makefile.in55
-rw-r--r--src/devices/wwan/nm-device-modem-glue.h73
-rw-r--r--src/devices/wwan/nm-device-modem.c33
-rw-r--r--src/devices/wwan/nm-device-modem.h3
-rw-r--r--src/devices/wwan/nm-modem-broadband.c368
-rw-r--r--src/devices/wwan/nm-modem-broadband.h1
-rw-r--r--src/devices/wwan/nm-modem-enum-types.c2
-rw-r--r--src/devices/wwan/nm-modem-manager.c15
-rw-r--r--src/devices/wwan/nm-modem-manager.h1
-rw-r--r--src/devices/wwan/nm-modem.c171
-rw-r--r--src/devices/wwan/nm-modem.h50
-rw-r--r--src/devices/wwan/nm-wwan-factory.c17
-rw-r--r--src/devices/wwan/nm-wwan-factory.h2
120 files changed, 10634 insertions, 10148 deletions
diff --git a/src/devices/Makefile.am b/src/devices/Makefile.am
new file mode 100644
index 00000000..3c6155b9
--- /dev/null
+++ b/src/devices/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = tests
diff --git a/src/devices/wimax/Makefile.in b/src/devices/Makefile.in
index b1b57a79..4415d31b 100644
--- a/src/devices/wimax/Makefile.in
+++ b/src/devices/Makefile.in
@@ -13,7 +13,6 @@
 # PARTICULAR PURPOSE.
 
 @SET_MAKE@
-
 VPATH = @srcdir@
 am__is_gnu_make = { \
   if test -z '$(MAKELEVEL)'; then \
@@ -88,7 +87,7 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-subdir = src/devices/wimax
+subdir = src/devices
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -110,50 +109,6 @@ 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 =
-libnm_device_plugin_wimax_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-am_libnm_device_plugin_wimax_la_OBJECTS = nm-wimax-factory.lo \
-	nm-device-wimax.lo nm-wimax-nsp.lo nm-wimax-util.lo iwmxsdk.lo
-libnm_device_plugin_wimax_la_OBJECTS =  \
-	$(am_libnm_device_plugin_wimax_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_wimax_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
-	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
-	$(AM_CFLAGS) $(CFLAGS) $(libnm_device_plugin_wimax_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
@@ -166,35 +121,29 @@ 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_wimax_la_SOURCES)
-DIST_SOURCES = $(libnm_device_plugin_wimax_la_SOURCES)
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
 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
@@ -214,9 +163,34 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in \
-	$(top_srcdir)/build-aux/depcomp
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
 AMTAR = @AMTAR@
@@ -232,7 +206,6 @@ 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@
@@ -244,8 +217,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -255,6 +226,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -309,16 +281,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -355,6 +324,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -383,7 +354,6 @@ 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@
@@ -398,6 +368,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -458,6 +430,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -465,6 +438,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -480,55 +454,10 @@ 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}/include \
-	-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) \
-	$(IWMX_SDK_CFLAGS)
-
-pkglib_LTLIBRARIES = libnm-device-plugin-wimax.la
-SYMBOL_VIS_FILE = $(srcdir)/exports.ver
-libnm_device_plugin_wimax_la_SOURCES = \
-	nm-wimax-factory.c \
-	nm-device-wimax.c \
-	nm-device-wimax.h \
-	nm-wimax-nsp.c \
-	nm-wimax-nsp.h \
-	nm-wimax-types.h \
-	nm-wimax-util.c \
-	nm-wimax-util.h \
-	iwmxsdk.c \
-	iwmxsdk.h
-
-libnm_device_plugin_wimax_la_LDFLAGS = \
-	-module -avoid-version \
-	-Wl,--version-script=$(SYMBOL_VIS_FILE)
-
-libnm_device_plugin_wimax_la_LIBADD = \
-	$(DBUS_LIBS) \
-	$(IWMX_SDK_LIBS) \
-	$(GUDEV_LIBS)
-
-BUILT_SOURCES = \
-	nm-wimax-nsp-glue.h \
-	nm-device-wimax-glue.h
-
-CLEANFILES = $(BUILT_SOURCES)
-EXTRA_DIST = $(SYMBOL_VIS_FILE)
-all: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) all-am
+SUBDIRS = tests
+all: all-recursive
 
 .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 \
@@ -538,9 +467,9 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	      exit 1;; \
 	  esac; \
 	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wimax/Makefile'; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu src/devices/wimax/Makefile
+	  $(AUTOMAKE) --gnu src/devices/Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -559,91 +488,67 @@ $(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-wimax.la: $(libnm_device_plugin_wimax_la_OBJECTS) $(libnm_device_plugin_wimax_la_DEPENDENCIES) $(EXTRA_libnm_device_plugin_wimax_la_DEPENDENCIES) 
-	$(AM_V_CCLD)$(libnm_device_plugin_wimax_la_LINK) -rpath $(pkglibdir) $(libnm_device_plugin_wimax_la_OBJECTS) $(libnm_device_plugin_wimax_la_LIBADD) $(LIBS)
-
-mostlyclean-compile:
-	-rm -f *.$(OBJEXT)
-
-distclean-compile:
-	-rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iwmxsdk.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-wimax.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wimax-factory.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wimax-nsp.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wimax-util.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 -o $@ $<
-
-.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 -o $@ `$(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
 
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
 ID: $(am__tagged_files)
 	$(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-am
+tags: tags-recursive
 TAGS: tags
 
 tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	set x; \
 	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
 	$(am__define_uniq_tagged_files); \
 	shift; \
 	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
@@ -656,7 +561,7 @@ tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
 	      $$unique; \
 	  fi; \
 	fi
-ctags: ctags-am
+ctags: ctags-recursive
 
 CTAGS: ctags
 ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
@@ -669,7 +574,7 @@ GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
 	  && $(am__cd) $(top_srcdir) \
 	  && gtags -i $(GTAGS_ARGS) "$$here"
-cscopelist: cscopelist-am
+cscopelist: cscopelist-recursive
 
 cscopelist-am: $(am__tagged_files)
 	list='$(am__tagged_files)'; \
@@ -718,26 +623,45 @@ distdir: $(DISTFILES)
 	    || 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"; \
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
 	done
-install: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
 
 install-am: all-am
 	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
 
-installcheck: installcheck-am
+installcheck: installcheck-recursive
 install-strip:
 	if test -z '$(STRIP)'; then \
 	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
@@ -751,7 +675,6 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -760,106 +683,89 @@ distclean-generic:
 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: clean-recursive
 
-clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
-	mostlyclean-am
+clean-am: clean-generic clean-libtool mostlyclean-am
 
-distclean: distclean-am
-	-rm -rf ./$(DEPDIR)
+distclean: distclean-recursive
 	-rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
-	distclean-tags
+distclean-am: clean-am distclean-generic distclean-tags
 
-dvi: dvi-am
+dvi: dvi-recursive
 
 dvi-am:
 
-html: html-am
+html: html-recursive
 
 html-am:
 
-info: info-am
+info: info-recursive
 
 info-am:
 
 install-data-am:
 
-install-dvi: install-dvi-am
+install-dvi: install-dvi-recursive
 
 install-dvi-am:
 
-install-exec-am: install-pkglibLTLIBRARIES
+install-exec-am:
 
-install-html: install-html-am
+install-html: install-html-recursive
 
 install-html-am:
 
-install-info: install-info-am
+install-info: install-info-recursive
 
 install-info-am:
 
 install-man:
 
-install-pdf: install-pdf-am
+install-pdf: install-pdf-recursive
 
 install-pdf-am:
 
-install-ps: install-ps-am
+install-ps: install-ps-recursive
 
 install-ps-am:
 
 installcheck-am:
 
-maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR)
+maintainer-clean: maintainer-clean-recursive
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
-mostlyclean: mostlyclean-am
+mostlyclean: mostlyclean-recursive
 
-mostlyclean-am: mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
 
-pdf: pdf-am
+pdf: pdf-recursive
 
 pdf-am:
 
-ps: ps-am
+ps: ps-recursive
 
 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
+uninstall-am:
 
-.PRECIOUS: Makefile
+.MAKE: $(am__recursive_targets) install-am install-strip
 
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-generic clean-libtool cscopelist-am ctags \
+	ctags-am distclean 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-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags tags-am uninstall uninstall-am
 
-nm-wimax-nsp-glue.h: $(top_srcdir)/introspection/nm-wimax-nsp.xml
-	dbus-binding-tool --prefix=nm_wimax_nsp --mode=glib-server --output=$@ $<
-
-nm-device-wimax-glue.h: $(top_srcdir)/introspection/nm-device-wimax.xml
-	dbus-binding-tool --prefix=nm_device_wimax --mode=glib-server --output=$@ $<
+.PRECIOUS: Makefile
 
-@ENABLE_TESTS_TRUE@check-local:
-@ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wimax.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.
diff --git a/src/devices/adsl/Makefile.am b/src/devices/adsl/Makefile.am
index d7457c88..e7034fa8 100644
--- a/src/devices/adsl/Makefile.am
+++ b/src/devices/adsl/Makefile.am
@@ -7,26 +7,16 @@ AM_CPPFLAGS = \
 	-I${top_builddir}/src \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-adsl"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
 	$(GUDEV_CFLAGS)
 
-GLIB_GENERATED = nm-adsl-enum-types.h nm-adsl-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_adsl_enum_types_sources = $(srcdir)/nm-device-adsl.h
-
-nm-device-adsl-glue.h: $(top_srcdir)/introspection/nm-device-adsl.xml
-	dbus-binding-tool --prefix=nm_device_adsl --mode=glib-server --output=$@ $<
-
-BUILT_SOURCES = $(GLIB_GENERATED) nm-device-adsl-glue.h
-
 pkglib_LTLIBRARIES = libnm-device-plugin-adsl.la
 
 SYMBOL_VIS_FILE=$(srcdir)/exports.ver
@@ -35,19 +25,16 @@ libnm_device_plugin_adsl_la_SOURCES = \
 	nm-atm-manager.c \
 	nm-atm-manager.h \
 	nm-device-adsl.c \
-	nm-device-adsl.h \
-	\
-	$(BUILT_SOURCES)
+	nm-device-adsl.h
 
 libnm_device_plugin_adsl_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_adsl_la_LIBADD = \
-	$(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
 	$(GUDEV_LIBS)
 
-CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
 
 if ENABLE_TESTS
diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in
index b6e944f8..e969a627 100644
--- a/src/devices/adsl/Makefile.in
+++ b/src/devices/adsl/Makefile.in
@@ -140,12 +140,11 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-libnm_device_plugin_adsl_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+libnm_device_plugin_adsl_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la \
 	$(am__DEPENDENCIES_1)
-am__objects_1 = nm-adsl-enum-types.lo
-am__objects_2 = $(am__objects_1)
 am_libnm_device_plugin_adsl_la_OBJECTS = nm-atm-manager.lo \
-	nm-device-adsl.lo $(am__objects_2)
+	nm-device-adsl.lo
 libnm_device_plugin_adsl_la_OBJECTS =  \
 	$(am_libnm_device_plugin_adsl_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
@@ -234,7 +233,6 @@ 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@
@@ -246,8 +244,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -257,6 +253,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -311,16 +308,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -357,6 +351,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -385,7 +381,6 @@ 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@
@@ -400,6 +395,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -460,6 +457,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -467,6 +465,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -487,43 +486,34 @@ AM_CPPFLAGS = \
 	-I${top_builddir}/src \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-adsl"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
 	$(GUDEV_CFLAGS)
 
-GLIB_GENERATED = nm-adsl-enum-types.h nm-adsl-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_adsl_enum_types_sources = $(srcdir)/nm-device-adsl.h
-BUILT_SOURCES = $(GLIB_GENERATED) nm-device-adsl-glue.h
 pkglib_LTLIBRARIES = libnm-device-plugin-adsl.la
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
 libnm_device_plugin_adsl_la_SOURCES = \
 	nm-atm-manager.c \
 	nm-atm-manager.h \
 	nm-device-adsl.c \
-	nm-device-adsl.h \
-	\
-	$(BUILT_SOURCES)
+	nm-device-adsl.h
 
 libnm_device_plugin_adsl_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_adsl_la_LIBADD = \
-	$(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
 	$(GUDEV_LIBS)
 
-CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
-all: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) all-am
+all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
@@ -601,7 +591,6 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-adsl-enum-types.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-atm-manager.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-adsl.Plo@am__quote@
 
@@ -717,15 +706,13 @@ distdir: $(DISTFILES)
 @ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) check-local
-check: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) check-am
+check: 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: install-am
 install-exec: install-exec-am
 install-data: install-data-am
 uninstall: uninstall-am
@@ -747,7 +734,6 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -756,7 +742,6 @@ distclean-generic:
 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 \
@@ -828,7 +813,7 @@ ps-am:
 
 uninstall-am: uninstall-pkglibLTLIBRARIES
 
-.MAKE: all check check-am install install-am install-strip
+.MAKE: check-am install-am install-strip
 
 .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \
 	clean-generic clean-libtool clean-pkglibLTLIBRARIES \
@@ -851,9 +836,6 @@ include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
 
-nm-device-adsl-glue.h: $(top_srcdir)/introspection/nm-device-adsl.xml
-	dbus-binding-tool --prefix=nm_device_adsl --mode=glib-server --output=$@ $<
-
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-adsl.so $(SYMBOL_VIS_FILE)
 
diff --git a/src/devices/adsl/nm-adsl-enum-types.c b/src/devices/adsl/nm-adsl-enum-types.c
deleted file mode 100644
index 08565331..00000000
--- a/src/devices/adsl/nm-adsl-enum-types.c
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-/* Generated by glib-mkenums. Do not edit */
-
-#include "nm-adsl-enum-types.h"
-
-#include "nm-device-adsl.h"
-
-
-
-
diff --git a/src/devices/adsl/nm-adsl-enum-types.h b/src/devices/adsl/nm-adsl-enum-types.h
deleted file mode 100644
index 88e1fe84..00000000
--- a/src/devices/adsl/nm-adsl-enum-types.h
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-/* Generated by glib-mkenums. Do not edit */
-
-#ifndef __NM_ADSL_ENUM_TYPES_H__
-#define __NM_ADSL_ENUM_TYPES_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-G_END_DECLS
-
-#endif /* __NM_ADSL_ENUM_TYPES_H__ */
-
-
-
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index aeb5af6c..d3f1423f 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -24,14 +24,12 @@
 #include <gudev/gudev.h>
 #include <gmodule.h>
 
+#include "nm-default.h"
 #include "nm-atm-manager.h"
 #include "nm-setting-adsl.h"
 #include "nm-device-adsl.h"
 #include "nm-device-factory.h"
-#include "nm-logging.h"
-#include "nm-glib-compat.h"
 #include "nm-platform.h"
-#include "gsystem-local-alloc.h"
 
 typedef struct {
 	GUdevClient *client;
@@ -42,7 +40,7 @@ typedef struct {
 
 static GType nm_atm_manager_get_type (void);
 
-static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+static void device_factory_interface_init (NMDeviceFactoryInterface *factory_iface);
 
 G_DEFINE_TYPE_EXTENDED (NMAtmManager, nm_atm_manager, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
@@ -239,7 +237,7 @@ nm_atm_manager_init (NMAtmManager *self)
 }
 
 static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
+device_factory_interface_init (NMDeviceFactoryInterface *factory_iface)
 {
 	factory_iface->get_supported_types = get_supported_types;
 	factory_iface->start = start;
diff --git a/src/devices/adsl/nm-atm-manager.h b/src/devices/adsl/nm-atm-manager.h
index e4ec3e6a..fe0bc14c 100644
--- a/src/devices/adsl/nm-atm-manager.h
+++ b/src/devices/adsl/nm-atm-manager.h
@@ -22,8 +22,7 @@
 #ifndef __NETWORKMANAGER_ATM_MANAGER_H__
 #define __NETWORKMANAGER_ATM_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
+#include "nm-default.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/devices/adsl/nm-device-adsl-glue.h b/src/devices/adsl/nm-device-adsl-glue.h
deleted file mode 100644
index 3a1b9108..00000000
--- a/src/devices/adsl/nm-device-adsl-glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-
-#ifndef __dbus_glib_marshal_nm_device_adsl_MARSHAL_H__
-#define __dbus_glib_marshal_nm_device_adsl_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_adsl_MARSHAL_H__ */
-
-#include <dbus/dbus-glib.h>
-static const DBusGMethodInfo dbus_glib_nm_device_adsl_methods[] = {
-};
-
-const DBusGObjectInfo dbus_glib_nm_device_adsl_object_info = {  1,
-  dbus_glib_nm_device_adsl_methods,
-  0,
-"\0",
-"org.freedesktop.NetworkManager.Device.Adsl\0PropertiesChanged\0\0",
-"org.freedesktop.NetworkManager.Device.Adsl\0Carrier\0carrier\0read\0\0"
-};
-
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index bcef39a2..dd9a1203 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -29,24 +29,20 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <glib.h>
-#include <glib/gi18n.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-device-adsl.h"
 #include "nm-device-private.h"
-#include "NetworkManagerUtils.h"
-#include "nm-logging.h"
 #include "nm-enum-types.h"
-#include "nm-dbus-manager.h"
 #include "nm-platform.h"
 
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-setting-adsl.h"
 #include "nm-utils.h"
 
-#include "nm-device-adsl-glue.h"
+#include "nmdbus-device-adsl.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDeviceAdsl);
@@ -238,7 +234,6 @@ link_changed_cb (NMPlatform *platform,
                  int ifindex,
                  NMPlatformLink *info,
                  NMPlatformSignalChangeType change_type,
-                 NMPlatformReason reason,
                  NMDeviceAdsl *self)
 {
 	if (change_type == NM_PLATFORM_SIGNAL_REMOVED) {
@@ -264,7 +259,7 @@ pppoe_vcc_config (NMDeviceAdsl *self)
 	NMDevice *device = NM_DEVICE (self);
 	NMSettingAdsl *s_adsl;
 
-	s_adsl = nm_connection_get_setting_adsl (nm_device_get_connection (device));
+	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
 	g_assert (s_adsl);
 
 	/* Set up the VCC */
@@ -272,7 +267,7 @@ pppoe_vcc_config (NMDeviceAdsl *self)
 		return FALSE;
 
 	/* Watch for the 'nas' interface going away */
-	g_signal_connect (nm_platform_get (), NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
 	                  G_CALLBACK (link_changed_cb),
 	                  self);
 
@@ -397,7 +392,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
 
 	g_assert (out_reason);
 
-	s_adsl = nm_connection_get_setting_adsl (nm_device_get_connection (device));
+	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
 	g_assert (s_adsl);
 
 	protocol = nm_setting_adsl_get_protocol (s_adsl);
@@ -464,7 +459,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (req);
 
 	s_adsl = nm_connection_get_setting_adsl (connection);
@@ -483,7 +478,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 
 	priv->ppp_manager = nm_ppp_manager_new (ppp_iface);
 	if (nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_adsl_get_username (s_adsl), 30, &err)) {
-		g_signal_connect (priv->ppp_manager, "state-changed",
+		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_STATE_CHANGED,
 		                  G_CALLBACK (ppp_state_changed),
 		                  self);
 		g_signal_connect (priv->ppp_manager, "ip4-config",
@@ -494,8 +489,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 		_LOGW (LOGD_ADSL, "PPP failed to start: %s", err->message);
 		g_error_free (err);
 
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
+		nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 
 		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
 	}
@@ -511,10 +505,10 @@ adsl_cleanup (NMDeviceAdsl *self)
 	if (priv->ppp_manager) {
 		g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_state_changed), self);
 		g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_ip4_config), self);
-		g_clear_object (&priv->ppp_manager);
+		nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 	}
 
-	g_signal_handlers_disconnect_by_func (nm_platform_get (), G_CALLBACK (link_changed_cb), self);
+	g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (link_changed_cb), self);
 
 	if (priv->brfd >= 0) {
 		close (priv->brfd);
@@ -666,7 +660,7 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 		                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 		                   G_PARAM_STATIC_STRINGS));
 
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_device_adsl_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_ADSL_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h
index bf41ed79..efcdaf69 100644
--- a/src/devices/adsl/nm-device-adsl.h
+++ b/src/devices/adsl/nm-device-adsl.h
@@ -22,9 +22,8 @@
 #ifndef __NETWORKMANAGER_DEVICE_ADSL_H__
 #define __NETWORKMANAGER_DEVICE_ADSL_H__
 
-#include <glib-object.h>
-
 // Parent class
+#include "nm-default.h"
 #include "nm-device.h"
 
 G_BEGIN_DECLS
diff --git a/src/devices/bluetooth/Makefile.am b/src/devices/bluetooth/Makefile.am
index e6e378f0..2054ea0f 100644
--- a/src/devices/bluetooth/Makefile.am
+++ b/src/devices/bluetooth/Makefile.am
@@ -9,25 +9,22 @@ AM_CPPFLAGS = \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/devices/wwan \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(GLIB_CFLAGS) \
-	$(DBUS_CFLAGS)
+	$(GLIB_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-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=$@ $<
-
-BUILT_SOURCES = $(GLIB_GENERATED) nm-device-bt-glue.h
+BUILT_SOURCES = $(GLIB_GENERATED)
 
 pkglib_LTLIBRARIES = libnm-device-plugin-bluetooth.la
 
@@ -58,8 +55,9 @@ libnm_device_plugin_bluetooth_la_LDFLAGS = \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_bluetooth_la_LIBADD = \
+	$(top_builddir)/introspection/libnmdbus.la \
 	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
-	$(DBUS_LIBS) \
+	$(GLIB_LIBS) \
 	$(GUDEV_LIBS)
 
 if WITH_BLUEZ5_DUN
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
index bdc3b5cb..f93f7242 100644
--- a/src/devices/bluetooth/Makefile.in
+++ b/src/devices/bluetooth/Makefile.in
@@ -148,6 +148,7 @@ LTLIBRARIES = $(pkglib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
 @WITH_BLUEZ5_DUN_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
 libnm_device_plugin_bluetooth_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la \
 	$(top_builddir)/src/devices/wwan/libnm-wwan.la \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_2)
@@ -157,8 +158,7 @@ am__libnm_device_plugin_bluetooth_la_SOURCES_DIST =  \
 	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
+	nm-bt-enum-types.c 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
@@ -254,7 +254,6 @@ 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@
@@ -266,8 +265,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -277,6 +274,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -331,16 +329,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -377,6 +372,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -405,7 +402,6 @@ 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@
@@ -420,6 +416,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -480,6 +478,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -487,6 +486,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -505,17 +505,18 @@ with_valgrind = @with_valgrind@
 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)/include \
-	-I${top_builddir}/libnm-core -I${top_srcdir}/libnm-core \
+	-I${top_builddir}/introspection -I${top_srcdir}/shared \
+	-I$(top_builddir)/shared -I${top_builddir}/libnm-core \
+	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-bluetooth"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(GLIB_CFLAGS) \
-	$(DBUS_CFLAGS) $(am__append_1)
+	$(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-bt-error.h
-BUILT_SOURCES = $(GLIB_GENERATED) nm-device-bt-glue.h
+BUILT_SOURCES = $(GLIB_GENERATED)
 pkglib_LTLIBRARIES = libnm-device-plugin-bluetooth.la
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
 libnm_device_plugin_bluetooth_la_SOURCES = nm-bluez-manager.c \
@@ -529,7 +530,8 @@ libnm_device_plugin_bluetooth_la_LDFLAGS = \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_bluetooth_la_LIBADD =  \
-	$(top_builddir)/src/devices/wwan/libnm-wwan.la $(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(top_builddir)/src/devices/wwan/libnm-wwan.la $(GLIB_LIBS) \
 	$(GUDEV_LIBS) $(am__append_3)
 CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
@@ -868,9 +870,6 @@ include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
 
-nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
-	dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
-
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-bluetooth.so $(SYMBOL_VIS_FILE)
 
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index f0cab43b..4262fa76 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_BLUEZ_COMMON_H__
 #define __NETWORKMANAGER_BLUEZ_COMMON_H__
 
-#include <config.h>
+#include "config.h"
 
 #define BLUETOOTH_CONNECT_DUN "dun"
 #define BLUETOOTH_CONNECT_NAP "nap"
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 89ad8270..b7032143 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -21,17 +21,14 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-core-internal.h"
 
 #include "nm-bt-error.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
-#include "nm-logging.h"
 #include "nm-settings-connection.h"
 #include "NetworkManagerUtils.h"
 
@@ -869,21 +866,14 @@ properties_changed (GDBusProxy *proxy,
 
 static void
 bluez4_property_changed (GDBusProxy *proxy,
-                         const char *sender,
-                         const char *signal_name,
-                         GVariant   *parameters,
-                         gpointer user_data)
+                         const char *property,
+                         GVariant   *v,
+                         gpointer    user_data)
 {
 	NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
 
-	if (g_strcmp0 (signal_name, "PropertyChanged") == 0) {
-		const char *property = NULL;
-		GVariant *v = NULL;
-
-		g_variant_get (parameters, "(&sv)", &property, &v);
-		_take_one_variant_property (self, property, v);
-		check_emit_usable (self);
-	}
+	_take_one_variant_property (self, property, v);
+	check_emit_usable (self);
 }
 
 static void
@@ -893,27 +883,22 @@ get_properties_cb_4 (GObject *source_object, GAsyncResult *res, gpointer user_da
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 	GError *err = NULL;
 	GVariant *v_properties, *v_dict;
-	GVariantType *v_type;
 
-	v_properties = g_dbus_proxy_call_finish (priv->proxy, res, &err);
+	v_properties = _nm_dbus_proxy_call_finish (priv->proxy, res,
+	                                           G_VARIANT_TYPE ("(a{sv})"),
+	                                           &err);
 	if (!v_properties) {
+		g_dbus_error_strip_remote_error (err);
 		nm_log_warn (LOGD_BT, "bluez[%s] error getting device properties: %s",
-		             priv->path, err && err->message ? err->message : "(unknown)");
+		             priv->path, err->message);
 		g_error_free (err);
 		g_signal_emit (self, signals[INITIALIZED], 0, FALSE);
 		goto END;
 	}
 
-	v_type = g_variant_type_new ("(a{sv})");
-	if (g_variant_is_of_type (v_properties, v_type)) {
-		v_dict = g_variant_get_child_value (v_properties, 0);
-		_set_properties (self, v_dict);
-		g_variant_unref (v_dict);
-	} else {
-		nm_log_warn (LOGD_BT, "bluez[%s] GetProperties returns unexpected result of type %s", priv->path, g_variant_get_type_string (v_properties));
-	}
-	g_variant_type_free (v_type);
-
+	v_dict = g_variant_get_child_value (v_properties, 0);
+	_set_properties (self, v_dict);
+	g_variant_unref (v_dict);
 	g_variant_unref (v_properties);
 
 	/* Check if any connections match this device */
@@ -922,7 +907,6 @@ get_properties_cb_4 (GObject *source_object, GAsyncResult *res, gpointer user_da
 	priv->initialized = TRUE;
 	g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
 
-
 	check_emit_usable (self);
 
 END:
@@ -990,8 +974,8 @@ on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
 		                  G_CALLBACK (properties_changed), self);
 		if (priv->bluez_version == 4) {
 			/* Watch for custom Bluez4 PropertyChanged signals */
-			g_signal_connect (priv->proxy, "g-signal",
-			                  G_CALLBACK (bluez4_property_changed), self);
+			_nm_dbus_signal_connect (priv->proxy, "PropertyChanged", G_VARIANT_TYPE ("(sv)"),
+			                         G_CALLBACK (bluez4_property_changed), self);
 		}
 
 		query_properties (self);
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
index 92d99fa7..0e54fcb0 100644
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -21,11 +21,9 @@
 #ifndef __NETWORKMANAGER_BLUEZ_DEVICE_H__
 #define __NETWORKMANAGER_BLUEZ_DEVICE_H__
 
-#include <glib.h>
-#include <glib-object.h>
-#include <gio/gio.h>
 
-#include <config.h>
+#include "config.h"
+#include "nm-default.h"
 #include "nm-connection.h"
 #include "nm-connection-provider.h"
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index df4eb46d..6903356c 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -24,9 +24,8 @@
 #include <string.h>
 #include <stdlib.h>
 #include <gmodule.h>
-#include <gio/gio.h>
 
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "nm-bluez-manager.h"
 #include "nm-device-factory.h"
 #include "nm-setting-bluetooth.h"
@@ -36,9 +35,9 @@
 #include "nm-bluez-common.h"
 #include "nm-connection-provider.h"
 #include "nm-device-bt.h"
-
-#include "nm-dbus-manager.h"
+#include "nm-core-internal.h"
 #include "nm-platform.h"
+#include "nm-dbus-compat.h"
 
 typedef struct {
 	int bluez_version;
@@ -57,7 +56,7 @@ typedef struct {
 
 static GType nm_bluez_manager_get_type (void);
 
-static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+static void device_factory_interface_init (NMDeviceFactoryInterface *factory_iface);
 
 G_DEFINE_TYPE_EXTENDED (NMBluezManager, nm_bluez_manager, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
@@ -278,10 +277,13 @@ check_bluez_and_try_setup_do_introspect (GObject *source_object,
 
 	g_clear_object (&priv->async_cancellable);
 
-	result = g_dbus_proxy_call_finish (priv->introspect_proxy, res, &error);
-
+	result = _nm_dbus_proxy_call_finish (priv->introspect_proxy, res,
+	                                     G_VARIANT_TYPE ("(s)"), &error);
 	if (!result) {
-		char *reason2 = g_strdup_printf ("introspect failed with %s", error->message);
+		char *reason2;
+
+		g_dbus_error_strip_remote_error (error);
+		reason2 = g_strdup_printf ("introspect failed with %s", error->message);
 		check_bluez_and_try_setup_final_step (self, 0, reason2);
 		g_error_free (error);
 		g_free (reason2);
@@ -407,7 +409,11 @@ nm_bluez_manager_init (NMBluezManager *self)
 }
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
 	g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP);
 	*out_ignore = TRUE;
@@ -415,10 +421,10 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore,
 }
 
 static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
+device_factory_interface_init (NMDeviceFactoryInterface *factory_iface)
 {
 	factory_iface->get_supported_types = get_supported_types;
-	factory_iface->new_link = new_link;
+	factory_iface->create_device = create_device;
 	factory_iface->start = start;
 }
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
index 8320b298..1f5597d8 100644
--- a/src/devices/bluetooth/nm-bluez-manager.h
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -22,8 +22,7 @@
 #ifndef __NETWORKMANAGER_BLUEZ_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
+#include "nm-default.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index 9d1cfe1f..04ae833a 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -20,17 +20,14 @@
 
 #include "config.h"
 
-#include <glib.h>
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-dbus-interface.h"
-#include "nm-dbus-manager.h"
 #include "nm-bluez4-adapter.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-logging.h"
-
+#include "nm-core-internal.h"
 
 G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
 
@@ -38,7 +35,7 @@ G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
 
 typedef struct {
 	char *path;
-	DBusGProxy *proxy;
+	GDBusProxy *proxy;
 	gboolean initialized;
 
 	char *address;
@@ -158,7 +155,7 @@ device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device)
 }
 
 static void
-device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
+device_created (GDBusProxy *proxy, const char *path, gpointer user_data)
 {
 	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
@@ -173,7 +170,7 @@ device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
 }
 
 static void
-device_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
+device_removed (GDBusProxy *proxy, const char *path, gpointer user_data)
 {
 	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
@@ -186,37 +183,36 @@ device_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
 		device_do_remove (self, device);
 }
 
-
 static void
-get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+get_properties_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	GHashTable *properties = NULL;
 	GError *err = NULL;
-	GValue *value;
-	GPtrArray *devices;
+	GVariant *ret, *properties;
+	char **devices;
 	int i;
 
-	if (!dbus_g_proxy_end_call (proxy, call, &err,
-	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
-	                            G_TYPE_INVALID)) {
-		nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s",
-		             err && err->message ? err->message : "(unknown)");
+	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
+	                                  G_VARIANT_TYPE ("(a{sv})"), &err);
+	if (!ret) {
+		g_dbus_error_strip_remote_error (err);
+		nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s", err->message);
 		g_error_free (err);
 		goto done;
 	}
 
-	value = g_hash_table_lookup (properties, "Address");
-	priv->address = value ? g_value_dup_string (value) : NULL;
-
-	value = g_hash_table_lookup (properties, "Devices");
-	devices = value ? g_value_get_boxed (value) : NULL;
+	properties = g_variant_get_child_value (ret, 0);
 
-	for (i = 0; devices && i < devices->len; i++)
- 		device_created (priv->proxy, g_ptr_array_index (devices, i), self);
+	g_variant_lookup (properties, "Address", "s", &priv->address);
+	if (g_variant_lookup (properties, "Devices", "^ao", &devices)) {
+		for (i = 0; devices[i]; i++)
+			device_created (priv->proxy, devices[i], self);
+		g_strfreev (devices);
+	}
 
-	g_hash_table_unref (properties);
+	g_variant_unref (properties);
+	g_variant_unref (ret);
 
 	priv->initialized = TRUE;
 
@@ -228,16 +224,12 @@ static void
 query_properties (NMBluez4Adapter *self)
 {
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	DBusGProxyCall *call;
-
-	call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties",
-	                                get_properties_cb,
-	                                self,
-	                                NULL, G_TYPE_INVALID);
-	if (!call) {
-		nm_log_warn (LOGD_BT, "failed to request Bluetooth adapter properties for %s.",
-		             priv->path);
-	}
+
+	g_dbus_proxy_call (priv->proxy, "GetProperties",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE, -1,
+	                   NULL,
+	                   get_properties_cb, self);
 }
 
 /***********************************************************/
@@ -247,34 +239,25 @@ nm_bluez4_adapter_new (const char *path, NMConnectionProvider *provider)
 {
 	NMBluez4Adapter *self;
 	NMBluez4AdapterPrivate *priv;
-	DBusGConnection *connection;
 
 	self = (NMBluez4Adapter *) g_object_new (NM_TYPE_BLUEZ4_ADAPTER,
 	                                         NM_BLUEZ4_ADAPTER_PATH, path,
 	                                         NULL);
-	if (!self)
-		return NULL;
-
 	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 
 	priv->provider = provider;
 
-	connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
-
-	priv->proxy = dbus_g_proxy_new_for_name (connection,
-	                                         BLUEZ_SERVICE,
-	                                         priv->path,
-	                                         BLUEZ4_ADAPTER_INTERFACE);
-
-	dbus_g_proxy_add_signal (priv->proxy, "DeviceCreated",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "DeviceCreated",
-	                             G_CALLBACK (device_created), self, NULL);
-
-	dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved",
-	                             G_CALLBACK (device_removed), self, NULL);
+	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+	                                             NULL,
+	                                             BLUEZ_SERVICE,
+	                                             priv->path,
+	                                             BLUEZ4_ADAPTER_INTERFACE,
+	                                             NULL, NULL);
+	_nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (device_created), self);
+	_nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (device_removed), self);
 
 	query_properties (self);
 	return self;
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
index a6256412..a8a2bfb4 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.h
+++ b/src/devices/bluetooth/nm-bluez4-adapter.h
@@ -21,9 +21,8 @@
 #ifndef __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
 #define __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
 
-#include <glib.h>
-#include <glib-object.h>
 
+#include "nm-default.h"
 #include "nm-bluez-device.h"
 #include "nm-connection-provider.h"
 
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
index 23b13d32..c5ba120c 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.c
+++ b/src/devices/bluetooth/nm-bluez4-manager.c
@@ -24,24 +24,20 @@
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
-#include <dbus/dbus-glib.h>
 
-#include "nm-logging.h"
-#include "nm-dbus-glib-types.h"
+#include "nm-default.h"
 #include "nm-bluez-manager.h"
 #include "nm-bluez4-manager.h"
 #include "nm-bluez4-adapter.h"
-#include "nm-dbus-manager.h"
 #include "nm-bluez-common.h"
-
+#include "nm-core-internal.h"
 
 typedef struct {
-	NMDBusManager *dbus_mgr;
 	gulong name_owner_changed_id;
 
 	NMConnectionProvider *provider;
 
-	DBusGProxy *proxy;
+	GDBusProxy *proxy;
 
 	NMBluez4Adapter *adapter;
 } NMBluez4ManagerPrivate;
@@ -120,7 +116,7 @@ adapter_initialized (NMBluez4Adapter *adapter, gboolean success, gpointer user_d
 }
 
 static void
-adapter_removed (DBusGProxy *proxy, const char *path, NMBluez4Manager *self)
+adapter_removed (GDBusProxy *proxy, const char *path, NMBluez4Manager *self)
 {
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
 
@@ -140,7 +136,7 @@ adapter_removed (DBusGProxy *proxy, const char *path, NMBluez4Manager *self)
 }
 
 static void
-default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluez4Manager *self)
+default_adapter_changed (GDBusProxy *proxy, const char *path, NMBluez4Manager *self)
 {
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
 	const char *cur_path = NULL;
@@ -166,130 +162,65 @@ default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluez4Manager *s
 }
 
 static void
-default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+default_adapter_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	const char *default_adapter = NULL;
+	GVariant *ret;
 	GError *err = NULL;
 
-	if (!dbus_g_proxy_end_call (proxy, call, &err,
-	                            DBUS_TYPE_G_OBJECT_PATH, &default_adapter,
-	                            G_TYPE_INVALID)) {
+	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
+	                                  G_VARIANT_TYPE ("(o)"), &err);
+	if (ret) {
+		const char *default_adapter;
+
+		g_variant_get (ret, "(&o)", &default_adapter);
+		default_adapter_changed (priv->proxy, default_adapter, self);
+		g_variant_unref (ret);
+	} else {
 		/* Ignore "No such adapter" errors; just means bluetooth isn't active */
-		if (   !dbus_g_error_has_name (err, "org.bluez.Error.NoSuchAdapter")
-		    && !dbus_g_error_has_name (err, "org.freedesktop.systemd1.LoadFailed")
-		    && !g_error_matches (err, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) {
+		if (   !_nm_dbus_error_has_name (err, "org.bluez.Error.NoSuchAdapter")
+		    && !_nm_dbus_error_has_name (err, "org.freedesktop.systemd1.LoadFailed")
+		    && !g_error_matches (err, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
+			g_dbus_error_strip_remote_error (err);
 			nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s",
-			             err && err->message ? err->message : "(unknown)");
+			             err->message);
 		}
 		g_error_free (err);
-		return;
 	}
-
-	default_adapter_changed (priv->proxy, default_adapter, self);
 }
 
 static void
 query_default_adapter (NMBluez4Manager *self)
 {
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	DBusGProxyCall *call;
-
-	call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter",
-	                                default_adapter_cb,
-	                                self,
-	                                NULL, G_TYPE_INVALID);
-	if (!call)
-		nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter.");
-}
-
-static void
-bluez_connect (NMBluez4Manager *self)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	DBusGConnection *connection;
-
-	g_return_if_fail (priv->proxy == NULL);
 
-	connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	if (!connection)
-		return;
-
-	priv->proxy = dbus_g_proxy_new_for_name (connection,
-	                                         BLUEZ_SERVICE,
-	                                         BLUEZ_MANAGER_PATH,
-	                                         BLUEZ4_MANAGER_INTERFACE);
-
-	dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved",
-	                             G_CALLBACK (adapter_removed), self, NULL);
-
-	dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged",
-	                             G_CALLBACK (default_adapter_changed), self, NULL);
-
-	query_default_adapter (self);
+	g_dbus_proxy_call (priv->proxy, "DefaultAdapter",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE, -1,
+	                   NULL,
+	                   default_adapter_cb, self);
 }
 
 static void
-name_owner_changed_cb (NMDBusManager *dbus_mgr,
-                       const char *name,
-                       const char *old_owner,
-                       const char *new_owner,
+name_owner_changed_cb (GObject *object,
+                       GParamSpec *pspec,
                        gpointer user_data)
 {
 	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	gboolean old_owner_good = (old_owner && strlen (old_owner));
-	gboolean new_owner_good = (new_owner && strlen (new_owner));
+	char *owner;
 
-	/* Can't handle the signal if its not from the Bluez */
-	if (strcmp (BLUEZ_SERVICE, name))
-		return;
-
-	if (!old_owner_good && new_owner_good)
+	owner = g_dbus_proxy_get_name_owner (priv->proxy);
+	if (owner) {
 		query_default_adapter (self);
-	else if (old_owner_good && !new_owner_good) {
+		g_free (owner);
+	} else {
 		/* Throwing away the adapter removes all devices too */
-		if (priv->adapter) {
-			g_object_unref (priv->adapter);
-			priv->adapter = NULL;
-		}
+		g_clear_object (&priv->adapter);
 	}
 }
 
-static void
-bluez_cleanup (NMBluez4Manager *self, gboolean do_signal)
-{
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-
-	if (priv->proxy) {
-		g_object_unref (priv->proxy);
-		priv->proxy = NULL;
-	}
-
-	if (priv->adapter) {
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
-	}
-}
-
-static void
-dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
-                            DBusGConnection *connection,
-                            gpointer user_data)
-{
-	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
-
-	if (!connection)
-		bluez_cleanup (self, TRUE);
-	else
-		bluez_connect (self);
-}
-
 /****************************************************************/
 
 NMBluez4Manager *
@@ -307,20 +238,21 @@ nm_bluez4_manager_init (NMBluez4Manager *self)
 {
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
 
-	priv->dbus_mgr = nm_dbus_manager_get ();
-	g_assert (priv->dbus_mgr);
+	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+	                                             NULL,
+	                                             BLUEZ_SERVICE,
+	                                             BLUEZ_MANAGER_PATH,
+	                                             BLUEZ4_MANAGER_INTERFACE,
+	                                             NULL, NULL);
+	_nm_dbus_signal_connect (priv->proxy, "AdapterRemoved", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (adapter_removed), self);
+	_nm_dbus_signal_connect (priv->proxy, "DefaultAdapterChanged", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (default_adapter_changed), self);
+	g_signal_connect (priv->proxy, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed_cb), self);
 
-	g_signal_connect (priv->dbus_mgr,
-	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-	                  G_CALLBACK (name_owner_changed_cb),
-	                  self);
-
-	g_signal_connect (priv->dbus_mgr,
-	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
-	                  G_CALLBACK (dbus_connection_changed_cb),
-	                  self);
-
-	bluez_connect (self);
+	query_default_adapter (self);
 }
 
 static void
@@ -329,13 +261,8 @@ dispose (GObject *object)
 	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (object);
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
 
-	bluez_cleanup (self, FALSE);
-
-	if (priv->dbus_mgr) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self);
-		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
-		priv->dbus_mgr = NULL;
-	}
+	g_clear_object (&priv->proxy);
+	g_clear_object (&priv->adapter);
 
 	G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object);
 }
diff --git a/src/devices/bluetooth/nm-bluez4-manager.h b/src/devices/bluetooth/nm-bluez4-manager.h
index 13cfef93..f6bf7658 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.h
+++ b/src/devices/bluetooth/nm-bluez4-manager.h
@@ -22,11 +22,8 @@
 #ifndef __NETWORKMANAGER_BLUEZ4_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ4_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
-#include  <config.h>
-#include  "nm-connection-provider.h"
+#include "nm-default.h"
+#include "nm-connection-provider.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index a97eaa77..875a662f 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -32,7 +32,7 @@
 
 #include "nm-bluez5-dun.h"
 #include "nm-bt-error.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "NetworkManagerUtils.h"
 
 struct _NMBluez5DunContext {
@@ -137,10 +137,7 @@ sdp_search_cleanup (NMBluez5DunContext *context)
 		context->sdp_session = NULL;
 	}
 
-	if (context->sdp_watch_id) {
-		g_source_remove (context->sdp_watch_id);
-		context->sdp_watch_id = 0;
-	}
+	nm_clear_g_source (&context->sdp_watch_id);
 }
 
 static void
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index 7e259725..c59cbb80 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -21,8 +21,7 @@
 #ifndef _NM_BLUEZ5_UTILS_H_
 #define _NM_BLUEZ5_UTILS_H_
 
-#include <glib.h>
-#include <gio/gio.h>
+#include "nm-default.h"
 
 typedef struct _NMBluez5DunContext NMBluez5DunContext;
 
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index b8568523..c9f5828d 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -25,20 +25,16 @@
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
-#include <gio/gio.h>
 
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "nm-bluez-manager.h"
 #include "nm-bluez5-manager.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 
-#include "nm-dbus-manager.h"
+#include "nm-core-internal.h"
 
 typedef struct {
-	NMDBusManager *dbus_mgr;
-	gulong name_owner_changed_id;
-
 	NMConnectionProvider *provider;
 
 	GDBusProxy *proxy;
@@ -170,39 +166,23 @@ device_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 }
 
 static void
-object_manager_g_signal (GDBusProxy     *proxy,
-                         gchar          *sender_name,
-                         gchar          *signal_name,
-                         GVariant       *parameters,
-                         NMBluez5Manager *self)
+object_manager_interfaces_added (GDBusProxy      *proxy,
+                                 const char      *path,
+                                 GVariant        *dict,
+                                 NMBluez5Manager *self)
 {
-	GVariant *variant;
-	const gchar *path;
-
-	if (!strcmp (signal_name, "InterfacesRemoved")) {
-		const gchar **ifaces;
-		gsize i, length;
-
-		g_variant_get (parameters, "(&o*)", &path, &variant);
-
-		ifaces = g_variant_get_strv (variant, &length);
-
-		for (i = 0; i < length; i++) {
-			if (!strcmp (ifaces[i], BLUEZ5_DEVICE_INTERFACE)) {
-				device_removed (proxy, path, self);
-				break;
-			}
-		}
-
-		g_free (ifaces);
-
-	} else if (!strcmp (signal_name, "InterfacesAdded")) {
-		g_variant_get (parameters, "(&o*)", &path, &variant);
+	if (g_variant_lookup (dict, BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
+		device_added (proxy, path, self);
+}
 
-		if (g_variant_lookup_value (variant, BLUEZ5_DEVICE_INTERFACE,
-		                            G_VARIANT_TYPE_DICTIONARY))
-			device_added (proxy, path, self);
-	}
+static void
+object_manager_interfaces_removed (GDBusProxy       *proxy,
+                                   const char       *path,
+                                   const char      **ifaces,
+                                   NMBluez5Manager  *self)
+{
+	if (_nm_utils_string_in_list (BLUEZ5_DEVICE_INTERFACE, ifaces))
+		device_removed (proxy, path, self);
 }
 
 static void
@@ -215,14 +195,15 @@ get_managed_objects_cb (GDBusProxy *proxy,
 	GError *error = NULL;
 	const char *path;
 
-	variant = g_dbus_proxy_call_finish (proxy, res, &error);
-
+	variant = _nm_dbus_proxy_call_finish (proxy, res,
+	                                      G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
+	                                      &error);
 	if (!variant) {
 		if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
 			nm_log_warn (LOGD_BT, "Couldn't get managed objects: not running Bluez5?");
 		else {
-			nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s",
-				         error && error->message ? error->message : "(unknown)");
+			g_dbus_error_strip_remote_error (error);
+			nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s", error->message);
 		}
 		g_clear_error (&error);
 		return;
@@ -238,6 +219,8 @@ get_managed_objects_cb (GDBusProxy *proxy,
 	g_variant_unref (variant);
 }
 
+static void name_owner_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data);
+
 static void
 on_proxy_acquired (GObject *object,
                    GAsyncResult *res,
@@ -255,6 +238,9 @@ on_proxy_acquired (GObject *object,
 		return;
 	}
 
+	g_signal_connect (priv->proxy, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed_cb), self);
+
 	/* Get already managed devices. */
 	g_dbus_proxy_call (priv->proxy, "GetManagedObjects",
 	                   NULL,
@@ -264,8 +250,10 @@ on_proxy_acquired (GObject *object,
 	                   (GAsyncReadyCallback) get_managed_objects_cb,
 	                   self);
 
-	g_signal_connect (priv->proxy, "g-signal",
-	                  G_CALLBACK (object_manager_g_signal), self);
+	_nm_dbus_signal_connect (priv->proxy, "InterfacesAdded", G_VARIANT_TYPE ("(oa{sa{sv}})"),
+	                         G_CALLBACK (object_manager_interfaces_added), self);
+	_nm_dbus_signal_connect (priv->proxy, "InterfacesRemoved", G_VARIANT_TYPE ("(oas)"),
+	                         G_CALLBACK (object_manager_interfaces_removed), self);
 }
 
 static void
@@ -287,24 +275,19 @@ bluez_connect (NMBluez5Manager *self)
 }
 
 static void
-name_owner_changed_cb (NMDBusManager *dbus_mgr,
-                       const char *name,
-                       const char *old_owner,
-                       const char *new_owner,
+name_owner_changed_cb (GObject *object,
+                       GParamSpec *pspec,
                        gpointer user_data)
 {
 	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-	gboolean old_owner_good = (old_owner && strlen (old_owner));
-	gboolean new_owner_good = (new_owner && strlen (new_owner));
+	char *owner;
 
-	/* Can't handle the signal if its not from the Bluez */
-	if (strcmp (BLUEZ_SERVICE, name))
-		return;
-
-	if (old_owner_good && !new_owner_good) {
-		if (priv->devices)
+	if (priv->devices) {
+		owner = g_dbus_proxy_get_name_owner (priv->proxy);
+		if (!owner)
 			remove_all_devices (self);
+		g_free (owner);
 	}
 }
 
@@ -314,8 +297,8 @@ bluez_cleanup (NMBluez5Manager *self, gboolean do_signal)
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 
 	if (priv->proxy) {
-		g_object_unref (priv->proxy);
-		priv->proxy = NULL;
+		g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
+		g_clear_object (&priv->proxy);
 	}
 
 	if (do_signal)
@@ -324,19 +307,6 @@ bluez_cleanup (NMBluez5Manager *self, gboolean do_signal)
 		g_hash_table_remove_all (priv->devices);
 }
 
-static void
-dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
-                            DBusGConnection *connection,
-                            gpointer user_data)
-{
-	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
-
-	if (!connection)
-		bluez_cleanup (self, TRUE);
-	else
-		bluez_connect (self);
-}
-
 /****************************************************************/
 
 NMBluez5Manager *
@@ -354,19 +324,6 @@ nm_bluez5_manager_init (NMBluez5Manager *self)
 {
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 
-	priv->dbus_mgr = nm_dbus_manager_get ();
-	g_assert (priv->dbus_mgr);
-
-	g_signal_connect (priv->dbus_mgr,
-	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-	                  G_CALLBACK (name_owner_changed_cb),
-	                  self);
-
-	g_signal_connect (priv->dbus_mgr,
-	                  NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
-	                  G_CALLBACK (dbus_connection_changed_cb),
-	                  self);
-
 	bluez_connect (self);
 
 	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
@@ -377,16 +334,9 @@ static void
 dispose (GObject *object)
 {
 	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 
 	bluez_cleanup (self, FALSE);
 
-	if (priv->dbus_mgr) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self);
-		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
-		priv->dbus_mgr = NULL;
-	}
-
 	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object);
 }
 
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
index 8152976d..ae5512f8 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ b/src/devices/bluetooth/nm-bluez5-manager.h
@@ -22,11 +22,8 @@
 #ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
-#include  <config.h>
-#include  "nm-connection-provider.h"
+#include "nm-default.h"
+#include "nm-connection-provider.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/devices/bluetooth/nm-bt-enum-types.c b/src/devices/bluetooth/nm-bt-enum-types.c
index 16741c9f..207055e1 100644
--- a/src/devices/bluetooth/nm-bt-enum-types.c
+++ b/src/devices/bluetooth/nm-bt-enum-types.c
@@ -3,6 +3,8 @@
 
 /* Generated by glib-mkenums. Do not edit */
 
+#include "config.h"
+
 #include "nm-bt-enum-types.h"
 
 #include "nm-bt-error.h"
diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c
index e3b8e7f5..9c35d8b6 100644
--- a/src/devices/bluetooth/nm-bt-error.c
+++ b/src/devices/bluetooth/nm-bt-error.c
@@ -20,7 +20,7 @@
 
 #include "config.h"
 
-#include <glib.h>
+#include "nm-default.h"
 #include "nm-bt-error.h"
 
 GQuark
diff --git a/src/devices/bluetooth/nm-device-bt-glue.h b/src/devices/bluetooth/nm-device-bt-glue.h
deleted file mode 100644
index b6f851b2..00000000
--- a/src/devices/bluetooth/nm-device-bt-glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-
-#ifndef __dbus_glib_marshal_nm_device_bt_MARSHAL_H__
-#define __dbus_glib_marshal_nm_device_bt_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_bt_MARSHAL_H__ */
-
-#include <dbus/dbus-glib.h>
-static const DBusGMethodInfo dbus_glib_nm_device_bt_methods[] = {
-};
-
-const DBusGObjectInfo dbus_glib_nm_device_bt_object_info = {  1,
-  dbus_glib_nm_device_bt_methods,
-  0,
-"\0",
-"org.freedesktop.NetworkManager.Device.Bluetooth\0PropertiesChanged\0\0",
-"org.freedesktop.NetworkManager.Device.Bluetooth\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.Bluetooth\0Name\0name\0read\0org.freedesktop.NetworkManager.Device.Bluetooth\0BtCapabilities\0bt_capabilities\0read\0\0"
-};
-
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 88033e2e..1e3f9cdc 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -23,16 +23,11 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-
-#include "nm-glib-compat.h"
+#include "nm-default.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
-#include "nm-dbus-manager.h"
 #include "nm-device-bt.h"
 #include "nm-device-private.h"
-#include "nm-logging.h"
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-bluetooth.h"
@@ -40,14 +35,17 @@
 #include "nm-setting-gsm.h"
 #include "nm-setting-serial.h"
 #include "nm-setting-ppp.h"
-#include "nm-device-bt-glue.h"
 #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"
+#include "nmdbus-device-bt.h"
+
+#define MM_DBUS_SERVICE   "org.freedesktop.ModemManager1"
+#define MM_DBUS_PATH      "/org/freedesktop/ModemManager1"
+#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager1"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBt);
@@ -59,8 +57,7 @@ G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
 static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason);
 
 typedef struct {
-	NMDBusManager *dbus_mgr;
-	guint mm_watch_id;
+	GDBusProxy *mm_proxy;
 	gboolean mm_running;
 
 	NMBluezDevice *bt_device;
@@ -461,7 +458,7 @@ modem_prepare_result (NMModem *modem,
 			 * the device to be auto-activated anymore, which would risk locking
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
-			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL);
+			nm_device_set_autoconnect (device, FALSE);
 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
 		}
 
@@ -635,10 +632,7 @@ component_added (NMDevice *device, GObject *component)
 	g_free (base);
 
 	/* Got the modem */
-	if (priv->timeout_id) {
-		g_source_remove (priv->timeout_id);
-		priv->timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->timeout_id);
 
 	/* Can only accept the modem in stage2, but since the interface matched
 	 * what we were expecting, don't let anything else claim the modem either.
@@ -706,10 +700,7 @@ check_connect_continue (NMDeviceBt *self)
 	       dun ? "DUN" : (pan ? "PAN" : "unknown"));
 
 	/* Kill the connect timeout since we're connected now */
-	if (priv->timeout_id) {
-		g_source_remove (priv->timeout_id);
-		priv->timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->timeout_id);
 
 	if (pan) {
 		/* Bluez says we're connected now.  Start IP config. */
@@ -824,7 +815,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
 	NMConnection *connection;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	priv->bt_type = get_connection_bt_type (connection);
 	if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
@@ -914,10 +905,7 @@ deactivate (NMDevice *device)
 	if (priv->bt_type != NM_BT_CAPABILITY_NONE)
 		nm_bluez_device_disconnect (priv->bt_device);
 
-	if (priv->timeout_id) {
-		g_source_remove (priv->timeout_id);
-		priv->timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->timeout_id);
 
 	priv->bt_type = NM_BT_CAPABILITY_NONE;
 
@@ -967,25 +955,17 @@ set_mm_running (NMDeviceBt *self, gboolean running)
 }
 
 static void
-mm_name_owner_changed (NMDBusManager *dbus_mgr,
-                       const char *name,
-                       const char *old_owner,
-                       const char *new_owner,
+mm_name_owner_changed (GObject *object,
+                       GParamSpec *pspec,
                        NMDeviceBt *self)
 {
-	gboolean old_owner_good;
-	gboolean new_owner_good;
-
-	/* Can't handle the signal if its not from the modem service */
-	if (strcmp (MM_DBUS_SERVICE, name) != 0)
-		return;
-
-	old_owner_good = (old_owner && strlen (old_owner));
-	new_owner_good = (new_owner && strlen (new_owner));
+	char *owner;
 
-	if (!old_owner_good && new_owner_good)
+	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
+	if (owner) {
 		set_mm_running (self, TRUE);
-	else if (old_owner_good && !new_owner_good)
+		g_free (owner);
+	} else
 		set_mm_running (self, FALSE);
 }
 
@@ -1021,18 +1001,27 @@ static void
 nm_device_bt_init (NMDeviceBt *self)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	gboolean mm_running;
-
-	priv->dbus_mgr = nm_dbus_manager_get ();
-
-	priv->mm_watch_id = g_signal_connect (priv->dbus_mgr,
-	                                      NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-	                                      G_CALLBACK (mm_name_owner_changed),
-	                                      self);
+	GError *error = NULL;
 
-	/* Initial check to see if ModemManager is running */
-	mm_running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE);
-	set_mm_running (self, mm_running);
+	priv->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+	                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+	                                                    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+	                                                    G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                                                NULL,
+	                                                MM_DBUS_SERVICE,
+	                                                MM_DBUS_PATH,
+	                                                MM_DBUS_INTERFACE,
+	                                                NULL, &error);
+	if (priv->mm_proxy) {
+		g_signal_connect (priv->mm_proxy, "notify::g-name-owner",
+		                  G_CALLBACK (mm_name_owner_changed),
+		                  self);
+		mm_name_owner_changed (G_OBJECT (priv->mm_proxy), NULL, self);
+	} else {
+		_LOGW (LOGD_MB, "Could not create proxy for '%s': %s",
+		       MM_DBUS_SERVICE, error->message);
+		g_clear_error (&error);
+	}
 }
 
 static void
@@ -1106,18 +1095,14 @@ dispose (GObject *object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
 
-	if (priv->timeout_id) {
-		g_source_remove (priv->timeout_id);
-		priv->timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->timeout_id);
 
 	g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
 
-	if (priv->dbus_mgr && priv->mm_watch_id) {
-		g_signal_handler_disconnect (priv->dbus_mgr, priv->mm_watch_id);
-		priv->mm_watch_id = 0;
+	if (priv->mm_proxy) {
+		g_signal_handlers_disconnect_by_func (priv->mm_proxy, G_CALLBACK (mm_name_owner_changed), object);
+		g_clear_object (&priv->mm_proxy);
 	}
-	priv->dbus_mgr = NULL;
 
 	modem_cleanup (NM_DEVICE_BT (object));
 	g_clear_object (&priv->bt_device);
@@ -1197,7 +1182,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 		              G_TYPE_NONE, 2,
 		              G_TYPE_UINT, G_TYPE_UINT);
 
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_device_bt_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BLUETOOTH_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index c1e95d91..c565745f 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_DEVICE_BT_H__
 #define __NETWORKMANAGER_DEVICE_BT_H__
 
-#include <nm-device.h>
+#include "nm-device.h"
 #include "nm-bluez-device.h"
 #include "nm-modem.h"
 
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index afcba3b3..dfd1de6d 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -20,26 +20,20 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
 #include <errno.h>
 #include <stdlib.h>
 
-#include "gsystem-local-alloc.h"
+#include "nm-default.h"
 #include "nm-device-bond.h"
-#include "nm-logging.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-ip4-config.h"
 
-#include "nm-device-bond-glue.h"
+#include "nmdbus-device-bond.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBond);
@@ -52,13 +46,6 @@ typedef struct {
 	int dummy;
 } NMDeviceBondPrivate;
 
-enum {
-	PROP_0,
-	PROP_SLAVES,
-
-	LAST_PROP
-};
-
 /******************************************************************/
 
 static NMDeviceCapabilities
@@ -143,7 +130,7 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value)
 	gboolean ret;
 	int ifindex = nm_device_get_ifindex (device);
 
-	ret = nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, attr, value);
+	ret = nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, attr, value);
 	if (!ret)
 		_LOGW (LOGD_HW, "failed to set bonding attribute '%s' to '%s'", attr, value);
 	return ret;
@@ -177,7 +164,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* Read bond options from sysfs and update the Bond setting to match */
 	options = nm_setting_bond_get_valid_options (s_bond);
 	while (options && *options) {
-		gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, *options);
+		gs_free char *value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, *options);
 		const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options);
 
 		if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) {
@@ -270,7 +257,7 @@ apply_bonding_config (NMDevice *device)
 	 *     arp_interval doesn't require miimon to be 0
 	 */
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_bond = nm_connection_get_setting_bond (connection);
 	g_assert (s_bond);
@@ -333,7 +320,7 @@ apply_bonding_config (NMDevice *device)
 	}
 
 	/* Clear ARP targets */
-	contents = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "arp_ip_target");
+	contents = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "arp_ip_target");
 	set_arp_targets (device, contents, " \n", "-");
 	g_free (contents);
 
@@ -383,7 +370,7 @@ ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
 	NMSettingWired *s_wired;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 	s_wired = nm_connection_get_setting_wired (connection);
 
@@ -421,17 +408,16 @@ enslave_slave (NMDevice *device,
 	} else
 		_LOGI (LOGD_BOND, "bond slave %s was enslaved", slave_iface);
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES);
 	return TRUE;
 }
 
-static gboolean
+static void
 release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
-	gboolean success = TRUE, no_firmware = FALSE;
+	gboolean success, no_firmware = FALSE;
 
 	if (configure) {
 		success = nm_platform_link_release (NM_PLATFORM_GET,
@@ -445,65 +431,48 @@ release_slave (NMDevice *device,
 			_LOGW (LOGD_BOND, "failed to release bond slave %s",
 			       nm_device_get_ip_iface (slave));
 		}
-	} else {
-		_LOGI (LOGD_BOND, "bond slave %s was released",
-		             nm_device_get_ip_iface (slave));
-	}
-
-	if (success)
-		g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES);
 
-	if (configure) {
 		/* Kernel bonding code "closes" the slave when releasing it, (which clears
 		 * 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))
 			_LOGW (LOGD_BOND, "released bond slave could not be brought up.");
+	} else {
+		_LOGI (LOGD_BOND, "bond slave %s was released",
+		       nm_device_get_ip_iface (slave));
 	}
-
-	return success;
 }
 
-/******************************************************************/
-
-static void
-nm_device_bond_init (NMDeviceBond * self)
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
 {
-}
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
 
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	GPtrArray *slaves;
-	GSList *list, *iter;
-
-	switch (prop_id) {
-		break;
-	case PROP_SLAVES:
-		slaves = g_ptr_array_new ();
-		list = nm_device_master_get_slaves (NM_DEVICE (object));
-		for (iter = list; iter; iter = iter->next)
-			g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
-		g_slist_free (list);
-		g_value_take_boxed (value, slaves);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	g_assert (iface);
+
+	plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, iface, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create bond interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
 	}
+	return TRUE;
 }
 
+/******************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
+nm_device_bond_init (NMDeviceBond * self)
 {
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
 }
 
 static void
@@ -514,11 +483,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceBondPrivate));
 
-	parent_class->connection_type = NM_SETTING_BOND_SETTING_NAME;
-
-	/* virtual methods */
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND)
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->is_available = is_available;
@@ -529,22 +494,15 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->update_connection = update_connection;
 	parent_class->master_update_slave_connection = master_update_slave_connection;
 
+	parent_class->create_and_realize = create_and_realize;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_BOND_SLAVES, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BOND_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -553,45 +511,18 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 #define NM_BOND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BOND_FACTORY, NMBondFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
-{
-	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);
-}
-
-static NMDevice *
-create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                      NMConnection *connection,
-                                      NMDevice *parent,
-                                      GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	const char *iface = nm_connection_get_interface_name (connection);
-	NMPlatformError plerr;
-	const NMPlatformLink *plink;
-
-	g_assert (iface);
-
-	plerr = nm_platform_bond_add (NM_PLATFORM_GET, iface, NULL);
-	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Failed to create bond interface '%s' for '%s': %s",
-		             iface,
-		             nm_connection_get_id (connection),
-		             nm_platform_error_to_string (plerr));
-		return NULL;
-	}
-	plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
-
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_DRIVER, "bonding",
 	                                  NM_DEVICE_TYPE_DESC, "Bond",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_BOND,
 	                                  NM_DEVICE_IS_MASTER, TRUE,
 	                                  NULL);
 }
@@ -599,7 +530,6 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (BOND, Bond, bond,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BOND)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BOND_SETTING_NAME),
-	factory_iface->new_link = new_link;
-	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->create_device = create_device;
 	)
 
diff --git a/src/devices/nm-device-bond.h b/src/devices/nm-device-bond.h
index 17217a57..9e86719e 100644
--- a/src/devices/nm-device-bond.h
+++ b/src/devices/nm-device-bond.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_BOND_H__
 #define __NETWORKMANAGER_DEVICE_BOND_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -34,8 +32,6 @@ 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))
 
-#define NM_DEVICE_BOND_SLAVES "slaves"
-
 typedef NMDevice NMDeviceBond;
 typedef NMDeviceClass NMDeviceBondClass;
 
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index d6435530..843c5be4 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -15,29 +15,23 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2011 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
 #include <stdlib.h>
 
-#include "gsystem-local-alloc.h"
+#include "nm-default.h"
 #include "nm-device-bridge.h"
-#include "nm-logging.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 "nmdbus-device-bridge.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBridge);
@@ -50,13 +44,6 @@ typedef struct {
 	int dummy;
 } NMDeviceBridgePrivate;
 
-enum {
-	PROP_0,
-	PROP_SLAVES,
-
-	LAST_PROP
-};
-
 /******************************************************************/
 
 static NMDeviceCapabilities
@@ -103,7 +90,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	mac_address = nm_setting_bridge_get_mac_address (s_bridge);
-	if (mac_address) {
+	if (mac_address && nm_device_is_real (device)) {
 		const char *hw_addr;
 
 		hw_addr = nm_device_get_hw_address (device);
@@ -156,6 +143,7 @@ static const Option master_options[] = {
 	{ NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", TRUE, TRUE },
 	{ NM_SETTING_BRIDGE_MAX_AGE, "max_age", TRUE, TRUE },
 	{ NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", TRUE, TRUE },
+	{ NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", FALSE, FALSE },
 	{ NULL, NULL }
 };
 
@@ -211,9 +199,9 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 
 	value = g_strdup_printf ("%u", uval);
 	if (slave)
-		nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
+		nm_platform_sysctl_slave_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
 	else
-		nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
+		nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
 }
 
 static void
@@ -257,7 +245,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 
 	for (option = master_options; option->name; option++) {
-		gs_free char *str = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option->sysname);
+		gs_free char *str = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, option->sysname);
 		int value;
 
 		if (str) {
@@ -296,7 +284,7 @@ master_update_slave_connection (NMDevice *device,
 	}
 
 	for (option = slave_options; option->name; option++) {
-		gs_free char *str = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex_slave, option->sysname);
+		gs_free char *str = nm_platform_sysctl_slave_get_option (NM_PLATFORM_GET, ifindex_slave, option->sysname);
 		int value;
 
 		if (str) {
@@ -322,7 +310,7 @@ static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMActStageReturn ret;
-	NMConnection *connection = nm_device_get_connection (device);
+	NMConnection *connection = nm_device_get_applied_connection (device);
 
 	g_assert (connection);
 
@@ -356,18 +344,16 @@ enslave_slave (NMDevice *device,
 		       nm_device_get_ip_iface (slave));
 	}
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
-
 	return TRUE;
 }
 
-static gboolean
+static void
 release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
 	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
-	gboolean success = TRUE;
+	gboolean success;
 
 	if (configure) {
 		success = nm_platform_link_release (NM_PLATFORM_GET,
@@ -385,49 +371,57 @@ release_slave (NMDevice *device,
 		_LOGI (LOGD_BRIDGE, "bridge port %s was detached",
 		       nm_device_get_ip_iface (slave));
 	}
-
-	g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES);
-	return success;
 }
 
-/******************************************************************/
-
-static void
-nm_device_bridge_init (NMDeviceBridge * self)
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
 {
-}
+	NMSettingBridge *s_bridge;
+	const char *iface = nm_device_get_iface (device);
+	const char *hwaddr;
+	guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX];
+	NMPlatformError plerr;
 
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	GPtrArray *slaves;
-	GSList *list, *iter;
-
-	switch (prop_id) {
-	case PROP_SLAVES:
-		slaves = g_ptr_array_new ();
-		list = nm_device_master_get_slaves (NM_DEVICE (object));
-		for (iter = list; iter; iter = iter->next)
-			g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
-		g_slist_free (list);
-		g_value_take_boxed (value, slaves);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	g_assert (iface);
+
+	s_bridge = nm_connection_get_setting_bridge (connection);
+	g_assert (s_bridge);
+	hwaddr = nm_setting_bridge_get_mac_address (s_bridge);
+	if (hwaddr) {
+		if (!nm_utils_hwaddr_aton (hwaddr, mac_address, ETH_ALEN)) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+			             "Invalid hardware address '%s'",
+			             hwaddr);
+			return FALSE;
+		}
+	}
+
+	plerr = nm_platform_link_bridge_add (NM_PLATFORM_GET,
+	                                     iface,
+	                                     hwaddr ? mac_address : NULL,
+	                                     hwaddr ? ETH_ALEN : 0,
+	                                     out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create bridge interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
 	}
+
+	return TRUE;
 }
 
+/******************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
+nm_device_bridge_init (NMDeviceBridge * self)
 {
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
 }
 
 static void
@@ -438,11 +432,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceBridgePrivate));
 
-	parent_class->connection_type = NM_SETTING_BRIDGE_SETTING_NAME;
-
-	/* virtual methods */
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE)
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->is_available = is_available;
@@ -453,21 +443,14 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	parent_class->update_connection = update_connection;
 	parent_class->master_update_slave_connection = master_update_slave_connection;
 
+	parent_class->create_and_realize = create_and_realize;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_BRIDGE_SLAVES, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BRIDGE_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -476,61 +459,18 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 #define NM_BRIDGE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BRIDGE_FACTORY, NMBridgeFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
-{
-	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);
-}
-
-static NMDevice *
-create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                      NMConnection *connection,
-                                      NMDevice *parent,
-                                      GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	const char *iface = nm_connection_get_interface_name (connection);
-	NMSettingBridge *s_bridge;
-	const char *mac_address_str;
-	guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX];
-	NMPlatformError plerr;
-	const NMPlatformLink *plink;
-
-	g_assert (iface);
-
-	s_bridge = nm_connection_get_setting_bridge (connection);
-	g_assert (s_bridge);
-
-	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;
-	}
-
-	plerr = nm_platform_bridge_add (NM_PLATFORM_GET,
-	                                iface,
-	                                mac_address_str ? mac_address : NULL,
-	                                mac_address_str ? ETH_ALEN : 0,
-	                                NULL);
-	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Failed to create bridge interface '%s' for '%s': %s",
-		             iface,
-		             nm_connection_get_id (connection),
-		             nm_platform_error_to_string (plerr));
-		return NULL;
-	}
-	plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
-
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_DRIVER, "bridge",
 	                                  NM_DEVICE_TYPE_DESC, "Bridge",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_BRIDGE,
 	                                  NM_DEVICE_IS_MASTER, TRUE,
 	                                  NULL);
 }
@@ -538,7 +478,6 @@ create_virtual_device_for_connection (NMDeviceFactory *factory,
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (BRIDGE, Bridge, bridge,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_BRIDGE)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BRIDGE_SETTING_NAME),
-	factory_iface->new_link = new_link;
-	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->create_device = create_device;
 	)
 
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index c11a2c40..46482675 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -21,8 +21,7 @@
 #ifndef __NETWORKMANAGER_DEVICE_BRIDGE_H__
 #define __NETWORKMANAGER_DEVICE_BRIDGE_H__
 
-#include <glib-object.h>
-
+#include "nm-default.h"
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -34,8 +33,6 @@ 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))
 
-#define NM_DEVICE_BRIDGE_SLAVES "slaves"
-
 typedef NMDevice NMDeviceBridge;
 typedef NMDeviceClass NMDeviceBridgeClass;
 
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
index 585f6a0e..9165de31 100644
--- a/src/devices/nm-device-ethernet-utils.c
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -19,10 +19,9 @@
 #include "config.h"
 
 #include <string.h>
-#include <glib.h>
-#include <glib/gi18n.h>
 
 #include <nm-connection.h>
+#include "nm-default.h"
 #include "nm-device-ethernet-utils.h"
 
 char *
diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h
index 5848f7e6..31d645d8 100644
--- a/src/devices/nm-device-ethernet-utils.h
+++ b/src/devices/nm-device-ethernet-utils.h
@@ -19,7 +19,7 @@
 #ifndef __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 #define __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 
-#include <glib.h>
+#include "nm-default.h"
 
 char *nm_device_ethernet_utils_get_default_wired_name (const GSList *connections);
 
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 2dda84b3..a2396ec8 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -21,8 +21,6 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
 #include <netinet/in.h>
 #include <string.h>
 #include <stdlib.h>
@@ -31,7 +29,7 @@
 
 #include <gudev/gudev.h>
 
-#include "nm-glib-compat.h"
+#include "nm-default.h"
 #include "nm-device-ethernet.h"
 #include "nm-device-private.h"
 #include "nm-activation-request.h"
@@ -40,9 +38,7 @@
 #include "nm-supplicant-interface.h"
 #include "nm-supplicant-config.h"
 #include "ppp-manager/nm-ppp-manager.h"
-#include "nm-logging.h"
 #include "nm-enum-types.h"
-#include "nm-dbus-manager.h"
 #include "nm-platform.h"
 #include "nm-platform-utils.h"
 #include "nm-dcb.h"
@@ -53,9 +49,8 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "gsystem-local-alloc.h"
 
-#include "nm-device-ethernet-glue.h"
+#include "nmdbus-device-ethernet.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceEthernet);
@@ -76,8 +71,8 @@ typedef struct Supplicant {
 	NMSupplicantInterface *iface;
 
 	/* signal handler ids */
-	guint iface_error_id;
-	guint iface_state_id;
+	gulong iface_error_id;
+	gulong iface_state_id;
 
 	/* Timeouts and idles */
 	guint iface_con_error_cb_id;
@@ -109,6 +104,7 @@ typedef struct {
 	char *              subchan2;
 	char *              subchan3;
 	char *              subchannels; /* Composite used for checking unmanaged specs */
+	char **             subchannels_dbus; /* Array exported on D-Bus */
 	char *              s390_nettype;
 	GHashTable *        s390_options;
 
@@ -121,13 +117,14 @@ typedef struct {
 	/* DCB */
 	DcbWait       dcb_wait;
 	guint         dcb_timeout_id;
-	guint         dcb_carrier_id;
+	gulong        dcb_carrier_id;
 } NMDeviceEthernetPrivate;
 
 enum {
 	PROP_0,
 	PROP_PERM_HW_ADDRESS,
 	PROP_SPEED,
+	PROP_S390_SUBCHANNELS,
 
 	LAST_PROP
 };
@@ -230,6 +227,12 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	} else
 		priv->subchannels = g_strdup (priv->subchan1);
 
+	priv->subchannels_dbus = g_new (char *, 3 + 1);
+	priv->subchannels_dbus[0] = g_strdup (priv->subchan1);
+	priv->subchannels_dbus[1] = g_strdup (priv->subchan2);
+	priv->subchannels_dbus[2] = g_strdup (priv->subchan3);
+	priv->subchannels_dbus[3] = NULL;
+
 	driver = nm_device_get_driver (NM_DEVICE (self));
 	_LOGI (LOGD_DEVICE | LOGD_HW, "found s390 '%s' subchannels [%s]",
 	       driver ? driver : "(unknown driver)", priv->subchannels);
@@ -276,7 +279,7 @@ clear_secrets_tries (NMDevice *device)
 
 	req = nm_device_get_act_request (device);
 	if (req) {
-		connection = nm_act_request_get_connection (req);
+		connection = nm_act_request_get_applied_connection (req);
 		/* Clear wired secrets tries on success, failure, or when deactivating */
 		g_object_set_data (G_OBJECT (connection), WIRED_SECRETS_TRIES, NULL);
 	}
@@ -301,6 +304,14 @@ nm_device_ethernet_init (NMDeviceEthernet *self)
 	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->realize_start_notify (device, plink);
+
+	g_object_notify (G_OBJECT (device), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
+}
+
 static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
@@ -315,21 +326,39 @@ get_generic_capabilities (NMDevice *device)
 	}
 }
 
+static guint32
+_subchannels_count_num (const char * const *array)
+{
+	int i;
+
+	if (!array)
+		return 0;
+	for (i = 0; array[i]; i++)
+		/* NOP */;
+	return i;
+}
+
 static gboolean
 match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	const char * const *subchans;
+	guint32 num1, num2;
 	int i;
 
 	*try_mac = TRUE;
 
 	subchans = nm_setting_wired_get_s390_subchannels (s_wired);
-	if (!subchans)
+	num1 = _subchannels_count_num (subchans);
+	num2 = _subchannels_count_num ((const char * const *) priv->subchannels_dbus);
+	/* connection has no subchannels */
+	if (num1 == 0)
 		return TRUE;
-
 	/* connection requires subchannels but the device has none */
-	if (!priv->subchannels)
+	if (num2 == 0)
+		return FALSE;
+	/* number of subchannels differ */
+	if (num1 != num2)
 		return FALSE;
 
 	/* Make sure each subchannel in the connection is a subchannel of this device */
@@ -412,7 +441,7 @@ device_get_setting (NMDevice *device, GType setting_type)
 	if (req) {
 		NMConnection *connection;
 
-		connection = nm_act_request_get_connection (req);
+		connection = nm_act_request_get_applied_connection (req);
 		if (connection)
 			setting = nm_connection_get_setting (connection, setting_type);
 	}
@@ -428,15 +457,8 @@ remove_supplicant_timeouts (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
-	if (priv->supplicant.con_timeout_id) {
-		g_source_remove (priv->supplicant.con_timeout_id);
-		priv->supplicant.con_timeout_id = 0;
-	}
-
-	if (priv->supplicant_timeout_id) {
-		g_source_remove (priv->supplicant_timeout_id);
-		priv->supplicant_timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->supplicant.con_timeout_id);
+	nm_clear_g_source (&priv->supplicant_timeout_id);
 }
 
 static void
@@ -477,17 +499,19 @@ supplicant_interface_release (NMDeviceEthernet *self)
 
 static void
 wired_secrets_cb (NMActRequest *req,
-                  guint32 call_id,
-                  NMConnection *connection,
+                  NMActRequestGetSecretsCallId call_id,
+                  NMSettingsConnection *connection,
                   GError *error,
                   gpointer 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));
+	if (req != nm_device_get_act_request (dev))
+		return;
+
 	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);
+	g_return_if_fail (nm_act_request_get_settings_connection (req) == connection);
 
 	if (error) {
 		_LOGW (LOGD_ETHER, "%s", error->message);
@@ -505,7 +529,7 @@ link_timeout_cb (gpointer user_data)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMDevice *dev = NM_DEVICE (self);
 	NMActRequest *req;
-	NMConnection *connection;
+	NMConnection *applied_connection;
 	const char *setting_name;
 
 	priv->supplicant_timeout_id = 0;
@@ -526,9 +550,10 @@ link_timeout_cb (gpointer user_data)
 	if (nm_device_get_state (dev) != NM_DEVICE_STATE_CONFIG)
 		goto time_out;
 
-	connection = nm_act_request_get_connection (req);
-	nm_connection_clear_secrets (connection);
-	setting_name = nm_connection_need_secrets (connection, NULL);
+	nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req));
+
+	applied_connection = nm_act_request_get_applied_connection (req);
+	setting_name = nm_connection_need_secrets (applied_connection, NULL);
 	if (!setting_name)
 		goto time_out;
 
@@ -554,7 +579,8 @@ time_out:
 }
 
 static NMSupplicantConfig *
-build_supplicant_config (NMDeviceEthernet *self)
+build_supplicant_config (NMDeviceEthernet *self,
+                         GError **error)
 {
 	const char *con_uuid;
 	NMSupplicantConfig *config = NULL;
@@ -562,7 +588,7 @@ build_supplicant_config (NMDeviceEthernet *self)
 	NMConnection *connection;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (NM_DEVICE (self));
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_assert (connection);
 	con_uuid = nm_connection_get_uuid (connection);
 	mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET,
@@ -571,10 +597,9 @@ build_supplicant_config (NMDeviceEthernet *self)
 	config = nm_supplicant_config_new ();
 
 	security = nm_connection_get_setting_802_1x (connection);
-	if (!nm_supplicant_config_add_setting_8021x (config, security, con_uuid, mtu, TRUE)) {
-		_LOGW (LOGD_DEVICE, "Couldn't add 802.1X security setting to supplicant config.");
-		g_object_unref (config);
-		config = NULL;
+	if (!nm_supplicant_config_add_setting_8021x (config, security, con_uuid, mtu, TRUE, error)) {
+		g_prefix_error (error, "802-1x-setting: ");
+		g_clear_object (&config);
 	}
 
 	return config;
@@ -593,6 +618,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMSupplicantConfig *config;
 	gboolean success = FALSE;
 	NMDeviceState devstate;
+	GError *error = NULL;
 
 	if (new_state == old_state)
 		return;
@@ -605,18 +631,22 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 
 	switch (new_state) {
 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		config = build_supplicant_config (self);
+		config = build_supplicant_config (self, &error);
 		if (config) {
-			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config);
+			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config, &error);
 			g_object_unref (config);
 
 			if (!success) {
 				_LOGE (LOGD_DEVICE | LOGD_ETHER,
-				       "Activation: (ethernet) couldn't send security configuration to the supplicant.");
+				       "Activation: (ethernet) couldn't send security configuration to the supplicant: %s",
+				       error ? error->message : "<BUG>");
+				g_clear_error (&error);
 			}
 		} else {
-			_LOGW (LOGD_DEVICE | LOGD_ETHER,
-			       "Activation: (ethernet) couldn't build security configuration.");
+			_LOGE (LOGD_DEVICE | LOGD_ETHER,
+			       "Activation: (ethernet) couldn't build security configuration: %s",
+			       error ? error->message : "<BUG>");
+			g_clear_error (&error);
 		}
 
 		if (!success) {
@@ -701,19 +731,19 @@ handle_auth_or_fail (NMDeviceEthernet *self,
 {
 	const char *setting_name;
 	guint32 tries;
-	NMConnection *connection;
+	NMConnection *applied_connection;
 
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
+	applied_connection = nm_act_request_get_applied_connection (req);
 
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRED_SECRETS_TRIES));
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), WIRED_SECRETS_TRIES));
 	if (tries > 3)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
-	nm_connection_clear_secrets (connection);
-	setting_name = nm_connection_need_secrets (connection, NULL);
+	nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req));
+
+	setting_name = nm_connection_need_secrets (applied_connection, NULL);
 	if (setting_name) {
 		NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
@@ -721,7 +751,7 @@ handle_auth_or_fail (NMDeviceEthernet *self,
 			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));
+		g_object_set_data (G_OBJECT (applied_connection), WIRED_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
 	} else
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
 
@@ -751,7 +781,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	/* Ask for new secrets only if we've never activated this connection
@@ -873,7 +903,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 	const char *setting_name;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
-	connection = nm_device_get_connection (NM_DEVICE (self));
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_assert (connection);
 	security = nm_connection_get_setting_802_1x (connection);
 	if (!security) {
@@ -959,7 +989,7 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_assert (req);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (req);
 
 	s_pppoe = nm_connection_get_setting_pppoe (connection);
@@ -967,19 +997,18 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 
 	priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (NM_DEVICE (self)));
 	if (nm_ppp_manager_start (priv->ppp_manager, req, nm_setting_pppoe_get_username (s_pppoe), 30, &err)) {
-		g_signal_connect (priv->ppp_manager, "state-changed",
-					   G_CALLBACK (ppp_state_changed),
-					   self);
+		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_STATE_CHANGED,
+		                  G_CALLBACK (ppp_state_changed),
+		                  self);
 		g_signal_connect (priv->ppp_manager, "ip4-config",
-					   G_CALLBACK (ppp_ip4_config),
-					   self);
+		                  G_CALLBACK (ppp_ip4_config),
+		                  self);
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
 		_LOGW (LOGD_DEVICE, "PPPoE failed to start: %s", err->message);
 		g_error_free (err);
 
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
+		nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 
 		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
 	}
@@ -994,10 +1023,7 @@ dcb_timeout_cleanup (NMDevice *device)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 
-	if (priv->dcb_timeout_id) {
-		g_source_remove (priv->dcb_timeout_id);
-		priv->dcb_timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->dcb_timeout_id);
 }
 
 static void
@@ -1005,10 +1031,7 @@ dcb_carrier_cleanup (NMDevice *device)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 
-	if (priv->dcb_carrier_id) {
-		g_signal_handler_disconnect (device, priv->dcb_carrier_id);
-		priv->dcb_carrier_id = 0;
-	}
+	nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 }
 
 static void dcb_state (NMDevice *device, gboolean timeout);
@@ -1280,7 +1303,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	}
 
 	/* PPPoE setup */
-	if (nm_connection_is_type (nm_device_get_connection (device),
+	if (nm_connection_is_type (nm_device_get_applied_connection (device),
 	                           NM_SETTING_PPPOE_SETTING_NAME)) {
 		NMSettingPpp *s_ppp;
 
@@ -1335,7 +1358,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	if (NM_DEVICE_ETHERNET_GET_PRIVATE (device)->ppp_manager)
 		return;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
@@ -1357,20 +1380,14 @@ deactivate (NMDevice *device)
 	/* Clear wired secrets tries when deactivating */
 	clear_secrets_tries (device);
 
-	if (priv->pppoe_wait_id) {
-		g_source_remove (priv->pppoe_wait_id);
-		priv->pppoe_wait_id = 0;
-	}
+	nm_clear_g_source (&priv->pppoe_wait_id);
 
 	if (priv->pending_ip4_config) {
 		g_object_unref (priv->pending_ip4_config);
 		priv->pending_ip4_config = NULL;
 	}
 
-	if (priv->ppp_manager) {
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
-	}
+	nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 
 	supplicant_interface_release (self);
 
@@ -1547,16 +1564,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* We don't set the MTU as we don't know whether it was set explicitly */
 
 	/* s390 */
-	if (priv->subchannels) {
-		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->subchannels_dbus)
+		g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, priv->subchannels_dbus, NULL);
 	if (priv->s390_nettype)
 		g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, priv->s390_nettype, NULL);
 	g_hash_table_iter_init (&iter, priv->s390_options);
@@ -1598,10 +1607,44 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	static const guint8 zero_hwaddr[ETH_ALEN];
+	const guint8 *hwaddr;
+	gsize hwaddrlen = 0;
 
 	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info);
 	if (!priv->subchan1 && info->initialized)
 		_update_s390_subchannels (self);
+
+	if (!nm_device_get_initial_hw_address (device)) {
+		hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET,
+		                                       nm_device_get_ifindex (self),
+		                                       &hwaddrlen);
+		if (!nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, ETH_ALEN)) {
+			_LOGD (LOGD_DEVICE, "device got a valid hw address");
+			nm_device_update_hw_address (self);
+			nm_device_update_initial_hw_address (self);
+			if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
+				/*
+				 * If the device is UNAVAILABLE, any previous try to
+				 * bring it up probably has failed because of the
+				 * invalid hardware address; try again.
+				 */
+				nm_device_bring_up (self, TRUE, NULL);
+				nm_device_queue_recheck_available (device,
+				                                   NM_DEVICE_STATE_REASON_NONE,
+				                                   NM_DEVICE_STATE_REASON_NONE);
+			}
+		}
+	}
+}
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->is_available (device, flags))
+		return FALSE;
+
+	return !!nm_device_get_initial_hw_address (device);
 }
 
 static void
@@ -1610,10 +1653,7 @@ dispose (GObject *object)
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
-	if (priv->pppoe_wait_id) {
-		g_source_remove (priv->pppoe_wait_id);
-		priv->pppoe_wait_id = 0;
-	}
+	nm_clear_g_source (&priv->pppoe_wait_id);
 
 	dcb_timeout_cleanup (NM_DEVICE (self));
 	dcb_carrier_cleanup (NM_DEVICE (self));
@@ -1632,6 +1672,7 @@ finalize (GObject *object)
 	g_free (priv->subchan2);
 	g_free (priv->subchan3);
 	g_free (priv->subchannels);
+	g_strfreev (priv->subchannels_dbus);
 	g_free (priv->s390_nettype);
 	g_hash_table_destroy (priv->s390_options);
 
@@ -1652,6 +1693,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SPEED:
 		g_value_set_uint (value, priv->speed);
 		break;
+	case PROP_S390_SUBCHANNELS:
+		g_value_set_boxed (value, priv->subchannels_dbus);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1677,7 +1721,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate));
 
-	parent_class->connection_type = NM_SETTING_WIRED_SETTING_NAME;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRED_SETTING_NAME, NM_LINK_TYPE_ETHERNET)
 
 	/* virtual methods */
 	object_class->constructor = constructor;
@@ -1687,6 +1731,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	object_class->set_property = set_property;
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
+	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
 	parent_class->new_default_connection = new_default_connection;
@@ -1700,6 +1745,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->update_connection = update_connection;
 	parent_class->carrier_changed = carrier_changed;
 	parent_class->link_changed = link_changed;
+	parent_class->is_available = is_available;
 
 	parent_class->state_changed = device_state_changed;
 
@@ -1718,9 +1764,16 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 		                    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);
+	g_object_class_install_property
+		(object_class, PROP_S390_SUBCHANNELS,
+		 g_param_spec_boxed (NM_DEVICE_ETHERNET_S390_SUBCHANNELS, "", "",
+		                     G_TYPE_STRV,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_ETHERNET_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -1729,18 +1782,23 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 #define NM_ETHERNET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ETHERNET_FACTORY, NMEthernetFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Ethernet",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_ETHERNET,
 	                                  NULL);
 }
 
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (ETHERNET, Ethernet, ethernet,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_ETHERNET)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_PPPOE_SETTING_NAME),
-	factory_iface->new_link = new_link;
+	factory_iface->create_device = create_device;
 	)
 
diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h
index ad9ce856..2d284822 100644
--- a/src/devices/nm-device-ethernet.h
+++ b/src/devices/nm-device-ethernet.h
@@ -22,8 +22,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_ETHERNET_H__
 #define __NETWORKMANAGER_DEVICE_ETHERNET_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -37,6 +35,7 @@ G_BEGIN_DECLS
 
 #define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address"
 #define NM_DEVICE_ETHERNET_SPEED "speed"
+#define NM_DEVICE_ETHERNET_S390_SUBCHANNELS "s390-subchannels"
 
 typedef NMDevice NMDeviceEthernet;
 typedef NMDeviceClass NMDeviceEthernetClass;
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index 13034808..75731b14 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -27,12 +27,14 @@
 #include <gmodule.h>
 
 #include "nm-device-factory.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "nm-platform.h"
 
 const NMLinkType _nm_device_factory_no_default_links[] = { NM_LINK_TYPE_NONE };
 const char *_nm_device_factory_no_default_settings[] = { NULL };
 
+G_DEFINE_INTERFACE (NMDeviceFactory, nm_device_factory, G_TYPE_OBJECT)
+
 enum {
 	DEVICE_ADDED,
 	COMPONENT_ADDED,
@@ -79,86 +81,64 @@ nm_device_factory_start (NMDeviceFactory *factory)
 }
 
 NMDevice *
-nm_device_factory_new_link (NMDeviceFactory *factory,
-                            NMPlatformLink *plink,
-                            gboolean *out_ignore,
-                            GError **error)
+nm_device_factory_create_device (NMDeviceFactory *factory,
+                                 const char *iface,
+                                 const NMPlatformLink *plink,
+                                 NMConnection *connection,
+                                 gboolean *out_ignore,
+                                 GError **error)
 {
-	NMDeviceFactory *interface;
+	NMDeviceFactoryInterface *interface;
 	const NMLinkType *link_types = NULL;
 	const char **setting_types = NULL;
 	int i;
 
-	g_return_val_if_fail (factory != NULL, NULL);
-	g_return_val_if_fail (plink != NULL, NULL);
+	g_return_val_if_fail (factory, NULL);
+	g_return_val_if_fail (iface && *iface, NULL);
+	g_return_val_if_fail (plink || connection, NULL);
+	g_return_val_if_fail (!plink || !connection, NULL);
 
-	/* Ensure the factory can create interfaces for this connection */
 	nm_device_factory_get_supported_types (factory, &link_types, &setting_types);
-	for (i = 0; link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) {
-		if (plink->type == link_types[i])
-			break;
-	}
 
-	if (link_types[i] == NM_LINK_TYPE_UNKNOWN) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Device factory %s does not support link type %s (%d)",
-		             G_OBJECT_TYPE_NAME (factory),
-		             plink->kind, plink->type);
-		return NULL;
-	}
+	if (plink) {
+		g_return_val_if_fail (strcmp (iface, plink->name) == 0, NULL);
 
-	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
-	if (!interface->new_link) {
-		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		             "Device factory %s cannot manage new devices",
-		             G_OBJECT_TYPE_NAME (factory));
-		return NULL;
-	}
-
-	return interface->new_link (factory, plink, out_ignore, error);
-}
-
-NMDevice *
-nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                                        NMConnection *connection,
-                                                        NMDevice *parent,
-                                                        GError **error)
-{
-	NMDeviceFactory *interface;
-	const char **setting_types = NULL;
-	gboolean found = FALSE;
-	int i;
+		for (i = 0; link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) {
+			if (plink->type == link_types[i])
+				break;
+		}
 
-	g_return_val_if_fail (factory, NULL);
-	g_return_val_if_fail (connection, NULL);
-	g_return_val_if_fail (!error || !*error, NULL);
-
-	/* Ensure the factory can create interfaces for this connection */
-	nm_device_factory_get_supported_types (factory, NULL, &setting_types);
-	for (i = 0; setting_types && setting_types[i]; i++) {
-		if (nm_connection_is_type (connection, setting_types[i])) {
-			found = TRUE;
-			break;
+		if (link_types[i] == NM_LINK_TYPE_UNKNOWN) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+			             "Device factory %s does not support link type %s (%d)",
+			             G_OBJECT_TYPE_NAME (factory),
+			             plink->kind, plink->type);
+			return NULL;
+		}
+	} else if (connection) {
+		for (i = 0; setting_types && setting_types[i]; i++) {
+			if (nm_connection_is_type (connection, setting_types[i]))
+				break;
 		}
-	}
 
-	if (!found) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
-		             "Device factory %s does not support connection type %s",
-		             G_OBJECT_TYPE_NAME (factory),
-		             nm_connection_get_connection_type (connection));
-		return NULL;
+		if (!setting_types[i]) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			             "Device factory %s does not support connection type %s",
+			             G_OBJECT_TYPE_NAME (factory),
+			             nm_connection_get_connection_type (connection));
+			return NULL;
+		}
 	}
 
 	interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
-	if (!interface->create_virtual_device_for_connection) {
+	if (!interface->create_device) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		             "Device factory %s cannot create virtual devices",
+		             "Device factory %s cannot manage new devices",
 		             G_OBJECT_TYPE_NAME (factory));
 		return NULL;
 	}
 
-	return interface->create_virtual_device_for_connection (factory, connection, parent, error);
+	return interface->create_device (factory, iface, plink, connection, out_ignore);
 }
 
 const char *
@@ -210,7 +190,7 @@ nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory,
 /*******************************************************************/
 
 static void
-default_init (NMDeviceFactory *factory_iface)
+nm_device_factory_default_init (NMDeviceFactoryInterface *factory_iface)
 {
 	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 
@@ -218,37 +198,18 @@ default_init (NMDeviceFactory *factory_iface)
 	signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED,
 	                                      NM_TYPE_DEVICE_FACTORY,
 	                                      G_SIGNAL_RUN_FIRST,
-	                                      G_STRUCT_OFFSET (NMDeviceFactory, device_added),
+	                                      G_STRUCT_OFFSET (NMDeviceFactoryInterface, device_added),
 	                                      NULL, NULL, NULL,
 	                                      G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED,
 	                                         NM_TYPE_DEVICE_FACTORY,
 	                                         G_SIGNAL_RUN_LAST,
-	                                         G_STRUCT_OFFSET (NMDeviceFactory, component_added),
+	                                         G_STRUCT_OFFSET (NMDeviceFactoryInterface, component_added),
 	                                         g_signal_accumulator_true_handled, NULL, NULL,
 	                                         G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
 }
 
-GType
-nm_device_factory_get_type (void)
-{
-	static volatile gsize g_define_type_id__volatile = 0;
-	if (g_once_init_enter (&g_define_type_id__volatile)) {
-		GType g_define_type_id =
-			g_type_register_static_simple (G_TYPE_INTERFACE,
-			                               g_intern_static_string ("NMDeviceFactory"),
-			                               sizeof (NMDeviceFactory),
-			                               (GClassInitFunc) default_init,
-			                               0,
-			                               (GInstanceInitFunc) NULL,
-			                               (GTypeFlags) 0);
-		g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
-		g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-	}
-	return g_define_type_id__volatile;
-}
-
 /*******************************************************************/
 
 static GSList *internal_types = NULL;
@@ -325,19 +286,20 @@ nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc ca
 	NMDeviceFactory *factory;
 	GSList *list_iter, *list = NULL;
 
-	g_return_if_fail (factories_by_link);
-	g_return_if_fail (factories_by_setting);
-
-	g_hash_table_iter_init (&iter, factories_by_link);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
-		if (!g_slist_find (list, factory))
-			list = g_slist_prepend (list, factory);
+	if (factories_by_link) {
+		g_hash_table_iter_init (&iter, factories_by_link);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
+			if (!g_slist_find (list, factory))
+				list = g_slist_prepend (list, factory);
+		}
 	}
 
-	g_hash_table_iter_init (&iter, factories_by_setting);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
-		if (!g_slist_find (list, factory))
-			list = g_slist_prepend (list, factory);
+	if (factories_by_setting) {
+		g_hash_table_iter_init (&iter, factories_by_setting);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) {
+			if (!g_slist_find (list, factory))
+				list = g_slist_prepend (list, factory);
+		}
 	}
 
 	for (list_iter = list; list_iter; list_iter = list_iter->next)
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index 34c056d2..9b7cb35a 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -21,9 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_FACTORY_H__
 #define __NETWORKMANAGER_DEVICE_FACTORY_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
 #include "nm-dbus-interface.h"
 #include "nm-device.h"
 
@@ -56,13 +53,13 @@ typedef NMDeviceFactory * (*NMDeviceFactoryCreateFunc) (GError **error);
 #define NM_TYPE_DEVICE_FACTORY               (nm_device_factory_get_type ())
 #define NM_DEVICE_FACTORY(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactory))
 #define NM_IS_DEVICE_FACTORY(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_FACTORY))
-#define NM_DEVICE_FACTORY_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactory))
+#define NM_DEVICE_FACTORY_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactoryInterface))
 
 /* signals */
 #define NM_DEVICE_FACTORY_COMPONENT_ADDED "component-added"
 #define NM_DEVICE_FACTORY_DEVICE_ADDED    "device-added"
 
-struct _NMDeviceFactory {
+typedef struct {
 	GTypeInterface g_iface;
 
 	/**
@@ -87,51 +84,7 @@ struct _NMDeviceFactory {
 	 * Start the factory and discover any existing devices that the factory
 	 * can manage.
 	 */
-	void (*start)                   (NMDeviceFactory *factory);
-
-	/**
-	 * new_link:
-	 * @factory: the #NMDeviceFactory
-	 * @plink: the new link
-	 * @out_ignore: on return, %TRUE if the link should be ignored
-	 * @error: error if the link could be claimed but an error occurred
-	 *
-	 * The NetworkManager core was notified of a new link which the plugin
-	 * may want to claim and create a #NMDevice subclass for.  If the link
-	 * represents a device which the factory does not support, or the link
-	 * is supported but the device could not be created, %NULL should be
-	 * returned and @error should be set.
-	 *
-	 * If the plugin cannot create a #NMDevice for the link and wants the
-	 * core to ignore it, set @out_ignore to %TRUE and return no error.
-	 *
-	 * @plink is guaranteed to be one of the types the factory returns in
-	 * get_supported_types().
-	 *
-	 * Returns: the #NMDevice if the link was claimed and created, %NULL if not
-	 */
-	NMDevice * (*new_link)        (NMDeviceFactory *factory,
-	                               NMPlatformLink *plink,
-	                               gboolean *out_ignore,
-	                               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);
+	void (*start)                 (NMDeviceFactory *factory);
 
 	/**
 	 * get_connection_parent:
@@ -162,6 +115,30 @@ struct _NMDeviceFactory {
 	                                  NMConnection *connection,
 	                                  const char *parent_iface);
 
+	/**
+	 * create_device:
+	 * @factory: the #NMDeviceFactory
+	 * @iface: the interface name of the device
+	 * @plink: the #NMPlatformLink if backed by a kernel device
+	 * @connection: the #NMConnection if not backed by a kernel device
+	 * @out_ignore: on return, %TRUE if the link should be ignored
+	 *
+	 * The plugin should create a new unrealized device using the details given
+	 * by @iface and @plink or @connection.  If both @iface and @plink are given,
+	 * they are guaranteed to match.  If both @iface and @connection are given,
+	 * @iface is guaranteed to be the interface name that @connection specifies.
+	 *
+	 * If the plugin cannot create a #NMDevice for the link and wants the
+	 * core to ignore it, set @out_ignore to %TRUE and return %NULL.
+	 *
+	 * Returns: the new unrealized #NMDevice, or %NULL
+	 */
+	NMDevice * (*create_device)   (NMDeviceFactory *factory,
+	                               const char *iface,
+	                               const NMPlatformLink *plink,
+	                               NMConnection *connection,
+	                               gboolean *out_ignore);
+
 	/* Signals */
 
 	/**
@@ -187,7 +164,7 @@ struct _NMDeviceFactory {
 	 * Returns: %TRUE if the component was claimed by a device, %FALSE if not
 	 */
 	gboolean   (*component_added) (NMDeviceFactory *factory, GObject *component);
-};
+} NMDeviceFactoryInterface;
 
 GType      nm_device_factory_get_type    (void);
 
@@ -204,15 +181,12 @@ char *     nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory,
 
 void       nm_device_factory_start       (NMDeviceFactory *factory);
 
-NMDevice * nm_device_factory_new_link    (NMDeviceFactory *factory,
-                                          NMPlatformLink *plink,
-                                          gboolean *out_ignore,
-                                          GError **error);
-
-NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                                                   NMConnection *connection,
-                                                                   NMDevice *parent,
-                                                                   GError **error);
+NMDevice * nm_device_factory_create_device (NMDeviceFactory *factory,
+                                            const char *iface,
+                                            const NMPlatformLink *plink,
+                                            NMConnection *connection,
+                                            gboolean *out_ignore,
+                                            GError **error);
 
 /* For use by implementations */
 gboolean   nm_device_factory_emit_component_added (NMDeviceFactory *factory,
@@ -249,7 +223,7 @@ extern const char *_nm_device_factory_no_default_settings[];
 	typedef GObjectClass NM##mixed##FactoryClass; \
  \
 	static GType nm_##lower##_factory_get_type (void); \
-	static void device_factory_interface_init (NMDeviceFactory *factory_iface); \
+	static void device_factory_interface_init (NMDeviceFactoryInterface *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) \
@@ -262,14 +236,14 @@ extern const char *_nm_device_factory_no_default_settings[];
 	static void __attribute__((constructor)) \
 	register_device_factory_internal_##lower (void) \
 	{ \
-		g_type_init (); \
+		nm_g_type_init (); \
 		g_type_ensure (NM_TYPE_##upper##_FACTORY); \
 	} \
  \
 	NM_DEVICE_FACTORY_DECLARE_TYPES(st_code) \
  \
 	static void \
-	device_factory_interface_init (NMDeviceFactory *factory_iface) \
+	device_factory_interface_init (NMDeviceFactoryInterface *factory_iface) \
 	{ \
 		factory_iface->get_supported_types = get_supported_types; \
 		dfi_code \
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 5841d834..2e9c134a 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -24,11 +24,9 @@
 #include "nm-device-private.h"
 #include "nm-enum-types.h"
 #include "nm-platform.h"
-#include "nm-glib-compat.h"
-#include "nm-dbus-manager.h"
 #include "nm-core-internal.h"
 
-#include "nm-device-generic-glue.h"
+#include "nmdbus-device-generic.h"
 
 G_DEFINE_TYPE (NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE)
 
@@ -62,6 +60,21 @@ get_type_description (NMDevice *device)
 	return NM_DEVICE_CLASS (nm_device_generic_parent_class)->get_type_description (device);
 }
 
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NMDeviceGeneric *self = NM_DEVICE_GENERIC (device);
+	NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (self);
+	int ifindex;
+
+	NM_DEVICE_CLASS (nm_device_generic_parent_class)->realize_start_notify (device, plink);
+
+	g_clear_pointer (&priv->type_description, g_free);
+	ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
+	if (ifindex > 0)
+		priv->type_description = g_strdup (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex));
+}
+
 static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
@@ -98,12 +111,12 @@ update_connection (NMDevice *device, NMConnection *connection)
 /**************************************************************/
 
 NMDevice *
-nm_device_generic_new (NMPlatformLink *platform_device)
+nm_device_generic_new (const NMPlatformLink *plink)
 {
-	g_return_val_if_fail (platform_device != NULL, NULL);
+	g_return_val_if_fail (plink != NULL, NULL);
 
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GENERIC,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
+	                                  NM_DEVICE_IFACE, plink->name,
 	                                  NM_DEVICE_TYPE_DESC, "Generic",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
 	                                  NULL);
@@ -112,23 +125,22 @@ nm_device_generic_new (NMPlatformLink *platform_device)
 static void
 nm_device_generic_init (NMDeviceGeneric *self)
 {
-	nm_device_set_initial_unmanaged_flag (NM_DEVICE (self), NM_UNMANAGED_DEFAULT, TRUE);
 }
 
-static void
-constructed (GObject *object)
+static GObject *
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
 {
-	NMDeviceGeneric *self = NM_DEVICE_GENERIC (object);
-	NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (self);
+	GObject *object;
 
-	if (!priv->type_description) {
-		int ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
+	object = G_OBJECT_CLASS (nm_device_generic_parent_class)->constructor (type,
+	                                                                       n_construct_params,
+	                                                                       construct_params);
 
-		if (ifindex != 0)
-			priv->type_description = g_strdup (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex));
-	}
+	nm_device_set_unmanaged_flags_initial (NM_DEVICE (object), NM_UNMANAGED_DEFAULT, TRUE);
 
-	G_OBJECT_CLASS (nm_device_generic_parent_class)->constructed (object);
+	return object;
 }
 
 static void
@@ -184,13 +196,14 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate));
 
-	parent_class->connection_type = NM_SETTING_GENERIC_SETTING_NAME;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_GENERIC_SETTING_NAME, NM_LINK_TYPE_ANY)
 
-	object_class->constructed = constructed;
+	object_class->constructor = constructor;
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
+	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->get_type_description = get_type_description;
 	parent_class->check_connection_compatible = check_connection_compatible;
@@ -204,7 +217,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 		                      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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_GENERIC_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/nm-device-generic.h b/src/devices/nm-device-generic.h
index c889b1dd..9303a278 100644
--- a/src/devices/nm-device-generic.h
+++ b/src/devices/nm-device-generic.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_GENERIC_H__
 #define __NETWORKMANAGER_DEVICE_GENERIC_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -47,7 +45,7 @@ typedef struct {
 
 GType nm_device_generic_get_type (void);
 
-NMDevice *nm_device_generic_new (NMPlatformLink *platform_device);
+NMDevice *nm_device_generic_new (const NMPlatformLink *plink);
 
 G_END_DECLS
 
diff --git a/src/devices/nm-device-gre.c b/src/devices/nm-device-gre.c
deleted file mode 100644
index b3510d2e..00000000
--- a/src/devices/nm-device-gre.c
+++ /dev/null
@@ -1,283 +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 2013 Red Hat, Inc.
- */
-
-#include "config.h"
-
-#include <string.h>
-#include <arpa/inet.h>
-
-#include "nm-device-gre.h"
-#include "nm-device-private.h"
-#include "nm-dbus-manager.h"
-#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))
-
-typedef struct {
-	NMPlatformGreProperties props;
-} NMDeviceGrePrivate;
-
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_INPUT_FLAGS,
-	PROP_OUTPUT_FLAGS,
-	PROP_INPUT_KEY,
-	PROP_OUTPUT_KEY,
-	PROP_LOCAL,
-	PROP_REMOTE,
-	PROP_TTL,
-	PROP_TOS,
-	PROP_PATH_MTU_DISCOVERY,
-
-	LAST_PROP
-};
-
-/**************************************************************/
-
-static void
-update_properties (NMDevice *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_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
-		_LOGW (LOGD_HW, "could not read gre properties");
-		return;
-	}
-
-	g_object_freeze_notify (object);
-
-	if (priv->props.parent_ifindex != props.parent_ifindex)
-		g_object_notify (object, NM_DEVICE_GRE_PARENT);
-	if (priv->props.input_flags != props.input_flags)
-		g_object_notify (object, NM_DEVICE_GRE_INPUT_FLAGS);
-	if (priv->props.output_flags != props.output_flags)
-		g_object_notify (object, NM_DEVICE_GRE_OUTPUT_FLAGS);
-	if (priv->props.input_key != props.input_key)
-		g_object_notify (object, NM_DEVICE_GRE_INPUT_KEY);
-	if (priv->props.output_key != props.output_key)
-		g_object_notify (object, NM_DEVICE_GRE_OUTPUT_KEY);
-	if (priv->props.local != props.local)
-		g_object_notify (object, NM_DEVICE_GRE_LOCAL);
-	if (priv->props.remote != props.remote)
-		g_object_notify (object, NM_DEVICE_GRE_REMOTE);
-	if (priv->props.ttl != props.ttl)
-		g_object_notify (object, NM_DEVICE_GRE_TTL);
-	if (priv->props.tos != props.tos)
-		g_object_notify (object, NM_DEVICE_GRE_TOS);
-	if (priv->props.path_mtu_discovery != props.path_mtu_discovery)
-		g_object_notify (object, NM_DEVICE_GRE_PATH_MTU_DISCOVERY);
-
-	memcpy (&priv->props, &props, sizeof (NMPlatformGreProperties));
-
-	g_object_thaw_notify (object);
-}
-
-static void
-link_changed (NMDevice *device, NMPlatformLink *info)
-{
-	NM_DEVICE_CLASS (nm_device_gre_parent_class)->link_changed (device, info);
-	update_properties (device);
-}
-
-/**************************************************************/
-
-static void
-nm_device_gre_init (NMDeviceGre *self)
-{
-}
-
-static void
-constructed (GObject *object)
-{
-	update_properties (NM_DEVICE (object));
-
-	G_OBJECT_CLASS (nm_device_gre_parent_class)->constructed (object);
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMDeviceGrePrivate *priv = NM_DEVICE_GRE_GET_PRIVATE (object);
-	char buf[INET_ADDRSTRLEN];
-	NMDevice *parent;
-
-	switch (prop_id) {
-	case PROP_PARENT:
-		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
-		g_value_set_boxed (value, parent ? nm_device_get_path (parent) : "/");
-		break;
-	case PROP_INPUT_FLAGS:
-		g_value_set_uint (value, priv->props.input_flags);
-		break;
-	case PROP_OUTPUT_FLAGS:
-		g_value_set_uint (value, priv->props.output_flags);
-		break;
-	case PROP_INPUT_KEY:
-		g_value_set_uint (value, priv->props.input_key);
-		break;
-	case PROP_OUTPUT_KEY:
-		g_value_set_uint (value, priv->props.output_key);
-		break;
-	case PROP_LOCAL:
-		g_value_set_string (value, inet_ntop (AF_INET, &priv->props.local, buf, sizeof (buf)));
-		break;
-	case PROP_REMOTE:
-		g_value_set_string (value, inet_ntop (AF_INET, &priv->props.remote, buf, sizeof (buf)));
-		break;
-	case PROP_TTL:
-		g_value_set_uchar (value, priv->props.ttl);
-		break;
-	case PROP_TOS:
-		g_value_set_uchar (value, priv->props.tos);
-		break;
-	case PROP_PATH_MTU_DISCOVERY:
-		g_value_set_boolean (value, priv->props.path_mtu_discovery);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-nm_device_gre_class_init (NMDeviceGreClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
-
-	g_type_class_add_private (klass, sizeof (NMDeviceGrePrivate));
-
-	object_class->constructed = constructed;
-	object_class->get_property = get_property;
-
-	device_class->link_changed = link_changed;
-
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_GRE_PARENT, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     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, "", "",
-		                    0, G_MAXUINT16, 0,
-		                    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, "", "",
-		                    0, G_MAXUINT16, 0,
-		                    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, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    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, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_DEVICE_GRE_LOCAL, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_REMOTE,
-		 g_param_spec_string (NM_DEVICE_GRE_REMOTE, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_TTL,
-		 g_param_spec_uchar (NM_DEVICE_GRE_TTL, "", "",
-		                     0, 255, 0,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_TOS,
-		 g_param_spec_uchar (NM_DEVICE_GRE_TOS, "", "",
-		                     0, 255, 0,
-		                     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, "", "",
-		                       FALSE,
-		                       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, gboolean *out_ignore, GError **error)
-{
-	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);
-}
-
-NM_DEVICE_FACTORY_DEFINE_INTERNAL (GRE, Gre, gre,
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE, NM_LINK_TYPE_GRETAP),
-	factory_iface->new_link = new_link;
-	)
-
diff --git a/src/devices/nm-device-gre.h b/src/devices/nm-device-gre.h
deleted file mode 100644
index 7acff5f7..00000000
--- a/src/devices/nm-device-gre.h
+++ /dev/null
@@ -1,55 +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 2013 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_DEVICE_GRE_H__
-#define __NETWORKMANAGER_DEVICE_GRE_H__
-
-#include <glib-object.h>
-
-#include "nm-device-generic.h"
-
-G_BEGIN_DECLS
-
-#define NM_TYPE_DEVICE_GRE            (nm_device_gre_get_type ())
-#define NM_DEVICE_GRE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GRE, NMDeviceGre))
-#define NM_DEVICE_GRE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_GRE, NMDeviceGreClass))
-#define NM_IS_DEVICE_GRE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_GRE))
-#define NM_IS_DEVICE_GRE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_GRE))
-#define NM_DEVICE_GRE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_GRE, NMDeviceGreClass))
-
-#define NM_DEVICE_GRE_PARENT             "parent"
-#define NM_DEVICE_GRE_INPUT_FLAGS        "input-flags"
-#define NM_DEVICE_GRE_OUTPUT_FLAGS       "output-flags"
-#define NM_DEVICE_GRE_INPUT_KEY          "input-key"
-#define NM_DEVICE_GRE_OUTPUT_KEY         "output-key"
-#define NM_DEVICE_GRE_LOCAL              "local"
-#define NM_DEVICE_GRE_REMOTE             "remote"
-#define NM_DEVICE_GRE_TTL                "ttl"
-#define NM_DEVICE_GRE_TOS                "tos"
-#define NM_DEVICE_GRE_PATH_MTU_DISCOVERY "path-mtu-discovery"
-
-typedef NMDeviceGeneric NMDeviceGre;
-typedef NMDeviceGenericClass NMDeviceGreClass;
-
-GType nm_device_gre_get_type (void);
-
-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 c1571fd9..45df49fe 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -20,25 +20,20 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
 #include <linux/if_infiniband.h>
 
+#include "nm-default.h"
 #include "nm-device-infiniband.h"
-#include "nm-logging.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"
-
+#include "nmdbus-device-infiniband.h"
 
 G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
 
@@ -57,10 +52,7 @@ enum {
 	LAST_PROP
 };
 
-static void
-nm_device_infiniband_init (NMDeviceInfiniband * self)
-{
-}
+/*************************************************************/
 
 static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
@@ -82,7 +74,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	NMSettingInfiniband *s_infiniband;
 	const char *transport_mode;
 	char *mode_path;
-	gboolean ok;
+	gboolean ok, no_firmware = FALSE;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
@@ -93,7 +85,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	req = nm_device_get_act_request (dev);
 	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 	s_infiniband = nm_connection_get_setting_infiniband (connection);
 	g_assert (s_infiniband);
@@ -113,8 +105,11 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 		}
 	}
 
+	/* With some drivers the interface must be down to set transport mode */
+	nm_device_take_down (dev, TRUE);
 	ok = nm_platform_sysctl_set (NM_PLATFORM_GET, mode_path, transport_mode);
 	g_free (mode_path);
+	nm_device_bring_up (dev, TRUE, &no_firmware);
 
 	if (!ok) {
 		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
@@ -131,7 +126,7 @@ ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
 	NMSettingInfiniband *s_infiniband;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 	s_infiniband = nm_connection_get_setting_infiniband (connection);
 	g_assert (s_infiniband);
@@ -157,7 +152,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	if (!s_infiniband)
 		return FALSE;
 
-	if (s_infiniband) {
+	if (nm_device_is_real (device)) {
 		const char *mac;
 
 		mac = nm_setting_infiniband_get_mac_address (s_infiniband);
@@ -234,12 +229,69 @@ update_connection (NMDevice *device, NMConnection *connection)
 
 	ifindex = nm_device_get_ifindex (device);
 	if (ifindex > 0) {
-		if (!nm_platform_infiniband_get_info (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode))
+		if (!nm_platform_link_infiniband_get_properties (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode))
 			transport_mode = "datagram";
 	}
 	g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL);
 }
 
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	NMSettingInfiniband *s_infiniband;
+	int parent_ifindex, p_key;
+	NMPlatformError plerr;
+
+	if (!NM_IS_DEVICE_INFINIBAND (parent)) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Parent interface %s must be an InfiniBand interface",
+		             nm_device_get_iface (parent));
+		return FALSE;
+	}
+
+	s_infiniband = nm_connection_get_setting_infiniband (connection);
+
+	/* Can only create partitions at this time */
+	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
+	if (p_key < 0) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		                     "only InfiniBand partitions can be created");
+		return FALSE;
+	}
+
+	parent_ifindex = nm_device_get_ifindex (parent);
+	if (parent_ifindex <= 0) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "failed to get InfiniBand parent %s ifindex",
+		             nm_device_get_iface (parent));
+		return FALSE;
+	}
+
+	plerr = nm_platform_link_infiniband_add (NM_PLATFORM_GET, parent_ifindex, p_key, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create InfiniBand P_Key interface '%s' for '%s': %s",
+		             nm_device_get_iface (device),
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->is_partition = TRUE;
+	return TRUE;
+}
+
+/*************************************************************/
+
+static void
+nm_device_infiniband_init (NMDeviceInfiniband * self)
+{
+}
+
 static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
@@ -276,10 +328,13 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceInfinibandPrivate));
 
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_INFINIBAND_SETTING_NAME, NM_LINK_TYPE_INFINIBAND)
+
 	/* virtual methods */
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
+	parent_class->create_and_realize = create_and_realize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
@@ -296,9 +351,9 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 		                       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_infiniband_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_INFINIBAND_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -307,60 +362,33 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 #define NM_INFINIBAND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INFINIBAND_FACTORY, NMInfinibandFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	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,
-	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, (plink->parent > 0 || plink->parent == NM_PLATFORM_LINK_OTHER_NETNS),
-	                                  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;
-	NMPlatformError plerr;
-	const NMPlatformLink *plink;
-
-	if (!NM_IS_DEVICE_INFINIBAND (parent)) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Parent interface %s must be an InfiniBand interface",
-		             nm_device_get_iface (parent));
-		return NULL;
-	}
-
-	s_infiniband = nm_connection_get_setting_infiniband (connection);
-
-	iface = nm_setting_infiniband_get_virtual_interface_name (s_infiniband);
-	g_assert (iface);
-
-	parent_ifindex = nm_device_get_ifindex (parent);
-	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
-
-	plerr = nm_platform_infiniband_partition_add (NM_PLATFORM_GET, parent_ifindex, p_key, NULL);
-	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Failed to create InfiniBand P_Key interface '%s' for '%s': %s",
-		             iface,
-		             nm_connection_get_id (connection),
-		             nm_platform_error_to_string (plerr));
-		return NULL;
+	gboolean is_partition = FALSE;
+
+	if (plink)
+		is_partition = (plink->parent > 0 || plink->parent == NM_PLATFORM_LINK_OTHER_NETNS);
+	else if (connection) {
+		NMSettingInfiniband *s_infiniband;
+
+		s_infiniband = nm_connection_get_setting_infiniband (connection);
+		g_return_val_if_fail (s_infiniband, NULL);
+		is_partition =    !!nm_setting_infiniband_get_parent (s_infiniband)
+		               || (   nm_setting_infiniband_get_p_key (s_infiniband) >= 0
+		                   && nm_setting_infiniband_get_mac_address (s_infiniband));
 	}
-	plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
 
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
-	                                  NM_DEVICE_DRIVER, nm_device_get_driver (parent),
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
-	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, TRUE,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_INFINIBAND,
+	                                  /* XXX: Partition should probably be a different link type! */
+	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, is_partition,
 	                                  NULL);
 }
 
@@ -400,8 +428,7 @@ get_virtual_iface_name (NMDeviceFactory *factory,
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (INFINIBAND, Infiniband, infiniband,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_INFINIBAND)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_INFINIBAND_SETTING_NAME),
-	factory_iface->new_link = new_link;
-	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->create_device = create_device;
 	factory_iface->get_connection_parent = get_connection_parent;
 	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
diff --git a/src/devices/nm-device-infiniband.h b/src/devices/nm-device-infiniband.h
index b81b762a..cac9a79d 100644
--- a/src/devices/nm-device-infiniband.h
+++ b/src/devices/nm-device-infiniband.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_INFINIBAND_H__
 #define __NETWORKMANAGER_DEVICE_INFINIBAND_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
new file mode 100644
index 00000000..6d0df059
--- /dev/null
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -0,0 +1,1052 @@
+/* -*- 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 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+
+#include "nm-device-ip-tunnel.h"
+#include "nm-device-private.h"
+#include "nm-default.h"
+#include "nm-manager.h"
+#include "nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-core-internal.h"
+#include "nm-connection-provider.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
+
+#include "nmdbus-device-ip-tunnel.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceIPTunnel);
+
+G_DEFINE_TYPE (NMDeviceIPTunnel, nm_device_ip_tunnel, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_IP_TUNNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnelPrivate))
+
+typedef struct {
+	NMIPTunnelMode mode;
+	NMDevice *parent;
+	int parent_ifindex;
+	char *local;
+	char *remote;
+	guint8 ttl;
+	guint8 tos;
+	gboolean path_mtu_discovery;
+	int addr_family;
+	char *input_key;
+	char *output_key;
+	guint8 encap_limit;
+	guint32 flow_label;
+} NMDeviceIPTunnelPrivate;
+
+enum {
+	PROP_0,
+	PROP_MODE,
+	PROP_PARENT,
+	PROP_LOCAL,
+	PROP_REMOTE,
+	PROP_TTL,
+	PROP_TOS,
+	PROP_PATH_MTU_DISCOVERY,
+	PROP_INPUT_KEY,
+	PROP_OUTPUT_KEY,
+	PROP_ENCAPSULATION_LIMIT,
+	PROP_FLOW_LABEL,
+
+	LAST_PROP
+};
+
+/**************************************************************/
+
+static gboolean
+address_equal_pp (int family, const char *a, const char *b)
+{
+	char buffer1[sizeof (struct in6_addr)] = { };
+	char buffer2[sizeof (struct in6_addr)] = { };
+
+	g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
+
+	if (a)
+		inet_pton (family, a, buffer1);
+	if (b)
+		inet_pton (family, b, buffer2);
+
+	return !memcmp (buffer1, buffer2,
+	                family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr));
+}
+
+static gboolean
+address_equal_pn (int family, const char *a, const void *b)
+{
+	char buffer1[sizeof (struct in6_addr)] = { };
+
+	g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
+
+	if (a)
+		inet_pton (family, a, buffer1);
+
+	return !memcmp (buffer1, b,
+	                family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr));
+
+}
+
+static void
+update_properties_from_ifindex (NMDevice *device, int ifindex)
+{
+	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
+	GObject *object = G_OBJECT (device);
+	NMDevice *parent;
+	int parent_ifindex;
+	in_addr_t local4, remote4;
+	struct in6_addr local6, remote6;
+	guint8 ttl = 0, tos = 0, encap_limit = 0;
+	gboolean pmtud = FALSE;
+	guint32 flow_label = 0;
+	char *key;
+
+	if (ifindex <= 0) {
+clear:
+		if (priv->parent || priv->parent_ifindex) {
+			g_clear_object (&priv->parent);
+			priv->parent_ifindex = 0;
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_PARENT);
+		}
+		if (priv->local) {
+			g_clear_pointer (&priv->local, g_free);
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_LOCAL);
+		}
+		if (priv->remote) {
+			g_clear_pointer (&priv->remote, g_free);
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_REMOTE);
+		}
+		if (priv->input_key) {
+			g_clear_pointer (&priv->input_key, g_free);
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_INPUT_KEY);
+		}
+		if (priv->output_key) {
+			g_clear_pointer (&priv->output_key, g_free);
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_OUTPUT_KEY);
+		}
+
+		goto out;
+	}
+
+	if (priv->mode == NM_IP_TUNNEL_MODE_GRE) {
+		const NMPlatformLnkGre *lnk;
+
+		lnk = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL);
+		if (!lnk) {
+			_LOGW (LOGD_HW, "could not read %s properties", "gre");
+			goto clear;
+		}
+
+		parent_ifindex = lnk->parent_ifindex;
+		local4 = lnk->local;
+		remote4 = lnk->remote;
+		ttl = lnk->ttl;
+		tos = lnk->tos;
+		pmtud = lnk->path_mtu_discovery;
+
+		if (NM_FLAGS_HAS (lnk->input_flags, NM_GRE_KEY)) {
+			key = g_strdup_printf ("%u", lnk->input_key);
+			if (g_strcmp0 (priv->input_key, key)) {
+				g_free (priv->input_key);
+				priv->input_key = key;
+				g_object_notify (object, NM_DEVICE_IP_TUNNEL_INPUT_KEY);
+			} else
+				g_free (key);
+		} else {
+			if (priv->input_key) {
+				g_clear_pointer (&priv->input_key, g_free);
+				g_object_notify (object, NM_DEVICE_IP_TUNNEL_INPUT_KEY);
+			}
+		}
+
+		if (NM_FLAGS_HAS (lnk->output_flags, NM_GRE_KEY)) {
+			key = g_strdup_printf ("%u", lnk->output_key);
+			if (g_strcmp0 (priv->output_key, key)) {
+				g_free (priv->output_key);
+				priv->output_key = key;
+				g_object_notify (object, NM_DEVICE_IP_TUNNEL_OUTPUT_KEY);
+			} else
+				g_free (key);
+		} else {
+			if (priv->output_key) {
+				g_clear_pointer (&priv->output_key, g_free);
+				g_object_notify (object, NM_DEVICE_IP_TUNNEL_OUTPUT_KEY);
+			}
+		}
+	} else if (priv->mode == NM_IP_TUNNEL_MODE_SIT) {
+		const NMPlatformLnkSit *lnk;
+
+		lnk = nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL);
+		if (!lnk) {
+			_LOGW (LOGD_HW, "could not read %s properties", "sit");
+			goto clear;
+		}
+
+		parent_ifindex = lnk->parent_ifindex;
+		local4 = lnk->local;
+		remote4 = lnk->remote;
+		ttl = lnk->ttl;
+		tos = lnk->tos;
+		pmtud = lnk->path_mtu_discovery;
+	} else if (priv->mode == NM_IP_TUNNEL_MODE_IPIP) {
+		const NMPlatformLnkIpIp *lnk;
+
+		lnk = nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL);
+		if (!lnk) {
+			_LOGW (LOGD_HW, "could not read %s properties", "ipip");
+			goto clear;
+		}
+
+		parent_ifindex = lnk->parent_ifindex;
+		local4 = lnk->local;
+		remote4 = lnk->remote;
+		ttl = lnk->ttl;
+		tos = lnk->tos;
+		pmtud = lnk->path_mtu_discovery;
+	} else if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
+	           || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6) {
+		const NMPlatformLnkIp6Tnl *lnk;
+
+		lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL);
+		if (!lnk) {
+			_LOGW (LOGD_HW, "could not read %s properties", "ip6tnl");
+			goto clear;
+		}
+
+		parent_ifindex = lnk->parent_ifindex;
+		local6 = lnk->local;
+		remote6 = lnk->remote;
+		ttl = lnk->ttl;
+		tos = lnk->tclass;
+		encap_limit = lnk->encap_limit;
+		flow_label = lnk->flow_label;
+	} else
+		g_return_if_reached ();
+
+	if (priv->parent_ifindex != parent_ifindex) {
+		g_clear_object (&priv->parent);
+		priv->parent_ifindex = parent_ifindex;
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+		if (parent)
+			priv->parent = g_object_ref (parent);
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_PARENT);
+	}
+
+	if (priv->addr_family == AF_INET) {
+		if (!address_equal_pn (AF_INET, priv->local, &local4)) {
+			g_clear_pointer (&priv->local, g_free);
+			if (local4)
+				priv->local = g_strdup (nm_utils_inet4_ntop (local4, NULL));
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_LOCAL);
+		}
+
+		if (!address_equal_pn (AF_INET, priv->remote, &remote4)) {
+			g_clear_pointer (&priv->remote, g_free);
+			if (remote4)
+				priv->remote = g_strdup (nm_utils_inet4_ntop (remote4, NULL));
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_REMOTE);
+		}
+	} else {
+		if (!address_equal_pn (AF_INET6, priv->local, &local6)) {
+			g_clear_pointer (&priv->local, g_free);
+			if (memcmp (&local6, &in6addr_any, sizeof (in6addr_any)))
+				priv->local = g_strdup (nm_utils_inet6_ntop (&local6, NULL));
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_LOCAL);
+		}
+
+		if (!address_equal_pn (AF_INET6, priv->remote, &remote6)) {
+			g_clear_pointer (&priv->remote, g_free);
+			if (memcmp (&remote6, &in6addr_any, sizeof (in6addr_any)))
+				priv->remote = g_strdup (nm_utils_inet6_ntop (&remote6, NULL));
+			g_object_notify (object, NM_DEVICE_IP_TUNNEL_REMOTE);
+		}
+	}
+
+out:
+
+	if (priv->ttl != ttl) {
+		priv->ttl = ttl;
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_TTL);
+	}
+
+	if (priv->tos != tos) {
+		priv->tos = tos;
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_TOS);
+	}
+
+	if (priv->path_mtu_discovery != pmtud) {
+		priv->path_mtu_discovery = pmtud;
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY);
+	}
+
+	if (priv->encap_limit != encap_limit) {
+		priv->encap_limit = encap_limit;
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT);
+	}
+
+	if (priv->flow_label != flow_label) {
+		priv->flow_label = flow_label;
+		g_object_notify (object, NM_DEVICE_IP_TUNNEL_FLOW_LABEL);
+	}
+}
+
+static void
+update_properties (NMDevice *device)
+{
+	update_properties_from_ifindex (device, nm_device_get_ifindex (device));
+}
+
+static void
+link_changed (NMDevice *device, NMPlatformLink *info)
+{
+	NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->link_changed (device, info);
+	update_properties (device);
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMSettingIPTunnel *s_ip_tunnel;
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_IP_TUNNEL_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("IP tunnel connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	if (!s_ip_tunnel) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'tunnel' setting is required.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
+	NMSettingIPTunnel *s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	NMDevice *parent = NULL;
+	const char *setting_parent, *new_parent;
+
+	if (!s_ip_tunnel) {
+		s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_ip_tunnel);
+	}
+
+	if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
+		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_MODE, priv->mode, NULL);
+
+	if (priv->parent_ifindex > 0)
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->parent_ifindex);
+
+	/* Update parent in the connection; default to parent's interface name */
+	if (parent) {
+		new_parent = nm_device_get_iface (parent);
+		setting_parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
+		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
+			NMConnection *parent_connection;
+
+			/* Don't change a parent specified by UUID if it's still valid */
+			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (),
+			                                                                   setting_parent);
+			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
+				new_parent = NULL;
+		}
+		if (new_parent)
+			g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, new_parent, NULL);
+	} else
+		g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, NULL, NULL);
+
+	if (!address_equal_pp (priv->addr_family,
+	                       nm_setting_ip_tunnel_get_local (s_ip_tunnel),
+	                       priv->local))
+		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_LOCAL, priv->local, NULL);
+
+	if (!address_equal_pp (priv->addr_family,
+	                       nm_setting_ip_tunnel_get_remote (s_ip_tunnel),
+	                       priv->remote))
+		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_REMOTE, priv->remote, NULL);
+
+	if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl)
+		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_TTL, priv->ttl, NULL);
+
+	if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos)
+		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_TOS, priv->tos, NULL);
+
+	if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery) {
+		g_object_set (G_OBJECT (s_ip_tunnel),
+		              NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY,
+		              priv->path_mtu_discovery,
+		              NULL);
+	}
+
+	if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit) {
+		g_object_set (G_OBJECT (s_ip_tunnel),
+		                        NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT,
+		                        priv->encap_limit,
+		                        NULL);
+	}
+
+	if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label) {
+		g_object_set (G_OBJECT (s_ip_tunnel),
+		                        NM_SETTING_IP_TUNNEL_FLOW_LABEL,
+		                        priv->flow_label,
+		                        NULL);
+	}
+
+	if (priv->mode == NM_IP_TUNNEL_MODE_GRE || priv->mode == NM_IP_TUNNEL_MODE_IP6GRE) {
+		if (g_strcmp0 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel), priv->input_key)) {
+			g_object_set (G_OBJECT (s_ip_tunnel),
+			              NM_SETTING_IP_TUNNEL_INPUT_KEY,
+			              priv->input_key,
+			              NULL);
+		}
+		if (g_strcmp0 (nm_setting_ip_tunnel_get_output_key (s_ip_tunnel), priv->output_key)) {
+			g_object_set (G_OBJECT (s_ip_tunnel),
+			              NM_SETTING_IP_TUNNEL_OUTPUT_KEY,
+			              priv->output_key,
+			              NULL);
+		}
+	}
+}
+
+static gboolean
+match_parent (NMDevice *dev_parent, const char *setting_parent)
+{
+	g_return_val_if_fail (setting_parent, FALSE);
+
+	if (!dev_parent)
+		return FALSE;
+
+	if (nm_utils_is_uuid (setting_parent)) {
+		NMActRequest *parent_req;
+		NMConnection *parent_connection;
+
+		/* If the parent is a UUID, the connection matches if our parent
+		 * device has that connection activated.
+		 */
+		parent_req = nm_device_get_act_request (dev_parent);
+		if (!parent_req)
+			return FALSE;
+
+		parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req));
+		if (!parent_connection)
+			return FALSE;
+
+		if (g_strcmp0 (setting_parent, nm_connection_get_uuid (parent_connection)) != 0)
+			return FALSE;
+	} else {
+		/* interface name */
+		if (g_strcmp0 (setting_parent, nm_device_get_ip_iface (dev_parent)) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
+	NMSettingIPTunnel *s_ip_tunnel;
+	const char *parent;
+
+	if (!NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	if (!s_ip_tunnel)
+		return FALSE;
+
+	if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
+		return FALSE;
+
+	if (nm_device_is_real (device)) {
+		/* Check parent interface; could be an interface name or a UUID */
+		parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
+		if (parent) {
+			if (!match_parent (priv->parent, parent))
+				return FALSE;
+		}
+
+		if (!address_equal_pp (priv->addr_family,
+		                       nm_setting_ip_tunnel_get_local (s_ip_tunnel),
+		                       priv->local))
+			return FALSE;
+
+		if (!address_equal_pp (priv->addr_family,
+		                       nm_setting_ip_tunnel_get_remote (s_ip_tunnel),
+		                       priv->remote))
+			return FALSE;
+
+		if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl)
+			return FALSE;
+
+		if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos)
+			return FALSE;
+
+		if (priv->addr_family == AF_INET) {
+			if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery)
+				return FALSE;
+		} else {
+			if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit)
+				return FALSE;
+
+			if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label)
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static NMIPTunnelMode
+platform_link_to_tunnel_mode (const NMPlatformLink *link)
+{
+	const NMPlatformLnkIp6Tnl *lnk;
+
+	switch (link->type) {
+	case NM_LINK_TYPE_GRE:
+		return NM_IP_TUNNEL_MODE_GRE;
+	case NM_LINK_TYPE_IP6TNL:
+		lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, link->ifindex, NULL);
+		if (lnk->proto == IPPROTO_IPIP)
+			return NM_IP_TUNNEL_MODE_IPIP6;
+		else if (lnk->proto == IPPROTO_IPV6)
+			return NM_IP_TUNNEL_MODE_IP6IP6;
+		else
+			return NM_IP_TUNNEL_MODE_UKNOWN;
+	case NM_LINK_TYPE_IPIP:
+		return NM_IP_TUNNEL_MODE_IPIP;
+	case NM_LINK_TYPE_SIT:
+		return NM_IP_TUNNEL_MODE_SIT;
+	default:
+		g_return_val_if_reached (NM_IP_TUNNEL_MODE_UKNOWN);
+	}
+}
+
+static NMLinkType
+tunnel_mode_to_link_type (NMIPTunnelMode tunnel_mode)
+{
+	switch (tunnel_mode) {
+	case NM_IP_TUNNEL_MODE_GRE:
+		return NM_LINK_TYPE_GRE;
+	case NM_IP_TUNNEL_MODE_IPIP6:
+	case NM_IP_TUNNEL_MODE_IP6IP6:
+		return NM_LINK_TYPE_IP6TNL;
+	case NM_IP_TUNNEL_MODE_IPIP:
+		return NM_LINK_TYPE_IPIP;
+	case NM_IP_TUNNEL_MODE_SIT:
+		return NM_LINK_TYPE_SIT;
+	default:
+		g_return_val_if_reached (NM_LINK_TYPE_UNKNOWN);
+	}
+}
+
+/**************************************************************/
+
+static void
+nm_device_ip_tunnel_init (NMDeviceIPTunnel *self)
+{
+}
+
+static void
+constructed (GObject *object)
+{
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (object);
+
+	if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
+	    || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6)
+		priv->addr_family = AF_INET6;
+	else
+		priv->addr_family = AF_INET;
+
+	G_OBJECT_CLASS (nm_device_ip_tunnel_parent_class)->constructed (object);
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMSettingIPTunnel *s_ip_tunnel;
+	NMPlatformError plerr;
+	NMPlatformLnkGre lnk_gre = { };
+	NMPlatformLnkSit lnk_sit = { };
+	NMPlatformLnkIpIp lnk_ipip = { };
+	NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
+	const char *str;
+	gint64 val;
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	g_assert (s_ip_tunnel);
+
+	switch (nm_setting_ip_tunnel_get_mode (s_ip_tunnel)) {
+	case NM_IP_TUNNEL_MODE_GRE:
+		if (parent)
+			lnk_gre.parent_ifindex = nm_device_get_ifindex (parent);
+
+		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
+		if (str)
+			inet_pton (AF_INET, str, &lnk_gre.local);
+
+		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
+		g_assert (str);
+		inet_pton (AF_INET, str, &lnk_gre.remote);
+
+		lnk_gre.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
+		lnk_gre.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
+		lnk_gre.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);
+
+		val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel),
+		                                    10,
+		                                    0,
+		                                    G_MAXUINT32,
+		                                    -1);
+		if (val != -1) {
+			lnk_gre.input_key = val;
+			lnk_gre.input_flags = NM_GRE_KEY;
+		}
+
+		val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_output_key (s_ip_tunnel),
+		                                    10,
+		                                    0,
+		                                    G_MAXUINT32,
+		                                    -1);
+		if (val != -1) {
+			lnk_gre.output_key = val;
+			lnk_gre.output_flags = NM_GRE_KEY;
+		}
+
+		plerr = nm_platform_link_gre_add (NM_PLATFORM_GET, iface, &lnk_gre, out_plink);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+			             "Failed to create GRE interface '%s' for '%s': %s",
+			             iface,
+			             nm_connection_get_id (connection),
+			             nm_platform_error_to_string (plerr));
+			return FALSE;
+		}
+		break;
+	case NM_IP_TUNNEL_MODE_SIT:
+		if (parent)
+			lnk_sit.parent_ifindex = nm_device_get_ifindex (parent);
+
+		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
+		if (str)
+			inet_pton (AF_INET, str, &lnk_sit.local);
+
+		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
+		g_assert (str);
+		inet_pton (AF_INET, str, &lnk_sit.remote);
+
+		lnk_sit.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
+		lnk_sit.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
+		lnk_sit.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);
+
+		plerr = nm_platform_link_sit_add (NM_PLATFORM_GET, iface, &lnk_sit, out_plink);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+					"Failed to create SIT interface '%s' for '%s': %s",
+					iface,
+					nm_connection_get_id (connection),
+					nm_platform_error_to_string (plerr));
+			return FALSE;
+		}
+		break;
+	case NM_IP_TUNNEL_MODE_IPIP:
+		if (parent)
+			lnk_ipip.parent_ifindex = nm_device_get_ifindex (parent);
+
+		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
+		if (str)
+			inet_pton (AF_INET, str, &lnk_ipip.local);
+
+		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
+		g_assert (str);
+		inet_pton (AF_INET, str, &lnk_ipip.remote);
+
+		lnk_ipip.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
+		lnk_ipip.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
+		lnk_ipip.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);
+
+		plerr = nm_platform_link_ipip_add (NM_PLATFORM_GET, iface, &lnk_ipip, out_plink);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+					"Failed to create IPIP interface '%s' for '%s': %s",
+					iface,
+					nm_connection_get_id (connection),
+					nm_platform_error_to_string (plerr));
+			return FALSE;
+		}
+		break;
+	case NM_IP_TUNNEL_MODE_IPIP6:
+	case NM_IP_TUNNEL_MODE_IP6IP6:
+		if (parent)
+			lnk_ip6tnl.parent_ifindex = nm_device_get_ifindex (parent);
+
+		str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
+		if (str)
+			inet_pton (AF_INET6, str, &lnk_ip6tnl.local);
+
+		str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
+		g_assert (str);
+		inet_pton (AF_INET6, str, &lnk_ip6tnl.remote);
+
+		lnk_ip6tnl.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
+		lnk_ip6tnl.tclass = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
+		lnk_ip6tnl.encap_limit = nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel);
+		lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel);
+		lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6;
+
+		plerr = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, iface, &lnk_ip6tnl, out_plink);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+			             "Failed to create IPIP interface '%s' for '%s': %s",
+			             iface,
+			             nm_connection_get_id (connection),
+			             nm_platform_error_to_string (plerr));
+			return FALSE;
+		}
+		break;
+	default:
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create IP tunnel interface '%s' for '%s': mode %d not supported",
+		             iface,
+		             nm_connection_get_id (connection),
+		             (int) nm_setting_ip_tunnel_get_mode (s_ip_tunnel));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->realize_start_notify (device, plink);
+
+	update_properties (device);
+}
+
+static void
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingIPTunnel *s_ip_tunnel;
+	guint32 mtu;
+
+	connection = nm_device_get_applied_connection (device);
+	g_assert (connection);
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	g_assert (s_ip_tunnel);
+
+	/* MTU override */
+	mtu = nm_setting_ip_tunnel_get_mtu (s_ip_tunnel);
+	if (mtu)
+		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+}
+
+static void
+unrealize_notify (NMDevice *device)
+{
+	NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->unrealize_notify (device);
+
+	update_properties_from_ifindex (device, 0);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (object);
+	NMDevice *parent;
+
+	switch (prop_id) {
+	case PROP_MODE:
+		g_value_set_uint (value, priv->mode);
+		break;
+	case PROP_PARENT:
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->parent_ifindex);
+		nm_utils_g_value_set_object_path (value, parent);
+		break;
+	case PROP_LOCAL:
+		g_value_set_string (value, priv->local);
+		break;
+	case PROP_REMOTE:
+		g_value_set_string (value, priv->remote);
+		break;
+	case PROP_TTL:
+		g_value_set_uchar (value, priv->ttl);
+		break;
+	case PROP_TOS:
+		g_value_set_uchar (value, priv->tos);
+		break;
+	case PROP_PATH_MTU_DISCOVERY:
+		g_value_set_boolean (value, priv->path_mtu_discovery);
+		break;
+	case PROP_INPUT_KEY:
+		g_value_set_string (value, priv->input_key);
+		break;
+	case PROP_OUTPUT_KEY:
+		g_value_set_string (value, priv->output_key);
+		break;
+	case PROP_ENCAPSULATION_LIMIT:
+		g_value_set_uchar (value, priv->encap_limit);
+		break;
+	case PROP_FLOW_LABEL:
+		g_value_set_uint (value, priv->flow_label);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MODE:
+		priv->mode = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
+nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMDeviceIPTunnelPrivate));
+
+	object_class->constructed = constructed;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	device_class->link_changed = link_changed;
+	device_class->complete_connection = complete_connection;
+	device_class->update_connection = update_connection;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->create_and_realize = create_and_realize;
+	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
+	device_class->realize_start_notify = realize_start_notify;
+	device_class->unrealize_notify = unrealize_notify;
+
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass,
+	                               NM_SETTING_IP_TUNNEL_SETTING_NAME,
+	                               NM_LINK_TYPE_GRE,
+	                               NM_LINK_TYPE_IP6TNL,
+	                               NM_LINK_TYPE_IPIP,
+	                               NM_LINK_TYPE_SIT);
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_MODE,
+		 g_param_spec_uint (NM_DEVICE_IP_TUNNEL_MODE, "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_PARENT,
+		 g_param_spec_string (NM_DEVICE_IP_TUNNEL_PARENT, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_LOCAL,
+		 g_param_spec_string (NM_DEVICE_IP_TUNNEL_LOCAL, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_REMOTE,
+		 g_param_spec_string (NM_DEVICE_IP_TUNNEL_REMOTE, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_TTL,
+		 g_param_spec_uchar (NM_DEVICE_IP_TUNNEL_TTL, "", "",
+		                     0, 255, 0,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_TOS,
+		 g_param_spec_uchar (NM_DEVICE_IP_TUNNEL_TOS, "", "",
+		                     0, 255, 0,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_PATH_MTU_DISCOVERY,
+		 g_param_spec_boolean (NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_INPUT_KEY,
+		 g_param_spec_string (NM_DEVICE_IP_TUNNEL_INPUT_KEY, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_OUTPUT_KEY,
+		 g_param_spec_string (NM_DEVICE_IP_TUNNEL_OUTPUT_KEY, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_ENCAPSULATION_LIMIT,
+		  g_param_spec_uchar (NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT, "", "",
+		                      0, 255, 0,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_FLOW_LABEL,
+		 g_param_spec_uint (NM_DEVICE_IP_TUNNEL_FLOW_LABEL, "", "",
+		                    0, (1 << 20) - 1, 0,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_IPTUNNEL_SKELETON,
+	                                        NULL);
+}
+/*************************************************************/
+
+#define NM_TYPE_IP_TUNNEL_FACTORY (nm_ip_tunnel_factory_get_type ())
+#define NM_IP_TUNNEL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP_TUNNEL_FACTORY, NMIPTunnelFactory))
+
+static NMDevice *
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
+{
+	NMSettingIPTunnel *s_ip_tunnel;
+	NMIPTunnelMode mode;
+	NMLinkType link_type;
+
+	if (connection) {
+		s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+		mode = nm_setting_ip_tunnel_get_mode (s_ip_tunnel);
+		link_type = tunnel_mode_to_link_type (mode);
+	} else {
+		link_type = plink->type;
+		mode = platform_link_to_tunnel_mode (plink);
+	}
+
+	if (mode == NM_IP_TUNNEL_MODE_UKNOWN)
+		return NULL;
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_IP_TUNNEL,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_TYPE_DESC, "IPTunnel",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_IP_TUNNEL,
+	                                  NM_DEVICE_LINK_TYPE, link_type,
+	                                  NM_DEVICE_IP_TUNNEL_MODE, mode,
+	                                  NULL);
+}
+
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSettingIPTunnel *s_ip_tunnel;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_IP_TUNNEL_SETTING_NAME), NULL);
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	g_assert (s_ip_tunnel);
+
+	return nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	const char *ifname;
+	NMSettingIPTunnel *s_ip_tunnel;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_IP_TUNNEL_SETTING_NAME), NULL);
+
+	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
+	g_assert (s_ip_tunnel);
+
+	if (nm_setting_ip_tunnel_get_parent (s_ip_tunnel) && !parent_iface)
+		return NULL;
+
+	ifname = nm_connection_get_interface_name (connection);
+
+	return g_strdup (ifname);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (IP_TUNNEL, IPTunnel, ip_tunnel,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE, NM_LINK_TYPE_SIT, NM_LINK_TYPE_IPIP)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_IP_TUNNEL_SETTING_NAME),
+	factory_iface->create_device = create_device;
+	factory_iface->get_connection_parent = get_connection_parent;
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
+)
diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h
new file mode 100644
index 00000000..11cb5bce
--- /dev/null
+++ b/src/devices/nm-device-ip-tunnel.h
@@ -0,0 +1,60 @@
+/* -*- 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 2015 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_IP_TUNNEL_H__
+#define __NETWORKMANAGER_DEVICE_IP_TUNNEL_H__
+
+#include "nm-core-types.h"
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_IP_TUNNEL            (nm_device_ip_tunnel_get_type ())
+#define NM_DEVICE_IP_TUNNEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnel))
+#define NM_DEVICE_IP_TUNNEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnelClass))
+#define NM_IS_DEVICE_IP_TUNNEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_IP_TUNNEL))
+#define NM_IS_DEVICE_IP_TUNNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_IP_TUNNEL))
+#define NM_DEVICE_IP_TUNNEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnelClass))
+
+#define NM_DEVICE_IP_TUNNEL_MODE                "mode"
+#define NM_DEVICE_IP_TUNNEL_PARENT              "parent"
+#define NM_DEVICE_IP_TUNNEL_LOCAL               "local"
+#define NM_DEVICE_IP_TUNNEL_REMOTE              "remote"
+#define NM_DEVICE_IP_TUNNEL_TTL                 "ttl"
+#define NM_DEVICE_IP_TUNNEL_TOS                 "tos"
+#define NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY  "path-mtu-discovery"
+#define NM_DEVICE_IP_TUNNEL_INPUT_KEY           "input-key"
+#define NM_DEVICE_IP_TUNNEL_OUTPUT_KEY          "output-key"
+#define NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit"
+#define NM_DEVICE_IP_TUNNEL_FLOW_LABEL          "flow-label"
+
+typedef struct {
+	NMDevice parent;
+} NMDeviceIPTunnel;
+
+typedef struct {
+	NMDeviceClass parent;
+} NMDeviceIPTunnelClass;
+
+GType nm_device_ip_tunnel_get_type (void);
+
+G_END_DECLS
+
+#endif	/* NM_DEVICE_IP_TUNNEL_H */
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index f3b76ee8..6255d6f3 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_DEVICE_LOGGING_H__
 #define __NETWORKMANAGER_DEVICE_LOGGING_H__
 
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "nm-device.h"
 
 #define _LOG_DECLARE_SELF(t) \
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 2a815cfd..8123a793 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -15,32 +15,41 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2013 Red Hat, Inc.
+ * Copyright 2013 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-device-macvlan.h"
 #include "nm-device-private.h"
-#include "nm-dbus-manager.h"
-#include "nm-logging.h"
+#include "nm-connection-provider.h"
+#include "nm-activation-request.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
 #include "nm-device-factory.h"
+#include "nm-setting-macvlan.h"
+#include "nm-setting-wired.h"
+#include "nm-active-connection.h"
+#include "nm-ip4-config.h"
+#include "nm-utils.h"
 
-#include "nm-device-macvlan-glue.h"
+#include "nmdbus-device-macvlan.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceMacvlan);
 
-G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE_GENERIC)
+G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanPrivate))
 
 typedef struct {
-	NMPlatformMacvlanProperties props;
+	int parent_ifindex;
+	gulong parent_state_id;
+	NMDevice *parent;
+	NMPlatformLnkMacvlan props;
 } NMDeviceMacvlanPrivate;
 
 enum {
@@ -48,37 +57,147 @@ enum {
 	PROP_PARENT,
 	PROP_MODE,
 	PROP_NO_PROMISC,
+	PROP_TAP,
 
 	LAST_PROP
 };
 
-/**************************************************************/
+static int modes[][2] = {
+	{ NM_SETTING_MACVLAN_MODE_VEPA,      MACVLAN_MODE_VEPA     },
+	{ NM_SETTING_MACVLAN_MODE_BRIDGE,    MACVLAN_MODE_BRIDGE   },
+	{ NM_SETTING_MACVLAN_MODE_PRIVATE,   MACVLAN_MODE_PRIVATE  },
+	{ NM_SETTING_MACVLAN_MODE_PASSTHRU,  MACVLAN_MODE_PASSTHRU },
+};
+
+static int
+setting_mode_to_platform (int mode)
+{
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (modes); i++) {
+		if (modes[i][0] == mode)
+			return modes[i][1];
+	}
+
+	return 0;
+}
+
+static int
+platform_mode_to_setting (int mode)
+{
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (modes); i++) {
+		if (modes[i][1] == mode)
+			return modes[i][0];
+	}
+
+	return 0;
+}
+
+static const char *
+platform_mode_to_string (guint mode)
+{
+	switch (mode) {
+	case MACVLAN_MODE_PRIVATE:
+		return "private";
+	case MACVLAN_MODE_VEPA:
+		return "vepa";
+	case MACVLAN_MODE_BRIDGE:
+		return "bridge";
+	case MACVLAN_MODE_PASSTHRU:
+		return "passthru";
+	default:
+		return "unknown";
+	}
+}
 
 /**************************************************************/
 
 static void
+parent_state_changed (NMDevice *parent,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (user_data);
+
+	/* We'll react to our own carrier state notifications. Ignore the parent's. */
+	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+		return;
+
+	nm_device_set_unmanaged_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
+}
+
+static void
+nm_device_macvlan_set_parent (NMDeviceMacvlan *self, NMDevice *parent)
+	{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+
+	if (parent == priv->parent)
+		return;
+
+	if (priv->parent_state_id)
+		nm_clear_g_signal_handler (priv->parent, &priv->parent_state_id);
+
+	g_clear_object (&priv->parent);
+
+	if (parent) {
+		priv->parent = g_object_ref (parent);
+		priv->parent_state_id = g_signal_connect (priv->parent,
+		                                          "state-changed",
+		                                          G_CALLBACK (parent_state_changed),
+		                                          device);
+
+		/* Set parent-dependent unmanaged flag */
+		nm_device_set_unmanaged_flags (device,
+		                               NM_UNMANAGED_PARENT,
+		                               !nm_device_get_managed (parent),
+		                               NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
+	}
+
+	/* Recheck availability now that the parent has changed */
+	nm_device_queue_recheck_available (self,
+	                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED,
+	                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED);
+	g_object_notify (G_OBJECT (device), NM_DEVICE_MACVLAN_PARENT);
+}
+
+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;
+	const NMPlatformLnkMacvlan *props;
+	const NMPlatformLink *plink;
+	NMDevice *parent = NULL;
 
-	if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
-		_LOGW (LOGD_HW, "could not read macvlan properties");
+	if (priv->props.tap)
+		props = nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+	else
+		props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+
+	if (!props) {
+		_LOGW (LOGD_HW, "could not get %s properties", priv->props.tap ? "macvtap" : "macvlan");
 		return;
 	}
 
 	g_object_freeze_notify (object);
 
-	if (priv->props.parent_ifindex != props.parent_ifindex)
-		g_object_notify (object, NM_DEVICE_MACVLAN_PARENT);
-	if (g_strcmp0 (priv->props.mode, props.mode) != 0)
+	if (priv->parent_ifindex != plink->parent) {
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->parent);
+		nm_device_macvlan_set_parent (self, parent);
+	}
+	if (priv->props.mode != props->mode)
 		g_object_notify (object, NM_DEVICE_MACVLAN_MODE);
-	if (priv->props.no_promisc != props.no_promisc)
+	if (priv->props.no_promisc != props->no_promisc)
 		g_object_notify (object, NM_DEVICE_MACVLAN_NO_PROMISC);
 
-	memcpy (&priv->props, &props, sizeof (NMPlatformMacvlanProperties));
+	priv->parent_ifindex = plink->parent;
+	priv->props = *props;
 
 	g_object_thaw_notify (object);
 }
@@ -90,19 +209,370 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 	update_properties (device);
 }
 
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
+	NMSettingMacvlan *s_macvlan;
+	NMPlatformLnkMacvlan lnk = { };
+	int parent_ifindex;
+
+	s_macvlan = nm_connection_get_setting_macvlan (connection);
+	g_assert (s_macvlan);
+
+	parent_ifindex = nm_device_get_ifindex (parent);
+	g_warn_if_fail (parent_ifindex > 0);
+
+	lnk.mode = setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan));
+	if (!lnk.mode) {
+		nm_log_info (LOGD_DEVICE, "unsupported MACVLAN mode %u in connection %s",
+		             nm_setting_macvlan_get_mode (s_macvlan),
+		             nm_connection_get_uuid (connection));
+		return FALSE;
+	}
+	lnk.no_promisc = !nm_setting_macvlan_get_promiscuous (s_macvlan);
+	lnk.tap = nm_setting_macvlan_get_tap (s_macvlan);
+
+	plerr = nm_platform_link_macvlan_add (NM_PLATFORM_GET, iface, parent_ifindex, &lnk, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create %s interface '%s' for '%s': %s",
+		             lnk.tap ? "macvtap" : "macvlan",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/******************************************************************/
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *dev)
+{
+	/* We assume MACVLAN interfaces always support carrier detect */
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static gboolean
+bring_up (NMDevice *dev, gboolean *no_firmware)
+{
+	gboolean success = FALSE;
+	guint i = 20;
+
+	while (i-- > 0 && !success) {
+		success = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->bring_up (dev, no_firmware);
+		g_usleep (50);
+	}
+
+	return success;
+}
+
+/******************************************************************/
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+	if (!NM_DEVICE_MACVLAN_GET_PRIVATE (device)->parent)
+		return FALSE;
+
+	return NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->is_available (device, flags);
+}
+
+static void
+notify_new_device_added (NMDevice *device, NMDevice *new_device)
+{
+	NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (device);
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+
+	if (priv->parent)
+		return;
+
+	if (!nm_device_is_real (device))
+		return;
+
+	update_properties (device);
+
+	if (   priv->parent_ifindex <= 0
+	    || nm_device_get_ifindex (new_device) != priv->parent_ifindex)
+		return;
+
+	priv->parent_ifindex = nm_device_get_ifindex (new_device);
+	nm_device_macvlan_set_parent (self, new_device);
+}
+
 /**************************************************************/
 
+
+static gboolean
+match_parent (NMDeviceMacvlan *self, const char *parent)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+
+	g_return_val_if_fail (parent != NULL, FALSE);
+
+	if (!priv->parent)
+		return FALSE;
+
+	if (nm_utils_is_uuid (parent)) {
+		NMActRequest *parent_req;
+		NMConnection *parent_connection;
+
+		/* If the parent is a UUID, the connection matches if our parent
+		 * device has that connection activated.
+		 */
+
+		parent_req = nm_device_get_act_request (priv->parent);
+		if (!parent_req)
+			return FALSE;
+
+		parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req));
+		if (!parent_connection)
+			return FALSE;
+
+		if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0)
+			return FALSE;
+	} else {
+		/* interface name */
+		if (g_strcmp0 (parent, nm_device_get_ip_iface (priv->parent)) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+	NMSettingWired *s_wired;
+	const char *setting_mac;
+	const char *parent_mac;
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (!s_wired)
+		return !fail_if_no_hwaddr;
+
+	setting_mac = nm_setting_wired_get_mac_address (s_wired);
+	if (!setting_mac)
+		return !fail_if_no_hwaddr;
+
+	if (!priv->parent)
+		return !fail_if_no_hwaddr;
+
+	parent_mac = nm_device_get_hw_address (priv->parent);
+
+	return nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1);
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+	NMSettingMacvlan *s_macvlan;
+	const char *parent, *iface = NULL;
+
+	if (!NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_macvlan = nm_connection_get_setting_macvlan (connection);
+	if (!s_macvlan)
+		return FALSE;
+
+	if (nm_setting_macvlan_get_tap (s_macvlan) != priv->props.tap)
+		return FALSE;
+
+	/* Before the device is realized some properties will not be set */
+	if (nm_device_is_real (device)) {
+
+		if (setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan)) != priv->props.mode)
+			return FALSE;
+
+		if (nm_setting_macvlan_get_promiscuous (s_macvlan) ==  priv->props.no_promisc)
+			return FALSE;
+
+		/* Check parent interface; could be an interface name or a UUID */
+		parent = nm_setting_macvlan_get_parent (s_macvlan);
+		if (parent) {
+			if (!match_parent (NM_DEVICE_MACVLAN (device), parent))
+				return FALSE;
+		} else {
+			/* Parent could be a MAC address in an NMSettingWired */
+			if (!match_hwaddr (device, connection, TRUE))
+				return FALSE;
+		}
+	}
+
+	/* Ensure the interface name matches */
+	iface = nm_connection_get_interface_name (connection);
+	if (iface) {
+		if (g_strcmp0 (nm_device_get_ip_iface (device), iface) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMSettingMacvlan *s_macvlan;
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_MACVLAN_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("MACVLAN connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_macvlan = nm_connection_get_setting_macvlan (connection);
+	if (!s_macvlan) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'macvlan' setting is required.");
+		return FALSE;
+	}
+
+	/* If there's no MACVLAN interface, no parent, and no hardware address in the
+	 * settings, then there's not enough information to complete the setting.
+	 */
+	if (   !nm_setting_macvlan_get_parent (s_macvlan)
+	    && !match_hwaddr (device, connection, TRUE)) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "The 'macvlan' setting had no interface name, parent, or hardware address.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static void
-nm_device_macvlan_init (NMDeviceMacvlan *self)
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+	NMSettingMacvlan *s_macvlan = nm_connection_get_setting_macvlan (connection);
+	const char *setting_parent, *new_parent;
+	int new_mode;
+
+	if (!s_macvlan) {
+		s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_macvlan);
+	}
+
+	new_mode = platform_mode_to_setting (priv->props.mode);
+	if (new_mode != nm_setting_macvlan_get_mode (s_macvlan))
+		g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, new_mode, NULL);
+
+	if (priv->props.no_promisc == nm_setting_macvlan_get_promiscuous (s_macvlan))
+		g_object_set (s_macvlan, NM_SETTING_MACVLAN_PROMISCUOUS, !priv->props.no_promisc, NULL);
+
+
+	if (priv->props.tap != nm_setting_macvlan_get_tap (s_macvlan))
+		g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, !!priv->props.tap, NULL);
+
+	/* Update parent in the connection; default to parent's interface name */
+	if (priv->parent) {
+		new_parent = nm_device_get_iface (priv->parent);
+		setting_parent = nm_setting_macvlan_get_parent (s_macvlan);
+		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
+			NMConnection *parent_connection;
+
+			/* Don't change a parent specified by UUID if it's still valid */
+			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
+			if (parent_connection && nm_device_check_connection_compatible (priv->parent, parent_connection))
+				new_parent = NULL;
+		}
+		if (new_parent)
+			g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, new_parent, NULL);
+	} else
+		g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, NULL, NULL);
+
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 {
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	const char *cloned_mac;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (dev, reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	req = nm_device_get_act_request (dev);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_applied_connection (req);
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	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)
+			nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW);
+	}
+
+	return TRUE;
+}
+
+static void
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	guint32 mtu;
+
+	connection = nm_device_get_applied_connection (device);
+	g_assert (connection);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired) {
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+	}
+}
+
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->realize_start_notify (device, plink);
+
+	update_properties (device);
 }
 
 static void
-constructed (GObject *object)
+deactivate (NMDevice *device)
 {
-	update_properties (NM_DEVICE (object));
+	/* Reset MAC address back to initial address */
+	if (nm_device_get_initial_hw_address (device)) {
+		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device),
+		                       "reset", LOGD_DEVICE);
+	}
+}
 
-	G_OBJECT_CLASS (nm_device_macvlan_parent_class)->constructed (object);
+/******************************************************************/
+
+static void
+nm_device_macvlan_init (NMDeviceMacvlan *self)
+{
 }
 
 static void
@@ -114,18 +584,21 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		if (priv->props.parent_ifindex > 0)
-			parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
+		if (priv->parent_ifindex > 0)
+			parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->parent_ifindex);
 		else
 			parent = NULL;
-		g_value_set_boxed (value, parent ? nm_device_get_path (parent) : "/");
+		nm_utils_g_value_set_object_path (value, parent);
 		break;
 	case PROP_MODE:
-		g_value_set_string (value, priv->props.mode);
+		g_value_set_string (value, platform_mode_to_string (priv->props.mode));
 		break;
 	case PROP_NO_PROMISC:
 		g_value_set_boolean (value, priv->props.no_promisc);
 		break;
+	case PROP_TAP:
+		g_value_set_boolean (value, priv->props.tap);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -133,6 +606,21 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
+set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_TAP:
+		priv->props.tap = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+	}
+}
+
+static void
 nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -140,18 +628,33 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMDeviceMacvlanPrivate));
 
-	object_class->constructed = constructed;
-	object_class->get_property = get_property;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
 
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->bring_up = bring_up;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->complete_connection = complete_connection;
+	device_class->connection_type = NM_SETTING_MACVLAN_SETTING_NAME;
+	device_class->create_and_realize = create_and_realize;
+	device_class->deactivate = deactivate;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
+	device_class->is_available = is_available;
 	device_class->link_changed = link_changed;
+	device_class->notify_new_device_added = notify_new_device_added;
+	device_class->realize_start_notify = realize_start_notify;
+	device_class->update_connection = update_connection;
 
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_MACVLAN_PARENT, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_MACVLAN_PARENT, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_MODE,
@@ -167,9 +670,17 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 		                       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);
+	g_object_class_install_property
+		(object_class, PROP_TAP,
+		 g_param_spec_boolean (NM_DEVICE_MACVLAN_TAP, "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_MACVLAN_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -178,17 +689,85 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 #define NM_MACVLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MACVLAN_FACTORY, NMMacvlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
+	NMSettingMacvlan *s_macvlan;
+	NMLinkType link_type;
+	gboolean tap;
+
+	if (connection) {
+		s_macvlan = nm_connection_get_setting_macvlan (connection);
+		g_assert (s_macvlan);
+		tap = nm_setting_macvlan_get_tap (s_macvlan);
+	} else {
+		g_assert (plink);
+		tap = plink->type == NM_LINK_TYPE_MACVTAP;
+	}
+
+	link_type = tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN;
+
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Macvlan",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MACVLAN,
+	                                  NM_DEVICE_LINK_TYPE, link_type,
+	                                  NM_DEVICE_MACVLAN_TAP, tap,
 	                                  NULL);
 }
 
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSettingMacvlan *s_macvlan;
+	NMSettingWired *s_wired;
+	const char *parent = NULL;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_MACVLAN_SETTING_NAME), NULL);
+
+	s_macvlan = nm_connection_get_setting_macvlan (connection);
+	g_assert (s_macvlan);
+
+	parent = nm_setting_macvlan_get_parent (s_macvlan);
+	if (parent)
+		return parent;
+
+	/* Try the hardware address from the MACVLAN connection's hardware setting */
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired)
+		return nm_setting_wired_get_mac_address (s_wired);
+
+	return NULL;
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	NMSettingMacvlan *s_macvlan;
+	const char *ifname;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_MACVLAN_SETTING_NAME), NULL);
+
+	s_macvlan = nm_connection_get_setting_macvlan (connection);
+	g_assert (s_macvlan);
+
+	if (!parent_iface)
+		return NULL;
+
+	ifname = nm_connection_get_interface_name (connection);
+	return g_strdup (ifname);
+}
+
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (MACVLAN, Macvlan, macvlan,
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP),
-	factory_iface->new_link = new_link;
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_MACVLAN_SETTING_NAME),
+	factory_iface->create_device = create_device;
+	factory_iface->get_connection_parent = get_connection_parent;
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
 
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index 24f15437..89f670bd 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -21,9 +21,7 @@
 #ifndef __NETWORKMANAGER_DEVICE_MACVLAN_H__
 #define __NETWORKMANAGER_DEVICE_MACVLAN_H__
 
-#include <glib-object.h>
-
-#include "nm-device-generic.h"
+#include "nm-device.h"
 
 G_BEGIN_DECLS
 
@@ -37,9 +35,10 @@ G_BEGIN_DECLS
 #define NM_DEVICE_MACVLAN_PARENT     "parent"
 #define NM_DEVICE_MACVLAN_MODE       "mode"
 #define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc"
+#define NM_DEVICE_MACVLAN_TAP        "tap"
 
-typedef NMDeviceGeneric NMDeviceMacvlan;
-typedef NMDeviceGenericClass NMDeviceMacvlanClass;
+typedef NMDevice NMDeviceMacvlan;
+typedef NMDeviceClass NMDeviceMacvlanClass;
 
 GType nm_device_macvlan_get_type (void);
 
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index f34969ac..5010e906 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -24,9 +24,9 @@
 
 #include "nm-device.h"
 
-/* This file should only be used by subclasses of NMDevice */
+#include "nm-default.h"
 
-#define NM_DEVICE_PLATFORM_DEVICE "platform-device"
+/* This file should only be used by subclasses of NMDevice */
 
 enum NMActStageReturn {
 	NM_ACT_STAGE_RETURN_FAILURE = 0,
@@ -91,7 +91,7 @@ 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 *self, NMDevice *slave,
-                                                 guint64 log_domain);
+                                                 NMLogDomain log_domain);
 
 void nm_device_set_carrier (NMDevice *self, gboolean carrier);
 
@@ -106,4 +106,11 @@ 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);
 
+#define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \
+	NM_DEVICE_CLASS (klass)->connection_type = conn_type; \
+	{ \
+		static const NMLinkType link_types[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; \
+		NM_DEVICE_CLASS (klass)->link_types = link_types; \
+	}
+
 #endif	/* NM_DEVICE_PRIVATE_H */
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index 5aaff8b1..91ef8fe7 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -15,41 +15,43 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2013 Red Hat, Inc.
+ * Copyright 2013 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 
+#include "nm-default.h"
+#include "nm-activation-request.h"
 #include "nm-device-tun.h"
 #include "nm-device-private.h"
-#include "nm-dbus-manager.h"
-#include "nm-logging.h"
+#include "nm-ip4-config.h"
 #include "nm-platform.h"
 #include "nm-device-factory.h"
+#include "nm-setting-tun.h"
+#include "nm-core-internal.h"
 
-#include "nm-device-tun-glue.h"
+#include "nmdbus-device-tun.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceTun);
 
-G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE_GENERIC)
+G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TUN, NMDeviceTunPrivate))
 
 typedef struct {
 	NMPlatformTunProperties props;
 	const char *mode;
-	guint delay_tun_get_properties_id;
 } NMDeviceTunPrivate;
 
 enum {
 	PROP_0,
 	PROP_OWNER,
 	PROP_GROUP,
-	PROP_FLAGS,
 	PROP_MODE,
 	PROP_NO_PI,
 	PROP_VNET_HDR,
@@ -64,11 +66,26 @@ reload_tun_properties (NMDeviceTun *self)
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
 	GObject *object = G_OBJECT (self);
 	NMPlatformTunProperties props;
-
-	if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &props)) {
-		_LOGD (LOGD_HW, "could not read tun properties");
-		return;
-	}
+	int ifindex;
+
+	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	if (ifindex > 0) {
+		if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) {
+			_LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex);
+			ifindex = 0;
+		} else if (g_strcmp0 (priv->mode, props.mode) != 0) {
+			/* if the mode differs, we ignore what we loaded. A NMDeviceTun cannot
+			 * change the mode after construction. */
+			_LOGD (LOGD_DEVICE, "tun-properties: loading tun properties yielded tun-mode %s%s%s, but %s%s%s expected (ifindex %d)",
+			       NM_PRINT_FMT_QUOTE_STRING (props.mode),
+			       NM_PRINT_FMT_QUOTE_STRING (priv->mode),
+			       ifindex);
+			ifindex = 0;
+		}
+	} else
+		_LOGD (LOGD_DEVICE, "tun-properties: ignore loading properties due to missing ifindex");
+	if (ifindex <= 0)
+		memset (&props, 0, sizeof (props));
 
 	g_object_freeze_notify (object);
 
@@ -97,54 +114,251 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 }
 
 static gboolean
-delay_tun_get_properties_cb (gpointer user_data)
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
 {
-	NMDeviceTun *self = user_data;
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	NMSettingTun *s_tun;
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_TUN_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("TUN connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_tun = nm_connection_get_setting_tun (connection);
+	if (!s_tun) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'tun' setting is required.");
+		return FALSE;
+	}
 
-	priv->delay_tun_get_properties_id = 0;
+	return TRUE;
+}
 
-	reload_tun_properties (self);
+static int
+tun_mode_from_string (const char *string)
+{
+	if (!g_strcmp0 (string, "tap"))
+		return NM_SETTING_TUN_MODE_TAP;
+	else
+		return NM_SETTING_TUN_MODE_TUN;
+}
 
-	return G_SOURCE_REMOVE;
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceTun *self = NM_DEVICE_TUN (device);
+	NMSettingTun *s_tun = nm_connection_get_setting_tun (connection);
+	NMPlatformTunProperties props;
+	NMSettingTunMode mode;
+	gint64 user, group;
+	char *str;
+
+	if (!s_tun) {
+		s_tun = (NMSettingTun *) nm_setting_tun_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_tun);
+	}
+
+	if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
+		_LOGW (LOGD_HW, "failed to get TUN interface info while updating connection.");
+		return;
+	}
+
+	mode = tun_mode_from_string (props.mode);
+
+	if (mode != nm_setting_tun_get_mode (s_tun))
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MODE, mode, NULL);
+
+	user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
+	group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);
+
+	if (props.owner != user) {
+		str = props.owner >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.owner) : NULL;
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_OWNER, str, NULL);
+		g_free (str);
+	}
+
+	if (props.group != group) {
+		str = props.group >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.group) : NULL;
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_GROUP, str, NULL);
+		g_free (str);
+	}
+
+	if ((!props.no_pi) != nm_setting_tun_get_pi (s_tun))
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, !props.no_pi, NULL);
+	if (props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun))
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, props.vnet_hdr, NULL);
+	if (props.multi_queue != nm_setting_tun_get_multi_queue (s_tun))
+		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, props.multi_queue, NULL);
 }
 
-/**************************************************************/
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
+	NMSettingTun *s_tun;
+	gint64 user, group;
+
+	s_tun = nm_connection_get_setting_tun (connection);
+	g_assert (s_tun);
+
+	user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
+	group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);
+
+	plerr = nm_platform_link_tun_add (NM_PLATFORM_GET, iface,
+	                                  nm_setting_tun_get_mode (s_tun) == NM_SETTING_TUN_MODE_TAP,
+	                                  user, group,
+	                                  nm_setting_tun_get_pi (s_tun),
+	                                  nm_setting_tun_get_vnet_hdr (s_tun),
+	                                  nm_setting_tun_get_multi_queue (s_tun),
+	                                  out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create TUN/TAP interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	return TRUE;
+}
 
 static void
-nm_device_tun_init (NMDeviceTun *self)
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
 {
+	NM_DEVICE_CLASS (nm_device_tun_parent_class)->realize_start_notify (device, plink);
+	reload_tun_properties (device);
 }
 
-static void
-constructed (GObject *object)
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
-	NMDeviceTun *self = NM_DEVICE_TUN (object);
-	gboolean properties_read;
+	NMDeviceTun *self = NM_DEVICE_TUN (device);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	NMSettingTunMode mode;
+	NMSettingTun *s_tun;
+	gint64 user, group;
+
+	if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_tun = nm_connection_get_setting_tun (connection);
+	if (!s_tun)
+		return FALSE;
+
+	if (nm_device_is_real (device)) {
+		mode = tun_mode_from_string (priv->mode);
+		if (mode != nm_setting_tun_get_mode (s_tun))
+			return FALSE;
+
+		user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
+		group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);
+
+		if (user != priv->props.owner)
+			return FALSE;
+		if (group != priv->props.group)
+			return FALSE;
+		if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi)
+			return FALSE;
+		if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr)
+			return FALSE;
+		if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue)
+			return FALSE;
+	}
 
-	properties_read = nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &priv->props);
+	return TRUE;
+}
 
-	G_OBJECT_CLASS (nm_device_tun_parent_class)->constructed (object);
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+{
+	NMDeviceTun *self = NM_DEVICE_TUN (device);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	const char *cloned_mac;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	ret = NM_DEVICE_CLASS (nm_device_tun_parent_class)->act_stage1_prepare (device, reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	/* Nothing to do for TUN devices */
+	if (g_strcmp0 (priv->mode, "tap"))
+		return NM_ACT_STAGE_RETURN_SUCCESS;
+
+	req = nm_device_get_act_request (device);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_applied_connection (req);
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	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)
+			nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE);
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
 
-	if (!properties_read) {
-		/* Error reading the tun properties. Maybe this was due to a race. Try again a bit later. */
-		_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);
+static void
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	guint32 mtu;
+
+	connection = nm_device_get_applied_connection (device);
+	g_assert (connection);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired) {
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 	}
 }
 
 static void
-dispose (GObject *object)
+unrealize_notify (NMDevice *device)
 {
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (object);
+	NMDeviceTun *self = NM_DEVICE_TUN (device);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	GParamSpec **properties;
+	guint n_properties, i;
 
-	if (priv->delay_tun_get_properties_id) {
-		g_source_remove (priv->delay_tun_get_properties_id);
-		priv->delay_tun_get_properties_id = 0;
-	}
+	NM_DEVICE_CLASS (nm_device_tun_parent_class)->unrealize_notify (device);
+
+	memset (&priv->props, 0, sizeof (NMPlatformTunProperties));
+
+	properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties);
+	for (i = 0; i < n_properties; i++)
+		g_object_notify_by_pspec (G_OBJECT (self), properties[i]);
+	g_free (properties);
+}
 
-	G_OBJECT_CLASS (nm_device_tun_parent_class)->dispose (object);
+/**************************************************************/
+
+static void
+nm_device_tun_init (NMDeviceTun *self)
+{
 }
 
 static void
@@ -214,12 +428,22 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMDeviceTunPrivate));
 
-	object_class->constructed = constructed;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP)
+
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
-	object_class->dispose = dispose;
+
+	device_class->connection_type = NM_SETTING_TUN_SETTING_NAME;
 
 	device_class->link_changed = link_changed;
+	device_class->complete_connection = complete_connection;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->create_and_realize = create_and_realize;
+	device_class->realize_start_notify = realize_start_notify;
+	device_class->unrealize_notify = unrealize_notify;
+	device_class->update_connection = update_connection;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
 
 	/* properties */
 	g_object_class_install_property
@@ -258,9 +482,9 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 		                       FALSE,
 		                       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_tun_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_TUN_SKELETON,
+	                                        NULL);
 }
 
 
@@ -270,29 +494,49 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 #define NM_TUN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TUN_FACTORY, NMTunFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	const char *mode = NULL;
-
-	if (plink->type == NM_LINK_TYPE_TUN)
-		mode = "tun";
-	else if (plink->type == NM_LINK_TYPE_TAP)
-		mode = "tap";
-	else {
-		g_warn_if_reached ();
-		mode = "unknown";
+	NMSettingTun *s_tun;
+	NMLinkType link_type = NM_LINK_TYPE_UNKNOWN;
+	const char *mode;
+
+	if (plink) {
+		link_type = plink->type;
+	} else if (connection) {
+		s_tun = nm_connection_get_setting_tun (connection);
+		if (!s_tun)
+			return NULL;
+		switch (nm_setting_tun_get_mode (s_tun)) {
+		case NM_SETTING_TUN_MODE_TUN:
+			link_type = NM_LINK_TYPE_TUN;
+			break;
+		case NM_SETTING_TUN_MODE_TAP:
+			link_type = NM_LINK_TYPE_TAP;
+			break;
+		case NM_SETTING_TUN_MODE_UNKNOWN:
+			g_return_val_if_reached (NULL);
+		}
 	}
 
+	g_return_val_if_fail (link_type != NM_LINK_TYPE_UNKNOWN, NULL);
+	mode = link_type == NM_LINK_TYPE_TUN ? "tun" : "tap";
+
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Tun",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TUN,
+	                                  NM_DEVICE_LINK_TYPE, link_type,
 	                                  NM_DEVICE_TUN_MODE, mode,
 	                                  NULL);
 }
 
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (TUN, Tun, tun,
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP),
-	factory_iface->new_link = new_link;
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_TUN_SETTING_NAME),
+	factory_iface->create_device = create_device;
 	)
 
diff --git a/src/devices/nm-device-tun.h b/src/devices/nm-device-tun.h
index 393c5801..32dcfd47 100644
--- a/src/devices/nm-device-tun.h
+++ b/src/devices/nm-device-tun.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_TUN_H__
 #define __NETWORKMANAGER_DEVICE_TUN_H__
 
-#include <glib-object.h>
-
 #include "nm-device-generic.h"
 
 G_BEGIN_DECLS
@@ -41,8 +39,8 @@ G_BEGIN_DECLS
 #define NM_DEVICE_TUN_VNET_HDR    "vnet-hdr"
 #define NM_DEVICE_TUN_MULTI_QUEUE "multi-queue"
 
-typedef NMDeviceGeneric NMDeviceTun;
-typedef NMDeviceGenericClass NMDeviceTunClass;
+typedef NMDevice NMDeviceTun;
+typedef NMDeviceClass NMDeviceTunClass;
 
 GType nm_device_tun_get_type (void);
 
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index 04f03fbb..a1bf0d21 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -29,13 +29,12 @@
 
 #include "nm-device-veth.h"
 #include "nm-device-private.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #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 "nmdbus-device-veth.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVeth);
@@ -77,17 +76,18 @@ get_peer (NMDeviceVeth *self)
 {
 	NMDeviceVethPrivate *priv = NM_DEVICE_VETH_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self), *peer = NULL;
-	NMPlatformVethProperties props;
+	int peer_ifindex;
 
 	if (priv->ever_had_peer)
 		return priv->peer;
 
-	if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_link_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &peer_ifindex)) {
 		_LOGW (LOGD_HW, "could not read veth properties");
 		return NULL;
 	}
 
-	peer = nm_manager_get_device_by_ifindex (nm_manager_get (), props.peer);
+	if (peer_ifindex > 0)
+		peer = nm_manager_get_device_by_ifindex (nm_manager_get (), peer_ifindex);
 	if (peer && NM_IS_DEVICE_VETH (peer)) {
 		set_peer (self, peer);
 		set_peer (NM_DEVICE_VETH (peer), device);
@@ -137,7 +137,7 @@ get_property (GObject *object, guint prop_id,
 	switch (prop_id) {
 	case PROP_PEER:
 		peer = get_peer (self);
-		g_value_set_boxed (value, peer ? nm_device_get_path (peer) : "/");
+		nm_utils_g_value_set_object_path (value, peer);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -153,6 +153,8 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMDeviceVethPrivate));
 
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH)
+
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
@@ -161,14 +163,14 @@ 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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     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);
+		 g_param_spec_string (NM_DEVICE_VETH_PEER, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VETH_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -177,17 +179,22 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 #define NM_VETH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VETH_FACTORY, NMVethFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Veth",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_VETH,
 	                                  NULL);
 }
 
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (VETH, Veth, veth,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VETH),
-	factory_iface->new_link = new_link;
+	factory_iface->create_device = create_device;
 	)
 
diff --git a/src/devices/nm-device-veth.h b/src/devices/nm-device-veth.h
index 51e8db45..c7b8ec3a 100644
--- a/src/devices/nm-device-veth.h
+++ b/src/devices/nm-device-veth.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_VETH_H__
 #define __NETWORKMANAGER_DEVICE_VETH_H__
 
-#include <glib-object.h>
-
 #include "nm-device-ethernet.h"
 
 G_BEGIN_DECLS
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 7cce6593..f8710412 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -20,19 +20,15 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-
 #include <sys/socket.h>
 
+#include "nm-default.h"
 #include "nm-device-vlan.h"
 #include "nm-manager.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-connection-provider.h"
 #include "nm-activation-request.h"
 #include "nm-ip4-config.h"
@@ -40,9 +36,9 @@
 #include "nm-device-factory.h"
 #include "nm-manager.h"
 #include "nm-core-internal.h"
-#include "gsystem-local-alloc.h"
+#include "nmp-object.h"
 
-#include "nm-device-vlan-glue.h"
+#include "nmdbus-device-vlan.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVlan);
@@ -52,12 +48,10 @@ 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))
 
 typedef struct {
-	gboolean invalid;
-
 	NMDevice *parent;
-	guint parent_state_id;
-	guint parent_hwaddr_id;
-	int vlan_id;
+	gulong parent_state_id;
+	gulong parent_hwaddr_id;
+	guint vlan_id;
 } NMDeviceVlanPrivate;
 
 enum {
@@ -65,8 +59,6 @@ enum {
 	PROP_PARENT,
 	PROP_VLAN_ID,
 
-	PROP_INT_PARENT_DEVICE,
-
 	LAST_PROP
 };
 
@@ -85,7 +77,7 @@ parent_state_changed (NMDevice *parent,
 	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
 		return;
 
-	nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
+	nm_device_set_unmanaged_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
 }
 
 static void
@@ -97,12 +89,13 @@ parent_hwaddr_changed (NMDevice *parent,
 	NMConnection *connection;
 	NMSettingWired *s_wired;
 	const char *cloned_mac = NULL, *new_mac;
+	NMSettingIPConfig *s_ip6;
 
 	/* Never touch assumed devices */
 	if (nm_device_uses_assumed_connection (self))
 		return;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 
@@ -118,12 +111,17 @@ parent_hwaddr_changed (NMDevice *parent,
 		if (new_mac) {
 			nm_device_set_hw_addr (self, nm_device_get_hw_address (parent),
 			                       "set", LOGD_VLAN);
+			/* When changing the hw address the interface is taken down,
+			 * removing the IPv6 configuration; reapply it.
+			 */
+			s_ip6 = nm_connection_get_setting_ip6_config (connection);
+			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6);
 		}
 	}
 }
 
 static void
-nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean construct)
+nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent)
 {
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
@@ -138,7 +136,7 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean constr
 	if (parent) {
 		priv->parent = g_object_ref (parent);
 		priv->parent_state_id = g_signal_connect (priv->parent,
-		                                          "state-changed",
+		                                          NM_DEVICE_STATE_CHANGED,
 		                                          G_CALLBACK (parent_state_changed),
 		                                          device);
 
@@ -146,16 +144,10 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean constr
 		                                           G_CALLBACK (parent_hwaddr_changed), device);
 
 		/* Set parent-dependent unmanaged flag */
-		if (construct) {
-			nm_device_set_initial_unmanaged_flag (device,
-			                                      NM_UNMANAGED_PARENT,
-			                                      !nm_device_get_managed (parent));
-		} else {
-			nm_device_set_unmanaged (device,
-			                         NM_UNMANAGED_PARENT,
-			                         !nm_device_get_managed (parent),
-			                         NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
-		}
+		nm_device_set_unmanaged_flags (device,
+		                               NM_UNMANAGED_PARENT,
+		                               !nm_device_get_managed (parent),
+		                               NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
 	}
 
 	/* Recheck availability now that the parent has changed */
@@ -165,6 +157,117 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean constr
 	g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT);
 }
 
+static void
+update_properties (NMDevice *device)
+{
+	NMDeviceVlanPrivate *priv;
+	const NMPlatformLink *plink = NULL;
+	const NMPlatformLnkVlan *plnk = NULL;
+	NMDevice *parent = NULL;
+	int ifindex;
+	guint vlan_id;
+
+	g_return_if_fail (NM_IS_DEVICE_VLAN (device));
+
+	priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
+
+	ifindex = nm_device_get_ifindex (device);
+
+	if (ifindex > 0)
+		plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink);
+	if (   plnk
+	    && plink->parent
+	    && plink->parent != NM_PLATFORM_LINK_OTHER_NETNS)
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->parent);
+
+	g_object_freeze_notify ((GObject *) device);
+
+	nm_device_vlan_set_parent ((NMDeviceVlan *) device, parent);
+
+	vlan_id = plnk ? plnk->id : 0;
+	if (vlan_id != priv->vlan_id) {
+		priv->vlan_id = vlan_id;
+		g_object_notify ((GObject *) device, NM_DEVICE_VLAN_ID);
+	}
+
+	g_object_thaw_notify ((GObject *) device);
+}
+
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NM_DEVICE_CLASS (nm_device_vlan_parent_class)->realize_start_notify (device, plink);
+
+	update_properties (device);
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
+	const char *iface = nm_device_get_iface (device);
+	NMSettingVlan *s_vlan;
+	int parent_ifindex;
+	guint vlan_id;
+	NMPlatformError plerr;
+
+	s_vlan = nm_connection_get_setting_vlan (connection);
+	g_assert (s_vlan);
+
+	if (!nm_device_supports_vlans (parent)) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "no support for VLANs on interface %s of type %s",
+		             nm_device_get_iface (parent),
+		             nm_device_get_type_desc (parent));
+		return FALSE;
+	}
+
+	parent_ifindex = nm_device_get_ifindex (parent);
+	g_warn_if_fail (parent_ifindex > 0);
+
+	vlan_id = nm_setting_vlan_get_id (s_vlan);
+
+	plerr = nm_platform_link_vlan_add (NM_PLATFORM_GET,
+	                                   iface,
+	                                   parent_ifindex,
+	                                   vlan_id,
+	                                   nm_setting_vlan_get_flags (s_vlan),
+	                                   out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create VLAN interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	g_warn_if_fail (priv->parent == NULL);
+	nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent);
+	if (vlan_id != priv->vlan_id) {
+		priv->vlan_id = vlan_id;
+		g_object_notify ((GObject *) device, NM_DEVICE_VLAN_ID);
+	}
+
+	return TRUE;
+}
+
+static void
+unrealize_notify (NMDevice *device)
+{
+	NM_DEVICE_CLASS (nm_device_vlan_parent_class)->unrealize_notify (device);
+
+	NM_DEVICE_VLAN_GET_PRIVATE (device)->vlan_id = 0;
+	g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID);
+	nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), NULL);
+}
+
+/******************************************************************/
+
 static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *dev)
 {
@@ -197,34 +300,29 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 	return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->is_available (device, flags);
 }
 
-static gboolean
-component_added (NMDevice *device, GObject *component)
+static void
+notify_new_device_added (NMDevice *device, NMDevice *new_device)
 {
 	NMDeviceVlan *self = NM_DEVICE_VLAN (device);
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
-	NMDevice *added_device;
-	int parent_ifindex = -1;
+	const NMPlatformLink *plink;
+	const NMPlatformLnkVlan *plnk;
 
 	if (priv->parent)
-		return FALSE;
-
-	if (!NM_IS_DEVICE (component))
-		return FALSE;
-	added_device = NM_DEVICE (component);
+		return;
 
-	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, nm_device_get_ifindex (device), &parent_ifindex, NULL)) {
-		_LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component.");
-		return FALSE;
-	}
+	if (!nm_device_is_real (device))
+		return;
 
-	if (   parent_ifindex <= 0
-	    || nm_device_get_ifindex (added_device) != parent_ifindex)
-		return FALSE;
+	plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+	if (!plnk)
+		return;
 
-	nm_device_vlan_set_parent (self, added_device, FALSE);
+	if (   plink->parent <= 0
+	    || nm_device_get_ifindex (new_device) != plink->parent)
+		return;
 
-	/* Don't claim parent exclusively */
-	return FALSE;
+	nm_device_vlan_set_parent (self, new_device);
 }
 
 /******************************************************************/
@@ -251,7 +349,7 @@ match_parent (NMDeviceVlan *self, const char *parent)
 		if (!parent_req)
 			return FALSE;
 
-		parent_connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (parent_req));
+		parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req));
 		if (!parent_connection)
 			return FALSE;
 
@@ -300,18 +398,21 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	if (!s_vlan)
 		return FALSE;
 
-	if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id)
-		return FALSE;
-
-	/* Check parent interface; could be an interface name or a UUID */
-	parent = nm_setting_vlan_get_parent (s_vlan);
-	if (parent) {
-		if (!match_parent (NM_DEVICE_VLAN (device), parent))
-			return FALSE;
-	} else {
-		/* Parent could be a MAC address in an NMSettingWired */
-		if (!match_hwaddr (device, connection, TRUE))
+	/* Before the device is realized some properties will not be set */
+	if (nm_device_is_real (device)) {
+		if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id)
 			return FALSE;
+
+		/* Check parent interface; could be an interface name or a UUID */
+		parent = nm_setting_vlan_get_parent (s_vlan);
+		if (parent) {
+			if (!match_parent (NM_DEVICE_VLAN (device), parent))
+				return FALSE;
+		} else {
+			/* Parent could be a MAC address in an NMSettingWired */
+			if (!match_hwaddr (device, connection, TRUE))
+				return FALSE;
+		}
 	}
 
 	/* Ensure the interface name matches.  If not specified we assume a match
@@ -328,6 +429,18 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 }
 
 static gboolean
+check_connection_available (NMDevice *device,
+                            NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
+                            const char *specific_object)
+{
+	if (!nm_device_is_real (device))
+		return TRUE;
+
+	return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_available (device, connection, flags, specific_object);
+}
+
+static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
@@ -367,54 +480,67 @@ complete_connection (NMDevice *device,
 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);
-	int parent_ifindex = -1, vlan_id = -1;
-	NMDevice *parent;
 	const char *setting_parent, *new_parent;
+	const NMPlatformLink *plink;
+	const NMPObject *polnk;
+	guint vlan_id;
+	guint vlan_flags;
 
 	if (!s_vlan) {
 		s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_vlan);
 	}
 
-	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) {
-		_LOGW (LOGD_VLAN, "failed to get VLAN interface info while updating connection.");
-		return;
-	}
+	polnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink);
 
-	if (priv->vlan_id != vlan_id) {
-		priv->vlan_id = vlan_id;
-		g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID);
-	}
-
-	if (vlan_id != nm_setting_vlan_get_id (s_vlan))
-		g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL);
-
-	if (parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS)
-		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+	if (polnk)
+		vlan_id = polnk->lnk_vlan.id;
 	else
-		parent = NULL;
-	nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent, FALSE);
+		vlan_id = priv->vlan_id;
+	if (vlan_id != nm_setting_vlan_get_id (s_vlan))
+		g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
 
 	/* Update parent in the connection; default to parent's interface name */
-	if (parent) {
-		new_parent = nm_device_get_iface (parent);
+	if (   priv->parent
+	    && polnk
+	    && plink->parent > 0
+	    && nm_device_get_ifindex (priv->parent) == plink->parent) {
+		new_parent = nm_device_get_iface (priv->parent);
 		setting_parent = nm_setting_vlan_get_parent (s_vlan);
 		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
 			NMConnection *parent_connection;
 
 			/* Don't change a parent specified by UUID if it's still valid */
 			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
-			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
+			if (parent_connection && nm_device_check_connection_compatible (priv->parent, parent_connection))
 				new_parent = NULL;
 		}
 		if (new_parent)
 			g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL);
 	} else
 		g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, NULL, NULL);
+
+	if (polnk)
+		vlan_flags = polnk->lnk_vlan.flags;
+	else
+		vlan_flags = NM_VLAN_FLAG_REORDER_HEADERS;
+	if (vlan_flags != nm_setting_vlan_get_flags (s_vlan))
+		g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, (NMVlanFlags) vlan_flags, NULL);
+
+	if (polnk) {
+		_nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_INGRESS_MAP,
+		                                 polnk->_lnk_vlan.ingress_qos_map,
+		                                 polnk->_lnk_vlan.n_ingress_qos_map);
+		_nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_EGRESS_MAP,
+		                                 polnk->_lnk_vlan.egress_qos_map,
+		                                 polnk->_lnk_vlan.n_egress_qos_map);
+	} else {
+		_nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_INGRESS_MAP, NULL, 0);
+		_nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_EGRESS_MAP, NULL, 0);
+	}
 }
 
 static NMActStageReturn
@@ -436,7 +562,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	req = nm_device_get_act_request (dev);
 	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_wired = nm_connection_get_setting_wired (connection);
@@ -449,20 +575,29 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	if (s_vlan) {
-		int ifindex = nm_device_get_ifindex (dev);
-		int num, i;
-		guint32 from, to;
-
-		num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP);
-		for (i = 0; i < num; i++) {
-			if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to))
-				nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to);
-		}
-		num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP);
-		for (i = 0; i < num; i++) {
-			if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to))
-				nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to);
-		}
+		gs_free NMVlanQosMapping *ingress_map = NULL;
+		gs_free NMVlanQosMapping *egress_map = NULL;
+		guint n_ingress_map = 0, n_egress_map = 0;
+
+		_nm_setting_vlan_get_priorities (s_vlan,
+		                                 NM_VLAN_INGRESS_MAP,
+		                                 &ingress_map,
+		                                 &n_ingress_map);
+		_nm_setting_vlan_get_priorities (s_vlan,
+		                                 NM_VLAN_EGRESS_MAP,
+		                                 &egress_map,
+		                                 &n_egress_map);
+
+		nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		                              nm_device_get_ifindex (dev),
+		                              NM_VLAN_FLAGS_ALL,
+		                              nm_setting_vlan_get_flags (s_vlan),
+		                              TRUE,
+		                              ingress_map,
+		                              n_ingress_map,
+		                              TRUE,
+		                              egress_map,
+		                              n_egress_map);
 	}
 
 	return ret;
@@ -475,7 +610,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	NMSettingWired *s_wired;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 
 	s_wired = nm_connection_get_setting_wired (connection);
@@ -502,54 +637,6 @@ nm_device_vlan_init (NMDeviceVlan * self)
 }
 
 static void
-constructed (GObject *object)
-{
-	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;
-
-	if (G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed)
-		G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object);
-
-	itype = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
-	if (itype != NM_LINK_TYPE_VLAN) {
-		_LOGE (LOGD_VLAN, "failed to get VLAN interface type.");
-		priv->invalid = TRUE;
-		return;
-	}
-
-	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) {
-		_LOGW (LOGD_VLAN, "failed to get VLAN interface info.");
-		priv->invalid = TRUE;
-		return;
-	}
-
-	if (parent_ifindex < 0 || vlan_id < 0) {
-		_LOGW (LOGD_VLAN, "VLAN parent ifindex (%d) or VLAN ID (%d) invalid.",
-		       parent_ifindex, priv->vlan_id);
-		priv->invalid = TRUE;
-		return;
-	}
-
-	if (priv->parent && parent_ifindex != nm_device_get_ip_ifindex (priv->parent)) {
-		_LOGW (LOGD_VLAN, "VLAN parent %s (%d) and parent ifindex %d don't match.",
-		       nm_device_get_iface (priv->parent),
-		       nm_device_get_ifindex (priv->parent),
-		       parent_ifindex);
-		priv->invalid = TRUE;
-		return;
-	}
-
-	priv->vlan_id = vlan_id;
-	_LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s (%d)",
-	       priv->vlan_id,
-	       priv->parent ? nm_device_get_iface (priv->parent) : "unknown",
-	       parent_ifindex);
-}
-
-static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
@@ -557,10 +644,7 @@ get_property (GObject *object, guint prop_id,
 
 	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);
+		nm_utils_g_value_set_object_path (value, priv->parent);
 		break;
 	case PROP_VLAN_ID:
 		g_value_set_uint (value, priv->vlan_id);
@@ -575,25 +659,13 @@ static void
 set_property (GObject *object, guint prop_id,
 			  const GValue *value, GParamSpec *pspec)
 {
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
-
-	switch (prop_id) {
-	case PROP_INT_PARENT_DEVICE:
-		nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value), TRUE);
-		break;
-	case PROP_VLAN_ID:
-		priv->vlan_id = g_value_get_uint (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static void
 dispose (GObject *object)
 {
-	nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), NULL, FALSE);
+	nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), NULL);
 
 	G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
 }
@@ -604,53 +676,47 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
-	parent_class->connection_type = NM_SETTING_VLAN_SETTING_NAME;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_VLAN_SETTING_NAME, NM_LINK_TYPE_VLAN)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate));
 
 	/* virtual methods */
-	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 
+	parent_class->create_and_realize = create_and_realize;
+	parent_class->realize_start_notify = realize_start_notify;
+	parent_class->unrealize_notify = unrealize_notify;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->bring_up = bring_up;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	parent_class->deactivate = deactivate;
 	parent_class->is_available = is_available;
-	parent_class->component_added = component_added;
+	parent_class->notify_new_device_added = notify_new_device_added;
 
 	parent_class->check_connection_compatible = check_connection_compatible;
+	parent_class->check_connection_available = check_connection_available;
 	parent_class->complete_connection = complete_connection;
 	parent_class->update_connection = update_connection;
 
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_VLAN_PARENT, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_VLAN_PARENT, "", "",
+		                      NULL,
+		                      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, "", "",
 		                    0, 4095, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
+		                    G_PARAM_READABLE | 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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VLAN_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -659,98 +725,19 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 #define NM_VLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VLAN_FACTORY, NMVlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	int parent_ifindex = -1;
-	NMDevice *parent, *device;
-
-	/* Find the parent device */
-	if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, plink->ifindex, &parent_ifindex, NULL)) {
-		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		                     "VLAN parent ifindex unknown");
-		return NULL;
-	}
-	if (parent_ifindex > 0)
-		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
-	else
-		parent = 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_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		                     "VLAN initialization failed");
-		g_object_unref (device);
-		device = NULL;
-	}
-
-	return device;
-}
-
-static NMDevice *
-create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                      NMConnection *connection,
-                                      NMDevice *parent,
-                                      GError **error)
-{
-	NMDevice *device;
-	NMSettingVlan *s_vlan;
-	gs_free char *iface = NULL;
-	NMPlatformError plerr;
-	const NMPlatformLink *plink;
-
-	if (!NM_IS_DEVICE (parent)) {
-		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		                     "VLAN interfaces must have parents");
-		return NULL;
-	}
-
-	s_vlan = nm_connection_get_setting_vlan (connection);
-	g_assert (s_vlan);
-
-	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));
-	}
-
-	plerr = nm_platform_vlan_add (NM_PLATFORM_GET,
-	                              iface,
-	                              nm_device_get_ifindex (parent),
-	                              nm_setting_vlan_get_id (s_vlan),
-	                              nm_setting_vlan_get_flags (s_vlan),
-	                              NULL);
-	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Failed to create VLAN interface '%s' for '%s': %s",
-		             iface,
-		             nm_connection_get_id (connection),
-		             nm_platform_error_to_string (plerr));
-		return NULL;
-	}
-	plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
-
-	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_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Failed to create VLAN interface '%s' for '%s': initialization failed",
-		             iface, nm_connection_get_id (connection));
-		g_object_unref (device);
-		device = NULL;
-	}
-
-	return device;
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_DRIVER, "8021q",
+	                                  NM_DEVICE_TYPE_DESC, "VLAN",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_VLAN,
+	                                  NULL);
 }
 
 static const char *
@@ -807,8 +794,7 @@ get_virtual_iface_name (NMDeviceFactory *factory,
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (VLAN, Vlan, vlan,
 	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_VLAN)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_VLAN_SETTING_NAME),
-	factory_iface->new_link = new_link;
-	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->create_device = create_device;
 	factory_iface->get_connection_parent = get_connection_parent;
 	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h
index 20fc3453..e31e171d 100644
--- a/src/devices/nm-device-vlan.h
+++ b/src/devices/nm-device-vlan.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_VLAN_H__
 #define __NETWORKMANAGER_DEVICE_VLAN_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -44,9 +42,6 @@ typedef enum {
 #define NM_DEVICE_VLAN_PARENT     "parent"
 #define NM_DEVICE_VLAN_ID         "vlan-id"
 
-/* Internal non-exported properties */
-#define NM_DEVICE_VLAN_INT_PARENT_DEVICE     "int-parent-device"
-
 typedef NMDevice NMDeviceVlan;
 typedef NMDeviceClass NMDeviceVlanClass;
 
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index 186e00de..aab90539 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -15,49 +15,54 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2013, 2014 Red Hat, Inc.
+ * Copyright 2013 - 2015 Red Hat, Inc.
  */
 
 #include "config.h"
 
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-device-vxlan.h"
 #include "nm-device-private.h"
-#include "nm-dbus-manager.h"
-#include "nm-logging.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
 #include "nm-utils.h"
 #include "nm-device-factory.h"
+#include "nm-setting-vxlan.h"
+#include "nm-setting-wired.h"
+#include "nm-connection-provider.h"
+#include "nm-activation-request.h"
+#include "nm-ip4-config.h"
 
-#include "nm-device-vxlan-glue.h"
+#include "nmdbus-device-vxlan.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVxlan);
 
-G_DEFINE_TYPE (NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE_GENERIC)
+G_DEFINE_TYPE (NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlanPrivate))
 
 typedef struct {
-	NMPlatformVxlanProperties props;
+	NMDevice *parent;
+	NMPlatformLnkVxlan props;
 } NMDeviceVxlanPrivate;
 
 enum {
 	PROP_0,
 	PROP_PARENT,
 	PROP_ID,
-	PROP_GROUP,
 	PROP_LOCAL,
+	PROP_GROUP,
 	PROP_TOS,
 	PROP_TTL,
 	PROP_LEARNING,
 	PROP_AGEING,
 	PROP_LIMIT,
-	PROP_DST_PORT,
 	PROP_SRC_PORT_MIN,
 	PROP_SRC_PORT_MAX,
+	PROP_DST_PORT,
 	PROP_PROXY,
 	PROP_RSC,
 	PROP_L2MISS,
@@ -74,53 +79,60 @@ 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;
+	const NMPlatformLnkVxlan *props;
+	NMDevice *parent;
 
-	if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
-		_LOGW (LOGD_HW, "could not read vxlan properties");
+	props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL);
+	if (!props) {
+		_LOGW (LOGD_HW, "could not get vxlan properties");
 		return;
 	}
 
 	g_object_freeze_notify (object);
 
-	if (priv->props.parent_ifindex != props.parent_ifindex)
+	if (priv->props.parent_ifindex != props->parent_ifindex) {
+		g_clear_object (&priv->parent);
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), props->parent_ifindex);
+		if (parent)
+			priv->parent = g_object_ref (parent);
 		g_object_notify (object, NM_DEVICE_VXLAN_PARENT);
-	if (priv->props.id != props.id)
+	}
+	if (priv->props.id != props->id)
 		g_object_notify (object, NM_DEVICE_VXLAN_ID);
-	if (priv->props.group != props.group)
-		g_object_notify (object, NM_DEVICE_VXLAN_GROUP);
-	if (priv->props.local != props.local)
+	if (priv->props.local != props->local)
 		g_object_notify (object, NM_DEVICE_VXLAN_LOCAL);
-	if (memcmp (&priv->props.group6, &props.group6, sizeof (props.group6)) != 0)
-		g_object_notify (object, NM_DEVICE_VXLAN_GROUP);
-	if (memcmp (&priv->props.local6, &props.local6, sizeof (props.local6)) != 0)
+	if (memcmp (&priv->props.local6, &props->local6, sizeof (props->local6)) != 0)
 		g_object_notify (object, NM_DEVICE_VXLAN_LOCAL);
-	if (priv->props.tos != props.tos)
+	if (priv->props.group != props->group)
+		g_object_notify (object, NM_DEVICE_VXLAN_GROUP);
+	if (memcmp (&priv->props.group6, &props->group6, sizeof (props->group6)) != 0)
+		g_object_notify (object, NM_DEVICE_VXLAN_GROUP);
+	if (priv->props.tos != props->tos)
 		g_object_notify (object, NM_DEVICE_VXLAN_TOS);
-	if (priv->props.ttl != props.ttl)
+	if (priv->props.ttl != props->ttl)
 		g_object_notify (object, NM_DEVICE_VXLAN_TTL);
-	if (priv->props.learning != props.learning)
+	if (priv->props.learning != props->learning)
 		g_object_notify (object, NM_DEVICE_VXLAN_LEARNING);
-	if (priv->props.ageing != props.ageing)
+	if (priv->props.ageing != props->ageing)
 		g_object_notify (object, NM_DEVICE_VXLAN_AGEING);
-	if (priv->props.limit != props.limit)
+	if (priv->props.limit != props->limit)
 		g_object_notify (object, NM_DEVICE_VXLAN_LIMIT);
-	if (priv->props.dst_port != props.dst_port)
-		g_object_notify (object, NM_DEVICE_VXLAN_DST_PORT);
-	if (priv->props.src_port_min != props.src_port_min)
+	if (priv->props.src_port_min != props->src_port_min)
 		g_object_notify (object, NM_DEVICE_VXLAN_SRC_PORT_MIN);
-	if (priv->props.src_port_max != props.src_port_max)
+	if (priv->props.src_port_max != props->src_port_max)
 		g_object_notify (object, NM_DEVICE_VXLAN_SRC_PORT_MAX);
-	if (priv->props.proxy != props.proxy)
+	if (priv->props.dst_port != props->dst_port)
+		g_object_notify (object, NM_DEVICE_VXLAN_DST_PORT);
+	if (priv->props.proxy != props->proxy)
 		g_object_notify (object, NM_DEVICE_VXLAN_PROXY);
-	if (priv->props.rsc != props.rsc)
+	if (priv->props.rsc != props->rsc)
 		g_object_notify (object, NM_DEVICE_VXLAN_RSC);
-	if (priv->props.l2miss != props.l2miss)
+	if (priv->props.l2miss != props->l2miss)
 		g_object_notify (object, NM_DEVICE_VXLAN_L2MISS);
-	if (priv->props.l3miss != props.l3miss)
+	if (priv->props.l3miss != props->l3miss)
 		g_object_notify (object, NM_DEVICE_VXLAN_L3MISS);
 
-	memcpy (&priv->props, &props, sizeof (NMPlatformVxlanProperties));
+	priv->props = *props;
 
 	g_object_thaw_notify (object);
 }
@@ -132,19 +144,424 @@ link_changed (NMDevice *device, NMPlatformLink *info)
 	update_properties (device);
 }
 
-/**************************************************************/
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	g_assert (plink->type == NM_LINK_TYPE_VXLAN);
+
+	NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->realize_start_notify (device, plink);
+
+	update_properties (device);
+}
 
 static void
-nm_device_vxlan_init (NMDeviceVxlan *self)
+unrealize_notify (NMDevice *device)
+{
+	NMDeviceVxlan *self = NM_DEVICE_VXLAN (device);
+	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (self);
+	GParamSpec **properties;
+	guint n_properties, i;
+
+	NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->unrealize_notify (device);
+
+	memset (&priv->props, 0, sizeof (NMPlatformLnkVxlan));
+
+	properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties);
+	for (i = 0; i < n_properties; i++)
+		g_object_notify_by_pspec (G_OBJECT (self), properties[i]);
+	g_free (properties);
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
+	NMPlatformLnkVxlan props = { };
+	NMSettingVxlan *s_vxlan;
+	const char *str;
+	int ret;
+
+	s_vxlan = nm_connection_get_setting_vxlan (connection);
+	g_assert (s_vxlan);
+
+	if (parent)
+		props.parent_ifindex = nm_device_get_ifindex (parent);
+
+	props.id = nm_setting_vxlan_get_id (s_vxlan);
+
+	str = nm_setting_vxlan_get_local (s_vxlan);
+	if (str) {
+		ret = inet_pton (AF_INET, str, &props.local);
+		if (ret != 1)
+			ret = inet_pton (AF_INET6, str, &props.local6);
+		if (ret != 1)
+			return FALSE;
+	}
+
+	str = nm_setting_vxlan_get_remote (s_vxlan);
+	ret = inet_pton (AF_INET, str, &props.group);
+	if (ret != 1)
+		ret = inet_pton (AF_INET6, str, &props.group6);
+	if (ret != 1)
+		return FALSE;
+
+	props.tos = nm_setting_vxlan_get_tos (s_vxlan);
+	props.ttl = nm_setting_vxlan_get_ttl (s_vxlan);
+	props.learning = nm_setting_vxlan_get_learning (s_vxlan);
+	props.ageing = nm_setting_vxlan_get_ageing (s_vxlan);
+	props.limit = nm_setting_vxlan_get_limit (s_vxlan);
+	props.src_port_min = nm_setting_vxlan_get_source_port_min (s_vxlan);
+	props.src_port_max = nm_setting_vxlan_get_source_port_max (s_vxlan);
+	props.dst_port = nm_setting_vxlan_get_destination_port (s_vxlan);
+	props.proxy = nm_setting_vxlan_get_proxy (s_vxlan);
+	props.rsc = nm_setting_vxlan_get_rsc (s_vxlan);
+	props.l2miss = nm_setting_vxlan_get_l2_miss (s_vxlan);
+	props.l3miss = nm_setting_vxlan_get_l3_miss (s_vxlan);
+
+	plerr = nm_platform_link_vxlan_add (NM_PLATFORM_GET, iface, &props, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create VXLAN interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+match_parent (NMDeviceVxlan *self, const char *parent)
+{
+	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (self);
+
+	g_return_val_if_fail (parent != NULL, FALSE);
+
+	if (!priv->parent)
+		return FALSE;
+
+	if (nm_utils_is_uuid (parent)) {
+		NMActRequest *parent_req;
+		NMConnection *parent_connection;
+
+		/* If the parent is a UUID, the connection matches if our parent
+		 * device has that connection activated.
+		 */
+		parent_req = nm_device_get_act_request (priv->parent);
+		if (!parent_req)
+			return FALSE;
+
+		parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req));
+		if (!parent_connection)
+			return FALSE;
+
+		if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0)
+			return FALSE;
+	} else {
+		/* interface name */
+		if (g_strcmp0 (parent, nm_device_get_ip_iface (priv->parent)) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+address_matches (const char *str, in_addr_t addr4, struct in6_addr *addr6)
+{
+	in_addr_t new_addr4 = 0;
+	struct in6_addr new_addr6 = { };
+
+	if (!str) {
+		return    addr4 == 0
+		       && !memcmp (addr6, &in6addr_any, sizeof (in6addr_any));
+	}
+
+	if (inet_pton (AF_INET, str, &new_addr4) == 1)
+		return new_addr4 == addr4;
+	else if (inet_pton (AF_INET6, str, &new_addr6) == 1)
+		return !memcmp (&new_addr6, addr6, sizeof (new_addr6));
+	else
+		return FALSE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device);
+	NMSettingVxlan *s_vxlan;
+	const char *iface, *parent;
+
+	if (!NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_vxlan = nm_connection_get_setting_vxlan (connection);
+	if (!s_vxlan)
+		return FALSE;
+
+	iface = nm_connection_get_interface_name (connection);
+	if (iface) {
+		if (g_strcmp0 (nm_device_get_ip_iface (device), iface) != 0)
+			return FALSE;
+	}
+
+	if (nm_device_is_real (device)) {
+		parent = nm_setting_vxlan_get_parent (s_vxlan);
+		if (   parent
+		    && !match_parent (NM_DEVICE_VXLAN (device), parent))
+			return FALSE;
+
+		if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
+			return FALSE;
+
+		if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6))
+			return FALSE;
+
+		if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6))
+			return FALSE;
+
+		if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan))
+			return FALSE;
+
+		if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan))
+			return FALSE;
+
+		if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan))
+			return FALSE;
+
+		if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan))
+			return FALSE;
+
+		if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan))
+			return FALSE;
+
+		if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan))
+			return FALSE;
+
+		if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan))
+			return FALSE;
+
+		if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan))
+			return FALSE;
+
+		if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan))
+			return FALSE;
+
+		if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan))
+			return FALSE;
+
+		if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMSettingVxlan *s_vxlan;
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_VXLAN_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("VXLAN connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_vxlan = nm_connection_get_setting_vxlan (connection);
+	if (!s_vxlan) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'vxlan' setting is required.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device);
+	NMSettingVxlan *s_vxlan = nm_connection_get_setting_vxlan (connection);
+	NMDevice *parent = NULL;
+	const char *setting_parent, *new_parent;
+
+	if (!s_vxlan) {
+		s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_vxlan);
+	}
+
+	if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_ID, priv->props.id, NULL);
+
+	if (priv->props.parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS)
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
+
+	/* Update parent in the connection; default to parent's interface name */
+	if (parent) {
+		new_parent = nm_device_get_iface (parent);
+		setting_parent = nm_setting_vxlan_get_parent (s_vxlan);
+		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
+			NMConnection *parent_connection;
+
+			/* Don't change a parent specified by UUID if it's still valid */
+			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (),
+			                                                                   setting_parent);
+			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
+				new_parent = NULL;
+		}
+		if (new_parent)
+			g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, new_parent, NULL);
+	} else
+		g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, NULL, NULL);
+
+	if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) {
+		if (priv->props.group) {
+			g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE,
+			              nm_utils_inet4_ntop (priv->props.group, NULL),
+			              NULL);
+		} else {
+			g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE,
+			              nm_utils_inet6_ntop (&priv->props.group6, NULL),
+			              NULL);
+		}
+	}
+
+	if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6)) {
+		if (priv->props.local) {
+			g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL,
+			              nm_utils_inet4_ntop (priv->props.local, NULL),
+			              NULL);
+		} else if (memcmp (&priv->props.local6, &in6addr_any, sizeof (in6addr_any))) {
+			g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL,
+			              nm_utils_inet6_ntop (&priv->props.local6, NULL),
+			              NULL);
+		}
+	}
+
+	if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_SOURCE_PORT_MIN,
+		              priv->props.src_port_min, NULL);
+	}
+
+	if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_SOURCE_PORT_MAX,
+		              priv->props.src_port_max, NULL);
+	}
+
+	if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_DESTINATION_PORT,
+		              priv->props.dst_port, NULL);
+	}
+
+	if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_TOS,
+		              priv->props.tos, NULL);
+	}
+
+	if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_TTL,
+		              priv->props.ttl, NULL);
+	}
+
+	if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_LEARNING,
+		              priv->props.learning, NULL);
+	}
+
+	if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_AGEING,
+		              priv->props.ageing, NULL);
+	}
+
+	if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_PROXY,
+		              priv->props.proxy, NULL);
+	}
+
+	if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_RSC,
+		              priv->props.rsc, NULL);
+	}
+
+	if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_L2_MISS,
+		              priv->props.l2miss, NULL);
+	}
+
+	if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan)) {
+		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_L3_MISS,
+		              priv->props.l3miss, NULL);
+	}
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	const char *cloned_mac;
+	NMActStageReturn ret;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	ret = NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->act_stage1_prepare (device, reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	req = nm_device_get_act_request (device);
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_applied_connection (req);
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	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)
+			nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE);
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static void
-constructed (GObject *object)
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 {
-	update_properties (NM_DEVICE (object));
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	guint32 mtu;
+
+	connection = nm_device_get_applied_connection (device);
+	g_assert (connection);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired) {
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+	}
+}
+
+/**************************************************************/
 
-	G_OBJECT_CLASS (nm_device_vxlan_parent_class)->constructed (object);
+static void
+nm_device_vxlan_init (NMDeviceVxlan *self)
+{
 }
 
 static void
@@ -157,7 +574,7 @@ get_property (GObject *object, guint prop_id,
 	switch (prop_id) {
 	case PROP_PARENT:
 		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
-		g_value_set_boxed (value, parent ? nm_device_get_path (parent) : "/");
+		nm_utils_g_value_set_object_path (value, parent);
 		break;
 	case PROP_ID:
 		g_value_set_uint (value, priv->props.id);
@@ -217,6 +634,15 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
+dispose (GObject *object)
+{
+	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (object);
+
+	g_clear_object (&priv->parent);
+	G_OBJECT_CLASS (nm_device_vxlan_parent_class)->dispose (object);
+}
+
+static void
 nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -224,18 +650,29 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMDeviceVxlanPrivate));
 
-	object_class->constructed = constructed;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VXLAN)
+
 	object_class->get_property = get_property;
+	object_class->dispose = dispose;
 
 	device_class->link_changed = link_changed;
+	device_class->realize_start_notify = realize_start_notify;
+	device_class->unrealize_notify = unrealize_notify;
+	device_class->connection_type = NM_SETTING_VXLAN_SETTING_NAME;
+	device_class->create_and_realize = create_and_realize;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->complete_connection = complete_connection;
+	device_class->update_connection = update_connection;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
 
 	/* properties */
 	g_object_class_install_property
 		(object_class, PROP_PARENT,
-		 g_param_spec_boxed (NM_DEVICE_VXLAN_PARENT, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_VXLAN_PARENT, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ID,
@@ -245,15 +682,15 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-		(object_class, PROP_GROUP,
-		 g_param_spec_string (NM_DEVICE_VXLAN_GROUP, "", "",
+		(object_class, PROP_LOCAL,
+		 g_param_spec_string (NM_DEVICE_VXLAN_LOCAL, "", "",
 		                      NULL,
 		                      G_PARAM_READABLE |
 		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_DEVICE_VXLAN_LOCAL, "", "",
+		(object_class, PROP_GROUP,
+		 g_param_spec_string (NM_DEVICE_VXLAN_GROUP, "", "",
 		                      NULL,
 		                      G_PARAM_READABLE |
 		                      G_PARAM_STATIC_STRINGS));
@@ -294,22 +731,22 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-		(object_class, PROP_DST_PORT,
-		 g_param_spec_uint (NM_DEVICE_VXLAN_DST_PORT, "", "",
+		(object_class, PROP_SRC_PORT_MIN,
+		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MIN, "", "",
 		                    0, 65535, 0,
 		                    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, "", "",
+		(object_class, PROP_SRC_PORT_MAX,
+		 g_param_spec_uint (NM_DEVICE_VXLAN_SRC_PORT_MAX, "", "",
 		                    0, 65535, 0,
 		                    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, "", "",
+		(object_class, PROP_DST_PORT,
+		 g_param_spec_uint (NM_DEVICE_VXLAN_DST_PORT, "", "",
 		                    0, 65535, 0,
 		                    G_PARAM_READABLE |
 		                    G_PARAM_STATIC_STRINGS));
@@ -342,9 +779,9 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 		                       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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VXLAN_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
@@ -353,17 +790,58 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 #define NM_VXLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VXLAN_FACTORY, NMVxlanFactory))
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Vxlan",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VXLAN,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_VXLAN,
 	                                  NULL);
 }
 
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSettingVxlan *s_vxlan;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VXLAN_SETTING_NAME), NULL);
+
+	s_vxlan = nm_connection_get_setting_vxlan (connection);
+	g_assert (s_vxlan);
+
+	return nm_setting_vxlan_get_parent (s_vxlan);
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+                        NMConnection *connection,
+                        const char *parent_iface)
+{
+	const char *ifname;
+	NMSettingVxlan *s_vxlan;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VXLAN_SETTING_NAME), NULL);
+
+	s_vxlan = nm_connection_get_setting_vxlan (connection);
+	g_assert (s_vxlan);
+
+	if (nm_setting_vxlan_get_parent (s_vxlan) && !parent_iface)
+		return NULL;
+
+	ifname = nm_connection_get_interface_name (connection);
+	return g_strdup (ifname);
+}
+
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (VXLAN, Vxlan, vxlan,
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VXLAN),
-	factory_iface->new_link = new_link;
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VXLAN)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_VXLAN_SETTING_NAME),
+	factory_iface->create_device = create_device;
+	factory_iface->get_connection_parent = get_connection_parent;
+	factory_iface->get_virtual_iface_name = get_virtual_iface_name;
 	)
 
diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h
index cc6081e6..7de04a12 100644
--- a/src/devices/nm-device-vxlan.h
+++ b/src/devices/nm-device-vxlan.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_VXLAN_H__
 #define __NETWORKMANAGER_DEVICE_VXLAN_H__
 
-#include <glib-object.h>
-
 #include "nm-device-generic.h"
 
 G_BEGIN_DECLS
@@ -51,8 +49,8 @@ G_BEGIN_DECLS
 #define NM_DEVICE_VXLAN_L2MISS       "l2miss"
 #define NM_DEVICE_VXLAN_L3MISS       "l3miss"
 
-typedef NMDeviceGeneric NMDeviceVxlan;
-typedef NMDeviceGenericClass NMDeviceVxlanClass;
+typedef NMDevice NMDeviceVxlan;
+typedef NMDeviceClass NMDeviceVxlanClass;
 
 GType nm_device_vxlan_get_type (void);
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index bb39ca55..93782a45 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -21,9 +21,6 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
@@ -35,9 +32,9 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <netlink/route/addr.h>
+#include <linux/if_addr.h>
 
-#include "gsystem-local-alloc.h"
-#include "nm-glib-compat.h"
+#include "nm-default.h"
 #include "nm-device.h"
 #include "nm-device-private.h"
 #include "NetworkManagerUtils.h"
@@ -46,8 +43,6 @@
 #include "nm-rdisc.h"
 #include "nm-lndp-rdisc.h"
 #include "nm-dhcp-manager.h"
-#include "nm-dbus-manager.h"
-#include "nm-logging.h"
 #include "nm-activation-request.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -56,35 +51,35 @@
 #include "nm-dhcp6-config.h"
 #include "nm-rfkill-manager.h"
 #include "nm-firewall-manager.h"
-#include "nm-properties-changed-signal.h"
 #include "nm-enum-types.h"
 #include "nm-settings-connection.h"
 #include "nm-connection-provider.h"
 #include "nm-auth-utils.h"
-#include "nm-dbus-glib-types.h"
 #include "nm-dispatcher.h"
 #include "nm-config.h"
 #include "nm-dns-manager.h"
 #include "nm-core-internal.h"
 #include "nm-default-route-manager.h"
 #include "nm-route-manager.h"
+#include "nm-lldp-listener.h"
+#include "sd-ipv4ll.h"
+#include "nm-audit-manager.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
 
-static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context);
-static void impl_device_delete     (NMDevice *self, DBusGMethodInvocation *context);
+#include "nmdbus-device.h"
+
 static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
 static gboolean ip_config_valid (NMDeviceState state);
-static void nm_device_update_metered (NMDevice *self);
 static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason);
 static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason);
+static void nm_device_start_ip_check (NMDevice *self);
+static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink);
 
-#include "nm-device-glue.h"
-
-G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
+#define NM_DEVICE_GET_PRIVATE(o) ((o)->priv)
 
 enum {
 	STATE_CHANGED,
@@ -95,14 +90,12 @@ enum {
 	REMOVED,
 	RECHECK_AUTO_ACTIVATE,
 	RECHECK_ASSUME,
-	LINK_INITIALIZED,
 	LAST_SIGNAL,
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
 enum {
 	PROP_0,
-	PROP_PLATFORM_DEVICE,
 	PROP_UDI,
 	PROP_IFACE,
 	PROP_IP_IFACE,
@@ -121,9 +114,11 @@ enum {
 	PROP_STATE_REASON,
 	PROP_ACTIVE_CONNECTION,
 	PROP_DEVICE_TYPE,
+	PROP_LINK_TYPE,
 	PROP_MANAGED,
 	PROP_AUTOCONNECT,
 	PROP_FIRMWARE_MISSING,
+	PROP_NM_PLUGIN_MISSING,
 	PROP_TYPE_DESC,
 	PROP_RFKILL_TYPE,
 	PROP_IFINDEX,
@@ -134,15 +129,27 @@ enum {
 	PROP_HW_ADDRESS,
 	PROP_HAS_PENDING_ACTION,
 	PROP_METERED,
+	PROP_LLDP_NEIGHBORS,
+	PROP_REAL,
+	PROP_SLAVES,
 	LAST_PROP
 };
 
+#define DEFAULT_AUTOCONNECT TRUE
+
 /***********************************************************/
 
 #define PENDING_ACTION_DHCP4 "dhcp4"
 #define PENDING_ACTION_DHCP6 "dhcp6"
 #define PENDING_ACTION_AUTOCONF6 "autoconf6"
 
+typedef void (*ActivationHandleFunc) (NMDevice *self);
+
+typedef struct {
+	ActivationHandleFunc func;
+	guint id;
+} ActivationHandleData;
+
 typedef enum {
 	CLEANUP_TYPE_DECONFIGURE,
 	CLEANUP_TYPE_KEEP,
@@ -165,13 +172,13 @@ typedef struct {
 
 typedef struct {
 	NMDevice *slave;
-	gboolean enslaved;
+	gboolean slave_is_enslaved;
 	gboolean configure;
-	guint watch_id;
+	gulong watch_id;
 } SlaveInfo;
 
 typedef struct {
-	guint log_domain;
+	NMLogDomain log_domain;
 	guint timeout;
 	guint watch;
 	GPid pid;
@@ -186,7 +193,7 @@ typedef struct {
 	int ifindex;
 } DeleteOnDeactivateData;
 
-typedef struct {
+typedef struct _NMDevicePrivate {
 	gboolean in_state_changed;
 	gboolean initialized;
 	gboolean platform_link_initialized;
@@ -200,22 +207,25 @@ typedef struct {
 	guint queued_ip4_config_id;
 	guint queued_ip6_config_id;
 	GSList *pending_actions;
+	GSList *dad6_failed_addrs;
 
 	char *        udi;
-	char *        path;
 	char *        iface;   /* may change, could be renamed by user */
 	int           ifindex;
+	gboolean      real;
 	char *        ip_iface;
 	int           ip_ifindex;
 	NMDeviceType  type;
 	char *        type_desc;
 	char *        type_description;
+	NMLinkType    link_type;
 	NMDeviceCapabilities capabilities;
 	char *        driver;
 	char *        driver_version;
 	char *        firmware_version;
 	RfKillType    rfkill_type;
 	gboolean      firmware_missing;
+	gboolean      nm_plugin_missing;
 	GHashTable *  available_connections;
 	char *        hw_addr;
 	guint         hw_addr_len;
@@ -224,6 +234,7 @@ typedef struct {
 	char *        physical_port_id;
 	guint         dev_id;
 
+	gboolean                managed_touched_by_user;
 	NMUnmanagedFlags        unmanaged_flags;
 	gboolean                is_nm_owned; /* whether the device is a device owned and created by NM */
 	DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */
@@ -235,13 +246,11 @@ typedef struct {
 	NMActRequest *  queued_act_request;
 	gboolean        queued_act_request_is_waiting_for_carrier;
 	NMActRequest *  act_request;
-	guint           act_source_id;
-	gpointer        act_source_func;
-	guint           act_source6_id;
-	gpointer        act_source6_func;
+	ActivationHandleData act_handle4; /* for layer2 and IPv4. */
+	ActivationHandleData act_handle6;
 	guint           recheck_assume_id;
 	struct {
-		guint       		call_id;
+		guint               call_id;
 		NMDeviceStateReason available_reason;
 		NMDeviceStateReason unavailable_reason;
 	}               recheck_available;
@@ -272,6 +281,7 @@ typedef struct {
 	NMIP4Config *   dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
 	NMIP4Config *   ext_ip4_config; /* Stuff added outside NM */
 	NMIP4Config *   wwan_ip4_config; /* WWAN configuration */
+	GSList *        vpn4_configs;   /* VPNs which use this device */
 	struct {
 		gboolean v4_has;
 		gboolean v4_is_assumed;
@@ -289,7 +299,6 @@ typedef struct {
 	gulong          dhcp4_state_sigid;
 	NMDhcp4Config * dhcp4_config;
 	guint           dhcp4_restart_id;
-	NMIP4Config *   vpn4_config;  /* routes added by a VPN which uses this device */
 
 	guint           arp_round2_id;
 	PingInfo        gw_ping;
@@ -299,20 +308,20 @@ typedef struct {
 	gulong            dnsmasq_state_id;
 
 	/* Firewall */
-	NMFirewallPendingCall fw_call;
+	gboolean       fw_ready;
+	NMFirewallManagerCallId fw_call;
 
-	/* avahi-autoipd stuff */
-	GPid    aipd_pid;
-	guint   aipd_watch;
-	guint   aipd_timeout;
+	/* IPv4LL stuff */
+	sd_ipv4ll *    ipv4ll;
+	guint          ipv4ll_timeout;
 
 	/* IP6 configuration info */
 	NMIP6Config *  ip6_config;
 	IpState        ip6_state;
 	NMIP6Config *  con_ip6_config; /* config from the setting */
-	NMIP6Config *  vpn6_config;  /* routes added by a VPN which uses this device */
 	NMIP6Config *  wwan_ip6_config;
 	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
+	GSList *       vpn6_configs;   /* VPNs which use this device */
 	gboolean       nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
 	guint32        ip6_mtu;
 
@@ -324,6 +333,7 @@ typedef struct {
 	NMIP6Config *  ac_ip6_config;
 
 	guint          linklocal6_timeout_id;
+	guint8         linklocal6_dad_counter;
 
 	GHashTable *   ip6_saved_properties;
 
@@ -342,9 +352,9 @@ typedef struct {
 
 	/* master interface for bridge/bond/team slave */
 	NMDevice *      master;
-	gboolean        enslaved;
+	gboolean        is_enslaved;
 	gboolean        master_ready_handled;
-	guint           master_ready_id;
+	gulong          master_ready_id;
 
 	/* slave management */
 	gboolean        is_master;
@@ -353,6 +363,9 @@ typedef struct {
 	NMMetered       metered;
 
 	NMConnectionProvider *con_provider;
+	NMLldpListener *lldp_listener;
+
+	guint check_delete_unrealized_id;
 } NMDevicePrivate;
 
 static gboolean nm_device_set_ip4_config (NMDevice *self,
@@ -372,7 +385,7 @@ static gboolean nm_device_set_ip6_config (NMDevice *self,
                                           gboolean routes_full_sync,
                                           NMDeviceStateReason *reason);
 
-static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
+static void 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);
 
@@ -384,12 +397,20 @@ static void _carrier_wait_check_queued_act_request (NMDevice *self);
 
 static gboolean nm_device_get_default_unmanaged (NMDevice *self);
 
+static const char *_activation_func_to_string (ActivationHandleFunc func);
+static void activation_source_handle_cb (NMDevice *self, int family);
+
 static void _set_state_full (NMDevice *self,
                              NMDeviceState state,
                              NMDeviceStateReason reason,
                              gboolean quitting);
 
-static void nm_device_update_hw_address (NMDevice *self);
+static gboolean queued_ip4_config_change (gpointer user_data);
+static gboolean queued_ip6_config_change (gpointer user_data);
+
+static void _set_unmanaged_flags (NMDevice *self,
+                                  NMUnmanagedFlags flags,
+                                  gboolean unmanaged);
 
 /***********************************************************/
 
@@ -425,7 +446,7 @@ state_to_string (NMDeviceState state)
 	return queued_state_to_string (state) + strlen (QUEUED_PREFIX);
 }
 
-static const char *reason_table[] = {
+NM_UTILS_STRING_LOOKUP_TABLE_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NULL,
 	[NM_DEVICE_STATE_REASON_UNKNOWN]                  = "unknown",
 	[NM_DEVICE_STATE_REASON_NONE]                     = "none",
 	[NM_DEVICE_STATE_REASON_NOW_MANAGED]              = "managed",
@@ -489,15 +510,10 @@ static const char *reason_table[] = {
 	[NM_DEVICE_STATE_REASON_NEW_ACTIVATION]           = "new-activation",
 	[NM_DEVICE_STATE_REASON_PARENT_CHANGED]           = "parent-changed",
 	[NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED]   = "parent-managed-changed",
-};
+);
 
-static const char *
-reason_to_string (NMDeviceStateReason reason)
-{
-	if ((gsize) reason < G_N_ELEMENTS (reason_table))
-		return reason_table[reason];
-	return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN];
-}
+#define reason_to_string(reason) \
+	NM_UTILS_STRING_LOOKUP_TABLE (_reason_to_string, reason)
 
 /***********************************************************/
 
@@ -521,30 +537,6 @@ nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps)
 
 /***********************************************************/
 
-void
-nm_device_dbus_export (NMDevice *self)
-{
-	static guint32 devcount = 0;
-	NMDevicePrivate *priv;
-
-	g_return_if_fail (NM_IS_DEVICE (self));
-
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->path == NULL);
-
-	priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++);
-	_LOGD (LOGD_DEVICE, "exported as %s", priv->path);
-	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self);
-}
-
-const char *
-nm_device_get_path (NMDevice *self)
-{
-	g_return_val_if_fail (self != NULL, NULL);
-
-	return NM_DEVICE_GET_PRIVATE (self)->path;
-}
-
 const char *
 nm_device_get_udi (NMDevice *self)
 {
@@ -564,17 +556,40 @@ nm_device_get_iface (NMDevice *self)
 int
 nm_device_get_ifindex (NMDevice *self)
 {
-	g_return_val_if_fail (self != NULL, 0);
+	g_return_val_if_fail (NM_IS_DEVICE (self), 0);
 
 	return NM_DEVICE_GET_PRIVATE (self)->ifindex;
 }
 
+/**
+ * nm_device_is_software:
+ * @self: the #NMDevice
+ *
+ * Indicates if the device is a software-based virtual device without
+ * backing hardware, which can be added and removed programmatically.
+ *
+ * Returns: %TRUE if the device is a software-based device
+ */
 gboolean
 nm_device_is_software (NMDevice *self)
 {
 	return NM_FLAGS_HAS (NM_DEVICE_GET_PRIVATE (self)->capabilities, NM_DEVICE_CAP_IS_SOFTWARE);
 }
 
+/**
+ * nm_device_is_real:
+ * @self: the #NMDevice
+ *
+ * Returns: %TRUE if the device exists, %FALSE if the device is a placeholder
+ */
+gboolean
+nm_device_is_real (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	return NM_DEVICE_GET_PRIVATE (self)->real;
+}
+
 const char *
 nm_device_get_ip_iface (NMDevice *self)
 {
@@ -701,13 +716,21 @@ nm_device_get_device_type (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->type;
 }
 
+NMLinkType
+nm_device_get_link_type (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_LINK_TYPE_UNKNOWN);
+
+	return NM_DEVICE_GET_PRIVATE (self)->link_type;
+}
+
 /**
  * nm_device_get_metered:
  * @setting: the #NMDevice
  *
  * Returns: the #NMDevice:metered property of the device.
  *
- * Since: 1.0.6
+ * Since: 1.2
  **/
 NMMetered
 nm_device_get_metered (NMDevice *self)
@@ -762,12 +785,20 @@ nm_device_get_priority (NMDevice *self)
 		return 350;
 	case NM_DEVICE_TYPE_VLAN:
 		return 400;
+	case NM_DEVICE_TYPE_MACVLAN:
+		return 410;
 	case NM_DEVICE_TYPE_BRIDGE:
 		return 425;
+	case NM_DEVICE_TYPE_TUN:
+		return 450;
+	case NM_DEVICE_TYPE_VXLAN:
+		return 500;
 	case NM_DEVICE_TYPE_WIFI:
 		return 600;
 	case NM_DEVICE_TYPE_OLPC_MESH:
 		return 650;
+	case NM_DEVICE_TYPE_IP_TUNNEL:
+		return 675;
 	case NM_DEVICE_TYPE_MODEM:
 		return 700;
 	case NM_DEVICE_TYPE_BT:
@@ -795,7 +826,7 @@ _get_ipx_route_metric (NMDevice *self,
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (connection) {
 		s_ip = is_v4
 		       ? nm_connection_get_setting_ip4_config (connection)
@@ -923,12 +954,31 @@ nm_device_get_act_request (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->act_request;
 }
 
+NMSettingsConnection *
+nm_device_get_settings_connection (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	return priv->act_request ? nm_act_request_get_settings_connection (priv->act_request) : NULL;
+}
+
 NMConnection *
-nm_device_get_connection (NMDevice *self)
+nm_device_get_applied_connection (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL;
+	return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL;
+}
+
+gboolean
+nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFlags compare_flags)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->act_request)
+		return FALSE;
+
+	return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request, compare_flags);
 }
 
 RfKillType
@@ -951,13 +1001,13 @@ static gboolean
 nm_device_uses_generated_assumed_connection (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection;
+	NMSettingsConnection *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);
+		connection = nm_act_request_get_settings_connection (priv->act_request);
 		if (   connection
-		    && nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection)))
+		    && nm_settings_connection_get_nm_generated_assumed (connection))
 			return TRUE;
 	}
 	return FALSE;
@@ -989,17 +1039,8 @@ find_slave_info (NMDevice *self, NMDevice *slave)
 	return NULL;
 }
 
-static void
-free_slave_info (SlaveInfo *info)
-{
-	g_signal_handler_disconnect (info->slave, info->watch_id);
-	g_clear_object (&info->slave);
-	memset (info, 0, sizeof (*info));
-	g_free (info);
-}
-
 /**
- * nm_device_enslave_slave:
+ * nm_device_master_enslave_slave:
  * @self: the master device
  * @slave: the slave device to enslave
  * @connection: (allow-none): the slave device's connection
@@ -1011,7 +1052,7 @@ free_slave_info (SlaveInfo *info)
  *  other devices.
  */
 static gboolean
-nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection)
+nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection)
 {
 	SlaveInfo *info;
 	gboolean success = FALSE;
@@ -1025,7 +1066,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti
 	if (!info)
 		return FALSE;
 
-	if (info->enslaved)
+	if (info->slave_is_enslaved)
 		success = TRUE;
 	else {
 		configure = (info->configure && connection != NULL);
@@ -1033,7 +1074,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti
 			g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
 		success = NM_DEVICE_GET_CLASS (self)->enslave_slave (self, slave, connection, configure);
-		info->enslaved = success;
+		info->slave_is_enslaved = success;
 	}
 
 	nm_device_slave_notify_enslave (info->slave, success);
@@ -1059,7 +1100,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti
 }
 
 /**
- * nm_device_release_one_slave:
+ * nm_device_master_release_one_slave:
  * @self: the master device
  * @slave: the slave device to release
  * @configure: whether @self needs to actually release @slave
@@ -1068,49 +1109,55 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti
  * 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 @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 *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason)
+static void
+nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+	NMDevicePrivate *slave_priv;
 	SlaveInfo *info;
-	gboolean success = FALSE;
+	gs_unref_object NMDevice *self_free = NULL;
 
-	g_return_val_if_fail (slave != NULL, FALSE);
-	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL, FALSE);
+	g_return_if_fail (NM_DEVICE (self));
+	g_return_if_fail (NM_DEVICE (slave));
+	g_return_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL);
 
 	info = find_slave_info (self, slave);
+
+	_LOGt (LOGD_CORE, "master: release one slave %p/%s%s", slave, nm_device_get_iface (slave),
+	       !info ? " (not registered)" : "");
+
 	if (!info)
-		return FALSE;
-	priv->slaves = g_slist_remove (priv->slaves, info);
+		g_return_if_reached ();
 
-	if (info->enslaved) {
-		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.
-		 */
-	}
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	slave_priv = NM_DEVICE_GET_PRIVATE (slave);
 
-	if (!configure) {
-		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 ();
-		reason = NM_DEVICE_STATE_REASON_UNKNOWN;
-	}
-	nm_device_slave_notify_release (info->slave, reason);
+	g_return_if_fail (self == slave_priv->master);
+	nm_assert (slave == info->slave);
+
+	/* first, let subclasses handle the release ... */
+	if (info->slave_is_enslaved)
+		NM_DEVICE_GET_CLASS (self)->release_slave (self, slave, configure);
+
+	/* raise notifications about the release, including clearing is_enslaved. */
+	nm_device_slave_notify_release (slave, reason);
+
+	/* keep both alive until the end of the function.
+	 * Transfers ownership from slave_priv->master.  */
+	self_free = self;
+
+	priv->slaves = g_slist_remove (priv->slaves, info);
+	slave_priv->master = NULL;
 
-	free_slave_info (info);
+	g_signal_handler_disconnect (slave, info->watch_id);
+	g_object_unref (slave);
+	g_slice_free (SlaveInfo, info);
 
 	/* Ensure the device's hardware address is up-to-date; it often changes
 	 * when slaves change.
 	 */
 	nm_device_update_hw_address (self);
-
-	return success;
 }
 
 /**
@@ -1123,8 +1170,7 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure
 static gboolean
 can_unmanaged_external_down (NMDevice *self)
 {
-	return   nm_device_is_software (self)
-	      && !nm_device_get_is_nm_owned (self);
+	return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 }
 
 /**
@@ -1138,7 +1184,6 @@ void
 nm_device_finish_init (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean platform_unmanaged = FALSE;
 
 	g_assert (priv->initialized == FALSE);
 
@@ -1147,29 +1192,29 @@ nm_device_finish_init (NMDevice *self)
 	    && priv->ifindex > 0
 	    && (   !priv->up
 	        || !priv->platform_link_initialized))
-		nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
+		nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE);
 
 	if (priv->master)
-		nm_device_enslave_slave (priv->master, self, NULL);
+		nm_device_master_enslave_slave (priv->master, self, NULL);
 
 	if (priv->ifindex > 0) {
 		if (priv->ifindex == 1) {
-			/* keep 'lo' as default-unmanaged. */
-
-			/* FIXME: either find a better way to unmange 'lo' that cannot be changed
-			 * by user configuration (NM_UNMANGED_LOOPBACK?) or fix managing 'lo'.
-			 * Currently it can happen that NM deletes 127.0.0.1 address. */
-			nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, TRUE);
+			/* Unmanaged the loopback device with an explicit NM_UNMANAGED_LOOPBACK flag.
+			 * Later we might want to manage 'lo' too. Currently that doesn't work because
+			 * NetworkManager might down the interface or remove the 127.0.0.1 address. */
+			nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_LOOPBACK, TRUE);
 		} else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) {
+			gboolean platform_unmanaged = FALSE;
+
 			if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged))
-				nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged);
+				nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_DEFAULT, platform_unmanaged);
 		} else {
 			/* Hardware and externally-created software links stay unmanaged
 			 * until they are fully initialized by the platform. NM created
 			 * links must be available for activation immediately and thus
 			 * do not get the PLATFORM_INIT unmanaged flag set.
 			 */
-			nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
+			nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_PLATFORM_INIT, TRUE);
 		}
 	}
 
@@ -1180,6 +1225,9 @@ static void
 update_dynamic_ip_setup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	GError *error;
+	gconstpointer addr;
+	size_t addr_length;
 
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
@@ -1205,6 +1253,18 @@ update_dynamic_ip_setup (NMDevice *self)
 	if (priv->dnsmasq_manager) {
 		/* FIXME: todo */
 	}
+
+	if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) {
+		nm_lldp_listener_stop (priv->lldp_listener);
+		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
+
+		if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
+		                             nm_device_get_iface (self), addr, addr_length, &error)) {
+			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
+			       priv->lldp_listener, error->message);
+			g_clear_error (&error);
+		}
+	}
 }
 
 static void
@@ -1342,23 +1402,40 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 }
 
 static void
-device_set_master (NMDevice *self, int ifindex)
+device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
 {
-	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 (NM_PLATFORM_GET, ifindex));
+	g_return_if_fail (plink);
+
+	if (plink->master <= 0)
+		return;
+
+	if (priv->master) {
+		if (   plink->master > 0
+		    && plink->master == nm_device_get_ifindex (priv->master)) {
+			/* call add-slave again. We expect @self already to be added to
+			 * the master, but this also triggers a recheck-assume. */
+			nm_device_master_add_slave (priv->master, self, FALSE);
+			return;
+		}
+
+		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+	}
+	if (plink->master > 0) {
+		NMDevice *master;
+
+		master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master);
+		if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave)
+			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",
+			       plink->master,
+			       nm_platform_link_get_name (NM_PLATFORM_GET, plink->master));
+		}
 	}
 }
 
@@ -1369,12 +1446,11 @@ device_link_changed (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMUtilsIPv6IfaceId token_iid;
 	gboolean ip_ifname_changed = FALSE;
-	gboolean platform_unmanaged = FALSE;
 	const char *udi;
 	NMPlatformLink info;
 	const NMPlatformLink *pllink;
 	int ifindex;
-	gboolean emit_link_initialized = FALSE;
+	gboolean just_initialized = FALSE;
 	gboolean was_up;
 
 	priv->device_link_changed_id = 0;
@@ -1407,6 +1483,12 @@ device_link_changed (NMDevice *self)
 		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
 	}
 
+	if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) {
+		g_free (priv->driver);
+		priv->driver = g_strdup (info.driver);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
+	}
+
 	if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
 		_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
 		       priv->ifindex, priv->iface, info.name);
@@ -1429,15 +1511,6 @@ device_link_changed (NMDevice *self)
 		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) {
-		device_set_master (self, info.master);
-		if (priv->master)
-			nm_device_enslave_slave (priv->master, self, NULL);
-	}
-
 	if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) {
 		_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
 		if (nm_rdisc_set_iid (priv->rdisc, token_iid))
@@ -1452,33 +1525,36 @@ device_link_changed (NMDevice *self)
 		update_dynamic_ip_setup (self);
 
 	if (priv->ifindex > 0 && !priv->platform_link_initialized && info.initialized) {
+		gboolean platform_unmanaged = FALSE;
+
 		priv->platform_link_initialized = TRUE;
 
 		if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) {
-			nm_device_set_unmanaged (self,
-			                         NM_UNMANAGED_DEFAULT,
-			                         platform_unmanaged,
-			                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			nm_device_set_unmanaged_flags (self,
+			                               NM_UNMANAGED_DEFAULT,
+			                               platform_unmanaged,
+			                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
 		}
 
-		nm_device_set_unmanaged (self,
-		                         NM_UNMANAGED_PLATFORM_INIT,
-		                         FALSE,
-		                         NM_DEVICE_STATE_REASON_NOW_MANAGED);
+		nm_device_set_unmanaged_flags (self,
+		                               NM_UNMANAGED_PLATFORM_INIT,
+		                               FALSE,
+		                               NM_DEVICE_STATE_REASON_NOW_MANAGED);
 
-		emit_link_initialized = TRUE;
+		just_initialized = TRUE;
 	}
 
 	was_up = priv->up;
 	priv->up = NM_FLAGS_HAS (info.flags, IFF_UP);
 
 	if (   priv->platform_link_initialized
-	    && (emit_link_initialized || priv->up != was_up)) {
+	    && (   just_initialized
+	        || priv->up != was_up)) {
 
 		/* Manage externally-created software interfaces only when they are IFF_UP */
 		g_assert (priv->ifindex > 0);
 		if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) {
-			gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN);
+			gboolean external_down = !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN);
 
 			if (external_down && NM_FLAGS_HAS (info.flags, IFF_UP)) {
 				if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) {
@@ -1492,32 +1568,30 @@ device_link_changed (NMDevice *self)
 					 * 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);
+					nm_device_set_unmanaged_flags (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;
+					_set_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN, FALSE);
 				}
 			} else if (!external_down && !NM_FLAGS_HAS (info.flags, IFF_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);
+				nm_device_set_unmanaged_flags (self,
+				                               NM_UNMANAGED_EXTERNAL_DOWN,
+				                               TRUE,
+				                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
 			}
 		}
 	}
 
-	if (emit_link_initialized)
-		g_signal_emit (self, signals[LINK_INITIALIZED], 0);
-
+	device_recheck_slave_status (self, &info);
 	return G_SOURCE_REMOVE;
 }
 
@@ -1526,12 +1600,13 @@ device_ip_link_changed (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const NMPlatformLink *pllink;
-	int ip_ifindex;
 
 	priv->device_ip_link_changed_id = 0;
 
-	ip_ifindex = nm_device_get_ip_ifindex (self);
-	pllink = nm_platform_link_get (NM_PLATFORM_GET, ip_ifindex);
+	if (!priv->ip_ifindex)
+		return G_SOURCE_REMOVE;
+
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ip_ifindex);
 	if (!pllink)
 		return G_SOURCE_REMOVE;
 
@@ -1554,7 +1629,6 @@ link_changed_cb (NMPlatform *platform,
                  int ifindex,
                  NMPlatformLink *info,
                  NMPlatformSignalChangeType change_type,
-                 NMPlatformReason reason,
                  NMDevice *self)
 {
 	NMDevicePrivate *priv;
@@ -1564,12 +1638,6 @@ link_changed_cb (NMPlatform *platform,
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	/* We don't filter by 'reason' because we are interested in *all* link
-	 * changes. For example a call to nm_platform_link_set_up() may result
-	 * in an internal carrier change (i.e. we ask the kernel to set IFF_UP
-	 * and it results in also setting IFF_LOWER_UP.
-	 */
-
 	if (ifindex == nm_device_get_ifindex (self)) {
 		if (!priv->device_link_changed_id) {
 			priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self);
@@ -1592,6 +1660,165 @@ link_changed (NMDevice *self, NMPlatformLink *info)
 		nm_device_set_carrier (self, info->connected);
 }
 
+static gboolean
+link_type_compatible (NMDevice *self,
+                      NMLinkType link_type,
+                      gboolean *out_compatible,
+                      GError **error)
+{
+	NMDeviceClass *klass;
+	NMLinkType device_type;
+	guint i = 0;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	klass = NM_DEVICE_GET_CLASS (self);
+
+	if (!klass->link_types) {
+		NM_SET_OUT (out_compatible, FALSE);
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "Device does not support platform links");
+		return FALSE;
+	}
+
+	device_type = self->priv->link_type;
+	if (device_type > NM_LINK_TYPE_UNKNOWN && device_type != link_type) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "Needed link type 0x%x does not match the platform link type 0x%X",
+		             device_type, link_type);
+		return FALSE;
+	}
+
+	for (i = 0; klass->link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) {
+		if (klass->link_types[i] == link_type)
+			return TRUE;
+		if (klass->link_types[i] == NM_LINK_TYPE_ANY)
+			return TRUE;
+	}
+
+	NM_SET_OUT (out_compatible, FALSE);
+	g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+	             "Device does not support platform link type 0x%X",
+	             link_type);
+	return FALSE;
+}
+
+/**
+ * nm_device_realize_start():
+ * @self: the #NMDevice
+ * @plink: an existing platform link or %NULL
+ * @out_compatible: %TRUE on return if @self is compatible with @plink
+ * @error: location to store error, or %NULL
+ *
+ * Initializes and sets up the device using existing backing resources. Before
+ * the device is ready for use nm_device_realize_finish() must be called.
+ * @out_compatible will only be set if @plink is not %NULL, and
+ *
+ * Important: if nm_device_realize_start() returns %TRUE, the caller MUST
+ * also call nm_device_realize_finish() to balance g_object_freeze_notify().
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+nm_device_realize_start (NMDevice *self,
+                         const NMPlatformLink *plink,
+                         gboolean *out_compatible,
+                         GError **error)
+{
+	NM_SET_OUT (out_compatible, TRUE);
+
+	if (plink) {
+		if (g_strcmp0 (nm_device_get_iface (self), plink->name) != 0) {
+			NM_SET_OUT (out_compatible, FALSE);
+			g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+			                     "Device interface name does not match platform link");
+			return FALSE;
+		}
+
+		if (!link_type_compatible (self, plink->type, out_compatible, error))
+			return FALSE;
+	}
+
+	realize_start_setup (self, plink);
+
+	return TRUE;
+}
+
+/**
+ * nm_device_create_and_realize():
+ * @self: the #NMDevice
+ * @connection: the #NMConnection being activated
+ * @parent: the parent #NMDevice if any
+ * @error: location to store error, or %NULL
+ *
+ * Creates any backing resources needed to realize the device to proceed
+ * with activating @connection.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+nm_device_create_and_realize (NMDevice *self,
+                              NMConnection *connection,
+                              NMDevice *parent,
+                              GError **error)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMPlatformLink plink_copy;
+	const NMPlatformLink *plink = NULL;
+
+	/* Must be set before device is realized */
+	priv->is_nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
+
+	_LOGD (LOGD_DEVICE, "create (is %snm-owned)", priv->is_nm_owned ? "" : "not ");
+
+	/* Create any resources the device needs */
+	if (NM_DEVICE_GET_CLASS (self)->create_and_realize) {
+		if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error))
+			return FALSE;
+		plink_copy = *plink;
+		plink = &plink_copy;
+	}
+
+	realize_start_setup (self, plink);
+	nm_device_realize_finish (self, plink);
+
+	g_return_val_if_fail (nm_device_check_connection_compatible (self, connection), TRUE);
+	return TRUE;
+}
+
+static void
+update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *udi;
+
+	g_return_if_fail (plink != NULL);
+
+	udi = nm_platform_link_get_udi (NM_PLATFORM_GET, plink->ifindex);
+	if (udi && !g_strcmp0 (udi, priv->udi)) {
+		g_free (priv->udi);
+		priv->udi = g_strdup (udi);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+	}
+
+	if (!g_strcmp0 (plink->name, priv->iface)) {
+		g_free (priv->iface);
+		priv->iface = g_strdup (plink->name);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE);
+	}
+
+	priv->ifindex = plink->ifindex;
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX);
+
+	priv->up = NM_FLAGS_HAS (plink->flags, IFF_UP);
+	if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) {
+		g_free (priv->driver);
+		priv->driver = g_strdup (plink->driver);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER);
+	}
+	priv->platform_link_initialized = plink->initialized;
+}
+
 static void
 config_changed_update_ignore_carrier (NMConfig *config,
                                       NMConfigData *config_data,
@@ -1615,6 +1842,317 @@ check_carrier (NMDevice *self)
 		nm_device_set_carrier (self, nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
 }
 
+static void
+realize_start_notify (NMDevice *self, const NMPlatformLink *plink)
+{
+	/* Stub implementation for realize_start_notify(). It does nothing,
+	 * but allows derived classes to uniformly invoke the parent
+	 * implementation. */
+}
+
+/**
+ * realize_start_setup():
+ * @self: the #NMDevice
+ * @plink: the #NMPlatformLink if backed by a kernel netdevice
+ *
+ * Update the device from backing resource properties (like hardware
+ * addresses, carrier states, driver/firmware info, etc).  This function
+ * should only change properties for this device, and should not perform
+ * any tasks that affect other interfaces (like master/slave or parent/child
+ * stuff).
+ */
+static void
+realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
+{
+	NMDevicePrivate *priv;
+	NMDeviceClass *klass;
+	static guint32 id = 0;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* The device should not be realized */
+	g_return_if_fail (!priv->real);
+	g_return_if_fail (priv->ip_ifindex <= 0);
+	g_return_if_fail (priv->ip_iface == NULL);
+
+	_LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0);
+
+	klass = NM_DEVICE_GET_CLASS (self);
+
+	/* Balanced by a thaw in nm_device_realize_finish() */
+	g_object_freeze_notify (G_OBJECT (self));
+
+	if (plink) {
+		g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL));
+		update_device_from_platform_link (self, plink);
+	}
+
+	if (priv->ifindex > 0) {
+		priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID);
+
+		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
+
+		if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex))
+			priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
+
+		priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_MTU);
+
+		nm_platform_link_get_driver_info (NM_PLATFORM_GET,
+		                                  priv->ifindex,
+		                                  NULL,
+		                                  &priv->driver_version,
+		                                  &priv->firmware_version);
+		if (priv->driver_version)
+			g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION);
+		if (priv->firmware_version)
+			g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION);
+
+		if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
+			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ifindex);
+	}
+
+	if (klass->get_generic_capabilities)
+		priv->capabilities |= klass->get_generic_capabilities (self);
+
+	if (!priv->udi) {
+		/* Use a placeholder UDI until we get a real one */
+		priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+	}
+
+	/* trigger initial ip config change to initialize ip-config */
+	priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
+	priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
+
+	nm_device_update_hw_address (self);
+	nm_device_update_initial_hw_address (self);
+
+	/* Note: initial hardware address must be read before calling get_ignore_carrier() */
+	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
+		NMConfig *config = nm_config_get ();
+
+		priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
+		g_signal_connect (G_OBJECT (config),
+		                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+		                  G_CALLBACK (config_changed_update_ignore_carrier),
+		                  self);
+
+		check_carrier (self);
+		_LOGD (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;
+	}
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES);
+
+	klass->realize_start_notify (self, plink);
+}
+
+/**
+ * nm_device_realize_finish():
+ * @self: the #NMDevice
+ * @plink: the #NMPlatformLink if backed by a kernel netdevice
+ *
+ * Update the device's master/slave or parent/child relationships from
+ * backing resource properties.  After this function finishes, the device
+ * is ready for network connectivity.
+ */
+void
+nm_device_realize_finish (NMDevice *self, const NMPlatformLink *plink)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (!plink || link_type_compatible (self, plink->type, NULL, NULL));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_return_if_fail (!priv->real);
+
+	if (plink) {
+		update_device_from_platform_link (self, plink);
+		device_recheck_slave_status (self, plink);
+	}
+
+	priv->real = TRUE;
+	g_object_notify (G_OBJECT (self), NM_DEVICE_REAL);
+
+	nm_device_recheck_available_connections (self);
+
+	/* Balanced by a freeze in realize_start_setup() */
+	g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+unrealize_notify (NMDevice *self)
+{
+	/* Stub implementation for unrealize_notify(). It does nothing,
+	 * but allows derived classes to uniformly invoke the parent
+	 * implementation. */
+}
+
+static gboolean
+available_connection_check_delete_unrealized_on_idle (gpointer user_data)
+{
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->check_delete_unrealized_id = 0;
+
+	if (   g_hash_table_size (priv->available_connections) == 0
+	    && !nm_device_is_real (self))
+		g_signal_emit (self, signals[REMOVED], 0);
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+available_connection_check_delete_unrealized (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* always rescheadule the remove signal. */
+	nm_clear_g_source (&priv->check_delete_unrealized_id);
+
+	if (   g_hash_table_size (priv->available_connections) == 0
+	    && !nm_device_is_real (self))
+		priv->check_delete_unrealized_id = g_idle_add (available_connection_check_delete_unrealized_on_idle, self);
+}
+
+/**
+ * nm_device_unrealize():
+ * @self: the #NMDevice
+ * @remove_resources: if %TRUE, remove backing resources
+ * @error: location to store error, or %NULL
+ *
+ * Clears any properties that depend on backing resources (kernel devices,
+ * etc) and removes those resources if @remove_resources is %TRUE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
+{
+	NMDevicePrivate *priv;
+	int ifindex;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (!nm_device_is_software (self) || !nm_device_is_real (self)) {
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_NOT_SOFTWARE,
+		                     "This device is not a software device or is not realized");
+		return FALSE;
+	}
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_return_val_if_fail (priv->iface != NULL, FALSE);
+	g_return_val_if_fail (priv->real, FALSE);
+
+	g_object_freeze_notify (G_OBJECT (self));
+
+	ifindex = nm_device_get_ifindex (self);
+
+	_LOGD (LOGD_DEVICE, "unrealize (ifindex %d)", ifindex > 0 ? ifindex : 0);
+
+	if (remove_resources) {
+		if (ifindex > 0)
+			nm_platform_link_delete (NM_PLATFORM_GET, ifindex);
+	}
+
+	NM_DEVICE_GET_CLASS (self)->unrealize_notify (self);
+
+	if (priv->ifindex > 0) {
+		priv->ifindex = 0;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX);
+	}
+	priv->ip_ifindex = 0;
+	if (priv->ip_iface) {
+		g_clear_pointer (&priv->ip_iface, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
+	}
+	if (priv->driver_version) {
+		g_clear_pointer (&priv->driver_version, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION);
+	}
+	if (priv->firmware_version) {
+		g_clear_pointer (&priv->firmware_version, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION);
+	}
+	if (priv->udi) {
+		g_clear_pointer (&priv->udi, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_UDI);
+	}
+	if (priv->hw_addr) {
+		g_clear_pointer (&priv->hw_addr, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
+	}
+	if (priv->physical_port_id) {
+		g_clear_pointer (&priv->physical_port_id, g_free);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID);
+	}
+
+	g_clear_pointer (&priv->perm_hw_addr, g_free);
+	g_clear_pointer (&priv->initial_hw_addr, g_free);
+
+	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
+	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
+		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES);
+
+	priv->real = FALSE;
+	g_object_notify (G_OBJECT (self), NM_DEVICE_REAL);
+
+	nm_device_set_autoconnect (self, DEFAULT_AUTOCONNECT);
+
+	g_object_thaw_notify (G_OBJECT (self));
+
+	nm_device_state_changed (self,
+	                         NM_DEVICE_STATE_UNMANAGED,
+	                         remove_resources ?
+	                             NM_DEVICE_STATE_REASON_USER_REQUESTED : NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+
+	/* Garbage-collect unneeded unrealized devices. */
+	nm_device_recheck_available_connections (self);
+
+	return TRUE;
+}
+
+/**
+ * nm_device_notify_new_device_added():
+ * @self: the #NMDevice
+ * @device: the newly added device
+ *
+ * Called by the manager to notify the device that a new device has
+ * been found and added.
+ */
+void
+nm_device_notify_new_device_added (NMDevice *self, NMDevice *device)
+{
+	NMDeviceClass *klass;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (NM_IS_DEVICE (device));
+
+	klass = NM_DEVICE_GET_CLASS (self);
+	if (klass->notify_new_device_added)
+		klass->notify_new_device_added (self, device);
+}
+
 /**
  * nm_device_notify_component_added():
  * @self: the #NMDevice
@@ -1630,8 +2168,14 @@ check_carrier (NMDevice *self)
 gboolean
 nm_device_notify_component_added (NMDevice *self, GObject *component)
 {
-	if (NM_DEVICE_GET_CLASS (self)->component_added)
-		return NM_DEVICE_GET_CLASS (self)->component_added (self, component);
+	NMDeviceClass *klass;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (G_IS_OBJECT (component), FALSE);
+
+	klass = NM_DEVICE_GET_CLASS (self);
+	if (klass->component_added)
+		return klass->component_added (self, component);
 	return FALSE;
 }
 
@@ -1687,7 +2231,7 @@ slave_state_changed (NMDevice *slave,
 		return;
 
 	if (slave_new_state == NM_DEVICE_STATE_IP_CONFIG)
-		nm_device_enslave_slave (self, slave, nm_device_get_connection (slave));
+		nm_device_master_enslave_slave (self, slave, nm_device_get_applied_connection (slave));
 	else if (slave_new_state > NM_DEVICE_STATE_ACTIVATED)
 		release = TRUE;
 	else if (   slave_new_state <= NM_DEVICE_STATE_DISCONNECTED
@@ -1697,7 +2241,7 @@ slave_state_changed (NMDevice *slave,
 	}
 
 	if (release) {
-		nm_device_release_one_slave (self, slave, TRUE, reason);
+		nm_device_master_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)
 			_LOGD (LOGD_DEVICE, "last slave removed; remaining activated");
@@ -1713,33 +2257,53 @@ slave_state_changed (NMDevice *slave,
  *
  * 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
+static void
 nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+	NMDevicePrivate *slave_priv;
 	SlaveInfo *info;
 
-	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 (self)->enslave_slave != NULL, FALSE);
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (NM_IS_DEVICE (slave));
+	g_return_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	slave_priv = NM_DEVICE_GET_PRIVATE (slave);
+
+	info = find_slave_info (self, slave);
+
+	_LOGt (LOGD_CORE, "master: add one slave %p/%s%s", slave, nm_device_get_iface (slave),
+	       info ? " (already registered)" : "");
 
 	if (configure)
-		g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
+		g_return_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED);
 
-	if (!find_slave_info (self, slave)) {
-		info = g_malloc0 (sizeof (SlaveInfo));
+	if (!info) {
+		g_return_if_fail (!slave_priv->master);
+		g_return_if_fail (!slave_priv->is_enslaved);
+
+		info = g_slice_new0 (SlaveInfo);
 		info->slave = g_object_ref (slave);
 		info->configure = configure;
-		info->watch_id = g_signal_connect (slave, "state-changed",
+		info->watch_id = g_signal_connect (slave,
+		                                   NM_DEVICE_STATE_CHANGED,
 		                                   G_CALLBACK (slave_state_changed), self);
 		priv->slaves = g_slist_append (priv->slaves, info);
-	}
-	nm_device_queue_recheck_assume (self);
+		slave_priv->master = g_object_ref (self);
 
-	return TRUE;
+		/* no need to emit
+		 *
+		 *   g_object_notify (G_OBJECT (slave), NM_DEVICE_MASTER);
+		 *
+		 * because slave_priv->is_enslaved is not true, thus the value
+		 * didn't change yet. */
+	} else
+		g_return_if_fail (slave_priv->master == self);
+
+	nm_device_queue_recheck_assume (self);
+	nm_device_queue_recheck_assume (slave);
 }
 
 
@@ -1749,7 +2313,7 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
  *
  * Returns: any slaves of which @self is the master.  Caller owns returned list.
  */
-GSList *
+static GSList *
 nm_device_master_get_slaves (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -1794,7 +2358,7 @@ nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex)
  */
 void
 nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave,
-                                            guint64 log_domain)
+                                            NMLogDomain log_domain)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *slave_physical_port_id, *existing_physical_port_id;
@@ -1831,6 +2395,7 @@ nm_device_master_release_slaves (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMDeviceStateReason reason;
+	gboolean configure = TRUE;
 
 	/* Don't release the slaves if this connection doesn't belong to NM. */
 	if (nm_device_uses_generated_assumed_connection (self))
@@ -1840,14 +2405,29 @@ nm_device_master_release_slaves (NMDevice *self)
 	if (priv->state == NM_DEVICE_STATE_FAILED)
 		reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 
+	if (!nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex))
+		configure = FALSE;
+
 	while (priv->slaves) {
 		SlaveInfo *info = priv->slaves->data;
 
-		nm_device_release_one_slave (self, info->slave, TRUE, reason);
+		nm_device_master_release_one_slave (self, info->slave, configure, reason);
 	}
 }
 
 /**
+ * nm_device_is_master:
+ * @self: the device
+ *
+ * Returns: %TRUE if the device can have slaves
+ */
+gboolean
+nm_device_is_master (NMDevice *self)
+{
+	return NM_DEVICE_GET_PRIVATE (self)->is_master;
+}
+
+/**
  * nm_device_get_master:
  * @self: the device
  *
@@ -1863,10 +2443,11 @@ nm_device_get_master (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->enslaved)
+	if (priv->is_enslaved) {
+		g_return_val_if_fail (priv->master, NULL);
 		return priv->master;
-	else
-		return NULL;
+	}
+	return NULL;
 }
 
 /**
@@ -1881,12 +2462,12 @@ static void
 nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection = nm_device_get_connection (self);
+	NMConnection *connection = nm_device_get_applied_connection (self);
 	gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG);
 
-	g_assert (priv->master);
+	g_return_if_fail (priv->master);
 
-	if (!priv->enslaved) {
+	if (!priv->is_enslaved) {
 		if (success) {
 			if (activating) {
 				_LOGI (LOGD_DEVICE, "Activation: connection '%s' enslaved, continuing activation",
@@ -1894,8 +2475,9 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 			} else
 				_LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master));
 
-			priv->enslaved = TRUE;
+			priv->is_enslaved = TRUE;
 			g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
+			g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES);
 		} else if (activating) {
 			_LOGW (LOGD_DEVICE, "Activation: connection '%s' could not be enslaved",
 			       nm_connection_get_id (connection));
@@ -1924,12 +2506,13 @@ static void
 nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection = nm_device_get_connection (self);
+	NMConnection *connection = nm_device_get_applied_connection (self);
 	NMDeviceState new_state;
 	const char *master_status;
 
-	if (   reason != NM_DEVICE_STATE_REASON_NONE
-	    && priv->state > NM_DEVICE_STATE_DISCONNECTED
+	g_return_if_fail (priv->master);
+
+	if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
 	    && priv->state <= NM_DEVICE_STATE_ACTIVATED) {
 		if (reason == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) {
 			new_state = NM_DEVICE_STATE_FAILED;
@@ -1947,14 +2530,13 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 		       master_status);
 
 		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)");
+	} else
+		_LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master));
 
-	if (priv->enslaved) {
-		priv->enslaved = FALSE;
+	if (priv->is_enslaved) {
+		priv->is_enslaved = FALSE;
 		g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
+		g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES);
 	}
 }
 
@@ -1968,7 +2550,7 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 gboolean
 nm_device_get_enslaved (NMDevice *self)
 {
-	return NM_DEVICE_GET_PRIVATE (self)->enslaved;
+	return NM_DEVICE_GET_PRIVATE (self)->is_enslaved;
 }
 
 /**
@@ -1981,12 +2563,17 @@ nm_device_get_enslaved (NMDevice *self)
 void
 nm_device_removed (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
 
-	if (priv->enslaved)
-		nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
-}
+	g_return_if_fail (NM_IS_DEVICE (self));
 
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->master) {
+		/* this is called when something externally messes with the slave or during shut-down.
+		 * Release the slave from master, but don't touch the device. */
+		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+	}
+}
 
 static gboolean
 is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
@@ -1996,7 +2583,7 @@ is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
 	if (priv->carrier || priv->ignore_carrier)
 		return TRUE;
 
-	if (NM_FLAGS_HAS (flags, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER))
+	if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER))
 		return TRUE;
 
 	return FALSE;
@@ -2068,7 +2655,7 @@ nm_device_get_autoconnect (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->autoconnect;
 }
 
-static void
+void
 nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect)
 {
 	NMDevicePrivate *priv;
@@ -2116,7 +2703,11 @@ 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)
+	if (!priv->autoconnect)
+		return FALSE;
+
+	/* Unrealized devices can always autoconnect. */
+	if (nm_device_is_real (self) && priv->state < NM_DEVICE_STATE_DISCONNECTED)
 		return FALSE;
 
 	/* The 'autoconnect-allowed' signal is emitted on a device to allow
@@ -2130,15 +2721,13 @@ nm_device_autoconnect_allowed (NMDevice *self)
 	g_value_set_object (&instance, self);
 
 	g_value_init (&retval, G_TYPE_BOOLEAN);
-	if (priv->autoconnect)
-		g_value_set_boolean (&retval, TRUE);
-	else
-		g_value_set_boolean (&retval, FALSE);
+	g_value_set_boolean (&retval, TRUE);
 
 	/* Use g_signal_emitv() rather than g_signal_emit() to avoid the return
 	 * value being changed if no handlers are connected */
 	g_signal_emitv (&instance, signals[AUTOCONNECT_ALLOWED], 0, &retval);
 	g_value_unset (&instance);
+
 	return g_value_get_boolean (&retval);
 }
 
@@ -2199,7 +2788,7 @@ device_has_config (NMDevice *self)
 		return TRUE;
 
 	/* The existence of a software device is good enough. */
-	if (nm_device_is_software (self))
+	if (nm_device_is_software (self) && nm_device_is_real (self))
 		return TRUE;
 
 	/* Slaves are also configured by definition */
@@ -2429,6 +3018,34 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection)
 	return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection);
 }
 
+gboolean
+nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave)
+{
+	NMDevicePrivate *priv;
+	NMSettingConnection *s_con;
+	const char *connection_type, *slave_type;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (slave), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->is_master)
+		return FALSE;
+
+	/* All masters should have connection type set */
+	connection_type = NM_DEVICE_GET_CLASS (self)->connection_type;
+	g_return_val_if_fail (connection_type, FALSE);
+
+	s_con = nm_connection_get_setting_connection (slave);
+	g_assert (s_con);
+	slave_type = nm_setting_connection_get_slave_type (s_con);
+	if (!slave_type)
+		return FALSE;
+
+	return strcmp (connection_type, slave_type) == 0;
+}
+
 /**
  * nm_device_can_assume_connections:
  * @self: #NMDevice instance
@@ -2441,7 +3058,8 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection)
 static gboolean
 nm_device_can_assume_connections (NMDevice *self)
 {
-	return !!NM_DEVICE_GET_CLASS (self)->update_connection;
+	return   !!NM_DEVICE_GET_CLASS (self)->update_connection
+	      && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 }
 
 /**
@@ -2481,7 +3099,7 @@ nm_device_can_assume_active_connection (NMDevice *self)
 	if (!nm_device_can_assume_connections (self))
 		return FALSE;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return FALSE;
 
@@ -2505,9 +3123,14 @@ nm_device_can_assume_active_connection (NMDevice *self)
 }
 
 static gboolean
-nm_device_emit_recheck_assume (gpointer self)
+nm_device_emit_recheck_assume (gpointer user_data)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->recheck_assume_id = 0;
 	if (!nm_device_get_act_request (self)) {
@@ -2522,7 +3145,8 @@ nm_device_queue_recheck_assume (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (nm_device_can_assume_connections (self) && !priv->recheck_assume_id)
+	if (   !priv->recheck_assume_id
+	    && nm_device_can_assume_connections (self))
 		priv->recheck_assume_id = g_idle_add (nm_device_emit_recheck_assume, self);
 }
 
@@ -2544,13 +3168,17 @@ recheck_available (gpointer user_data)
 		new_state = NM_DEVICE_STATE_UNAVAILABLE;
 		nm_device_queue_state (self, new_state, priv->recheck_available.unavailable_reason);
 	}
-	_LOGD (LOGD_DEVICE, "device is %savailable, %s %s",
-	       now_available ? "" : "not ",
-	       new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to",
-	       state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state));
 
-	priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE;
-	priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE;
+	if (new_state > NM_DEVICE_STATE_UNKNOWN) {
+		_LOGD (LOGD_DEVICE, "device is %savailable, %s %s",
+			   now_available ? "" : "not ",
+			   new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to",
+			   state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state));
+
+		priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE;
+		priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE;
+	}
+
 	return G_SOURCE_REMOVE;
 }
 
@@ -2587,63 +3215,119 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us
 	}
 }
 
-static void
-activation_source_clear (NMDevice *self, gboolean remove_source, int family)
+/*****************************************************************************/
+
+static gboolean
+activation_source_handle_cb4 (gpointer user_data)
+{
+	activation_source_handle_cb (user_data, AF_INET);
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+activation_source_handle_cb6 (gpointer user_data)
+{
+	activation_source_handle_cb (user_data, AF_INET6);
+	return G_SOURCE_REMOVE;
+}
+
+static ActivationHandleData *
+activation_source_get_by_family (NMDevice *self,
+                                 int family,
+                                 GSourceFunc *out_idle_func)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	guint *act_source_id;
-	gpointer *act_source_func;
 
 	if (family == AF_INET6) {
-		act_source_id = &priv->act_source6_id;
-		act_source_func = &priv->act_source6_func;
+		NM_SET_OUT (out_idle_func, activation_source_handle_cb6);
+		return &priv->act_handle6;
 	} else {
-		act_source_id = &priv->act_source_id;
-		act_source_func = &priv->act_source_func;
+		NM_SET_OUT (out_idle_func, activation_source_handle_cb4);
+		g_return_val_if_fail (family == AF_INET, &priv->act_handle4);
+		return &priv->act_handle4;
 	}
+}
 
-	if (*act_source_id) {
-		if (remove_source)
-			g_source_remove (*act_source_id);
-		*act_source_id = 0;
-		*act_source_func = NULL;
+static void
+activation_source_clear (NMDevice *self, int family)
+{
+	ActivationHandleData *act_data;
+
+	act_data = activation_source_get_by_family (self, family, NULL);
+
+	if (act_data->id) {
+		_LOGD (LOGD_DEVICE, "activation-stage: clear %s,%d (id %u)",
+		       _activation_func_to_string (act_data->func), family, act_data->id);
+		nm_clear_g_source (&act_data->id);
+		act_data->func = NULL;
 	}
 }
 
 static void
-activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
+activation_source_handle_cb (NMDevice *self, int family)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	guint *act_source_id;
-	gpointer *act_source_func;
+	ActivationHandleData *act_data, a;
 
-	if (family == AF_INET6) {
-		act_source_id = &priv->act_source6_id;
-		act_source_func = &priv->act_source6_func;
-	} else {
-		act_source_id = &priv->act_source_id;
-		act_source_func = &priv->act_source_func;
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	act_data = activation_source_get_by_family (self, family, NULL);
+
+	g_return_if_fail (act_data->id);
+	g_return_if_fail (act_data->func);
+
+	a = *act_data;
+
+	act_data->func = NULL;
+	act_data->id = 0;
+
+	_LOGD (LOGD_DEVICE, "activation-stage: invoke %s,%d (id %u)",
+	       _activation_func_to_string (a.func), family, a.id);
+
+	a.func (self);
+
+	_LOGD (LOGD_DEVICE, "activation-stage: complete %s,%d (id %u)",
+	       _activation_func_to_string (a.func), family, a.id);
+}
+
+static void
+activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int family)
+{
+	ActivationHandleData *act_data;
+	GSourceFunc source_func;
+	guint new_id = 0;
+
+	act_data = activation_source_get_by_family (self, family, &source_func);
+
+	if (act_data->id && act_data->func != func) {
+		/* Don't bother rescheduling the same function that's about to
+		 * run anyway.  Fixes issues with crappy wireless drivers sending
+		 * streams of associate events before NM has had a chance to process
+		 * the first one.
+		 */
+		_LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,%d (id %u)",
+		       _activation_func_to_string (func), family, act_data->id);
+		return;
 	}
 
-	if (*act_source_id) {
-		if (*act_source_func == func) {
-			/* Don't bother rescheduling the same function that's about to
-			 * run anyway.  Fixes issues with crappy wireless drivers sending
-			 * streams of associate events before NM has had a chance to process
-			 * the first one.
-			 */
-			_LOGD (LOGD_DEVICE, "activation stage already scheduled");
-			return;
-		} else {
-			_LOGW (LOGD_DEVICE, "a different activation stage already scheduled");
-			activation_source_clear (self, TRUE, family);
-		}
+	new_id = g_idle_add (source_func, self);
+
+	if (act_data->id) {
+		_LOGW (LOGD_DEVICE, "activation-stage: schedule %s,%d which replaces %s,%d (id %u -> %u)",
+		       _activation_func_to_string (func), family,
+		       _activation_func_to_string (act_data->func), family,
+		       act_data->id, new_id);
+		nm_clear_g_source (&act_data->id);
+	} else {
+		_LOGD (LOGD_DEVICE, "activation-stage: schedule %s,%d (id %u)",
+		       _activation_func_to_string (func), family, new_id);
 	}
 
-	*act_source_id = g_idle_add (func, self);
-	*act_source_func = func;
+	act_data->func = func;
+	act_data->id = new_id;
 }
 
+/*****************************************************************************/
+
 static gboolean
 get_ip_config_may_fail (NMDevice *self, int family)
 {
@@ -2652,7 +3336,7 @@ get_ip_config_may_fail (NMDevice *self, int family)
 
 	g_return_val_if_fail (self != NULL, TRUE);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	/* Fail the connection if the failed IP method is required to complete */
@@ -2675,26 +3359,31 @@ master_ready (NMDevice *self,
               NMActiveConnection *active)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActiveConnection *master;
+	NMActiveConnection *master_connection;
+	NMDevice *master;
 
 	g_return_if_fail (priv->state == NM_DEVICE_STATE_PREPARE);
 	g_return_if_fail (!priv->master_ready_handled);
 
 	/* Notify a master device that it has a new slave */
 	g_return_if_fail (nm_active_connection_get_master_ready (active));
-	master = nm_active_connection_get_master (active);
+	master_connection = nm_active_connection_get_master (active);
 
 	priv->master_ready_handled = TRUE;
 	nm_clear_g_signal_handler (active, &priv->master_ready_id);
 
-	priv->master = g_object_ref (nm_active_connection_get_device (master));
-	nm_device_master_add_slave (priv->master,
-	                            self,
-	                            nm_active_connection_get_assumed (active) ? FALSE : TRUE);
+	master = nm_active_connection_get_device (master_connection);
 
 	_LOGD (LOGD_DEVICE, "master connection ready; master device %s",
-	       nm_device_get_iface (priv->master));
+	       nm_device_get_iface (master));
+
+	if (priv->master && priv->master != master)
+		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
+	/* If the master didn't change, add-slave only rechecks whether to assume a connection. */
+	nm_device_master_add_slave (master,
+	                            self,
+	                            nm_active_connection_get_assumed (active) ? FALSE : TRUE);
 }
 
 static void
@@ -2706,6 +3395,45 @@ master_ready_cb (NMActiveConnection *active,
 	nm_device_activate_schedule_stage2_device_config (self);
 }
 
+static void
+lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
+                        gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_LLDP_NEIGHBORS);
+}
+
+static gboolean
+lldp_rx_enabled (NMDevice *self)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingConnectionLldp lldp = NM_SETTING_CONNECTION_LLDP_DEFAULT;
+
+	connection = nm_device_get_applied_connection (self);
+	g_return_val_if_fail (connection, FALSE);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_return_val_if_fail (s_con, FALSE);
+
+	lldp = nm_setting_connection_get_lldp (s_con);
+	if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) {
+		gs_free char *value = NULL;
+
+		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+		                                               "connection.lldp",
+		                                               self);
+		lldp = _nm_utils_ascii_str_to_int64 (value, 10,
+		                                     NM_SETTING_CONNECTION_LLDP_DEFAULT,
+		                                     NM_SETTING_CONNECTION_LLDP_ENABLE_RX,
+		                                     NM_SETTING_CONNECTION_LLDP_DEFAULT);
+		if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT)
+			lldp = NM_SETTING_CONNECTION_LLDP_DISABLE;
+	}
+	return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
+}
+
 static NMActStageReturn
 act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
 {
@@ -2713,48 +3441,39 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
 }
 
 /*
- * nm_device_activate_stage1_device_prepare
+ * activate_stage1_device_prepare
  *
  * Prepare for device activation
  *
  */
-static gboolean
-nm_device_activate_stage1_device_prepare (gpointer user_data)
+static void
+activate_stage1_device_prepare (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
 
-	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, 0);
-
 	priv->ip4_state = priv->ip6_state = IP_NONE;
 
 	/* Notify the new ActiveConnection along with the state change */
 	g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
 
-	_LOGD (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 */
 	if (!nm_active_connection_get_assumed (active)) {
 		ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
 		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
-			goto out;
+			return;
 		} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-			goto out;
+			return;
 		}
 		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
 
 	nm_device_activate_schedule_stage2_device_config (self);
-
-out:
-	_LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete.");
-	return FALSE;
 }
 
 
@@ -2774,9 +3493,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0);
-
-	_LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled...");
+	activation_source_schedule (self, activate_stage1_device_prepare, AF_INET);
 }
 
 static NMActStageReturn
@@ -2787,16 +3504,15 @@ act_stage2_config (NMDevice *self, NMDeviceStateReason *reason)
 }
 
 /*
- * nm_device_activate_stage2_device_config
+ * activate_stage2_device_config
  *
  * Determine device parameters and set those on the device, ie
  * for wireless devices, set SSID, keys, etc.
  *
  */
-static gboolean
-nm_device_activate_stage2_device_config (gpointer user_data)
+static void
+activate_stage2_device_config (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -2804,10 +3520,6 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 	NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
 	GSList *iter;
 
-	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, 0);
-
-	_LOGD (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 */
@@ -2817,15 +3529,15 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING);
 			else
 				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-			goto out;
+			return;
 		}
 
 		ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason);
 		if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
-			goto out;
+			return;
 		else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-			goto out;
+			return;
 		}
 		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
@@ -2836,19 +3548,39 @@ nm_device_activate_stage2_device_config (gpointer user_data)
 		NMDeviceState slave_state = nm_device_get_state (info->slave);
 
 		if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
-			nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave));
+			nm_device_master_enslave_slave (self, info->slave, nm_device_get_applied_connection (info->slave));
 		else if (   nm_device_uses_generated_assumed_connection (self)
 		         && slave_state <= NM_DEVICE_STATE_DISCONNECTED)
 			nm_device_queue_recheck_assume (info->slave);
 	}
 
-	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful.");
+	if (lldp_rx_enabled (self)) {
+		gs_free_error GError *error = NULL;
+		gconstpointer addr;
+		size_t addr_length;
 
-	nm_device_activate_schedule_stage3_ip_config_start (self);
+		if (priv->lldp_listener)
+			nm_lldp_listener_stop (priv->lldp_listener);
+		else {
+			priv->lldp_listener = nm_lldp_listener_new ();
+			g_signal_connect (priv->lldp_listener,
+			                  "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+			                  G_CALLBACK (lldp_neighbors_changed),
+			                  self);
+		}
 
-out:
-	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete.");
-	return FALSE;
+		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
+
+		if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
+		                            nm_device_get_iface (self), addr, addr_length, &error))
+			_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
+		else {
+			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",
+			       priv->lldp_listener, error->message);
+		}
+	}
+
+	nm_device_activate_schedule_stage3_ip_config_start (self);
 }
 
 
@@ -2893,56 +3625,74 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 		}
 	}
 
-	activation_source_schedule (self, nm_device_activate_stage2_device_config, 0);
-
-	_LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled...");
+	activation_source_schedule (self, activate_stage2_device_config, AF_INET);
 }
 
-/*********************************************/
-/* avahi-autoipd stuff */
-
+/*
+ * nm_device_check_ip_failed
+ *
+ * Progress the device to appropriate state if both IPv4 and IPv6 failed
+ */
 static void
-aipd_timeout_remove (NMDevice *self)
+nm_device_check_ip_failed (NMDevice *self, gboolean may_fail)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceState state;
 
-	if (priv->aipd_timeout) {
-		g_source_remove (priv->aipd_timeout);
-		priv->aipd_timeout = 0;
+	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);
 }
 
+/*********************************************/
+/* IPv4LL stuff */
+
 static void
-aipd_cleanup (NMDevice *self)
+ipv4ll_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->aipd_watch) {
-		g_source_remove (priv->aipd_watch);
-		priv->aipd_watch = 0;
+	if (priv->ipv4ll) {
+		sd_ipv4ll_set_callback (priv->ipv4ll, NULL, NULL);
+		sd_ipv4ll_stop (priv->ipv4ll);
+		priv->ipv4ll = sd_ipv4ll_unref (priv->ipv4ll);
 	}
 
-	if (priv->aipd_pid > 0) {
-		nm_utils_kill_child_sync (priv->aipd_pid, SIGKILL, LOGD_AUTOIP4, "avahi-autoipd", NULL, 0, 0);
-		priv->aipd_pid = -1;
-	}
-
-	aipd_timeout_remove (self);
+	nm_clear_g_source (&priv->ipv4ll_timeout);
 }
 
 static NMIP4Config *
-aipd_get_ip4_config (NMDevice *self, guint32 lla)
+ipv4ll_get_ip4_config (NMDevice *self, guint32 lla)
 {
 	NMIP4Config *config = NULL;
 	NMPlatformIP4Address address;
 	NMPlatformIP4Route route;
 
-	config = nm_ip4_config_new ();
+	config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 	g_assert (config);
 
 	memset (&address, 0, sizeof (address));
-	address.address = lla;
-	address.plen = 16;
+	nm_platform_ip4_address_set_addr (&address, lla, 16);
 	address.source = NM_IP_CONFIG_SOURCE_IP4LL;
 	nm_ip4_config_add_address (config, &address);
 
@@ -2960,22 +3710,21 @@ aipd_get_ip4_config (NMDevice *self, guint32 lla)
 #define IPV4LL_NETWORK (htonl (0xA9FE0000L))
 #define IPV4LL_NETMASK (htonl (0xFFFF0000L))
 
-void
-nm_device_handle_autoip4_event (NMDevice *self,
-                                const char *event,
-                                const char *address)
+static void
+nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 {
+	NMDevice *self = data;
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection = NULL;
 	const char *method;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
-
-	g_return_if_fail (event != NULL);
+	struct in_addr address;
+	NMIP4Config *config;
+	int r;
 
 	if (priv->act_request == NULL)
 		return;
 
-	connection = nm_act_request_get_connection (priv->act_request);
+	connection = nm_act_request_get_applied_connection (priv->act_request);
 	g_assert (connection);
 
 	/* Ignore if the connection isn't an AutoIP connection */
@@ -2983,86 +3732,62 @@ nm_device_handle_autoip4_event (NMDevice *self,
 	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0)
 		return;
 
-	if (strcmp (event, "BIND") == 0) {
-		guint32 lla;
-		NMIP4Config *config;
-
-		if (inet_pton (AF_INET, address, &lla) <= 0) {
-			_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);
+	switch (event) {
+	case SD_IPV4LL_EVENT_BIND:
+		r = sd_ipv4ll_get_address (ll, &address);
+		if (r < 0) {
+			_LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r);
+			priv->ip4_state = IP_FAIL;
+			nm_device_check_ip_failed (self, FALSE);
 			return;
 		}
 
-		if ((lla & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
-			_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);
+		if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
+			_LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr);
+			priv->ip4_state = IP_FAIL;
+			nm_device_check_ip_failed (self, FALSE);
 			return;
 		}
 
-		config = aipd_get_ip4_config (self, lla);
+		config = ipv4ll_get_ip4_config (self, address.s_addr);
 		if (config == NULL) {
-			_LOGE (LOGD_AUTOIP4, "failed to get autoip config");
-			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+			_LOGE (LOGD_AUTOIP4, "failed to get IPv4LL config");
+			priv->ip4_state = IP_FAIL;
+			nm_device_check_ip_failed (self, FALSE);
 			return;
 		}
 
 		if (priv->ip4_state == IP_CONF) {
-			aipd_timeout_remove (self);
+			nm_clear_g_source (&priv->ipv4ll_timeout);
 			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)) {
+			if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) {
 				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
-				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+				priv->ip4_state = IP_FAIL;
+				nm_device_check_ip_failed (self, FALSE);
 			}
 		} else
 			g_assert_not_reached ();
 
 		g_object_unref (config);
-	} else {
-		_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);
+		break;
+	default:
+		_LOGW (LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event);
+		priv->ip4_state = IP_FAIL;
+		nm_device_check_ip_failed (self, FALSE);
 	}
 }
 
-static void
-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;
-
-	if (!priv->aipd_watch)
-		return;
-	priv->aipd_watch = 0;
-
-	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);
-
-	state = nm_device_get_state (self);
-	if (nm_device_is_activating (self) || (state == NM_DEVICE_STATE_ACTIVATED))
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
-}
-
 static gboolean
-aipd_timeout_cb (gpointer user_data)
+ipv4ll_timeout_cb (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->aipd_timeout) {
-		_LOGI (LOGD_AUTOIP4, "avahi-autoipd timed out.");
-		priv->aipd_timeout = 0;
-		aipd_cleanup (self);
+	if (priv->ipv4ll_timeout) {
+		_LOGI (LOGD_AUTOIP4, "IPv4LL configuration timed out.");
+		priv->ipv4ll_timeout = 0;
+		ipv4ll_cleanup (self);
 
 		if (priv->ip4_state == IP_CONF)
 			nm_device_activate_schedule_ip4_config_timeout (self);
@@ -3071,66 +3796,68 @@ aipd_timeout_cb (gpointer user_data)
 	return FALSE;
 }
 
-/* default to installed helper, but can be modified for testing */
-const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action";
-
 static NMActStageReturn
-aipd_start (NMDevice *self, NMDeviceStateReason *reason)
+ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *argv[6];
-	char *cmdline;
-	const char *aipd_binary;
-	int i = 0;
-	GError *error = NULL;
+	const struct ether_addr *addr;
+	int ifindex, r;
+	size_t addr_len;
 
-	aipd_cleanup (self);
+	ipv4ll_cleanup (self);
 
-	/* Find avahi-autoipd */
-	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;
+	r = sd_ipv4ll_new (&priv->ipv4ll);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: new() failed with error %d", r);
+		goto fail;
 	}
 
-	argv[i++] = aipd_binary;
-	argv[i++] = "--script";
-	argv[i++] = nm_device_autoipd_helper_path;
+	r = sd_ipv4ll_attach_event (priv->ipv4ll, NULL, 0);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: attach_event() failed with error %d", r);
+		goto fail;
+	}
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_AUTOIP4))
-		argv[i++] = "--debug";
-	argv[i++] = nm_device_get_ip_iface (self);
-	argv[i++] = NULL;
+	ifindex = nm_device_get_ip_ifindex (self);
+	addr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addr_len);
+	if (!addr || addr_len != ETH_ALEN) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: can't retrieve hardware address");
+		goto fail;
+	}
 
-	cmdline = g_strjoinv (" ", (char **) argv);
-	_LOGD (LOGD_AUTOIP4, "running: %s", cmdline);
-	g_free (cmdline);
+	r = sd_ipv4ll_set_mac (priv->ipv4ll, addr);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_mac() failed with error %d", r);
+		goto fail;
+	}
 
-	if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    nm_utils_setpgid, NULL, &(priv->aipd_pid), &error)) {
-		_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;
+	r = sd_ipv4ll_set_index (priv->ipv4ll, ifindex);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_index() failed with error %d", r);
+		goto fail;
 	}
 
-	_LOGD (LOGD_DEVICE | LOGD_AUTOIP4,
-	       "Activation: Stage 3 of 5 (IP Configure Start) started"
-	       " avahi-autoipd...");
+	r = sd_ipv4ll_set_callback (priv->ipv4ll, nm_device_handle_ipv4ll_event, self);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_callback() failed with error %d", r);
+		goto fail;
+	}
+
+	r = sd_ipv4ll_start (priv->ipv4ll);
+	if (r < 0) {
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: start() failed with error %d", r);
+		goto fail;
+	}
 
-	/* Monitor the child process so we know when it dies */
-	priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self);
+	_LOGI (LOGD_DEVICE | LOGD_AUTOIP4, "IPv4LL: started");
 
 	/* Start a timeout to bound the address attempt */
-	priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self);
+	priv->ipv4ll_timeout = g_timeout_add_seconds (20, ipv4ll_timeout_cb, self);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
+fail:
+	*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
+	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 /*********************************************/
@@ -3185,16 +3912,17 @@ static void
 ensure_con_ip4_config (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ip_ifindex = nm_device_get_ip_ifindex (self);
 	NMConnection *connection;
 
 	if (priv->con_ip4_config)
 		return;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 
-	priv->con_ip4_config = nm_ip4_config_new ();
+	priv->con_ip4_config = nm_ip4_config_new (ip_ifindex);
 	nm_ip4_config_merge_setting (priv->con_ip4_config,
 	                             nm_connection_get_setting_ip4_config (connection),
 	                             nm_device_get_ip4_route_metric (self));
@@ -3210,16 +3938,17 @@ static void
 ensure_con_ip6_config (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ip_ifindex = nm_device_get_ip_ifindex (self);
 	NMConnection *connection;
 
 	if (priv->con_ip6_config)
 		return;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 
-	priv->con_ip6_config = nm_ip6_config_new ();
+	priv->con_ip6_config = nm_ip6_config_new (ip_ifindex);
 	nm_ip6_config_merge_setting (priv->con_ip6_config,
 	                             nm_connection_get_setting_ip6_config (connection),
 	                             nm_device_get_ip6_route_metric (self));
@@ -3243,10 +3972,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	if (priv->dhcp4_client) {
 		/* Stop any ongoing DHCP transaction on this device */
-		if (priv->dhcp4_state_sigid) {
-			g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid);
-			priv->dhcp4_state_sigid = 0;
-		}
+		nm_clear_g_signal_handler (priv->dhcp4_client, &priv->dhcp4_state_sigid);
 
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 
@@ -3258,11 +3984,20 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	}
 
 	if (priv->dhcp4_config) {
-		g_clear_object (&priv->dhcp4_config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp4_config);
 		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
 	}
 }
 
+static void
+_ip4_config_merge_default (gpointer value, gpointer user_data)
+{
+	NMIP4Config *src = (NMIP4Config *) value;
+	NMIP4Config *dst = (NMIP4Config *) user_data;
+
+	nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT);
+}
+
 static gboolean
 ip4_config_merge_and_apply (NMDevice *self,
                             NMIP4Config *config,
@@ -3288,7 +4023,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	}
 
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (connection) {
 		NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
@@ -3298,7 +4033,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 		}
 	}
 
-	composite = nm_ip4_config_new ();
+	composite = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 
 	if (commit)
 		ensure_con_ip4_config (self);
@@ -3308,8 +4043,9 @@ ip4_config_merge_and_apply (NMDevice *self,
 		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
 		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
-	if (priv->vpn4_config)
-		nm_ip4_config_merge (composite, priv->vpn4_config, NM_IP_CONFIG_MERGE_DEFAULT);
+
+	g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite);
+
 	if (priv->ext_ip4_config)
 		nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
@@ -3327,7 +4063,6 @@ ip4_config_merge_and_apply (NMDevice *self,
 	if (priv->con_ip4_config)
 		nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
-
 	/* Add the default route.
 	 *
 	 * We keep track of the default route of a device in a private field.
@@ -3366,7 +4101,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 
 	if (   !priv->v4_commit_first_time
 	    && connection_is_never_default) {
-		/* If the connection is explicitly configured as never-default, we enforce the (absense of the)
+		/* If the connection is explicitly configured as never-default, we enforce the (absence of the)
 		 * default-route only once. That allows the user to configure a connection as never-default,
 		 * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */
 		goto END_ADD_DEFAULT_ROUTE;
@@ -3390,7 +4125,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 		goto END_ADD_DEFAULT_ROUTE;
 
 	has_direct_route = (   gateway == 0
-	                    || nm_ip4_config_get_subnet_for_host (composite, gateway)
+	                    || nm_ip4_config_destination_is_direct (composite, gateway, 32)
 	                    || nm_ip4_config_get_direct_route_for_host (composite, gateway));
 
 	priv->default_route.v4_has = TRUE;
@@ -3452,7 +4187,8 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 	} else {
 		/* Notify dispatcher scripts of new DHCP4 config */
 		nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
-		                    nm_device_get_connection (self),
+		                    nm_device_get_settings_connection (self),
+		                    nm_device_get_applied_connection (self),
 		                    self,
 		                    NULL,
 		                    NULL,
@@ -3472,7 +4208,7 @@ dhcp4_restart_cb (gpointer user_data)
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	priv->dhcp4_restart_id = 0;
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 
 	if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE)
 		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
@@ -3517,22 +4253,6 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 }
 
 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,
                      NMIP4Config *ip4_config,
@@ -3558,7 +4278,8 @@ dhcp4_state_changed (NMDhcpClient *client,
 			break;
 		}
 
-		dhcp4_update_config (self, priv->dhcp4_config, options);
+		nm_dhcp4_config_set_options (priv->dhcp4_config, options);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
 
 		if (priv->ip4_state == IP_CONF)
 			nm_device_activate_schedule_ip4_config_result (self, ip4_config);
@@ -3584,6 +4305,28 @@ dhcp4_state_changed (NMDhcpClient *client,
 	}
 }
 
+static int
+dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gs_free char *value = NULL;
+	int timeout;
+
+	timeout = nm_setting_ip4_config_get_dhcp_timeout (s_ip4);
+	if (timeout)
+		return timeout;
+
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               "ipv4.dhcp-timeout",
+	                                               self);
+	timeout = _nm_utils_ascii_str_to_int64 (value, 10,
+	                                        0, G_MAXINT32, 0);
+	if (timeout)
+		return timeout;
+
+	return priv->dhcp_timeout;
+}
+
 static NMActStageReturn
 dhcp4_start (NMDevice *self,
              NMConnection *connection,
@@ -3598,8 +4341,7 @@ dhcp4_start (NMDevice *self,
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
 	/* Clear old exported DHCP options */
-	if (priv->dhcp4_config)
-		g_object_unref (priv->dhcp4_config);
+	nm_exported_object_clear_and_unexport (&priv->dhcp4_config);
 	priv->dhcp4_config = nm_dhcp4_config_new ();
 
 	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
@@ -3618,8 +4360,9 @@ dhcp4_start (NMDevice *self,
 	                                                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_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)),
 	                                                nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)),
-	                                                priv->dhcp_timeout,
+	                                                dhcp4_get_timeout (self, NM_SETTING_IP4_CONFIG (s_ip4)),
 	                                                priv->dhcp_anycast_address,
 	                                                NULL);
 
@@ -3657,7 +4400,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	/* Terminate old DHCP instance and release the old lease */
 	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	/* Start DHCP again on the interface */
@@ -3687,10 +4430,11 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
 	if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) {
 		/* Use the first user-supplied address */
 		NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0);
+		in_addr_t a;
 
 		g_assert (user);
-		nm_ip_address_get_address_binary (user, &address->address);
-		address->plen = nm_ip_address_get_prefix (user);
+		nm_ip_address_get_address_binary (user, &a);
+		nm_platform_ip4_address_set_addr (address, a, 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 */
@@ -3703,8 +4447,7 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
 				return FALSE;
 			}
 		}
-		address->address = start + count;
-		address->plen = 24;
+		nm_platform_ip4_address_set_addr (address, start + count, 24);
 
 		g_hash_table_insert (shared_ips,
 		                     GUINT_TO_POINTER (address->address),
@@ -3727,7 +4470,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso
 		return NULL;
 	}
 
-	config = nm_ip4_config_new ();
+	config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 	address.source = NM_IP_CONFIG_SOURCE_SHARED;
 	nm_ip4_config_add_address (config, &address);
 
@@ -3852,7 +4595,7 @@ act_stage3_ip4_config_start (NMDevice *self,
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	if (   connection_ip4_method_requires_carrier (connection, NULL)
@@ -3884,10 +4627,10 @@ act_stage3_ip4_config_start (NMDevice *self,
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
 		ret = dhcp4_start (self, connection, reason);
 	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0)
-		ret = aipd_start (self, reason);
+		ret = ipv4ll_start (self, reason);
 	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
 		/* Use only IPv4 config from the connection data */
-		*out_config = nm_ip4_config_new ();
+		*out_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 		g_assert (*out_config);
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
@@ -3917,14 +4660,10 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
 	g_clear_object (&priv->dhcp6_ip6_config);
 	g_clear_pointer (&priv->dhcp6_event_id, g_free);
-
 	nm_clear_g_source (&priv->dhcp6_restart_id);
 
 	if (priv->dhcp6_client) {
-		if (priv->dhcp6_state_sigid) {
-			g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid);
-			priv->dhcp6_state_sigid = 0;
-		}
+		nm_clear_g_signal_handler (priv->dhcp6_client, &priv->dhcp6_state_sigid);
 
 		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
 		    || cleanup_type == CLEANUP_TYPE_REMOVED)
@@ -3936,11 +4675,20 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
 
 	if (priv->dhcp6_config) {
-		g_clear_object (&priv->dhcp6_config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp6_config);
 		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
 	}
 }
 
+static void
+_ip6_config_merge_default (gpointer value, gpointer user_data)
+{
+	NMIP6Config *src = (NMIP6Config *) value;
+	NMIP6Config *dst = (NMIP6Config *) user_data;
+
+	nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT);
+}
+
 static gboolean
 ip6_config_merge_and_apply (NMDevice *self,
                             gboolean commit,
@@ -3958,7 +4706,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 	gboolean ignore_auto_dns = FALSE;
 
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (connection) {
 		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
@@ -3969,10 +4717,11 @@ ip6_config_merge_and_apply (NMDevice *self,
 	}
 
 	/* If no config was passed in, create a new one */
-	composite = nm_ip6_config_new ();
+	composite = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
 
 	if (commit)
 		ensure_con_ip6_config (self);
+	g_assert (composite);
 
 	/* Merge all the IP configs into the composite config */
 	if (priv->ac_ip6_config) {
@@ -3985,8 +4734,9 @@ ip6_config_merge_and_apply (NMDevice *self,
 		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
 		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
-	if (priv->vpn6_config)
-		nm_ip6_config_merge (composite, priv->vpn6_config, NM_IP_CONFIG_MERGE_DEFAULT);
+
+	g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite);
+
 	if (priv->ext_ip6_config)
 		nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
@@ -4121,7 +4871,7 @@ static void
 dhcp6_lease_change (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection;
+	NMSettingsConnection *settings_connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	if (priv->dhcp6_ip6_config == NULL) {
@@ -4132,8 +4882,8 @@ dhcp6_lease_change (NMDevice *self)
 
 	g_assert (priv->dhcp6_client);  /* sanity check */
 
-	connection = nm_device_get_connection (self);
-	g_assert (connection);
+	settings_connection = nm_device_get_settings_connection (self);
+	g_assert (settings_connection);
 
 	/* Apply the updated config */
 	if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) {
@@ -4141,7 +4891,10 @@ dhcp6_lease_change (NMDevice *self)
 		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, self, NULL, NULL, NULL);
+		nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
+		                    settings_connection,
+		                    nm_device_get_applied_connection (self),
+		                    self, NULL, NULL, NULL);
 	}
 }
 
@@ -4221,22 +4974,6 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client)
 }
 
 static void
-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,
@@ -4273,7 +5010,7 @@ dhcp6_state_changed (NMDhcpClient *client,
 			if (ip6_config) {
 				priv->dhcp6_ip6_config = g_object_ref (ip6_config);
 				priv->dhcp6_event_id = g_strdup (event_id);
-				dhcp6_update_config (self, priv->dhcp6_config, options);
+				nm_dhcp6_config_set_options (priv->dhcp6_config, options);
 				g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
 			}
 		}
@@ -4320,6 +5057,9 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	GByteArray *tmp = NULL;
 	const guint8 *hw_addr;
 	size_t hw_addr_len = 0;
+	const struct in6_addr *ll_addr = NULL;
+	NMIP6Config *ip6_config;
+	int i;
 
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
@@ -4331,10 +5071,22 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
 	}
 
+	ip6_config = priv->ext_ip6_config;
+	for (i = 0; ip6_config && 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)) {
+			ll_addr = &addr->address;
+			break;
+		}
+	}
+	g_return_val_if_fail (ll_addr, FALSE);
+
 	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,
+	                                                ll_addr,
 	                                                nm_connection_get_uuid (connection),
 	                                                nm_device_get_ip6_route_metric (self),
 	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip6),
@@ -4363,14 +5115,14 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip6;
 
-	g_clear_object (&priv->dhcp6_config);
+	nm_exported_object_clear_and_unexport (&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);
 	g_clear_pointer (&priv->dhcp6_event_id, g_free);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	if (!nm_setting_ip_config_get_may_fail (s_ip6) ||
@@ -4443,10 +5195,14 @@ linklocal6_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->linklocal6_timeout_id) {
-		g_source_remove (priv->linklocal6_timeout_id);
-		priv->linklocal6_timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->linklocal6_timeout_id);
+}
+
+static void
+linklocal6_failed (NMDevice *self)
+{
+	linklocal6_cleanup (self);
+	nm_device_activate_schedule_ip6_config_timeout (self);
 }
 
 static gboolean
@@ -4454,11 +5210,8 @@ linklocal6_timeout_cb (gpointer user_data)
 {
 	NMDevice *self = user_data;
 
-	linklocal6_cleanup (self);
-
 	_LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout");
-
-	nm_device_activate_schedule_ip6_config_timeout (self);
+	linklocal6_failed (self);
 	return G_SOURCE_REMOVE;
 }
 
@@ -4474,7 +5227,7 @@ linklocal6_complete (NMDevice *self)
 
 	linklocal6_cleanup (self);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
@@ -4502,9 +5255,11 @@ 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;
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6 = NULL;
+	GError *error = NULL;
 
 	if (priv->nm_ipv6ll == FALSE)
 		return;
@@ -4515,27 +5270,59 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 			const NMPlatformIP6Address *addr;
 
 			addr = nm_ip6_config_get_address (priv->ip6_config, i);
-			if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
+			if (   IN6_IS_ADDR_LINKLOCAL (&addr->address)
+			    && !(addr->flags & IFA_F_DADFAILED)) {
 				/* 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));
+
+	connection = nm_device_get_applied_connection (self);
+	if (connection)
+		s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
+
+	if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr,
+		                                            nm_device_get_iface (self),
+			                                    nm_connection_get_uuid (connection),
+		                                            priv->linklocal6_dad_counter++,
+			                                    &error)) {
+			_LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message);
+			g_clear_error (&error);
+			linklocal6_failed (self);
+			return;
+		}
+		_LOGD (LOGD_IP6, "linklocal6: using IPv6 stable-privacy addressing");
+	} else {
+		NMUtilsIPv6IfaceId iid;
+
+		if (priv->linklocal6_timeout_id) {
+			/* We already started and attempt to add a LL address. For the EUI-64
+			 * mode we can't pick a new one, we'll just fail. */
+			_LOGW (LOGD_IP6, "linklocal6: DAD failed for an EUI-64 address");
+			linklocal6_failed (self);
+			return;
+		}
+
+		if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+			_LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue");
+			return;
+		}
+		_LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address");
+
+		nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
+	}
+
+	_LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
 	if (!nm_platform_ip6_address_add (NM_PLATFORM_GET,
 	                                  ip_ifindex,
 	                                  lladdr,
-	                                  in6addr_any,
 	                                  64,
+	                                  in6addr_any,
 	                                  NM_PLATFORM_LIFETIME_PERMANENT,
 	                                  NM_PLATFORM_LIFETIME_PERMANENT,
 	                                  0)) {
@@ -4556,7 +5343,7 @@ linklocal6_start (NMDevice *self)
 	if (have_ip6_address (priv->ip6_config, TRUE))
 		return NM_ACT_STAGE_RETURN_FINISH;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
@@ -4576,53 +5363,6 @@ linklocal6_start (NMDevice *self)
 
 /******************************************/
 
-static void
-print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr)
-{
-	static gint8 warn = 0;
-	static gint8 s_libnl = -1, s_kernel;
-
-	if (warn >= 2)
-		return;
-
-	if (s_libnl == -1) {
-		s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags ();
-		s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
-
-		if (s_libnl && s_kernel) {
-			nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)");
-			warn = 2;
-			return;
-		}
-	}
-
-	if (   use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
-	    && use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) {
-		if (warn == 0) {
-			nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)",
-			                      !s_kernel ? "kernel" : "",
-			                      !s_kernel && !s_libnl ? " and " : "",
-			                      !s_libnl ? "libnl" : "",
-			                      !s_kernel && !s_libnl ? "do" : "does");
-			warn = 1;
-		}
-		return;
-	}
-
-	if (!s_libnl && !s_kernel) {
-		nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for "
-		                       "IPv6 private addresses. This feature is not available");
-	} else if (!s_libnl) {
-		nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for "
-		                       "IPv6 private addresses. This feature is not available");
-	} else if (!s_kernel) {
-		nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
-		                       "IPv6 private addresses. This feature is not available");
-	}
-
-	warn = 2;
-}
-
 static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu);
 
 static void
@@ -4680,24 +5420,20 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int i;
-	static int system_support = -1;
+	int system_support;
 	guint ifa_flags = 0x00;
 
-	if (system_support == -1) {
-		/*
-		 * Check, if both libnl and the kernel are recent enough,
-		 * to help user space handling RA. If it's not supported,
-		 * we have no ipv6-privacy and must add autoconf addresses
-		 * as /128. The reason for the /128 is to prevent the kernel
-		 * from adding a prefix route for this address.
-		 **/
-		system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
-		                 nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
-	}
+	/*
+	 * Check, whether kernel is recent enough to help user space handling RA.
+	 * If it's not supported, we have no ipv6-privacy and must add autoconf
+	 * addresses as /128. The reason for the /128 is to prevent the kernel
+	 * from adding a prefix route for this address.
+	 **/
+	system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
 
 	if (system_support)
 		ifa_flags = IFA_F_NOPREFIXROUTE;
-	if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
+	if (   priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
 	    || priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
 	{
 		/* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
@@ -4707,7 +5443,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 	g_return_if_fail (priv->act_request);
 
 	if (!priv->ac_ip6_config)
-		priv->ac_ip6_config = nm_ip6_config_new ();
+		priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
 
 	if (changed & NM_RDISC_CONFIG_GATEWAYS) {
 		/* Use the first gateway as ordered in router discovery cache. */
@@ -4805,10 +5541,11 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 			       "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)
+				if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
 					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+					return;
+				}
 			}
-			return;
 		}
 	}
 
@@ -4854,10 +5591,15 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	g_assert (priv->rdisc);
 
 	if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) {
-		_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
-	} else if (!nm_device_get_ip_iface_identifier (self, &iid)) {
-		_LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue");
-		return FALSE;
+		_LOGD (LOGD_IP6, "addrconf6: IPv6 tokenized identifier present");
+		nm_rdisc_set_iid (priv->rdisc, iid);
+	} else if (nm_device_get_ip_iface_identifier (self, &iid)) {
+		_LOGD (LOGD_IP6, "addrconf6: using the device EUI-64 identifier");
+		nm_rdisc_set_iid (priv->rdisc, iid);
+	} else {
+		/* Don't abort the addrconf at this point -- if rdisc needs the iid
+		 * it will notice this itself. */
+		_LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 adddress creation may fail");
 	}
 
 	/* Apply any manual configuration before starting RA */
@@ -4878,7 +5620,6 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	                                           G_CALLBACK (rdisc_ra_timeout),
 	                                           self);
 
-	nm_rdisc_set_iid (priv->rdisc, iid);
 	nm_rdisc_start (priv->rdisc);
 	return TRUE;
 }
@@ -4889,9 +5630,10 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	NMActStageReturn ret;
-	const char *ip_iface = nm_device_get_ip_iface (self);
+	NMSettingIP6Config *s_ip6 = NULL;
+	GError *error = NULL;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	g_warn_if_fail (priv->ac_ip6_config == NULL);
@@ -4900,14 +5642,27 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 		priv->ac_ip6_config = NULL;
 	}
 
-	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface);
+	s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
+	g_assert (s_ip6);
+
+	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self),
+	                                 nm_device_get_ip_iface (self),
+	                                 nm_connection_get_uuid (connection),
+	                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
+	                                 &error);
 	if (!priv->rdisc) {
-		_LOGE (LOGD_IP6, "failed to start router discovery (%s)", ip_iface);
+		_LOGE (LOGD_IP6, "addrconf6: failed to start router discovery: %s", error->message);
+		g_error_free (error);
 		return FALSE;
 	}
 
 	priv->rdisc_use_tempaddr = use_tempaddr;
-	print_support_extended_ifa_flags (use_tempaddr);
+
+	if (   NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
+	    && !nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) {
+		_LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
+		                 "IPv6 private addresses. This feature is not available");
+	}
 
 	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);
@@ -4929,15 +5684,8 @@ addrconf6_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	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_clear_g_signal_handler (priv->rdisc, &priv->rdisc_changed_id);
+	nm_clear_g_signal_handler (priv->rdisc, &priv->rdisc_timeout_id);
 
 	nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE);
 
@@ -5060,7 +5808,7 @@ _ip6_privacy_get (NMDevice *self)
 
 	/* 1.) First look at the per-connection setting. If it is not -1 (unknown),
 	 * use it. */
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (connection) {
 		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
 
@@ -5126,7 +5874,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	if (   connection_ip6_method_requires_carrier (connection, NULL)
@@ -5208,7 +5956,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 			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 ();
+		*out_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
 		g_assert (*out_config);
 
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
@@ -5318,61 +6066,20 @@ 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
+ * activate_stage3_ip_config_start
  *
  * Begin automatic/manual IP configuration
  *
  */
-static gboolean
-nm_device_activate_stage3_ip_config_start (gpointer user_data)
+static void
+activate_stage3_ip_config_start (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActiveConnection *master;
 	NMDevice *master_device;
 
-	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, 0);
-
 	priv->ip4_state = priv->ip6_state = IP_WAIT;
 
-	_LOGD (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 */
@@ -5393,50 +6100,75 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 		master_device = nm_active_connection_get_device (master);
 		if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) {
 			_LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'",
-			       nm_connection_get_id (nm_device_get_connection (self)),
+			       nm_connection_get_id (nm_device_get_applied_connection (self)),
 			       master_device ? nm_device_get_iface (master_device) : "(unknown)");
 		}
-		goto out;
+		return;
 	}
 
 	/* IPv4 */
 	if (   nm_device_activate_ip4_state_in_wait (self)
 	    && !nm_device_activate_stage3_ip4_start (self))
-		goto out;
+		return;
 
 	/* IPv6 */
 	if (   nm_device_activate_ip6_state_in_wait (self)
 	    && !nm_device_activate_stage3_ip6_start (self))
-		goto out;
+		return;
 
 	nm_device_check_ip_failed (self, TRUE);
-
-out:
-	_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete.");
-	return FALSE;
 }
 
+static gboolean
+fw_change_zone_handle (NMDevice *self,
+                       NMFirewallManagerCallId call_id,
+                       GError *error)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	g_return_val_if_fail (priv->fw_call == call_id, FALSE);
+	priv->fw_call = NULL;
+
+	return !nm_utils_error_is_cancelled (error, FALSE);
+}
 
 static void
-fw_change_zone_cb (GError *error, gpointer user_data)
+fw_change_zone_cb_stage2 (NMFirewallManager *firewall_manager,
+                          NMFirewallManagerCallId call_id,
+                          GError *error,
+                          gpointer user_data)
 {
-	NMDevice *self;
+	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+	if (!fw_change_zone_handle (self, call_id, error))
 		return;
 
-	self = NM_DEVICE (user_data);
+	/* FIXME: fail the device on error? */
+
 	priv = NM_DEVICE_GET_PRIVATE (self);
+	priv->fw_ready = TRUE;
 
-	priv->fw_call = NULL;
+	nm_device_activate_schedule_stage3_ip_config_start (self);
+}
 
-	if (error) {
-		/* FIXME: fail the device activation? */
-	}
+static void
+fw_change_zone_cb_ip_check (NMFirewallManager *firewall_manager,
+                            NMFirewallManagerCallId call_id,
+                            GError *error,
+                            gpointer user_data)
+{
+	NMDevice *self = user_data;
 
-	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
-	_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
+	if (!fw_change_zone_handle (self, call_id, error))
+		return;
+
+	/* FIXME: fail the device on error? */
+	nm_device_start_ip_check (self);
 }
 
 /*
@@ -5457,29 +6189,31 @@ 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);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 	s_con = nm_connection_get_setting_connection (connection);
 
-	zone = nm_setting_connection_get_zone (s_con);
-
-	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);
-		_LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled.");
-		return;
+	if (!priv->fw_ready) {
+		if (nm_device_uses_assumed_connection (self))
+			priv->fw_ready = TRUE;
+		else {
+			if (!priv->fw_call) {
+				zone = nm_setting_connection_get_zone (s_con);
+
+				_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,
+				                                                        fw_change_zone_cb_stage2,
+				                                                        self);
+			}
+			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,
-	                                                        fw_change_zone_cb,
-	                                                        self);
+	activation_source_schedule (self, activate_stage3_ip_config_start, AF_INET);
 }
 
 static NMActStageReturn
@@ -5499,35 +6233,25 @@ act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
  * Time out on retrieving the IPv4 config.
  *
  */
-static gboolean
-nm_device_activate_ip4_config_timeout (gpointer user_data)
+static void
+activate_stage4_ip4_config_timeout (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	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);
-
-	_LOGD (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)
-		goto out;
+		return;
 	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
+		return;
 	}
-	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 
 	priv->ip4_state = IP_FAIL;
 
 	nm_device_check_ip_failed (self, FALSE);
-
-out:
-	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete.");
-	return FALSE;
 }
 
 
@@ -5547,9 +6271,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET);
-
-	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled...");
+	activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET);
 }
 
 
@@ -5566,40 +6288,30 @@ act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 
 
 /*
- * nm_device_activate_ip6_config_timeout
+ * activate_stage4_ip6_config_timeout
  *
  * Time out on retrieving the IPv6 config.
  *
  */
-static gboolean
-nm_device_activate_ip6_config_timeout (gpointer user_data)
+static void
+activate_stage4_ip6_config_timeout (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	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);
-
-	_LOGD (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)
-		goto out;
-	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+		return;
+	if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
+		return;
 	}
 	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 
 	priv->ip6_state = IP_FAIL;
 
 	nm_device_check_ip_failed (self, FALSE);
-
-out:
-	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete.");
-	return FALSE;
 }
 
 
@@ -5619,9 +6331,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6);
-
-	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled...");
+	activation_source_schedule (self, activate_stage4_ip6_config_timeout, AF_INET6);
 }
 
 static gboolean
@@ -5721,7 +6431,7 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 		return FALSE;
 	}
 
-	priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed",
+	priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, NM_DNS_MASQ_MANAGER_STATE_CHANGED,
 	                                           G_CALLBACK (dnsmasq_state_changed_cb),
 	                                           self);
 	return TRUE;
@@ -5738,7 +6448,7 @@ send_arps (NMDevice *self, const char *mode_arg)
 	NMIPAddress *addr;
 	GError *error = NULL;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -5776,10 +6486,14 @@ send_arps (NMDevice *self, const char *mode_arg)
 }
 
 static gboolean
-arp_announce_round2 (gpointer self)
+arp_announce_round2 (gpointer user_data)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
 
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
 	priv->arp_round2_id = 0;
 
 	if (   priv->state >= NM_DEVICE_STATE_IP_CONFIG
@@ -5794,10 +6508,7 @@ arp_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->arp_round2_id) {
-		g_source_remove (priv->arp_round2_id);
-		priv->arp_round2_id = 0;
-	}
+	nm_clear_g_source (&priv->arp_round2_id);
 }
 
 static void
@@ -5813,7 +6524,7 @@ arp_announce (NMDevice *self)
 	/* We only care about manually-configured addresses; DHCP- and autoip-configured
 	 * ones should already have been seen on the network at this point.
 	 */
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -5827,10 +6538,9 @@ arp_announce (NMDevice *self)
 	priv->arp_round2_id = g_timeout_add_seconds (2, arp_announce_round2, self);
 }
 
-static gboolean
-nm_device_activate_ip4_config_commit (gpointer user_data)
+static void
+activate_stage5_ip4_config_commit (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
 	const char *method;
@@ -5838,14 +6548,9 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 	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);
-
-	_LOGD (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);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	/* Interface must be IFF_UP before IP config can be applied */
@@ -5860,7 +6565,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 	if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
 		_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
+		return;
 	}
 
 	/* Start IPv4 sharing if we need it */
@@ -5870,7 +6575,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 		if (!start_sharing (self, priv->ip4_config)) {
 			_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;
+			return;
 		}
 	}
 
@@ -5882,7 +6587,8 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
 	    && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) {
 		/* Notify dispatcher scripts of new DHCP4 config */
 		nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
-		                    nm_device_get_connection (self),
+		                    nm_device_get_settings_connection (self),
+		                    nm_device_get_applied_connection (self),
 		                    self,
 		                    NULL,
 		                    NULL,
@@ -5898,15 +6604,10 @@ nm_device_activate_ip4_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);
-
-out:
-	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete.");
-
-	return FALSE;
 }
 
 static void
-nm_device_queued_ip_config_change_clear (NMDevice *self)
+queued_ip4_config_change_clear (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
@@ -5915,6 +6616,13 @@ nm_device_queued_ip_config_change_clear (NMDevice *self)
 		g_source_remove (priv->queued_ip4_config_id);
 		priv->queued_ip4_config_id = 0;
 	}
+}
+
+static void
+queued_ip6_config_change_clear (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
 	if (priv->queued_ip6_config_id) {
 		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
 		g_source_remove (priv->queued_ip6_config_id);
@@ -5934,10 +6642,8 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf
 	if (config)
 		priv->dev_ip4_config = g_object_ref (config);
 
-	nm_device_queued_ip_config_change_clear (self);
-	activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET);
-
-	_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled...");
+	queued_ip4_config_change_clear (self);
+	activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET);
 }
 
 gboolean
@@ -5954,24 +6660,18 @@ nm_device_activate_ip4_state_in_wait (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT;
 }
 
-static gboolean
-nm_device_activate_ip6_config_commit (gpointer user_data)
+static void
+activate_stage5_ip6_config_commit (NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
 	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);
-
-	_LOGD (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);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	/* Interface must be IFF_UP before IP config can be applied */
@@ -5983,19 +6683,23 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 	}
 
 	if (ip6_config_merge_and_apply (self, TRUE, &reason)) {
-		/* If IPv6 wasn't the first IP to complete, and DHCP was used,
-		 * then ensure dispatcher scripts get the DHCP lease information.
-		 */
-		if (   priv->dhcp6_client
-		    && nm_device_activate_ip6_state_in_conf (self)
-		    && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) {
-			/* Notify dispatcher scripts of new DHCP6 config */
-			nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
-			                    nm_device_get_connection (self),
-			                    self,
-			                    NULL,
-			                    NULL,
-			                    NULL);
+		if (   priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE
+		    && priv->ip6_state == IP_CONF) {
+			if (priv->dhcp6_ip6_config) {
+				/* If IPv6 wasn't the first IP to complete, and DHCP was used,
+				 * then ensure dispatcher scripts get the DHCP lease information.
+				 */
+				nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
+						    nm_device_get_settings_connection (self),
+						    nm_device_get_applied_connection (self),
+						    self,
+						    NULL,
+						    NULL,
+						    NULL);
+			} else {
+				/* still waiting for first dhcp6 lease. */
+				return;
+			}
 		}
 
 		/* Enter the IP_CHECK state if this is the first method to complete */
@@ -6010,10 +6714,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
 		_LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed");
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	}
-
-	_LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete.");
-
-	return FALSE;
 }
 
 void
@@ -6029,9 +6729,7 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self)
 	if (priv->ip6_state == IP_FAIL)
 		priv->ip6_state = IP_CONF;
 
-	activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6);
-
-	_LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) scheduled...");
+	activation_source_schedule (self, activate_stage5_ip6_config_commit, AF_INET6);
 }
 
 gboolean
@@ -6073,10 +6771,7 @@ dnsmasq_cleanup (NMDevice *self)
 	if (!priv->dnsmasq_manager)
 		return;
 
-	if (priv->dnsmasq_state_id) {
-		g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id);
-		priv->dnsmasq_state_id = 0;
-	}
+	nm_clear_g_signal_handler (priv->dnsmasq_manager, &priv->dnsmasq_state_id);
 
 	nm_dnsmasq_manager_stop (priv->dnsmasq_manager);
 	g_object_unref (priv->dnsmasq_manager);
@@ -6108,14 +6803,6 @@ nm_device_get_is_nm_owned (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned;
 }
 
-void
-nm_device_set_nm_owned (NMDevice *self)
-{
-	g_return_if_fail (NM_IS_DEVICE (self));
-
-	NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE;
-}
-
 /*
  * delete_on_deactivate_link_delete
  *
@@ -6129,16 +6816,21 @@ delete_on_deactivate_link_delete (gpointer user_data)
 	DeleteOnDeactivateData *data = user_data;
 	NMDevice *self = data->device;
 
+	_LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
+	       data->ifindex, data->idle_add_id);
+
 	if (data->device) {
 		NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (data->device);
+		gs_free_error GError *error = NULL;
 
 		g_object_remove_weak_pointer (G_OBJECT (data->device), (void **) &data->device);
 		priv->delete_on_deactivate_data = NULL;
-	}
 
-	_LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
-	       data->ifindex, data->idle_add_id);
-	nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex);
+		if (!nm_device_unrealize (data->device, TRUE, &error))
+			_LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message);
+	} else
+		nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex);
+
 	g_free (data);
 	return FALSE;
 }
@@ -6173,7 +6865,7 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
 		return;
 	if (priv->queued_act_request)
 		return;
-	if (!nm_device_is_software (self))
+	if (!nm_device_is_software (self) || !nm_device_is_real (self))
 		return;
 	if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED)
 		return;
@@ -6193,8 +6885,330 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
 }
 
 static void
+_cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->ip4_state =  IP_NONE;
+	queued_ip4_config_change_clear (self);
+
+	dhcp4_cleanup (self, cleanup_type, FALSE);
+	arp_cleanup (self);
+	dnsmasq_cleanup (self);
+	ipv4ll_cleanup (self);
+}
+
+static void
+_cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	priv->ip6_state = IP_NONE;
+	queued_ip6_config_change_clear (self);
+
+	dhcp6_cleanup (self, cleanup_type, FALSE);
+	linklocal6_cleanup (self);
+	addrconf6_cleanup (self);
+}
+
+G_GNUC_NULL_TERMINATED
+static gboolean
+_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **error, ...)
+{
+	va_list ap;
+	const char *key;
+	guint found_keys = 0;
+
+#if NM_MORE_ASSERTS > 10
+	/* Assert that the keys are unique. */
+	{
+		gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+
+		va_start (ap, error);
+		while ((key = va_arg (ap, const char *))) {
+			if (!g_hash_table_add (check_dups, (char *) key))
+				nm_assert (FALSE);
+		}
+		va_end (ap);
+		nm_assert (g_hash_table_size (check_dups) > 0);
+	}
+#endif
+
+	if (!hash || g_hash_table_size (hash) == 0)
+		return TRUE;
+
+	va_start (ap, error);
+	while ((key = va_arg (ap, const char *))) {
+		if (g_hash_table_contains (hash, key))
+			found_keys++;
+	}
+	va_end (ap);
+
+	if (found_keys != g_hash_table_size (hash)) {
+		GHashTableIter iter;
+		const char *k = NULL;
+		const char *first_invalid_key = NULL;
+
+		if (!error)
+			return FALSE;
+
+		g_hash_table_iter_init (&iter, hash);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) {
+			va_start (ap, error);
+			while ((key = va_arg (ap, const char *))) {
+				if (!strcmp (key, k)) {
+					first_invalid_key = k;
+					break;
+				}
+			}
+			va_end (ap);
+			if (first_invalid_key)
+				break;
+		}
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+		             "Can't reapply changes to '%s%s%s' setting",
+		             setting_name ? : "",
+		             setting_name ? "." : "",
+		             first_invalid_key ? : "<UNKNOWN>");
+		g_return_val_if_fail (first_invalid_key, FALSE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+nm_device_reactivate_ip4_config (NMDevice *self,
+                                 NMSettingIPConfig *s_ip4_old,
+                                 NMSettingIPConfig *s_ip4_new)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->ip4_state != IP_NONE) {
+		g_clear_object (&priv->con_ip4_config);
+		priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
+		nm_ip4_config_merge_setting (priv->con_ip4_config,
+		                             s_ip4_new,
+		                             nm_device_get_ip4_route_metric (self));
+
+		if (strcmp (nm_setting_ip_config_get_method (s_ip4_new),
+		            nm_setting_ip_config_get_method (s_ip4_old))) {
+			_cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE);
+			priv->ip4_state = IP_WAIT;
+			if (!nm_device_activate_stage3_ip4_start (self))
+				_LOGW (LOGD_IP4, "Failed to apply IPv4 configuration");
+		} else
+			ip4_config_merge_and_apply (self, NULL, TRUE, NULL);
+	}
+}
+
+void
+nm_device_reactivate_ip6_config (NMDevice *self,
+                                 NMSettingIPConfig *s_ip6_old,
+                                 NMSettingIPConfig *s_ip6_new)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->ip6_state != IP_NONE) {
+		g_clear_object (&priv->con_ip6_config);
+		priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
+		nm_ip6_config_merge_setting (priv->con_ip6_config,
+		                             s_ip6_new,
+		                             nm_device_get_ip6_route_metric (self));
+
+		if (strcmp (nm_setting_ip_config_get_method (s_ip6_new),
+		            nm_setting_ip_config_get_method (s_ip6_old))) {
+			_cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE);
+			priv->ip6_state = IP_WAIT;
+			if (!nm_device_activate_stage3_ip6_start (self))
+				_LOGW (LOGD_IP6, "Failed to apply IPv6 configuration");
+		} else
+			ip6_config_merge_and_apply (self, TRUE, NULL);
+	}
+}
+
+
+/* reapply_connection:
+ * @connection: the new connection settings to be applied or %NULL to reapply
+ *   the current settings connection
+ * @error: the error if %FALSE is returned
+ *
+ * Change configuration of an already configured device if possible.
+ * Updates the device's applied connection upon success.
+ *
+ * Return: %FALSE if the new configuration can not be reapplied.
+ */
+static gboolean
+reapply_connection (NMDevice *self,
+                    NMConnection *connection,
+                    GError **error)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *applied = nm_device_get_applied_connection (self);
+	gs_unref_object NMConnection *applied_clone = NULL;
+	gs_unref_hashtable GHashTable *diffs = NULL;
+	NMConnection *con_old, *con_new;
+	NMSettingIPConfig *s_ip4_old, *s_ip4_new;
+	NMSettingIPConfig *s_ip6_old, *s_ip6_new;
+
+	if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
+		g_set_error_literal (error,
+		                     NM_DEVICE_ERROR,
+		                     NM_DEVICE_ERROR_NOT_ACTIVE,
+		                     "Device is not activated");
+		return FALSE;
+	}
+
+	nm_connection_diff (connection,
+	                    applied,
+	                    NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP |
+	                    NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS,
+	                    &diffs);
+
+	/**************************************************************************
+	 * check for unsupported changes and reject to reapply
+	 *************************************************************************/
+	if (!_hash_check_invalid_keys (diffs, NULL, error,
+	                               NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	                               NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	                               NM_SETTING_CONNECTION_SETTING_NAME,
+	                               NULL))
+		return FALSE;
+
+	if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL,
+	                               NM_SETTING_CONNECTION_SETTING_NAME,
+	                               error,
+	                               NM_SETTING_CONNECTION_ZONE,
+	                               NM_SETTING_CONNECTION_METERED,
+	                               NULL))
+		return FALSE;
+
+	_LOGD (LOGD_DEVICE, "reapply");
+
+	/**************************************************************************
+	 * Update applied connection
+	 *************************************************************************/
+
+	if (diffs) {
+		con_old = applied_clone  = nm_simple_connection_new_clone (applied);
+		con_new = applied;
+		nm_connection_replace_settings_from_connection (applied, connection);
+	} else
+		con_old = con_new = applied;
+
+	s_ip4_new = nm_connection_get_setting_ip4_config (con_new);
+	s_ip4_old = nm_connection_get_setting_ip4_config (con_old);
+	s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
+	s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
+
+	/**************************************************************************
+	 * Reapply changes
+	 *************************************************************************/
+
+	nm_device_update_firewall_zone (self);
+	nm_device_update_metered (self);
+
+	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new);
+	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new);
+
+	return TRUE;
+}
+
+static void
+reapply_cb (NMDevice *self,
+            GDBusMethodInvocation *context,
+            NMAuthSubject *subject,
+            GError *error,
+            gpointer user_data)
+{
+	gs_unref_object NMConnection *connection = NM_CONNECTION (user_data);
+	GError *local = NULL;
+
+	if (error) {
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message);
+		g_dbus_method_invocation_return_gerror (context, error);
+		return;
+	}
+
+	if (!reapply_connection (self,
+	                         connection ? : (NMConnection *) nm_device_get_settings_connection (self),
+	                         &local)) {
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message);
+		g_dbus_method_invocation_take_error (context, local);
+		local = NULL;
+	} else {
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, subject, NULL);
+		g_dbus_method_invocation_return_value (context, NULL);
+	}
+}
+
+static void
+impl_device_reapply (NMDevice *self,
+                     GDBusMethodInvocation *context,
+                     GVariant *settings,
+                     guint flags)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMSettingsConnection *settings_connection;
+	NMConnection *connection = NULL;
+	GError *error = NULL;
+
+	/* No flags supported as of now. */
+	if (flags != 0) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "Invalid flags specified");
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "Device is not activated");
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	settings_connection = nm_device_get_settings_connection (self);
+	g_return_if_fail (settings_connection);
+
+	if (settings && g_variant_n_children (settings)) {
+		/* New settings specified inline. */
+		connection = nm_simple_connection_new_from_dbus (settings, &error);
+		if (!connection) {
+			g_prefix_error (&error, "The settings specified are invalid: ");
+			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+			g_dbus_method_invocation_take_error (context, error);
+			return;
+		}
+		nm_connection_clear_secrets (connection);
+	}
+
+	/* Ask the manager to authenticate this request for us */
+	g_signal_emit (self, signals[AUTH_REQUEST], 0,
+	               context,
+	               nm_device_get_applied_connection (self),
+	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
+	               TRUE,
+	               reapply_cb,
+	               connection);
+}
+
+static void
 disconnect_cb (NMDevice *self,
-               DBusGMethodInvocation *context,
+               GDBusMethodInvocation *context,
+               NMAuthSubject *subject,
                GError *error,
                gpointer user_data)
 {
@@ -6202,7 +7216,8 @@ disconnect_cb (NMDevice *self,
 	GError *local = NULL;
 
 	if (error) {
-		dbus_g_method_return_error (context, error);
+		g_dbus_method_invocation_return_gerror (context, error);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, error->message);
 		return;
 	}
 
@@ -6211,15 +7226,16 @@ disconnect_cb (NMDevice *self,
 		local = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "Device is not active");
-		dbus_g_method_return_error (context, local);
-		g_error_free (local);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, local->message);
+		g_dbus_method_invocation_take_error (context, local);
 	} else {
 		nm_device_set_autoconnect (self, FALSE);
 
 		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
 		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
-		dbus_g_method_return (context);
+		g_dbus_method_invocation_return_value (context, NULL);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, TRUE, subject, NULL);
 	}
 }
 
@@ -6233,7 +7249,7 @@ _clear_queued_act_request (NMDevicePrivate *priv)
 }
 
 static void
-impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context)
+impl_device_disconnect (NMDevice *self, GDBusMethodInvocation *context)
 {
 	NMConnection *connection;
 	GError *error = NULL;
@@ -6242,12 +7258,11 @@ impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context)
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "This device is not active");
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	/* Ask the manager to authenticate this request for us */
@@ -6262,31 +7277,37 @@ impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context)
 
 static void
 delete_cb (NMDevice *self,
-           DBusGMethodInvocation *context,
+           GDBusMethodInvocation *context,
+           NMAuthSubject *subject,
            GError *error,
            gpointer user_data)
 {
+	GError *local = NULL;
+
 	if (error) {
-		dbus_g_method_return_error (context, error);
+		g_dbus_method_invocation_return_gerror (context, error);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, FALSE, subject, error->message);
 		return;
 	}
 
 	/* Authorized */
-	nm_platform_link_delete (NM_PLATFORM_GET, nm_device_get_ifindex (self));
-	dbus_g_method_return (context);
+	nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, subject, NULL);
+	if (nm_device_unrealize (self, TRUE, &local))
+		g_dbus_method_invocation_return_value (context, NULL);
+	else
+		g_dbus_method_invocation_take_error (context, local);
 }
 
 static void
-impl_device_delete (NMDevice *self, DBusGMethodInvocation *context)
+impl_device_delete (NMDevice *self, GDBusMethodInvocation *context)
 {
 	GError *error = NULL;
 
-	if (!nm_device_is_software (self)) {
+	if (!nm_device_is_software (self) || !nm_device_is_real (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);
+		                             "This device is not a software device or is not realized");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -6317,7 +7338,7 @@ _device_activate (NMDevice *self, NMActRequest *req)
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	_LOGI (LOGD_DEVICE, "Activation: starting connection '%s' (%s)",
@@ -6378,7 +7399,7 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
 	if (priv->carrier_wait_id == 0)
 		return FALSE;
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	if (!connection_requires_carrier (connection))
 		return FALSE;
 
@@ -6408,19 +7429,19 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
 }
 
 void
-nm_device_steal_connection (NMDevice *self, NMConnection *connection)
+nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	_LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.",
-	       nm_connection_get_id (connection));
+	       nm_settings_connection_get_id (connection));
 
 	if (   priv->queued_act_request
-	    && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request)))
+	    && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request)))
 		_clear_queued_act_request (priv);
 
 	if (   priv->act_request
-	    && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request))
+	    && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request))
 	    && priv->state < NM_DEVICE_STATE_DEACTIVATING)
 		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
@@ -6481,7 +7502,7 @@ nm_device_is_activating (NMDevice *self)
 	 * handler is actually run.  If there's an activation handler scheduled
 	 * we're activating anyway.
 	 */
-	return priv->act_source_id ? TRUE : FALSE;
+	return priv->act_handle4.id ? TRUE : FALSE;
 }
 
 /* IP Configuration stuff */
@@ -6516,13 +7537,19 @@ nm_device_set_ip4_config (NMDevice *self,
 	gboolean has_changes = FALSE;
 	gboolean success = TRUE;
 	NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE;
-	int ip_ifindex;
+	int ip_ifindex, config_ifindex;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	ip_ifindex = nm_device_get_ip_ifindex (self);
 
+	if (new_config) {
+		config_ifindex = nm_ip4_config_get_ifindex (new_config);
+		if (config_ifindex > 0)
+			g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE);
+	}
+
 	old_config = priv->ip4_config;
 
 	/* Always commit to nm-platform to update lifetimes */
@@ -6548,25 +7575,23 @@ nm_device_set_ip4_config (NMDevice *self,
 			nm_ip4_config_replace (old_config, new_config, &has_changes);
 			if (has_changes) {
 				_LOGD (LOGD_IP4, "update IP4Config instance (%s)",
-				       nm_ip4_config_get_dbus_path (old_config));
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
 			}
 		} else {
 			has_changes = TRUE;
 			priv->ip4_config = g_object_ref (new_config);
 
-			if (success && !nm_ip4_config_get_dbus_path (new_config)) {
-				/* Export over D-Bus */
-				nm_ip4_config_export (new_config);
-			}
+			if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config)))
+				nm_exported_object_export (NM_EXPORTED_OBJECT (new_config));
 
 			_LOGD (LOGD_IP4, "set IP4Config instance (%s)",
-			       nm_ip4_config_get_dbus_path (new_config));
+			       nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config)));
 		}
 	} else if (old_config) {
 		has_changes = TRUE;
 		priv->ip4_config = NULL;
 		_LOGD (LOGD_IP4, "clear IP4Config instance (%s)",
-		       nm_ip4_config_get_dbus_path (old_config));
+		       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
 		/* Device config is invalid if combined config is invalid */
 		g_clear_object (&priv->dev_ip4_config);
 	}
@@ -6580,17 +7605,26 @@ nm_device_set_ip4_config (NMDevice *self,
 			g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG);
 		g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, old_config);
 
-		if (old_config != priv->ip4_config && old_config)
-			g_object_unref (old_config);
+		if (old_config != priv->ip4_config)
+			nm_exported_object_clear_and_unexport (&old_config);
 
 		if (nm_device_uses_generated_assumed_connection (self)) {
-			NMConnection *connection = nm_device_get_connection (self);
+			NMConnection *connection = nm_device_get_applied_connection (self);
+			NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self));
 			NMSetting *s_ip4;
 
 			g_object_freeze_notify (G_OBJECT (connection));
+			g_object_freeze_notify (G_OBJECT (settings_connection));
+
+			nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP4_CONFIG);
+			s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
+			nm_connection_add_setting (settings_connection, s_ip4);
+
 			nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
 			s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
 			nm_connection_add_setting (connection, s_ip4);
+
+			g_object_thaw_notify (G_OBJECT (settings_connection));
 			g_object_thaw_notify (G_OBJECT (connection));
 		}
 
@@ -6603,18 +7637,49 @@ nm_device_set_ip4_config (NMDevice *self,
 	return success;
 }
 
+static gboolean
+_replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new)
+{
+	GSList *old_link;
+
+	/* Below, assert that we have an @old instance to replace and that
+	 * @new is not yet tracked. But still, behave correctly in any
+	 * case. */
+
+	if (   old
+	    && (old_link = g_slist_find (*plist, old))) {
+		if (old != new) {
+			if (new)
+				old_link->data = g_object_ref (new);
+			else
+				*plist = g_slist_remove_link (*plist, old_link);
+			g_object_unref (old);
+		}
+		return TRUE;
+	}
+
+	if (new) {
+		if (!g_slist_find (*plist, new))
+			*plist = g_slist_append (*plist, g_object_ref (new));
+		else
+			g_return_val_if_reached (TRUE);
+		g_return_val_if_fail (!old, TRUE);
+		return TRUE;
+	}
+
+	/* return FALSE if both @old and @new are unset. */
+	g_return_val_if_fail (!old, FALSE);
+	return FALSE;
+}
+
 void
-nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config)
+nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->vpn4_config == config)
+	if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config))
 		return;
 
-	g_clear_object (&priv->vpn4_config);
-	if (config)
-		priv->vpn4_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 VPN routes for device");
@@ -6649,13 +7714,19 @@ nm_device_set_ip6_config (NMDevice *self,
 	gboolean has_changes = FALSE;
 	gboolean success = TRUE;
 	NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE;
-	int ip_ifindex;
+	int ip_ifindex, config_ifindex;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	ip_ifindex = nm_device_get_ip_ifindex (self);
 
+	if (new_config) {
+		config_ifindex = nm_ip6_config_get_ifindex (new_config);
+		if (config_ifindex > 0)
+			g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE);
+	}
+
 	old_config = priv->ip6_config;
 
 	/* Always commit to nm-platform to update lifetimes */
@@ -6675,25 +7746,23 @@ nm_device_set_ip6_config (NMDevice *self,
 			nm_ip6_config_replace (old_config, new_config, &has_changes);
 			if (has_changes) {
 				_LOGD (LOGD_IP6, "update IP6Config instance (%s)",
-				       nm_ip6_config_get_dbus_path (old_config));
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
 			}
 		} else {
 			has_changes = TRUE;
 			priv->ip6_config = g_object_ref (new_config);
 
-			if (success && !nm_ip6_config_get_dbus_path (new_config)) {
-				/* Export over D-Bus */
-				nm_ip6_config_export (new_config);
-			}
+			if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config)))
+				nm_exported_object_export (NM_EXPORTED_OBJECT (new_config));
 
 			_LOGD (LOGD_IP6, "set IP6Config instance (%s)",
-			       nm_ip6_config_get_dbus_path (new_config));
+			       nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config)));
 		}
 	} else if (old_config) {
 		has_changes = TRUE;
 		priv->ip6_config = NULL;
 		_LOGD (LOGD_IP6, "clear IP6Config instance (%s)",
-		       nm_ip6_config_get_dbus_path (old_config));
+		       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
 	}
 
 	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
@@ -6703,17 +7772,26 @@ nm_device_set_ip6_config (NMDevice *self,
 			g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
 		g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config);
 
-		if (old_config != priv->ip6_config && old_config)
-			g_object_unref (old_config);
+		if (old_config != priv->ip6_config)
+			nm_exported_object_clear_and_unexport (&old_config);
 
 		if (nm_device_uses_generated_assumed_connection (self)) {
-			NMConnection *connection = nm_device_get_connection (self);
+			NMConnection *connection = nm_device_get_applied_connection (self);
+			NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self));
 			NMSetting *s_ip6;
 
 			g_object_freeze_notify (G_OBJECT (connection));
+			g_object_freeze_notify (G_OBJECT (settings_connection));
+
+			nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP6_CONFIG);
+			s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
+			nm_connection_add_setting (settings_connection, s_ip6);
+
 			nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
 			s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
 			nm_connection_add_setting (connection, s_ip6);
+
+			g_object_thaw_notify (G_OBJECT (settings_connection));
 			g_object_thaw_notify (G_OBJECT (connection));
 		}
 
@@ -6727,17 +7805,13 @@ nm_device_set_ip6_config (NMDevice *self,
 }
 
 void
-nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config)
+nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->vpn6_config == config)
+	if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config))
 		return;
 
-	g_clear_object (&priv->vpn6_config);
-	if (config)
-		priv->vpn6_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 VPN routes for device");
@@ -6821,7 +7895,8 @@ ip_check_pre_up (NMDevice *self)
 	priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES;
 	priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
 	if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_UP,
-	                         nm_device_get_connection (self),
+	                         nm_device_get_settings_connection (self),
+	                         nm_device_get_applied_connection (self),
 	                         self,
 	                         dispatcher_complete_proceed_state,
 	                         self,
@@ -6904,7 +7979,7 @@ 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);
-	guint log_domain = priv->gw_ping.log_domain;
+	NMLogDomain log_domain = priv->gw_ping.log_domain;
 	gboolean success = FALSE;
 
 	if (!priv->gw_ping.watch)
@@ -6951,7 +8026,7 @@ ip_check_ping_timeout_cb (gpointer user_data)
 
 static gboolean
 start_ping (NMDevice *self,
-            guint log_domain,
+            NMLogDomain log_domain,
             const char *binary,
             const char *address,
             guint timeout)
@@ -6985,7 +8060,7 @@ nm_device_start_ip_check (NMDevice *self)
 	guint timeout = 0;
 	const char *ping_binary = NULL;
 	char buf[INET6_ADDRSTRLEN] = { 0 };
-	guint log_domain = LOGD_IP4;
+	NMLogDomain log_domain = LOGD_IP4;
 
 	/* Shouldn't be any active ping here, since IP_CHECK happens after the
 	 * first IP method completes.  Any subsequently completing IP method doesn't
@@ -6996,7 +8071,7 @@ nm_device_start_ip_check (NMDevice *self)
 	g_assert (!priv->gw_ping.pid);
 	g_assert (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	s_con = nm_connection_get_setting_connection (connection);
@@ -7210,12 +8285,33 @@ nm_device_get_firmware_missing (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->firmware_missing;
 }
 
+void
+nm_device_set_nm_plugin_missing (NMDevice *self, gboolean new_missing)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->nm_plugin_missing != new_missing) {
+		priv->nm_plugin_missing = new_missing;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_NM_PLUGIN_MISSING);
+	}
+}
+
+gboolean
+nm_device_get_nm_plugin_missing (NMDevice *self)
+{
+	return NM_DEVICE_GET_PRIVATE (self)->nm_plugin_missing;
+}
+
 static NMIP4Config *
 find_ip4_lease_config (NMDevice *self,
                        NMConnection *connection,
                        NMIP4Config *ext_ip4_config)
 {
 	const char *ip_iface = nm_device_get_ip_iface (self);
+	int ip_ifindex = nm_device_get_ip_ifindex (self);
 	GSList *leases, *liter;
 	NMIP4Config *found = NULL;
 
@@ -7224,6 +8320,7 @@ find_ip4_lease_config (NMDevice *self,
 
 	leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (),
 	                                               ip_iface,
+	                                               ip_ifindex,
 	                                               nm_connection_get_uuid (connection),
 	                                               FALSE,
 	                                               nm_device_get_ip4_route_metric (self));
@@ -7310,6 +8407,24 @@ capture_lease_config (NMDevice *self,
 }
 
 static void
+_ip4_config_intersect (gpointer value, gpointer user_data)
+{
+	NMIP4Config *dst = (NMIP4Config *) value;
+	NMIP4Config *src = (NMIP4Config *) user_data;
+
+	nm_ip4_config_intersect (dst, src);
+}
+
+static void
+_ip4_config_subtract (gpointer value, gpointer user_data)
+{
+	NMIP4Config *dst = (NMIP4Config *) user_data;
+	NMIP4Config *src = (NMIP4Config *) value;
+
+	nm_ip4_config_subtract (dst, src);
+}
+
+static void
 update_ip4_config (NMDevice *self, gboolean initial)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7339,15 +8454,16 @@ update_ip4_config (NMDevice *self, gboolean initial)
 		 * kernel routes. */
 
 		/* This function was called upon external changes. Remove the configuration
-		 * (adresses,routes) that is no longer present externally from the interal
-		 * config. This way, we don't readd addresses that were manually removed
+		 * (addresses,routes) that is no longer present externally from the internal
+		 * config. This way, we don't re-add addresses that were manually removed
 		 * by the user. */
 		if (priv->con_ip4_config)
 			nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config);
 		if (priv->dev_ip4_config)
 			nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config);
-		if (priv->vpn4_config)
-			nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config);
+
+		g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config);
+
 		if (priv->wwan_ip4_config)
 			nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config);
 
@@ -7358,8 +8474,9 @@ update_ip4_config (NMDevice *self, gboolean initial)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config);
 		if (priv->dev_ip4_config)
 			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);
+
+		g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config);
+
 		if (priv->wwan_ip4_config)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config);
 
@@ -7368,6 +8485,24 @@ update_ip4_config (NMDevice *self, gboolean initial)
 }
 
 static void
+_ip6_config_intersect (gpointer value, gpointer user_data)
+{
+	NMIP6Config *dst = (NMIP6Config *) value;
+	NMIP6Config *src = (NMIP6Config *) user_data;
+
+	nm_ip6_config_intersect (dst, src);
+}
+
+static void
+_ip6_config_subtract (gpointer value, gpointer user_data)
+{
+	NMIP6Config *dst = (NMIP6Config *) user_data;
+	NMIP6Config *src = (NMIP6Config *) value;
+
+	nm_ip6_config_subtract (dst, src);
+}
+
+static void
 update_ip6_config (NMDevice *self, gboolean initial)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7393,8 +8528,8 @@ update_ip6_config (NMDevice *self, gboolean initial)
 		                            have_ip6_address (priv->ext_ip6_config, TRUE);
 
 		/* This function was called upon external changes. Remove the configuration
-		 * (adresses,routes) that is no longer present externally from the interal
-		 * config. This way, we don't readd addresses that were manually removed
+		 * (addresses,routes) that is no longer present externally from the internal
+		 * config. This way, we don't re-add addresses that were manually removed
 		 * by the user. */
 		if (priv->con_ip6_config)
 			nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config);
@@ -7404,8 +8539,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
 			nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config);
 		if (priv->wwan_ip6_config)
 			nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config);
-		if (priv->vpn6_config)
-			nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config);
+		g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config);
 
 		/* Remove parts from ext_ip6_config to only contain the information that
 		 * was configured externally -- we already have the same configuration from
@@ -7418,8 +8552,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
 			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);
+		g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config);
 
 		ip6_config_merge_and_apply (self, FALSE, NULL);
 	}
@@ -7462,6 +8595,8 @@ queued_ip6_config_change (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	GSList *iter;
+	gboolean need_ipv6ll = FALSE;
 
 	/* Wait for any queued state changes */
 	if (priv->queued_state.id)
@@ -7471,13 +8606,38 @@ queued_ip6_config_change (gpointer user_data)
 	g_object_ref (self);
 	update_ip6_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);
+	if (   nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex)
+	    && priv->state < NM_DEVICE_STATE_DEACTIVATING) {
+		/* Handle DAD falures */
+		for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
+			NMPlatformIP6Address *addr = iter->data;
+
+			if (addr->source >= NM_IP_CONFIG_SOURCE_USER)
+				continue;
+
+			_LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address",
+			       nm_platform_ip6_address_to_string (addr, NULL, 0));
+
+			if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
+				need_ipv6ll = TRUE;
+			else if (priv->rdisc)
+				nm_rdisc_dad_failed (priv->rdisc, &addr->address);
+		}
+
+		/* 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))
+			need_ipv6ll = TRUE;
+
+		if (need_ipv6ll)
+			check_and_add_ipv6ll_addr (self);
+	}
+
+	g_slist_free_full (priv->dad6_failed_addrs, g_free);
+	priv->dad6_failed_addrs = NULL;
 
 	g_object_unref (self);
 
@@ -7490,15 +8650,16 @@ device_ipx_changed (NMPlatform *platform,
                     int ifindex,
                     gpointer platform_object,
                     NMPlatformSignalChangeType change_type,
-                    NMPlatformReason reason,
                     NMDevice *self)
 {
 	NMDevicePrivate *priv;
+	NMPlatformIP6Address *addr;
 
 	if (nm_device_get_ip_ifindex (self) != ifindex)
 		return;
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
+
 	switch (obj_type) {
 	case NMP_OBJECT_TYPE_IP4_ADDRESS:
 	case NMP_OBJECT_TYPE_IP4_ROUTE:
@@ -7508,6 +8669,16 @@ device_ipx_changed (NMPlatform *platform,
 		}
 		break;
 	case NMP_OBJECT_TYPE_IP6_ADDRESS:
+		addr = platform_object;
+
+		if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
+		    && priv->state < NM_DEVICE_STATE_DEACTIVATING
+                    && (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED)
+		        || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->flags & IFA_F_TENTATIVE))) {
+			priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs,
+			                                          g_memdup (addr, sizeof (NMPlatformIP6Address)));
+		}
+		/* fallthrough */
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
 		if (!priv->queued_ip6_config_id) {
 			priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
@@ -7540,23 +8711,24 @@ nm_device_get_managed (NMDevice *self)
 	 * default-unmanaged flag (eg, only NM_UNMANAGED_DEFAULT is set) then
 	 * the device is managed whenever it's not in the UNMANAGED state.
 	 */
-	managed = !(priv->unmanaged_flags & ~NM_UNMANAGED_DEFAULT);
-	if (managed && (priv->unmanaged_flags & NM_UNMANAGED_DEFAULT))
+	managed = !NM_FLAGS_ANY (priv->unmanaged_flags, ~NM_UNMANAGED_DEFAULT);
+	if (managed && NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_DEFAULT))
 		managed = (priv->state > NM_DEVICE_STATE_UNMANAGED);
 
 	return managed;
 }
 
 /**
- * nm_device_get_unmanaged_flag():
+ * nm_device_get_unmanaged_flags():
  * @self: the #NMDevice
+ * @flag: return only the selected flags
  *
- * Returns: %TRUE if the device is unmanaged for @flag.
+ * Returns: the unmanage flags of the device (filtered with @flag)
  */
-gboolean
-nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag)
+NMUnmanagedFlags
+nm_device_get_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flag)
 {
-	return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags, flag);
+	return NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags & flag;
 }
 
 /**
@@ -7568,14 +8740,42 @@ nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag)
 static gboolean
 nm_device_get_default_unmanaged (NMDevice *self)
 {
-	return nm_device_get_unmanaged_flag (self, NM_UNMANAGED_DEFAULT);
+	return !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_DEFAULT);
+}
+
+static void
+_set_unmanaged_flags (NMDevice *self,
+                      NMUnmanagedFlags flags,
+                      gboolean unmanaged)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (unmanaged) {
+		if (!NM_FLAGS_ALL (priv->unmanaged_flags, flags)) {
+			_LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)",
+			       (long long unsigned) (priv->unmanaged_flags | flags),
+			       (long long unsigned) priv->unmanaged_flags,
+			       "set",
+			       (long long unsigned) flags);
+			priv->unmanaged_flags |= flags;
+		}
+	} else {
+		if (NM_FLAGS_ANY (priv->unmanaged_flags, flags)) {
+			_LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)",
+			       (long long unsigned) (priv->unmanaged_flags & (~flags)),
+			       (long long unsigned) priv->unmanaged_flags,
+			       "clear",
+			       (long long unsigned) flags);
+			priv->unmanaged_flags &= ~flags;
+		}
+	}
 }
 
 void
-nm_device_set_unmanaged (NMDevice *self,
-                         NMUnmanagedFlags flag,
-                         gboolean unmanaged,
-                         NMDeviceStateReason reason)
+nm_device_set_unmanaged_flags (NMDevice *self,
+                               NMUnmanagedFlags flag,
+                               gboolean unmanaged,
+                               NMDeviceStateReason reason)
 {
 	NMDevicePrivate *priv;
 	gboolean was_managed, now_managed;
@@ -7586,10 +8786,7 @@ nm_device_set_unmanaged (NMDevice *self,
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	was_managed = nm_device_get_managed (self);
-	if (unmanaged)
-		priv->unmanaged_flags |= flag;
-	else
-		priv->unmanaged_flags &= ~flag;
+	_set_unmanaged_flags (self, flag, unmanaged);
 	now_managed = nm_device_get_managed (self);
 
 	if (was_managed != now_managed) {
@@ -7605,34 +8802,41 @@ nm_device_set_unmanaged (NMDevice *self,
 }
 
 void
-nm_device_set_unmanaged_quitting (NMDevice *self)
+nm_device_set_unmanaged_flags_by_device_spec (NMDevice *self, const GSList *unmanaged_specs)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+	gboolean unmanaged;
 
-	/* It's OK to block here because we're quitting */
-	if (nm_device_is_activating (self) || priv->state == NM_DEVICE_STATE_ACTIVATED)
-		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE);
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->managed_touched_by_user)
+		return;
 
-	nm_device_set_unmanaged (self,
-	                         NM_UNMANAGED_INTERNAL,
-	                         TRUE,
-	                         NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+	unmanaged = nm_device_spec_match_list (self, unmanaged_specs);
+	nm_device_set_unmanaged_flags (self,
+	                               NM_UNMANAGED_USER,
+	                               unmanaged,
+	                               unmanaged
+	                                   ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED
+	                                   : NM_DEVICE_STATE_REASON_NOW_MANAGED);
 }
 
 /**
- * nm_device_set_initial_unmanaged_flag():
+ * nm_device_set_unmanaged_flags_initial():
  * @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
+ * Like nm_device_set_unmanaged_flags(), 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 *self,
-                                      NMUnmanagedFlags flag,
-                                      gboolean unmanaged)
+nm_device_set_unmanaged_flags_initial (NMDevice *self,
+                                       NMUnmanagedFlags flag,
+                                       gboolean unmanaged)
 {
 	NMDevicePrivate *priv;
 
@@ -7642,13 +8846,30 @@ nm_device_set_initial_unmanaged_flag (NMDevice *self,
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->initialized == FALSE);
 
-	if (unmanaged)
-		priv->unmanaged_flags |= flag;
-	else
-		priv->unmanaged_flags &= ~flag;
+	_set_unmanaged_flags (self, flag, unmanaged);
 }
 
 void
+nm_device_set_unmanaged_quitting (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean need_deactivate = nm_device_is_activating (self) ||
+	                           priv->state == NM_DEVICE_STATE_ACTIVATED;
+
+	/* It's OK to block here because we're quitting */
+	if (need_deactivate)
+		_set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE);
+
+	nm_device_set_unmanaged_flags (self,
+	                               NM_UNMANAGED_INTERNAL,
+	                               TRUE,
+	                               need_deactivate ? NM_DEVICE_STATE_REASON_REMOVED
+	                                               : NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+}
+
+/*****************************************************************************/
+
+void
 nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout)
 {
 	g_return_if_fail (NM_IS_DEVICE (self));
@@ -7670,7 +8891,84 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr)
 	priv->dhcp_anycast_address = g_strdup (addr);
 }
 
-static void
+void
+nm_device_reapply_settings_immediately (NMDevice *self)
+{
+	NMConnection *applied_connection;
+	NMSettingsConnection *settings_connection;
+	NMDeviceState state;
+	NMSettingConnection *s_con_settings;
+	NMSettingConnection *s_con_applied;
+	const char *zone;
+	NMMetered metered;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	state = nm_device_get_state (self);
+	if (   state <= NM_DEVICE_STATE_DISCONNECTED
+	    || state > NM_DEVICE_STATE_ACTIVATED)
+		return;
+
+	applied_connection = nm_device_get_applied_connection (self);
+	settings_connection = nm_device_get_settings_connection (self);
+
+	if (!nm_settings_connection_has_unmodified_applied_connection (settings_connection,
+	                                                               applied_connection,
+	                                                               NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY))
+		return;
+
+	s_con_settings = nm_connection_get_setting_connection ((NMConnection *) settings_connection);
+	s_con_applied = nm_connection_get_setting_connection (applied_connection);
+
+	if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)),
+	               nm_setting_connection_get_zone (s_con_applied)) != 0) {
+
+		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s", NM_PRINT_FMT_QUOTE_STRING (zone));
+
+		g_object_set (G_OBJECT (s_con_applied),
+		              NM_SETTING_CONNECTION_ZONE, zone,
+		              NULL);
+
+		nm_device_update_firewall_zone (self);
+	}
+
+	if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) {
+
+		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d", (int) metered);
+
+		g_object_set (G_OBJECT (s_con_applied),
+		              NM_SETTING_CONNECTION_METERED, metered,
+		              NULL);
+
+		nm_device_update_metered (self);
+	}
+}
+
+void
+nm_device_update_firewall_zone (NMDevice *self)
+{
+	NMConnection *applied_connection;
+	NMSettingConnection *s_con;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	applied_connection = nm_device_get_applied_connection (self);
+	if (!applied_connection)
+		return;
+
+	s_con = nm_connection_get_setting_connection (applied_connection);
+	if (    nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED
+	    && !nm_device_uses_assumed_connection (self)) {
+		nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
+		                                        nm_device_get_ip_iface (self),
+		                                        nm_setting_connection_get_zone (s_con),
+		                                        FALSE, /* change zone */
+		                                        NULL,
+		                                        NULL);
+	}
+}
+
+void
 nm_device_update_metered (NMDevice *self)
 {
 #define NM_METERED_INVALID ((NMMetered) -1)
@@ -7688,7 +8986,7 @@ nm_device_update_metered (NMDevice *self)
 		value = NM_METERED_UNKNOWN;
 
 	if (value == NM_METERED_INVALID) {
-		connection = nm_device_get_connection (self);
+		connection = nm_device_get_applied_connection (self);
 		if (connection) {
 			setting = nm_connection_get_setting_connection (connection);
 			if (setting) {
@@ -7723,6 +9021,34 @@ nm_device_update_metered (NMDevice *self)
 	}
 }
 
+static gboolean
+_nm_device_check_connection_available (NMDevice *self,
+                                       NMConnection *connection,
+                                       NMDeviceCheckConAvailableFlags flags,
+                                       const char *specific_object)
+{
+	NMDeviceState state;
+
+	state = nm_device_get_state (self);
+	if (state < NM_DEVICE_STATE_UNMANAGED)
+		return FALSE;
+	if (   state < NM_DEVICE_STATE_UNAVAILABLE
+	    && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
+		return FALSE;
+	if (   state < NM_DEVICE_STATE_DISCONNECTED
+	    && !nm_device_is_software (self)
+	    && (   (   !NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
+	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE))
+	        || (    NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
+	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST))))
+		return FALSE;
+
+	if (!nm_device_check_connection_compatible (self, connection))
+		return FALSE;
+
+	return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object);
+}
+
 /**
  * nm_device_check_connection_available():
  * @self: the #NMDevice
@@ -7744,25 +9070,33 @@ nm_device_check_connection_available (NMDevice *self,
                                       NMDeviceCheckConAvailableFlags flags,
                                       const char *specific_object)
 {
-	NMDeviceState state;
+	gboolean available;
 
-	state = nm_device_get_state (self);
-	if (state < NM_DEVICE_STATE_UNMANAGED)
-		return FALSE;
-	if (   state < NM_DEVICE_STATE_UNAVAILABLE
-	    && nm_device_get_unmanaged_flag (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT))
-		return FALSE;
-	if (   state < NM_DEVICE_STATE_DISCONNECTED
-	    && (   (   !NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
-	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE))
-	        || (    NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER)
-	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER))))
-		return FALSE;
+	available = _nm_device_check_connection_available (self, connection, flags, specific_object);
 
-	if (!nm_device_check_connection_compatible (self, connection))
-		return FALSE;
+#if NM_MORE_ASSERTS >= 2
+	{
+		/* The meaning of the flags is so that *adding* a flag relaxes a condition, thus making
+		 * the device *more* available. Assert against that requirement by testing all the flags. */
+		NMDeviceCheckConAvailableFlags i, j, k;
+		gboolean available_all[NM_DEVICE_CHECK_CON_AVAILABLE_ALL + 1] = { FALSE };
+
+		for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++)
+			available_all[i] = _nm_device_check_connection_available (self, connection, i, specific_object);
+
+		for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++) {
+			for (j = 1; j <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; j <<= 1) {
+				if (NM_FLAGS_HAS (i, j)) {
+					k = i & ~j;
+					nm_assert (   available_all[i] == available_all[k]
+					           || available_all[i]);
+				}
+			}
+		}
+	}
+#endif
 
-	return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object);
+	return available;
 }
 
 static void
@@ -7842,6 +9176,8 @@ nm_device_recheck_available_connections (NMDevice *self)
 
 		_signal_available_connections_changed (self);
 	}
+
+	available_connection_check_delete_unrealized (self);
 }
 
 /**
@@ -7883,29 +9219,44 @@ nm_device_get_available_connections (NMDevice *self, const char *specific_object
 static void
 cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
-	if (_try_add_available_connection (NM_DEVICE (user_data), connection))
-		_signal_available_connections_changed (NM_DEVICE (user_data));
+	NMDevice *self = user_data;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (_try_add_available_connection (self, connection))
+		_signal_available_connections_changed (self);
 }
 
 static void
 cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
-	if (_del_available_connection (NM_DEVICE (user_data), connection))
-		_signal_available_connections_changed (NM_DEVICE (user_data));
+	NMDevice *self = user_data;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (_del_available_connection (self, connection)) {
+		_signal_available_connections_changed (self);
+		available_connection_check_delete_unrealized (self);
+	}
 }
 
 static void
 cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
+	NMDevice *self = user_data;
 	gboolean added, deleted;
 
+	g_return_if_fail (NM_IS_DEVICE (self));
+
 	/* FIXME: don't remove it from the hash if it's just going to get re-added */
-	deleted = _del_available_connection (NM_DEVICE (user_data), connection);
-	added = _try_add_available_connection (NM_DEVICE (user_data), connection);
+	deleted = _del_available_connection (self, connection);
+	added = _try_add_available_connection (self, connection);
 
 	/* Only signal if the connection was removed OR added, but not both */
-	if (added != deleted)
-		_signal_available_connections_changed (NM_DEVICE (user_data));
+	if (added != deleted) {
+		_signal_available_connections_changed (self);
+		available_connection_check_delete_unrealized (self);
+	}
 }
 
 gboolean
@@ -7979,12 +9330,13 @@ gboolean
 nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean assert_is_pending)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	GSList *iter;
+	GSList *iter, *next;
 	guint count = 0;
 
 	g_return_val_if_fail (action, FALSE);
 
-	for (iter = priv->pending_actions; iter; iter = iter->next) {
+	for (iter = priv->pending_actions; iter; iter = next) {
+		next = iter->next;
 		if (!strcmp (action, iter->data)) {
 			_LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s'",
 			       count + g_slist_length (iter->next), /* length excluding 'iter' */
@@ -8018,38 +9370,23 @@ nm_device_has_pending_action (NMDevice *self)
 /***********************************************************/
 
 static void
-_cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type)
-{
-	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, cleanup_type, FALSE);
-	arp_cleanup (self);
-	dhcp6_cleanup (self, cleanup_type, FALSE);
-	linklocal6_cleanup (self);
-	addrconf6_cleanup (self);
-	dnsmasq_cleanup (self);
-	aipd_cleanup (self);
-}
-
-static void
 _cancel_activation (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Clean up when device was deactivated during call to firewall */
 	if (priv->fw_call) {
-		nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call);
+		nm_firewall_manager_cancel_call (priv->fw_call);
+		g_warn_if_fail (!priv->fw_call);
 		priv->fw_call = NULL;
 	}
+	priv->fw_ready = FALSE;
 
 	ip_check_gw_ping_cleanup (self);
 
 	/* Break the activation chain */
-	activation_source_clear (self, TRUE, AF_INET);
-	activation_source_clear (self, TRUE, AF_INET6);
+	activation_source_clear (self, AF_INET);
+	activation_source_clear (self, AF_INET6);
 }
 
 static void
@@ -8059,19 +9396,22 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
 
 	_cancel_activation (self);
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	if (   cleanup_type == CLEANUP_TYPE_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,
+		                                      NULL,
 		                                      NULL);
 	}
 
 	/* Clear any queued transitions */
 	nm_device_queued_state_clear (self);
 
-	_cleanup_ip_pre (self, cleanup_type);
+	_cleanup_ip4_pre (self, cleanup_type);
+	_cleanup_ip6_pre (self, cleanup_type);
 }
 
 static void
@@ -8098,6 +9438,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	priv->v4_commit_first_time = TRUE;
 	priv->v6_commit_first_time = TRUE;
 
+	priv->linklocal6_dad_counter = 0;
+
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface; the caller must flush routes and addresses explicitly.
 	 */
@@ -8107,15 +9449,18 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	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->con_ip6_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);
 
+	g_slist_free_full (priv->vpn4_configs, g_object_unref);
+	priv->vpn4_configs = NULL;
+	g_slist_free_full (priv->vpn6_configs, g_object_unref);
+	priv->vpn6_configs = NULL;
+
 	clear_act_request (self);
 
 	/* Clear legacy IPv4 address property */
@@ -8177,11 +9522,9 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	nm_device_master_release_slaves (self);
 
 	/* slave: mark no longer enslaved */
-	if (nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) {
-		g_clear_object (&priv->master);
-		priv->enslaved = FALSE;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
-	}
+	if (   priv->master
+	    && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0)
+		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
 	/* Take out any entries in the routing table and any IP address the device had. */
 	ifindex = nm_device_get_ip_ifindex (self);
@@ -8190,6 +9533,9 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 		nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
 	}
 
+	if (priv->lldp_listener)
+		nm_lldp_listener_stop (priv->lldp_listener);
+
 	nm_device_update_metered (self);
 	_cleanup_generic_post (self, cleanup_type);
 }
@@ -8241,13 +9587,14 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	const char *method;
 	GPtrArray *argv;
 	gs_free char *dhcp4_address = NULL;
+	char *logging_backend;
 
 	if (priv->state != NM_DEVICE_STATE_ACTIVATED)
 		return;
 	if (!nm_device_can_assume_connections (self))
 		return;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
 	argv = g_ptr_array_sized_new (10);
@@ -8259,6 +9606,17 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	g_ptr_array_add (argv, g_strdup ("--uuid"));
 	g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
 
+	logging_backend = nm_config_get_is_debug (nm_config_get ())
+	                  ? g_strdup ("debug")
+	                  : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
+	                                              NM_CONFIG_KEYFILE_GROUP_LOGGING,
+	                                              NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
+	                                              NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+	if (logging_backend) {
+		g_ptr_array_add (argv, g_strdup ("--logging-backend"));
+		g_ptr_array_add (argv, logging_backend);
+	}
+
 	dhcp4_address = find_dhcp4_address (self);
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
@@ -8278,7 +9636,7 @@ nm_device_spawn_iface_helper (NMDevice *self)
 			g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
 
 		if (priv->dhcp4_client) {
-			const char *hostname;
+			const char *hostname, *fqdn;
 			GBytes *client_id;
 
 			client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
@@ -8294,6 +9652,12 @@ nm_device_spawn_iface_helper (NMDevice *self)
 				g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
 				g_ptr_array_add (argv, g_strdup (hostname));
 			}
+
+			fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4_client);
+			if (fqdn) {
+				g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn"));
+				g_ptr_array_add (argv, g_strdup (fqdn));
+			}
 		}
 
 		configured = TRUE;
@@ -8325,6 +9689,9 @@ nm_device_spawn_iface_helper (NMDevice *self)
 			g_ptr_array_add (argv, hex_iid);
 		}
 
+		g_ptr_array_add (argv, g_strdup ("--addr-gen-mode"));
+		g_ptr_array_add (argv, g_strdup_printf ("%d", nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6))));
+
 		configured = TRUE;
 	}
 
@@ -8398,15 +9765,14 @@ deactivate_async_ready (NMDevice *self,
 	/* If operation cancelled, just return */
 	if (   g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
 	    || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) {
-		nm_log_warn (LOGD_DEVICE, "Deactivation (%s) cancelled",
-		             nm_device_get_iface (self));
+		_LOGW (LOGD_DEVICE, "Deactivation cancelled");
 	}
 	/* In every other case, transition to the DISCONNECTED state */
 	else {
-		if (error)
-			nm_log_warn (LOGD_DEVICE, "Deactivation (%s) failed: %s",
-			             nm_device_get_iface (self),
-			             error->message);
+		if (error) {
+			_LOGW (LOGD_DEVICE, "Deactivation failed: %s",
+			       error->message);
+		}
 		nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason);
 	}
 
@@ -8453,17 +9819,21 @@ _set_state_full (NMDevice *self,
                  NMDeviceStateReason reason,
                  gboolean quitting)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
 	NMDeviceState old_state;
 	NMActRequest *req;
 	gboolean no_firmware = FALSE;
-	NMConnection *connection;
+	NMSettingsConnection *connection;
+	NMConnection *applied_connection;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Track re-entry */
 	g_warn_if_fail (priv->in_state_changed == FALSE);
-	priv->in_state_changed = TRUE;
 
-	g_return_if_fail (NM_IS_DEVICE (self));
+	old_state = priv->state;
 
 	/* 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
@@ -8471,14 +9841,16 @@ _set_state_full (NMDevice *self,
 	 */
 	if (   (priv->state == state)
 	    && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) {
-		priv->in_state_changed = FALSE;
+		_LOGD (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d] (skip due to missing firmware)",
+		       state_to_string (old_state),
+		       state_to_string (state),
+		       reason_to_string (reason),
+		       old_state,
+		       state,
+		       reason);
 		return;
 	}
 
-	old_state = priv->state;
-	priv->state = state;
-	priv->state_reason = reason;
-
 	_LOGI (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]",
 	       state_to_string (old_state),
 	       state_to_string (state),
@@ -8487,6 +9859,11 @@ _set_state_full (NMDevice *self,
 	       state,
 	       reason);
 
+	priv->in_state_changed = TRUE;
+
+	priv->state = state;
+	priv->state_reason = reason;
+
 	/* Clear any queued transitions */
 	nm_device_queued_state_clear (self);
 
@@ -8573,7 +9950,8 @@ _set_state_full (NMDevice *self,
 			/* Clean up any half-done IP operations if the device's layer2
 			 * finds out it needs authentication during IP config.
 			 */
-			_cleanup_ip_pre (self, CLEANUP_TYPE_DECONFIGURE);
+			_cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE);
+			_cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE);
 		}
 		break;
 	default:
@@ -8587,7 +9965,7 @@ _set_state_full (NMDevice *self,
 
 	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);
+	g_signal_emit_by_name (self, NM_DEVICE_STATE_CHANGED, state, old_state, reason);
 
 	/* Post-process the event after internal notification */
 
@@ -8622,13 +10000,15 @@ _set_state_full (NMDevice *self,
 
 		if (quitting) {
 			nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
-			                         nm_act_request_get_connection (req),
+			                         nm_act_request_get_settings_connection (req),
+			                         nm_act_request_get_applied_connection (req),
 			                         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),
+			                         nm_act_request_get_settings_connection (req),
+			                         nm_act_request_get_applied_connection (req),
 			                         self,
 			                         deactivate_dispatcher_complete,
 			                         self,
@@ -8659,7 +10039,10 @@ _set_state_full (NMDevice *self,
 	case NM_DEVICE_STATE_ACTIVATED:
 		_LOGI (LOGD_DEVICE, "Activation: successful, device activated.");
 		nm_device_update_metered (self);
-		nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL);
+		nm_dispatcher_call (DISPATCHER_ACTION_UP,
+		                    nm_act_request_get_settings_connection (req),
+		                    nm_act_request_get_applied_connection (req),
+		                    self, NULL, NULL, NULL);
 		break;
 	case NM_DEVICE_STATE_FAILED:
 		/* Usually upon failure the activation chain is interrupted in
@@ -8678,10 +10061,10 @@ _set_state_full (NMDevice *self,
 			break;
 		}
 
-		connection = nm_device_get_connection (self);
+		connection = nm_device_get_settings_connection (self);
 		_LOGW (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: failed for connection '%s'",
-		       connection ? nm_connection_get_id (connection) : "<unknown>");
+		       connection ? nm_settings_connection_get_id (connection) : "<unknown>");
 
 		/* Notify any slaves of the unexpected failure */
 		nm_device_master_release_slaves (self);
@@ -8691,11 +10074,8 @@ _set_state_full (NMDevice *self,
 		 * failed (zero timestamp), connections that succeeded (non-zero timestamp),
 		 * and those we haven't tried yet (no timestamp).
 		 */
-		if (connection && !nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), NULL)) {
-			nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (connection),
-			                                         (guint64) 0,
-			                                         TRUE);
-		}
+		if (connection && !nm_settings_connection_get_timestamp (connection, NULL))
+			nm_settings_connection_update_timestamp (connection, (guint64) 0, TRUE);
 
 		/* Schedule the transition to DISCONNECTED.  The device can't transition
 		 * immediately because we can't change states again from the state
@@ -8704,7 +10084,28 @@ _set_state_full (NMDevice *self,
 		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 (self);
+		/* Now that IP config has completed, check if the firewall
+		 * zone must be set again for the IP interface.
+		 */
+		applied_connection = nm_device_get_applied_connection (self);
+
+		if (   applied_connection
+		    && priv->ifindex != priv->ip_ifindex
+		    && !nm_device_uses_assumed_connection (self)) {
+			NMSettingConnection *s_con;
+			const char *zone;
+
+			s_con = nm_connection_get_setting_connection (applied_connection);
+			zone = nm_setting_connection_get_zone (s_con);
+			g_assert (!priv->fw_call);
+			priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
+			                                                        nm_device_get_ip_iface (self),
+			                                                        zone,
+			                                                        FALSE,
+			                                                        fw_change_zone_cb_ip_check,
+			                                                        self);
+		} else
+			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.
@@ -8724,10 +10125,17 @@ _set_state_full (NMDevice *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), self);
-		else
-			nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self, NULL, NULL, NULL);
+		if (quitting) {
+			nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN,
+			                         nm_act_request_get_settings_connection (req),
+			                         nm_act_request_get_applied_connection (req),
+			                         self);
+		} else {
+			nm_dispatcher_call (DISPATCHER_ACTION_DOWN,
+			                    nm_act_request_get_settings_connection (req),
+			                    nm_act_request_get_applied_connection (req),
+			                    self, NULL, NULL, NULL);
+		}
 	}
 
 	/* IP-related properties are only valid when the device has IP configuration.
@@ -8836,7 +10244,7 @@ nm_device_queued_state_clear (NMDevice *self)
 	if (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_clear_g_source (&priv->queued_state.id);
 		nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE);
 	}
 	memset (&priv->queued_state, 0, sizeof (priv->queued_state));
@@ -8864,20 +10272,26 @@ nm_device_get_hw_address (NMDevice *self)
 	return priv->hw_addr_len ? priv->hw_addr : NULL;
 }
 
-static void
+void
 nm_device_update_hw_address (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ifindex (self);
 	const guint8 *hwaddr;
 	gsize hwaddrlen = 0;
+	static const guint8 zero_hwaddr[ETH_ALEN];
 
 	if (ifindex <= 0)
 		return;
 
 	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen);
 
+	if (   priv->type == NM_DEVICE_TYPE_ETHERNET
+	    && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, sizeof (zero_hwaddr)))
+		hwaddrlen = 0;
+
 	if (hwaddrlen) {
+		priv->hw_addr_len = 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);
@@ -8889,12 +10303,39 @@ nm_device_update_hw_address (NMDevice *self)
 		/* Invalid or no hardware address */
 		if (priv->hw_addr_len != 0) {
 			g_clear_pointer (&priv->hw_addr, g_free);
+			priv->hw_addr_len = 0;
 			_LOGD (LOGD_HW | LOGD_DEVICE,
 			       "previous hardware address is no longer valid");
 			g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS);
 		}
 	}
-	priv->hw_addr_len = hwaddrlen;
+}
+
+void
+nm_device_update_initial_hw_address (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->hw_addr_len) {
+		priv->initial_hw_addr = g_strdup (priv->hw_addr);
+		_LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr);
+
+		if (priv->ifindex > 0) {
+			guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+			size_t len = 0;
+
+			if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) {
+				g_warn_if_fail (len == priv->hw_addr_len);
+				priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
+				_LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s",
+				       priv->perm_hw_addr);
+			} else {
+				/* Fall back to current address */
+				_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address");
+				priv->perm_hw_addr = g_strdup (priv->hw_addr);
+			}
+		}
+	}
 }
 
 gboolean
@@ -9004,7 +10445,7 @@ spec_match_list (NMDevice *self, const GSList *specs)
 			break;
 		}
 	}
-	if (priv->hw_addr_len) {
+	if (priv->hw_addr_len && priv->hw_addr) {
 		m = nm_match_spec_hwaddr (specs, priv->hw_addr);
 		matched = MAX (matched, m);
 	}
@@ -9021,12 +10462,34 @@ spec_match_list (NMDevice *self, const GSList *specs)
 
 /***********************************************************/
 
-#define DEFAULT_AUTOCONNECT TRUE
+static const char *
+_activation_func_to_string (ActivationHandleFunc func)
+{
+#define FUNC_TO_STRING_CHECK_AND_RETURN(func, f) \
+	G_STMT_START { \
+		if ((func) == (f)) \
+			return #f; \
+	} G_STMT_END
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage1_device_prepare);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage2_device_config);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage3_ip_config_start);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip4_config_timeout);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip6_config_timeout);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_commit);
+	FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip6_config_commit);
+	g_return_val_if_reached ("unknown");
+}
+
+/***********************************************************/
 
 static void
 nm_device_init (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DEVICE, NMDevicePrivate);
+
+	self->priv = priv;
 
 	priv->type = NM_DEVICE_TYPE_UNKNOWN;
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
@@ -9052,133 +10515,50 @@ constructor (GType type,
              GObjectConstructParam *construct_params)
 {
 	GObject *object;
+	GObjectClass *klass;
 	NMDevice *self;
 	NMDevicePrivate *priv;
-	NMPlatform *platform;
-	static guint32 id = 0;
+	const NMPlatformLink *pllink;
 
-	object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
-	                         n_construct_params,
-	                         construct_params);
+	klass = G_OBJECT_CLASS (nm_device_parent_class);
+	object = klass->constructor (type, n_construct_params, construct_params);
 	if (!object)
 		return NULL;
 
 	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) {
+		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
 
-	if (!priv->iface) {
-		_LOGE (LOGD_DEVICE, "No device interface provided, ignoring");
-		goto error;
-	}
-
-	if (!priv->udi) {
-		/* Use a placeholder UDI until we get a real one */
-		priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++);
+		if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) {
+			priv->ifindex = pllink->ifindex;
+			priv->up = NM_FLAGS_HAS (pllink->flags, IFF_UP);
+		}
 	}
 
-	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
-		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
+	return object;
+}
 
-	if (priv->ifindex > 0) {
-		priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
-		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
-		if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex))
-			priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
-		priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
+static void
+constructed (GObject *object)
+{
+	NMDevice *self = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMPlatform *platform;
 
-		nm_platform_link_get_driver_info (NM_PLATFORM_GET,
-		                                  priv->ifindex,
-		                                  NULL,
-		                                  &priv->driver_version,
-		                                  &priv->firmware_version);
-	}
+	platform = nm_platform_get ();
 
 	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
 	/* Watch for external IP config changes */
-	platform = nm_platform_get ();
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
 
-	/* trigger initial ip config change to initialize ip-config */
-	priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
-	priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
-
-	if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) {
-		int ip_ifindex = nm_device_get_ip_ifindex (self);
-
-		if (ip_ifindex > 0)
-			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, ip_ifindex);
-	}
-
-	return object;
-
-error:
-	g_object_unref (self);
-	return NULL;
-}
-
-static void
-constructed (GObject *object)
-{
-	NMDevice *self = NM_DEVICE (object);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	int master;
-
-	nm_device_update_hw_address (self);
-
-	if (priv->hw_addr_len) {
-		priv->initial_hw_addr = g_strdup (priv->hw_addr);
-		_LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr);
-
-		if (priv->ifindex > 0) {
-			guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
-			size_t len = 0;
-
-			if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) {
-				g_warn_if_fail (len == priv->hw_addr_len);
-				priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
-				_LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s",
-				       priv->perm_hw_addr);
-			} else {
-				/* Fall back to current address */
-				_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address");
-				priv->perm_hw_addr = g_strdup (priv->hw_addr);
-			}
-		}
-	}
-
-	/* Note: initial hardware address must be read before calling get_ignore_carrier() */
-	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-		NMConfig *config = nm_config_get ();
-
-		priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
-		g_signal_connect (G_OBJECT (config),
-		                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
-		                  G_CALLBACK (config_changed_update_ignore_carrier),
-		                  self);
-
-		check_carrier (self);
-		_LOGD (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;
-	}
-
-	/* Enslave ourselves */
-	master = nm_platform_link_get_master (NM_PLATFORM_GET, 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,
@@ -9206,6 +10586,8 @@ constructed (GObject *object)
 	}
 
 	G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
+
+	_LOGD (LOGD_DEVICE, "constructed (%s)", G_OBJECT_TYPE_NAME (self));
 }
 
 static void
@@ -9215,7 +10597,7 @@ dispose (GObject *object)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatform *platform;
 
-	_LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self));
+	_LOGD (LOGD_DEVICE, "disposing");
 
 	g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self);
 
@@ -9233,15 +10615,10 @@ dispose (GObject *object)
 
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
-	if (priv->recheck_assume_id) {
-		g_source_remove (priv->recheck_assume_id);
-		priv->recheck_assume_id = 0;
-	}
+	nm_clear_g_source (&priv->recheck_assume_id);
+	nm_clear_g_source (&priv->recheck_available.call_id);
 
-	if (priv->recheck_available.call_id) {
-		g_source_remove (priv->recheck_available.call_id);
-		priv->recheck_available.call_id = 0;
-	}
+	nm_clear_g_source (&priv->check_delete_unrealized_id);
 
 	link_disconnect_action_cancel (self);
 
@@ -9265,7 +10642,22 @@ dispose (GObject *object)
 	nm_clear_g_source (&priv->device_link_changed_id);
 	nm_clear_g_source (&priv->device_ip_link_changed_id);
 
+	if (priv->lldp_listener) {
+		g_signal_handlers_disconnect_by_func (priv->lldp_listener,
+		                                      G_CALLBACK (lldp_neighbors_changed),
+		                                      self);
+		nm_lldp_listener_stop (priv->lldp_listener);
+		g_clear_object (&priv->lldp_listener);
+	}
+
 	G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
+
+	if (nm_clear_g_source (&priv->queued_state.id)) {
+		/* FIXME: we'd expect the queud_state to be alredy cleared and this statement
+		 * not being necessary. Add this check here to hopefully investigate crash
+		 * rh#1270247. */
+		g_return_if_reached ();
+	}
 }
 
 static void
@@ -9280,9 +10672,9 @@ finalize (GObject *object)
 	g_free (priv->perm_hw_addr);
 	g_free (priv->initial_hw_addr);
 	g_slist_free_full (priv->pending_actions, g_free);
+	g_slist_free_full (priv->dad6_failed_addrs, g_free);
 	g_clear_pointer (&priv->physical_port_id, g_free);
 	g_free (priv->udi);
-	g_free (priv->path);
 	g_free (priv->iface);
 	g_free (priv->ip_iface);
 	g_free (priv->driver);
@@ -9304,25 +10696,11 @@ set_property (GObject *object, guint prop_id,
 {
 	NMDevice *self = NM_DEVICE (object);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMPlatformLink *platform_device;
 	const char *hw_addr, *p;
 	guint count;
+	gboolean val_bool;
 
 	switch (prop_id) {
-	case PROP_PLATFORM_DEVICE:
-		platform_device = g_value_get_pointer (value);
-		if (platform_device) {
-			g_free (priv->udi);
-			priv->udi = g_strdup (nm_platform_link_get_udi (NM_PLATFORM_GET, platform_device->ifindex));
-			g_free (priv->iface);
-			priv->iface = g_strdup (platform_device->name);
-			priv->ifindex = platform_device->ifindex;
-			priv->up = NM_FLAGS_HAS (platform_device->flags, IFF_UP);
-			g_free (priv->driver);
-			priv->driver = g_strdup (platform_device->driver);
-			priv->platform_link_initialized = platform_device->initialized;
-		}
-		break;
 	case PROP_UDI:
 		if (g_value_get_string (value)) {
 			g_free (priv->udi);
@@ -9330,13 +10708,9 @@ set_property (GObject *object, guint prop_id,
 		}
 		break;
 	case PROP_IFACE:
-		if (g_value_get_string (value)) {
-			g_free (priv->iface);
-			priv->iface = g_value_dup_string (value);
-			priv->ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->iface);
-			if (priv->ifindex > 0)
-				priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex);
-		}
+		/* construct only */
+		g_return_if_fail (!priv->iface);
+		priv->iface = g_value_dup_string (value);
 		break;
 	case PROP_DRIVER:
 		if (g_value_get_string (value)) {
@@ -9358,16 +10732,32 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IP4_ADDRESS:
 		priv->ip4_address = g_value_get_uint (value);
 		break;
+	case PROP_MANAGED:
+		val_bool = g_value_get_boolean (value);
+		priv->managed_touched_by_user = TRUE;
+		nm_device_set_unmanaged_flags (self,
+		                               NM_UNMANAGED_USER | (val_bool ? NM_UNMANAGED_DEFAULT : NM_UNMANAGED_NONE),
+		                               !val_bool,
+		                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+		break;
 	case PROP_AUTOCONNECT:
 		nm_device_set_autoconnect (self, g_value_get_boolean (value));
 		break;
 	case PROP_FIRMWARE_MISSING:
 		priv->firmware_missing = g_value_get_boolean (value);
 		break;
+	case PROP_NM_PLUGIN_MISSING:
+		priv->nm_plugin_missing = g_value_get_boolean (value);
+		break;
 	case PROP_DEVICE_TYPE:
 		g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
 		priv->type = g_value_get_uint (value);
 		break;
+	case PROP_LINK_TYPE:
+		/* construct only */
+		g_return_if_fail (priv->link_type == NM_LINK_TYPE_NONE);
+		priv->link_type = g_value_get_uint (value);
+		break;
 	case PROP_TYPE_DESC:
 		g_free (priv->type_desc);
 		priv->type_desc = g_value_dup_string (value);
@@ -9411,18 +10801,16 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
-#define DBUS_TYPE_STATE_REASON_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
-
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMDevice *self = NM_DEVICE (object);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *ac_path = NULL;
 	GPtrArray *array;
 	GHashTableIter iter;
 	NMConnection *connection;
+	GVariantBuilder array_builder;
 
 	switch (prop_id) {
 	case PROP_UDI:
@@ -9462,44 +10850,33 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->mtu);
 		break;
 	case PROP_IP4_CONFIG:
-		if (ip_config_valid (priv->state) && priv->ip4_config)
-			g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL);
 		break;
 	case PROP_DHCP4_CONFIG:
-		if (ip_config_valid (priv->state) && priv->dhcp4_config)
-			g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4_config : NULL);
 		break;
 	case PROP_IP6_CONFIG:
-		if (ip_config_valid (priv->state) && priv->ip6_config)
-			g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL);
 		break;
 	case PROP_DHCP6_CONFIG:
-		if (ip_config_valid (priv->state) && priv->dhcp6_config)
-			g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6_config : NULL);
 		break;
 	case PROP_STATE:
 		g_value_set_uint (value, priv->state);
 		break;
 	case PROP_STATE_REASON:
-		g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_TYPE_STATE_REASON_STRUCT));
-		dbus_g_type_struct_set (value, 0, priv->state, 1, priv->state_reason, G_MAXUINT);
+		g_value_take_variant (value,
+		                      g_variant_new ("(uu)", priv->state, priv->state_reason));
 		break;
 	case PROP_ACTIVE_CONNECTION:
-		if (priv->act_request)
-			ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request));
-		g_value_set_boxed (value, ac_path ? ac_path : "/");
+		nm_utils_g_value_set_object_path (value, priv->act_request);
 		break;
 	case PROP_DEVICE_TYPE:
 		g_value_set_uint (value, priv->type);
 		break;
+	case PROP_LINK_TYPE:
+		g_value_set_uint (value, priv->link_type);
+		break;
 	case PROP_MANAGED:
 		g_value_set_boolean (value, nm_device_get_managed (self));
 		break;
@@ -9509,6 +10886,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_FIRMWARE_MISSING:
 		g_value_set_boolean (value, priv->firmware_missing);
 		break;
+	case PROP_NM_PLUGIN_MISSING:
+		g_value_set_boolean (value, priv->nm_plugin_missing);
+		break;
 	case PROP_TYPE_DESC:
 		g_value_set_string (value, priv->type_desc);
 		break;
@@ -9520,7 +10900,8 @@ get_property (GObject *object, guint prop_id,
 		g_hash_table_iter_init (&iter, priv->available_connections);
 		while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL))
 			g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection)));
-		g_value_take_boxed (value, array);
+		g_ptr_array_add (array, NULL);
+		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
 		break;
 	case PROP_PHYSICAL_PORT_ID:
 		g_value_set_string (value, priv->physical_port_id);
@@ -9529,7 +10910,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, priv->is_master);
 		break;
 	case PROP_MASTER:
-		g_value_set_object (value, priv->master);
+		g_value_set_object (value, nm_device_get_master (self));
 		break;
 	case PROP_HW_ADDRESS:
 		g_value_set_string (value, priv->hw_addr);
@@ -9540,6 +10921,37 @@ get_property (GObject *object, guint prop_id,
 	case PROP_METERED:
 		g_value_set_uint (value, priv->metered);
 		break;
+	case PROP_LLDP_NEIGHBORS:
+		if (priv->lldp_listener)
+			g_value_set_variant (value, nm_lldp_listener_get_neighbors (priv->lldp_listener));
+		else {
+			g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
+			g_value_take_variant (value, g_variant_builder_end (&array_builder));
+		}
+		break;
+	case PROP_REAL:
+		g_value_set_boolean (value, nm_device_is_real (self));
+		break;
+	case PROP_SLAVES: {
+		GSList *slave_iter;
+		char **slave_list;
+		guint i;
+
+		slave_list = g_new (char *, g_slist_length (priv->slaves) + 1);
+		for (slave_iter = priv->slaves, i = 0; slave_iter; slave_iter = slave_iter->next) {
+			SlaveInfo *info = slave_iter->data;
+			const char *path;
+
+			if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved)
+				continue;
+			path = nm_exported_object_get_path ((NMExportedObject *) info->slave);
+			if (path)
+				slave_list[i++] = g_strdup (path);
+		}
+		slave_list[i] = NULL;
+		g_value_take_boxed (value, slave_list);
+		break;
+	}
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -9550,9 +10962,12 @@ static void
 nm_device_class_init (NMDeviceClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass);
 
 	g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
 
+	exported_object_class->export_path = NM_DBUS_PATH "/Devices/%u";
+
 	/* Virtual methods */
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
@@ -9578,6 +10993,8 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->check_connection_compatible = check_connection_compatible;
 	klass->check_connection_available = check_connection_available;
 	klass->can_unmanaged_external_down = can_unmanaged_external_down;
+	klass->realize_start_notify = realize_start_notify;
+	klass->unrealize_notify = unrealize_notify;
 	klass->is_up = is_up;
 	klass->bring_up = bring_up;
 	klass->take_down = take_down;
@@ -9586,12 +11003,6 @@ nm_device_class_init (NMDeviceClass *klass)
 
 	/* Properties */
 	g_object_class_install_property
-		(object_class, PROP_PLATFORM_DEVICE,
-		 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, "", "",
 		                      NULL,
@@ -9663,31 +11074,31 @@ nm_device_class_init (NMDeviceClass *klass)
 
 	g_object_class_install_property
 		(object_class, PROP_IP4_CONFIG,
-		 g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_IP4_CONFIG, "", "",
+		                      NULL,
+		                      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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_DHCP4_CONFIG, "", "",
+		                      NULL,
+		                      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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_IP6_CONFIG, "", "",
+		                      NULL,
+		                      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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_DHCP6_CONFIG, "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_STATE,
@@ -9698,17 +11109,18 @@ nm_device_class_init (NMDeviceClass *klass)
 
 	g_object_class_install_property
 		(object_class, PROP_STATE_REASON,
-		 g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "",
-		                     DBUS_TYPE_STATE_REASON_STRUCT,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_variant (NM_DEVICE_STATE_REASON, "", "",
+		                       G_VARIANT_TYPE ("(uu)"),
+		                       NULL,
+		                       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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_ACTIVE_CONNECTION, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_DEVICE_TYPE,
@@ -9718,10 +11130,17 @@ nm_device_class_init (NMDeviceClass *klass)
 		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
+		(object_class, PROP_LINK_TYPE,
+		 g_param_spec_uint (NM_DEVICE_LINK_TYPE, "", "",
+		                    0, G_MAXUINT32, NM_LINK_TYPE_NONE,
+		                    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, "", "",
 		                       FALSE,
-		                       G_PARAM_READABLE |
+		                       G_PARAM_READWRITE |
 		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
@@ -9739,6 +11158,13 @@ nm_device_class_init (NMDeviceClass *klass)
 		                       G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
+		(object_class, PROP_NM_PLUGIN_MISSING,
+		 g_param_spec_boolean (NM_DEVICE_NM_PLUGIN_MISSING, "", "",
+		                       FALSE,
+		                       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, "", "",
 		                      NULL,
@@ -9764,7 +11190,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	g_object_class_install_property
 		(object_class, PROP_AVAILABLE_CONNECTIONS,
 		 g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_TYPE_STRV,
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
@@ -9808,7 +11234,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	 *
 	 * Whether the connection is metered.
 	 *
-	 * Since: 1.0.6
+	 * Since: 1.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_METERED,
@@ -9817,9 +11243,31 @@ nm_device_class_init (NMDeviceClass *klass)
 		                    G_PARAM_READABLE |
 		                    G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property
+		(object_class, PROP_LLDP_NEIGHBORS,
+		 g_param_spec_variant (NM_DEVICE_LLDP_NEIGHBORS, "", "",
+		                       G_VARIANT_TYPE ("aa{sv}"),
+		                       NULL,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+		(object_class, PROP_REAL,
+		 g_param_spec_boolean (NM_DEVICE_REAL, "", "",
+		                       FALSE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_SLAVES,
+	     g_param_spec_boxed (NM_DEVICE_SLAVES, "", "",
+	                         G_TYPE_STRV,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS));
+
 	/* Signals */
 	signals[STATE_CHANGED] =
-		g_signal_new ("state-changed",
+		g_signal_new (NM_DEVICE_STATE_CHANGED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_LAST,
 		              G_STRUCT_OFFSET (NMDeviceClass, state_changed),
@@ -9840,8 +11288,8 @@ nm_device_class_init (NMDeviceClass *klass)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              0, NULL, NULL, NULL,
-		              /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */
-		              G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
+		              /* context, connection, permission, allow_interaction, callback, user_data */
+		              G_TYPE_NONE, 6, G_TYPE_DBUS_METHOD_INVOCATION, NM_TYPE_CONNECTION, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
 
 	signals[IP4_CONFIG_CHANGED] =
 		g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED,
@@ -9878,17 +11326,10 @@ nm_device_class_init (NMDeviceClass *klass)
 		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 0);
 
-	signals[LINK_INITIALIZED] =
-		g_signal_new (NM_DEVICE_LINK_INITIALIZED,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 0);
-
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_device_object_info);
-
-	dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_SKELETON,
+	                                        "Reapply", impl_device_reapply,
+	                                        "Disconnect", impl_device_disconnect,
+	                                        "Delete", impl_device_delete,
+	                                        NULL);
 }
-
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 49cef8e1..e173b2db 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -22,13 +22,11 @@
 #ifndef __NETWORKMANAGER_DEVICE_H__
 #define __NETWORKMANAGER_DEVICE_H__
 
-#include <glib-object.h>
-#include <gio/gio.h>
-#include <dbus/dbus-glib.h>
 #include <netinet/in.h>
 
+#include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
-#include "nm-types.h"
+#include "nm-default.h"
 #include "nm-connection.h"
 #include "nm-rfkill-manager.h"
 #include "NetworkManagerUtils.h"
@@ -51,14 +49,23 @@
 #define NM_DEVICE_STATE_REASON     "state-reason"
 #define NM_DEVICE_ACTIVE_CONNECTION "active-connection"
 #define NM_DEVICE_DEVICE_TYPE      "device-type" /* ugh */
+#define NM_DEVICE_LINK_TYPE        "link-type"
 #define NM_DEVICE_MANAGED          "managed"
 #define NM_DEVICE_AUTOCONNECT      "autoconnect"
 #define NM_DEVICE_FIRMWARE_MISSING "firmware-missing"
+#define NM_DEVICE_NM_PLUGIN_MISSING "nm-plugin-missing"
 #define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections"
 #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
 #define NM_DEVICE_MTU              "mtu"
 #define NM_DEVICE_HW_ADDRESS       "hw-address"
 #define NM_DEVICE_METERED          "metered"
+#define NM_DEVICE_LLDP_NEIGHBORS  "lldp-neighbors"
+#define NM_DEVICE_REAL             "real"
+
+/* the "slaves" property is internal in the parent class, but exposed
+ * by the derived classes NMDeviceBond, NMDeviceBridge and NMDeviceTeam.
+ * It is thus important that the property name matches. */
+#define NM_DEVICE_SLAVES           "slaves"         /* partially internal */
 
 #define NM_DEVICE_TYPE_DESC        "type-desc"      /* Internal only */
 #define NM_DEVICE_RFKILL_TYPE      "rfkill-type"    /* Internal only */
@@ -74,6 +81,7 @@
 #define NM_DEVICE_REMOVED               "removed"
 #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate"
 #define NM_DEVICE_RECHECK_ASSUME        "recheck-assume"
+#define NM_DEVICE_STATE_CHANGED         "state-changed"
 #define NM_DEVICE_LINK_INITIALIZED      "link-initialized"
 
 G_BEGIN_DECLS
@@ -100,28 +108,34 @@ typedef enum { /*< skip >*/
 	NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST                      = _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
 	                                                                    | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP,
 
-	__NM_DEVICE_CHECK_CON_AVAILABLE_ALL,
-	NM_DEVICE_CHECK_CON_AVAILABLE_ALL                                   = (((__NM_DEVICE_CHECK_CON_AVAILABLE_ALL - 1) << 1) - 1),
+	NM_DEVICE_CHECK_CON_AVAILABLE_ALL                                   = (1L << 2) - 1,
 } NMDeviceCheckConAvailableFlags;
 
+struct _NMDevicePrivate;
+
 struct _NMDevice {
-	GObject parent;
+	NMExportedObject parent;
+
+	/* private */
+	struct _NMDevicePrivate *priv;
 };
 
 /* The flags have an relaxing meaning, that means, specifying more flags, can make
  * a device appear more available. It can never make a device less available. */
 typedef enum { /*< skip >*/
 	NM_DEVICE_CHECK_DEV_AVAILABLE_NONE                                  = 0,
-	NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER                        = (1L << 0),
 
-	__NM_DEVICE_CHECK_DEV_AVAILABLE_ALL,
-	NM_DEVICE_CHECK_DEV_AVAILABLE_ALL                                   = (((__NM_DEVICE_CHECK_DEV_AVAILABLE_ALL - 1) << 1) - 1),
+	_NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER                       = (1L << 0),
+	NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST                      = _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
+
+	NM_DEVICE_CHECK_DEV_AVAILABLE_ALL                                   = (1L << 1) - 1,
 } NMDeviceCheckDevAvailableFlags;
 
 typedef struct {
-	GObjectClass parent;
+	NMExportedObjectClass parent;
 
 	const char *connection_type;
+	const NMLinkType *link_types;
 
 	void (*state_changed) (NMDevice *device,
 	                       NMDeviceState new_state,
@@ -130,6 +144,48 @@ typedef struct {
 
 	void            (* link_changed) (NMDevice *self, NMPlatformLink *info);
 
+	/**
+	 * create_and_realize():
+	 * @self: the #NMDevice
+	 * @connection: the #NMConnection being activated
+	 * @parent: the parent #NMDevice, if any
+	 * @out_plink: on success, a backing kernel network device if one exists.
+	 *   The returned pointer is owned by platform and only valid until the
+	 *   next platform operation.
+	 * @error: location to store error, or %NULL
+	 *
+	 * Create any backing resources (kernel devices, etc) required for this
+	 * device to activate @connection.  If the device is backed by a kernel
+	 * network device, that device should be returned in @out_plink after
+	 * being created.
+	 *
+	 * Returns: %TRUE on success, %FALSE on error
+	 */
+	gboolean        (*create_and_realize) (NMDevice *self,
+	                                       NMConnection *connection,
+	                                       NMDevice *parent,
+	                                       const NMPlatformLink **out_plink,
+	                                       GError **error);
+
+	/**
+	 * realize_start_notify():
+	 * @self: the #NMDevice
+	 * @plink: the #NMPlatformLink if backed by a kernel netdevice
+	 *
+	 * Hook for derived classes to be notfied during realize_start_setup()
+	 * and perform additional setup.
+	 */
+	void        (*realize_start_notify) (NMDevice *self, const NMPlatformLink *plink);
+
+	/**
+	 * unrealize_notify():
+	 * @self: the #NMDevice
+	 *
+	 * Hook for derived classes to clear any properties that depend on backing resources
+	 * (kernel devices, etc). This is called by nm_device_unrealize() during unrealization.
+	 */
+	void            (*unrealize_notify)  (NMDevice *self);
+
 	/* Hardware state (IFF_UP) */
 	gboolean        (*can_unmanaged_external_down)  (NMDevice *self);
 	gboolean        (*is_up)                        (NMDevice *self);
@@ -155,7 +211,7 @@ typedef struct {
 
 	/* Checks whether the connection is compatible with the device using
 	 * only the devices type and characteristics.  Does not use any live
-	 * network information like WiFi/WiMAX scan lists etc.
+	 * network information like WiFi scan lists etc.
 	 */
 	gboolean    (* check_connection_compatible) (NMDevice *self, NMConnection *connection);
 
@@ -186,11 +242,11 @@ typedef struct {
 	NMActStageReturn	(* act_stage2_config)	(NMDevice *self,
 	                                             NMDeviceStateReason *reason);
 	NMActStageReturn	(* act_stage3_ip4_config_start) (NMDevice *self,
-														 NMIP4Config **out_config,
-														 NMDeviceStateReason *reason);
+	                                                     NMIP4Config **out_config,
+	                                                     NMDeviceStateReason *reason);
 	NMActStageReturn	(* act_stage3_ip6_config_start) (NMDevice *self,
-														 NMIP6Config **out_config,
-														 NMDeviceStateReason *reason);
+	                                                     NMIP6Config **out_config,
+	                                                     NMDeviceStateReason *reason);
 	NMActStageReturn	(* act_stage4_ip4_config_timeout)	(NMDevice *self,
 	                                                         NMDeviceStateReason *reason);
 	NMActStageReturn	(* act_stage4_ip6_config_timeout)	(NMDevice *self,
@@ -229,13 +285,15 @@ typedef struct {
 	                                   NMConnection *connection,
 	                                   gboolean configure);
 
-	gboolean        (* release_slave) (NMDevice *self,
+	void            (* release_slave) (NMDevice *self,
 	                                   NMDevice *slave,
 	                                   gboolean configure);
 
 	gboolean        (* have_any_ready_slaves) (NMDevice *self,
 	                                           const GSList *slaves);
 
+	void            (* notify_new_device_added) (NMDevice *self, NMDevice *new_device);
+
 	/**
 	 * component_added:
 	 * @self: the #NMDevice
@@ -258,23 +316,21 @@ typedef struct {
 	NMConnection *  (* new_default_connection) (NMDevice *self);
 } NMDeviceClass;
 
-
 typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
-                                         DBusGMethodInvocation *context,
+                                         GDBusMethodInvocation *context,
+                                         NMAuthSubject *subject,
                                          GError *error,
                                          gpointer user_data);
 
 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);
 gboolean        nm_device_is_software   (NMDevice *dev);
+gboolean        nm_device_is_real       (NMDevice *dev);
 const char *	nm_device_get_ip_iface	(NMDevice *dev);
 int             nm_device_get_ip_ifindex(NMDevice *dev);
 const char *	nm_device_get_driver	(NMDevice *dev);
@@ -282,6 +338,7 @@ const char *	nm_device_get_driver_version	(NMDevice *dev);
 const char *	nm_device_get_type_desc (NMDevice *dev);
 const char *	nm_device_get_type_description (NMDevice *dev);
 NMDeviceType	nm_device_get_device_type	(NMDevice *dev);
+NMLinkType	nm_device_get_link_type	(NMDevice *dev);
 NMMetered       nm_device_get_metered	(NMDevice *dev);
 
 int			nm_device_get_priority (NMDevice *dev);
@@ -296,21 +353,28 @@ 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);
+void            nm_device_replace_vpn4_config   (NMDevice *dev,
+                                                 NMIP4Config *old,
+                                                 NMIP4Config *config);
 
 NMIP6Config *	nm_device_get_ip6_config	(NMDevice *dev);
-void            nm_device_set_vpn6_config   (NMDevice *dev, NMIP6Config *config);
+void            nm_device_replace_vpn6_config   (NMDevice *dev,
+                                                 NMIP6Config *old,
+                                                 NMIP6Config *config);
 
 void            nm_device_capture_initial_config (NMDevice *dev);
 
 /* Master */
-GSList *        nm_device_master_get_slaves (NMDevice *dev);
+gboolean        nm_device_is_master         (NMDevice *dev);
 
 /* Slave */
 NMDevice *      nm_device_get_master        (NMDevice *dev);
 
-NMActRequest *	nm_device_get_act_request	(NMDevice *dev);
-NMConnection *  nm_device_get_connection	(NMDevice *dev);
+NMActRequest *          nm_device_get_act_request               (NMDevice *dev);
+NMSettingsConnection *  nm_device_get_settings_connection       (NMDevice *dev);
+NMConnection *          nm_device_get_applied_connection        (NMDevice *dev);
+gboolean                nm_device_has_unmodified_applied_connection (NMDevice *self,
+                                                                     NMSettingCompareFlags compare_flags);
 
 void            nm_device_removed        (NMDevice *dev);
 
@@ -335,6 +399,7 @@ gboolean nm_device_complete_connection (NMDevice *device,
                                         GError **error);
 
 gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
+gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnection *connection);
 
 gboolean nm_device_uses_assumed_connection (NMDevice *device);
 
@@ -356,23 +421,27 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device);
 /**
  * NMUnmanagedFlags:
  * @NM_UNMANAGED_NONE: placeholder value
- * @NM_UNMANAGED_DEFAULT: %TRUE when unmanaged by default (ie, Generic devices)
  * @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
+ * @NM_UNMANAGED_LOOPBACK: %TRUE for unmanaging loopback device
  * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not
  *   yet initialized
+ * @NM_UNMANAGED_USER: %TRUE when unmanaged by user decision (via unmanaged-specs)
+ * @NM_UNMANAGED_DEFAULT: %TRUE when unmanaged by default (ie, Generic devices)
+ * @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_PARENT        = 0x08,
-	NM_UNMANAGED_EXTERNAL_DOWN = 0x10,
-	NM_UNMANAGED_PLATFORM_INIT = 0x20,
+typedef enum { /*< skip >*/
+	NM_UNMANAGED_NONE          = 0,
+
+	NM_UNMANAGED_INTERNAL      = (1LL <<  0),
+	NM_UNMANAGED_PARENT        = (1LL <<  1),
+	NM_UNMANAGED_LOOPBACK      = (1LL <<  2),
+	NM_UNMANAGED_PLATFORM_INIT = (1LL <<  3),
+	NM_UNMANAGED_USER          = (1LL <<  4),
+
+	NM_UNMANAGED_DEFAULT       = (1LL <<  8),
+	NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 11),
 
 	/* Boundary value */
 	__NM_UNMANAGED_LAST,
@@ -381,26 +450,37 @@ typedef enum {
 } NMUnmanagedFlags;
 
 gboolean nm_device_get_managed (NMDevice *device);
-gboolean nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag);
-void nm_device_set_unmanaged (NMDevice *device,
-                              NMUnmanagedFlags flag,
-                              gboolean unmanaged,
-                              NMDeviceStateReason reason);
+NMUnmanagedFlags nm_device_get_unmanaged_flags (NMDevice *device, NMUnmanagedFlags flag);
+void nm_device_set_unmanaged_flags (NMDevice *device,
+                                    NMUnmanagedFlags flag,
+                                    gboolean unmanaged,
+                                    NMDeviceStateReason reason);
+void nm_device_set_unmanaged_flags_by_device_spec (NMDevice *self, const GSList *unmanaged_specs);
+void nm_device_set_unmanaged_flags_initial (NMDevice *device,
+                                            NMUnmanagedFlags flag,
+                                            gboolean unmanaged);
 void nm_device_set_unmanaged_quitting (NMDevice *device);
-void nm_device_set_initial_unmanaged_flag (NMDevice *device,
-                                           NMUnmanagedFlags flag,
-                                           gboolean unmanaged);
 
 gboolean nm_device_get_is_nm_owned (NMDevice *device);
-void     nm_device_set_nm_owned    (NMDevice *device);
 
 gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps);
 
-gboolean nm_device_get_autoconnect (NMDevice *device);
+gboolean nm_device_realize_start      (NMDevice *device,
+                                       const NMPlatformLink *plink,
+                                       gboolean *out_compatible,
+                                       GError **error);
+void     nm_device_realize_finish     (NMDevice *self,
+                                       const NMPlatformLink *plink);
+gboolean nm_device_create_and_realize (NMDevice *self,
+                                       NMConnection *connection,
+                                       NMDevice *parent,
+                                       GError **error);
+gboolean nm_device_unrealize          (NMDevice *device,
+                                       gboolean remove_resources,
+                                       GError **error);
 
-void nm_device_handle_autoip4_event (NMDevice *self,
-                                     const char *event,
-                                     const char *address);
+gboolean nm_device_get_autoconnect (NMDevice *device);
+void nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect);
 
 void nm_device_state_changed (NMDevice *device,
                               NMDeviceState state,
@@ -411,8 +491,11 @@ void nm_device_queue_state   (NMDevice *self,
                               NMDeviceStateReason reason);
 
 gboolean nm_device_get_firmware_missing (NMDevice *self);
+gboolean nm_device_get_nm_plugin_missing (NMDevice *self);
+void     nm_device_set_nm_plugin_missing (NMDevice *self,
+                                          gboolean missing);
 
-void nm_device_steal_connection (NMDevice *device, NMConnection *connection);
+void nm_device_steal_connection (NMDevice *device, NMSettingsConnection *connection);
 
 void nm_device_queue_activation (NMDevice *device, NMActRequest *req);
 
@@ -430,6 +513,7 @@ gboolean   nm_device_check_connection_available (NMDevice *device,
                                                  NMDeviceCheckConAvailableFlags flags,
                                                  const char *specific_object);
 
+void     nm_device_notify_new_device_added (NMDevice *self, NMDevice *new_device);
 gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
 
 gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
@@ -441,9 +525,20 @@ const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self, gbool
 
 void nm_device_spawn_iface_helper (NMDevice *self);
 
-G_END_DECLS
+void nm_device_reapply_settings_immediately (NMDevice *self);
+
+void nm_device_update_firewall_zone (NMDevice *self);
+void nm_device_update_metered (NMDevice *self);
+void nm_device_reactivate_ip4_config (NMDevice *device,
+                                      NMSettingIPConfig *s_ip4_old,
+                                      NMSettingIPConfig *s_ip4_new);
+void nm_device_reactivate_ip6_config (NMDevice *device,
+                                      NMSettingIPConfig *s_ip6_old,
+                                      NMSettingIPConfig *s_ip6_new);
 
-/* For testing only */
-extern const char* nm_device_autoipd_helper_path;
+void nm_device_update_hw_address (NMDevice *self);
+void nm_device_update_initial_hw_address (NMDevice *self);
+
+G_END_DECLS
 
 #endif	/* NM_DEVICE_H */
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
new file mode 100644
index 00000000..91181292
--- /dev/null
+++ b/src/devices/nm-lldp-listener.c
@@ -0,0 +1,648 @@
+/* -*- 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) 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <net/ethernet.h>
+
+#include "sd-lldp.h"
+#include "lldp.h"
+#include "nm-lldp-listener.h"
+#include "nm-platform.h"
+#include "nm-utils.h"
+
+#define MAX_NEIGHBORS         4096
+#define MIN_UPDATE_INTERVAL   2
+
+typedef struct {
+	char         *iface;
+	int           ifindex;
+	sd_lldp      *lldp_handle;
+	GHashTable   *lldp_neighbors;
+	guint         timer;
+	guint         num_pending_events;
+	GVariant     *variant;
+} NMLldpListenerPrivate;
+
+enum {
+	PROP_0,
+	PROP_NEIGHBORS,
+
+	LAST_PROP
+};
+
+G_DEFINE_TYPE (NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT)
+
+#define NM_LLDP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LLDP_LISTENER, NMLldpListenerPrivate))
+
+typedef struct {
+	guint8 chassis_id_type;
+	guint8 port_id_type;
+	char *chassis_id;
+	char *port_id;
+
+	int dest;
+
+	GHashTable *tlvs;
+} LLDPNeighbor;
+
+static void process_lldp_neighbors (NMLldpListener *self);
+
+static void
+gvalue_destroy (gpointer data)
+{
+	GValue *value = (GValue *) data;
+
+	g_value_unset (value);
+	g_slice_free (GValue, value);
+}
+
+static GValue *
+gvalue_new_nstr (const char *str, guint16 len)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_STRING);
+	g_value_take_string (value, strndup (str, len));
+	return value;
+}
+
+static GValue *
+gvalue_new_uint (guint val)
+{
+	GValue *value;
+
+	value = g_slice_new0 (GValue);
+	g_value_init (value, G_TYPE_UINT);
+	g_value_set_uint (value, val);
+	return value;
+}
+
+static guint
+lldp_neighbor_id_hash (gconstpointer ptr)
+{
+	const LLDPNeighbor *neigh = ptr;
+
+	return g_str_hash (neigh->chassis_id) ^
+	       g_str_hash (neigh->port_id) ^
+	       neigh->chassis_id_type ^
+	       (neigh->port_id_type * 33);
+}
+
+static gboolean
+lldp_neighbor_id_equal (gconstpointer a, gconstpointer b)
+{
+	const LLDPNeighbor *x = a, *y = b;
+
+	return x->chassis_id_type == y->chassis_id_type &&
+	       x->port_id_type == y->port_id_type &&
+	       !g_strcmp0 (x->chassis_id, y->chassis_id) &&
+	       !g_strcmp0 (x->port_id, y->port_id);
+}
+
+static void
+lldp_neighbor_free (gpointer data)
+{
+	LLDPNeighbor *neighbor = data;
+
+	if (neighbor) {
+		g_free (neighbor->chassis_id);
+		g_free (neighbor->port_id);
+		g_hash_table_unref (neighbor->tlvs);
+		g_free (neighbor);
+	}
+}
+
+static gboolean
+lldp_neighbor_equal (LLDPNeighbor *a, LLDPNeighbor *b)
+{
+	GHashTableIter iter;
+	gpointer k, v;
+
+	g_return_val_if_fail (a && a->tlvs, FALSE);
+	g_return_val_if_fail (b && b->tlvs, FALSE);
+
+	if (   a->chassis_id_type != b->chassis_id_type
+	    || a->port_id_type != b->port_id_type
+	    || a->dest != b->dest
+	    || g_strcmp0 (a->chassis_id, b->chassis_id)
+	    || g_strcmp0 (a->port_id, b->port_id))
+		return FALSE;
+
+	if (g_hash_table_size (a->tlvs) != g_hash_table_size (b->tlvs))
+		return FALSE;
+
+	g_hash_table_iter_init (&iter, a->tlvs);
+	while (g_hash_table_iter_next (&iter, &k, &v)) {
+		GValue *value_a, *value_b;
+
+		value_a = v;
+		value_b = g_hash_table_lookup (b->tlvs, k);
+
+		if (!value_b)
+			return FALSE;
+
+		g_return_val_if_fail (G_VALUE_TYPE (value_a) == G_VALUE_TYPE (value_b), FALSE);
+
+		if (G_VALUE_HOLDS_STRING (value_a)) {
+			if (g_strcmp0 (g_value_get_string (value_a), g_value_get_string (value_b)))
+				return FALSE;
+		} else if (G_VALUE_HOLDS_UINT (value_a)) {
+			if (g_value_get_uint (value_a) != g_value_get_uint (value_b))
+				return FALSE;
+		} else
+			g_return_val_if_reached (FALSE);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+lldp_hash_table_equal (GHashTable *a, GHashTable *b)
+{
+	GHashTableIter iter;
+	gpointer val;
+
+	g_return_val_if_fail (a, FALSE);
+	g_return_val_if_fail (b, FALSE);
+
+	if (g_hash_table_size (a) != g_hash_table_size (b))
+		return FALSE;
+
+	g_hash_table_iter_init (&iter, a);
+	while (g_hash_table_iter_next (&iter, NULL, &val)) {
+		LLDPNeighbor *neigh_a, *neigh_b;
+
+		neigh_a = val;
+		neigh_b = g_hash_table_lookup (b, val);
+
+		if (!neigh_b)
+			return FALSE;
+
+		if (!lldp_neighbor_equal (neigh_a, neigh_b))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+lldp_timeout (gpointer user_data)
+{
+	NMLldpListener *self = NM_LLDP_LISTENER (user_data);
+	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	priv->timer = 0;
+
+	if (priv->num_pending_events)
+		process_lldp_neighbors (self);
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+process_lldp_neighbors (NMLldpListener *self)
+{
+	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+	sd_lldp_packet **packets = NULL;
+	GHashTable *hash;
+	int num, i;
+
+	num = sd_lldp_get_packets (priv->lldp_handle, &packets);
+	if (num < 0) {
+		nm_log_dbg (LOGD_DEVICE, "LLDP: error %d retrieving neighbor packets for %s",
+		            num, priv->iface);
+		return;
+	}
+
+	hash = g_hash_table_new_full (lldp_neighbor_id_hash, lldp_neighbor_id_equal,
+	                              lldp_neighbor_free, NULL);
+
+	for (i = 0; packets && i < num; i++) {
+		uint8_t chassis_id_type, port_id_type, *chassis_id, *port_id, data8;
+		uint16_t chassis_id_len, port_id_len, len, data16;
+		LLDPNeighbor *neigh;
+		GValue *value;
+		char *str;
+		int r;
+
+		if (i >= MAX_NEIGHBORS)
+			goto next_packet;
+
+		r = sd_lldp_packet_read_chassis_id (packets[i], &chassis_id_type,
+		                                    &chassis_id, &chassis_id_len);
+		if (r < 0)
+			goto next_packet;
+
+		r = sd_lldp_packet_read_port_id (packets[i], &port_id_type,
+		                                 &port_id, &port_id_len);
+		if (r < 0)
+			goto next_packet;
+
+		neigh = g_malloc0 (sizeof (LLDPNeighbor));
+		neigh->tlvs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
+		neigh->chassis_id_type = chassis_id_type;
+		neigh->port_id_type = port_id_type;
+		sd_lldp_packet_get_destination_type (packets[i], &neigh->dest);
+
+		if (chassis_id_len < 1) {
+			lldp_neighbor_free (neigh);
+			goto next_packet;
+		}
+
+		switch (chassis_id_type) {
+		case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
+		case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
+		case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
+		case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
+			neigh->chassis_id = strndup ((char *) chassis_id, chassis_id_len);
+			break;
+		case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+			neigh->chassis_id = nm_utils_hwaddr_ntoa (chassis_id, chassis_id_len);
+			break;
+		default:
+			nm_log_dbg (LOGD_DEVICE, "LLDP: unsupported chassis ID type %d", chassis_id_type);
+			lldp_neighbor_free (neigh);
+			goto next_packet;
+		}
+
+		if (port_id_len < 1) {
+			lldp_neighbor_free (neigh);
+			goto next_packet;
+		}
+
+		switch (port_id_type) {
+		case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+		case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+		case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+		case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+			neigh->port_id = strndup ((char *) port_id, port_id_len);
+			break;
+		case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+			neigh->port_id = nm_utils_hwaddr_ntoa (port_id, port_id_len);
+			break;
+		default:
+			nm_log_dbg (LOGD_DEVICE, "LLDP: unsupported port ID type %d", port_id_type);
+			lldp_neighbor_free (neigh);
+			goto next_packet;
+		}
+
+		if (sd_lldp_packet_read_port_description (packets[i], &str, &len) == 0) {
+			value = gvalue_new_nstr (str, len);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_PORT_DESCRIPTION, value);
+		}
+
+		if (sd_lldp_packet_read_system_name (packets[i], &str, &len) == 0) {
+			value = gvalue_new_nstr (str, len);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_NAME, value);
+		}
+
+		if (sd_lldp_packet_read_system_description (packets[i], &str, &len) == 0) {
+			value = gvalue_new_nstr (str, len);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, value);
+		}
+
+		if (sd_lldp_packet_read_system_capability (packets[i], &data16) == 0) {
+			value = gvalue_new_uint (data16);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, value);
+		}
+
+		if (sd_lldp_packet_read_port_vlan_id (packets[i], &data16) == 0) {
+			value = gvalue_new_uint (data16);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID, value);
+		}
+
+		if (sd_lldp_packet_read_port_protocol_vlan_id (packets[i], &data8, &data16) == 0) {
+			value = gvalue_new_uint (data16);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID, value);
+
+			value = gvalue_new_uint (data8);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, value);
+		}
+
+		if (sd_lldp_packet_read_vlan_name (packets[i], &data16, &str, &len) == 0) {
+			value = gvalue_new_uint (data16);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID, value);
+
+			value = gvalue_new_nstr (str, len);
+			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, value);
+		}
+
+		nm_log_dbg (LOGD_DEVICE, "LLDP: new neigh: CHASSIS='%s' PORT='%s'",
+		            neigh->chassis_id, neigh->port_id);
+
+		g_hash_table_add (hash, neigh);
+next_packet:
+		sd_lldp_packet_unref (packets[i]);
+	}
+
+	g_free (packets);
+
+	if (lldp_hash_table_equal (priv->lldp_neighbors, hash)) {
+		g_hash_table_destroy (hash);
+	} else {
+		g_hash_table_destroy (priv->lldp_neighbors);
+		priv->lldp_neighbors = hash;
+		nm_clear_g_variant (&priv->variant);
+		g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
+	}
+
+	/* Since the processing of the neighbor list is potentially
+	 * expensive when there are many neighbors, coalesce multiple
+	 * events arriving in short time.
+	 */
+	priv->timer = g_timeout_add_seconds (MIN_UPDATE_INTERVAL, lldp_timeout, self);
+	priv->num_pending_events = 0;
+}
+
+static void
+lldp_event_handler (sd_lldp *lldp, int event, void *userdata)
+{
+	NMLldpListener *self = userdata;
+	NMLldpListenerPrivate *priv;
+
+	g_return_if_fail (NM_IS_LLDP_LISTENER (self));
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	if (priv->timer > 0) {
+		priv->num_pending_events++;
+		return;
+	}
+
+	process_lldp_neighbors (self);
+}
+
+gboolean
+nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
+                        const guint8 *mac, guint mac_len, GError **error)
+{
+	NMLldpListenerPrivate *priv;
+	int ret;
+
+	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (iface, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	if (priv->lldp_handle) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "already running");
+		return FALSE;
+	}
+
+	if (!mac || mac_len != ETH_ALEN) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "unsupported device");
+		return FALSE;
+	}
+
+	ret = sd_lldp_new (ifindex, iface, (struct ether_addr *) mac, &priv->lldp_handle);
+	if (ret) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "initialization failed");
+		return FALSE;
+	}
+
+	ret = sd_lldp_attach_event (priv->lldp_handle, NULL, 0);
+	if (ret) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "attach event failed");
+		goto err_free;
+	}
+
+	ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
+	if (ret) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "set callback failed");
+		goto err;
+	}
+
+	ret = sd_lldp_start (priv->lldp_handle);
+	if (ret) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "start failed");
+		goto err;
+	}
+
+	priv->ifindex = ifindex;
+	priv->iface = strdup (iface);
+	return TRUE;
+
+err:
+	sd_lldp_detach_event (priv->lldp_handle);
+err_free:
+	sd_lldp_unref (priv->lldp_handle);
+	priv->lldp_handle = NULL;
+	return FALSE;
+}
+
+void
+nm_lldp_listener_stop (NMLldpListener *self)
+{
+	NMLldpListenerPrivate *priv;
+	guint size;
+
+	g_return_if_fail (NM_IS_LLDP_LISTENER (self));
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	if (priv->lldp_handle) {
+		sd_lldp_stop (priv->lldp_handle);
+		sd_lldp_detach_event (priv->lldp_handle);
+		sd_lldp_unref (priv->lldp_handle);
+		g_clear_pointer (&priv->iface, g_free);
+		priv->lldp_handle = NULL;
+
+		size = g_hash_table_size (priv->lldp_neighbors);
+		g_hash_table_remove_all (priv->lldp_neighbors);
+		if (size) {
+			nm_clear_g_variant (&priv->variant);
+			g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
+		}
+	}
+
+	nm_clear_g_source (&priv->timer);
+}
+
+gboolean
+nm_lldp_listener_is_running (NMLldpListener *self)
+{
+	NMLldpListenerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
+
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+	return !!priv->lldp_handle;
+}
+
+GVariant *
+nm_lldp_listener_get_neighbors (NMLldpListener *self)
+{
+	GVariantBuilder array_builder, neigh_builder;
+	GHashTableIter iter;
+	NMLldpListenerPrivate *priv;
+	LLDPNeighbor *neigh;
+	char *dest_str = NULL;
+
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	if (priv->variant)
+		goto out;
+
+	g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
+	g_hash_table_iter_init (&iter, priv->lldp_neighbors);
+
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &neigh)) {
+		GHashTableIter val_iter;
+		gpointer key, val;
+
+		g_variant_builder_init (&neigh_builder, G_VARIANT_TYPE ("a{sv}"));
+
+		g_variant_builder_add (&neigh_builder, "{sv}",
+		                       NM_LLDP_ATTR_CHASSIS_ID_TYPE,
+		                       g_variant_new_uint32 (neigh->chassis_id_type));
+		g_variant_builder_add (&neigh_builder, "{sv}",
+		                       NM_LLDP_ATTR_CHASSIS_ID,
+		                       g_variant_new_string (neigh->chassis_id));
+		g_variant_builder_add (&neigh_builder, "{sv}",
+		                       NM_LLDP_ATTR_PORT_ID_TYPE,
+		                       g_variant_new_uint32 (neigh->port_id_type));
+		g_variant_builder_add (&neigh_builder, "{sv}",
+		                       NM_LLDP_ATTR_PORT_ID,
+		                       g_variant_new_string (neigh->port_id));
+
+		switch (neigh->dest) {
+		case SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE:
+			dest_str = NM_LLDP_DEST_NEAREST_BRIDGE;
+			break;
+		case SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE:
+			dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
+			break;
+		case SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE:
+			dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
+			break;
+		}
+
+		if (dest_str) {
+			g_variant_builder_add (&neigh_builder, "{sv}",
+			                       NM_LLDP_ATTR_DESTINATION,
+			                       g_variant_new_string (dest_str));
+		}
+
+		g_hash_table_iter_init (&val_iter, neigh->tlvs);
+		while (g_hash_table_iter_next (&val_iter, &key, &val)) {
+			GValue *item = val;
+
+			if (G_VALUE_HOLDS_STRING (item)) {
+				g_variant_builder_add (&neigh_builder, "{sv}",
+				                       key,
+				                       g_variant_new_string (g_value_get_string (item)));
+			} else if (G_VALUE_HOLDS_UINT (item)) {
+				g_variant_builder_add (&neigh_builder, "{sv}",
+				                       key,
+				                       g_variant_new_uint32 (g_value_get_uint (item)));
+			}
+		}
+
+		g_variant_builder_add (&array_builder, "a{sv}", &neigh_builder);
+	}
+
+	priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder));
+
+out:
+	return priv->variant;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMLldpListener *self = NM_LLDP_LISTENER (object);
+
+	switch (prop_id) {
+	case PROP_NEIGHBORS:
+		g_value_set_variant (value, nm_lldp_listener_get_neighbors (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_lldp_listener_init (NMLldpListener *self)
+{
+	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	priv->lldp_neighbors = g_hash_table_new_full (lldp_neighbor_id_hash,
+	                                              lldp_neighbor_id_equal,
+	                                              lldp_neighbor_free, NULL);
+}
+
+NMLldpListener *
+nm_lldp_listener_new (void)
+{
+	return (NMLldpListener *) g_object_new (NM_TYPE_LLDP_LISTENER, NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+	nm_lldp_listener_stop (NM_LLDP_LISTENER (object));
+
+	G_OBJECT_CLASS (nm_lldp_listener_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMLldpListener *self = NM_LLDP_LISTENER (object);
+	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+
+	nm_lldp_listener_stop (self);
+	g_hash_table_unref (priv->lldp_neighbors);
+
+	nm_clear_g_variant (&priv->variant);
+
+	G_OBJECT_CLASS (nm_lldp_listener_parent_class)->finalize (object);
+}
+
+static void
+nm_lldp_listener_class_init (NMLldpListenerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMLldpListenerPrivate));
+
+	object_class->dispose = dispose;
+	object_class->finalize = finalize;
+	object_class->get_property = get_property;
+
+	g_object_class_install_property
+		(object_class, PROP_NEIGHBORS,
+		 g_param_spec_variant (NM_LLDP_LISTENER_NEIGHBORS, "", "",
+		                       G_VARIANT_TYPE ("aa{sv}"),
+		                       NULL,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+}
+
diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h
new file mode 100644
index 00000000..492752c1
--- /dev/null
+++ b/src/devices/nm-lldp-listener.h
@@ -0,0 +1,57 @@
+/* -*- 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) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_LLDP_LISTENER__
+#define __NM_LLDP_LISTENER__
+
+#include "nm-glib.h"
+#include "nm-types.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_LLDP_LISTENER            (nm_lldp_listener_get_type ())
+#define NM_LLDP_LISTENER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener))
+#define NM_LLDP_LISTENER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
+#define NM_IS_LLDP_LISTENER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LLDP_LISTENER))
+#define NM_IS_LLDP_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_LLDP_LISTENER))
+#define NM_LLDP_LISTENER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
+
+#define NM_LLDP_LISTENER_NEIGHBORS "neighbors"
+
+struct _NMLldpListener {
+	GObject parent;
+};
+
+typedef struct {
+	GObjectClass parent;
+} NMLldpListenerClass;
+
+GType nm_lldp_listener_get_type (void);
+NMLldpListener *nm_lldp_listener_new (void);
+gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
+                                 const guint8 *mac, guint mac_len, GError **error);
+void nm_lldp_listener_stop (NMLldpListener *self);
+gboolean nm_lldp_listener_is_running (NMLldpListener *self);
+
+GVariant *nm_lldp_listener_get_neighbors (NMLldpListener *self);
+
+G_END_DECLS
+
+#endif /* __NM_LLDP_LISTENER__ */
diff --git a/src/devices/team/Makefile.am b/src/devices/team/Makefile.am
index 46c5fe1d..a6645833 100644
--- a/src/devices/team/Makefile.am
+++ b/src/devices/team/Makefile.am
@@ -7,32 +7,20 @@ AM_CPPFLAGS = \
 	-I${top_builddir}/src \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-team"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS)
+	$(GLIB_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
@@ -41,23 +29,21 @@ libnm_device_plugin_team_la_SOURCES = \
 	nm-device-team.c \
 	nm-device-team.h \
 	nm-team-factory.c \
-	nm-team-factory.h \
-	\
-	$(BUILT_SOURCES)
+	nm-team-factory.h
 
 libnm_device_plugin_team_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_team_la_LIBADD = \
-	$(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(GLIB_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
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
index 7305b05a..6dce463e 100644
--- a/src/devices/team/Makefile.in
+++ b/src/devices/team/Makefile.in
@@ -143,13 +143,12 @@ 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)
+libnm_device_plugin_team_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_2)
 am_libnm_device_plugin_team_la_OBJECTS = nm-device-team.lo \
-	nm-team-factory.lo $(am__objects_3)
+	nm-team-factory.lo
 libnm_device_plugin_team_la_OBJECTS =  \
 	$(am_libnm_device_plugin_team_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
@@ -238,7 +237,6 @@ 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@
@@ -250,8 +248,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -261,6 +257,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -315,16 +312,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -361,6 +355,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -389,7 +385,6 @@ 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@
@@ -404,6 +399,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -464,6 +461,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -471,6 +469,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -488,40 +487,30 @@ 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)/include \
-	-I${top_builddir}/libnm-core -I${top_srcdir}/libnm-core \
+	-I${top_builddir}/introspection -I${top_srcdir}/shared \
+	-I$(top_builddir)/shared -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) \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(GLIB_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)
+	nm-team-factory.h
 
 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)
+libnm_device_plugin_team_la_LIBADD =  \
+	$(top_builddir)/introspection/libnmdbus.la $(GLIB_LIBS) \
+	$(GUDEV_LIBS) $(am__append_2)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
-all: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) all-am
+all: all-am
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
@@ -600,7 +589,6 @@ 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:
@@ -715,15 +703,13 @@ distdir: $(DISTFILES)
 @ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) check-local
-check: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) check-am
+check: 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: install-am
 install-exec: install-exec-am
 install-data: install-data-am
 uninstall: uninstall-am
@@ -745,7 +731,6 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -754,7 +739,6 @@ distclean-generic:
 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 \
@@ -826,7 +810,7 @@ ps-am:
 
 uninstall-am: uninstall-pkglibLTLIBRARIES
 
-.MAKE: all check check-am install install-am install-strip
+.MAKE: check-am install-am install-strip
 
 .PHONY: CTAGS GTAGS TAGS all all-am check check-am check-local clean \
 	clean-generic clean-libtool clean-pkglibLTLIBRARIES \
@@ -849,9 +833,6 @@ 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)
 
diff --git a/src/devices/team/nm-device-team-glue.h b/src/devices/team/nm-device-team-glue.h
deleted file mode 100644
index 405975c8..00000000
--- a/src/devices/team/nm-device-team-glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* 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/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 1950fd2a..c0b83b84 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -24,26 +24,20 @@
 #include <unistd.h>
 #include <signal.h>
 #include <sys/wait.h>
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
 #include <teamdctl.h>
 #include <stdlib.h>
 
+#include "nm-default.h"
 #include "nm-device-team.h"
-#include "nm-logging.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-core-internal.h"
 #include "nm-ip4-config.h"
-#include "gsystem-local-alloc.h"
+#include "nm-dbus-compat.h"
 
-#include "nm-device-team-glue.h"
+#include "nmdbus-device-team.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceTeam);
@@ -60,13 +54,6 @@ typedef struct {
 	guint teamd_dbus_watch;
 } NMDeviceTeamPrivate;
 
-enum {
-	PROP_0,
-	PROP_SLAVES,
-
-	LAST_PROP
-};
-
 static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
 
 /******************************************************************/
@@ -265,15 +252,8 @@ teamd_cleanup (NMDevice *device, gboolean free_tdc)
 {
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
-	if (priv->teamd_process_watch) {
-		g_source_remove (priv->teamd_process_watch);
-		priv->teamd_process_watch = 0;
-	}
-
-	if (priv->teamd_timeout) {
-		g_source_remove (priv->teamd_timeout);
-		priv->teamd_timeout = 0;
-	}
+	nm_clear_g_source (&priv->teamd_process_watch);
+	nm_clear_g_source (&priv->teamd_timeout);
 
 	if (priv->teamd_pid > 0) {
 		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL);
@@ -333,9 +313,9 @@ teamd_dbus_appeared (GDBusConnection *connection,
 		guint32 pid;
 
 		ret = g_dbus_connection_call_sync (connection,
-		                                   "org.freedesktop.DBus",
-		                                   "/org/freedesktop/DBus",
-		                                   "org.freedesktop.DBus",
+		                                   DBUS_SERVICE_DBUS,
+		                                   DBUS_PATH_DBUS,
+		                                   DBUS_INTERFACE_DBUS,
 		                                   "GetConnectionUnixProcessID",
 		                                   g_variant_new ("(s)", name_owner),
 		                                   NULL,
@@ -388,7 +368,7 @@ teamd_dbus_vanished (GDBusConnection *dbus_connection,
 
 	/* Attempt to respawn teamd */
 	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) {
-		NMConnection *connection = nm_device_get_connection (device);
+		NMConnection *connection = nm_device_get_applied_connection (device);
 
 		g_assert (connection);
 		if (!teamd_start (device, nm_connection_get_setting_team (connection)))
@@ -531,7 +511,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_team = nm_connection_get_setting_team (connection);
 	g_assert (s_team);
@@ -572,7 +552,7 @@ ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
 	NMSettingWired *s_wired;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (self);
+	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 	s_wired = nm_connection_get_setting_wired (connection);
 
@@ -650,18 +630,16 @@ enslave_slave (NMDevice *device,
 	} else
 		_LOGI (LOGD_TEAM, "team port %s was enslaved", slave_iface);
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES);
-
 	return TRUE;
 }
 
-static gboolean
+static void
 release_slave (NMDevice *device,
                NMDevice *slave,
                gboolean configure)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
-	gboolean success = TRUE, no_firmware = FALSE;
+	gboolean success, no_firmware = FALSE;
 
 	if (configure) {
 		success = nm_platform_link_release (NM_PLATFORM_GET,
@@ -672,13 +650,7 @@ release_slave (NMDevice *device,
 			_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);
 
-	if (configure) {
 		/* Kernel team code "closes" the port when releasing it, (which clears
 		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
 		 * other state is noticed by the now-released port.
@@ -686,50 +658,44 @@ release_slave (NMDevice *device,
 		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;
-}
-
-/******************************************************************/
-
-NMDevice *
-nm_device_team_new (NMPlatformLink *platform_device)
-{
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TEAM,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_DRIVER, "team",
-	                                  NM_DEVICE_TYPE_DESC, "Team",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TEAM,
-	                                  NM_DEVICE_IS_MASTER, TRUE,
-	                                  NULL);
+	} else
+		_LOGI (LOGD_TEAM, "team port %s was released", nm_device_get_ip_iface (slave));
 }
 
-NMDevice *
-nm_device_team_new_for_connection (NMConnection *connection, GError **error)
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
 {
-	const char *iface = nm_connection_get_interface_name (connection);
+	const char *iface = nm_device_get_iface (device);
 	NMPlatformError plerr;
-	const NMPlatformLink *plink;
-
-	g_assert (iface);
 
-	plerr = nm_platform_team_add (NM_PLATFORM_GET, iface, NULL);
+	plerr = nm_platform_link_team_add (NM_PLATFORM_GET, iface, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create team master interface '%s' for '%s': %s",
 		             iface,
 		             nm_connection_get_id (connection),
 		             nm_platform_error_to_string (plerr));
-		return NULL;
+		return FALSE;
 	}
-	plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface);
 
+	return TRUE;
+}
+
+/******************************************************************/
+
+NMDevice *
+nm_device_team_new (const char *iface)
+{
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TEAM,
-	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_DRIVER, "team",
 	                                  NM_DEVICE_TYPE_DESC, "Team",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TEAM,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_TEAM,
 	                                  NM_DEVICE_IS_MASTER, TRUE,
 	                                  NULL);
 }
@@ -761,40 +727,6 @@ constructed (GObject *object)
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	GPtrArray *slaves;
-	GSList *list, *iter;
-
-	switch (prop_id) {
-		break;
-	case PROP_SLAVES:
-		slaves = g_ptr_array_new ();
-		list = nm_device_master_get_slaves (NM_DEVICE (object));
-		for (iter = list; iter; iter = iter->next)
-			g_ptr_array_add (slaves, g_strdup (nm_device_get_path (NM_DEVICE (iter->data))));
-		g_slist_free (list);
-		g_value_take_boxed (value, slaves);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
-{
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
 dispose (GObject *object)
 {
 	NMDevice *device = NM_DEVICE (object);
@@ -818,14 +750,12 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceTeamPrivate));
 
-	parent_class->connection_type = NM_SETTING_TEAM_SETTING_NAME;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_TEAM_SETTING_NAME, NM_LINK_TYPE_TEAM)
 
-	/* virtual methods */
 	object_class->constructed = constructed;
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 
+	parent_class->create_and_realize = create_and_realize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->is_available = is_available;
 	parent_class->check_connection_compatible = check_connection_compatible;
@@ -840,15 +770,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_SLAVES,
-		 g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_TEAM_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/team/nm-device-team.h b/src/devices/team/nm-device-team.h
index 26ba5737..0e81afa7 100644
--- a/src/devices/team/nm-device-team.h
+++ b/src/devices/team/nm-device-team.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_TEAM_H__
 #define __NETWORKMANAGER_DEVICE_TEAM_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -34,22 +32,12 @@ 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))
 
-#define NM_DEVICE_TEAM_SLAVES "slaves"
-
-typedef struct {
-	NMDevice parent;
-} NMDeviceTeam;
-
-typedef struct {
-	NMDeviceClass parent;
-
-} NMDeviceTeamClass;
-
+typedef NMDevice NMDeviceTeam;
+typedef NMDeviceClass NMDeviceTeamClass;
 
 GType nm_device_team_get_type (void);
 
-NMDevice *nm_device_team_new (NMPlatformLink *platform_device);
-NMDevice *nm_device_team_new_for_connection (NMConnection *connection, GError **error);
+NMDevice *nm_device_team_new (const char *iface);
 
 G_END_DECLS
 
diff --git a/src/devices/team/nm-team-enum-types.c b/src/devices/team/nm-team-enum-types.c
deleted file mode 100644
index 5ac3494d..00000000
--- a/src/devices/team/nm-team-enum-types.c
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-/* 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
deleted file mode 100644
index 136cadc2..00000000
--- a/src/devices/team/nm-team-enum-types.h
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-/* 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
index d87919b6..5f9e142a 100644
--- a/src/devices/team/nm-team-factory.c
+++ b/src/devices/team/nm-team-factory.c
@@ -26,13 +26,13 @@
 #include "nm-device-factory.h"
 #include "nm-team-factory.h"
 #include "nm-device-team.h"
-#include "nm-logging.h"
+#include "nm-default.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);
+static void device_factory_interface_init (NMDeviceFactoryInterface *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))
@@ -48,18 +48,13 @@ nm_device_factory_create (GError **error)
 /************************************************************************/
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	return nm_device_team_new (plink);
-}
-
-static NMDevice *
-create_virtual_device_for_connection (NMDeviceFactory *factory,
-                                      NMConnection *connection,
-                                      NMDevice *parent,
-                                      GError **error)
-{
-	return nm_device_team_new_for_connection (connection, error);
+	 return nm_device_team_new (iface);
 }
 
 NM_DEVICE_FACTORY_DECLARE_TYPES (
@@ -75,10 +70,9 @@ nm_team_factory_init (NMTeamFactory *self)
 }
 
 static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
+device_factory_interface_init (NMDeviceFactoryInterface *factory_iface)
 {
-	factory_iface->new_link = new_link;
-	factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection;
+	factory_iface->create_device = create_device;
 	factory_iface->get_supported_types = get_supported_types;
 }
 
diff --git a/src/devices/team/nm-team-factory.h b/src/devices/team/nm-team-factory.h
index 2f02ac57..9049478d 100644
--- a/src/devices/team/nm-team-factory.h
+++ b/src/devices/team/nm-team-factory.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_TEAM_FACTORY_H__
 #define __NETWORKMANAGER_TEAM_FACTORY_H__
 
-#include <glib-object.h>
+#include "nm-default.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))
diff --git a/src/devices/tests/Makefile.am b/src/devices/tests/Makefile.am
new file mode 100644
index 00000000..330377b0
--- /dev/null
+++ b/src/devices/tests/Makefile.am
@@ -0,0 +1,42 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/shared \
+	-I$(top_builddir)/shared \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	-I$(top_srcdir)/src/platform \
+	-I$(top_srcdir)/src/devices \
+	-I$(top_srcdir)/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/libsystemd-network/ \
+	-I$(top_srcdir)/src \
+	-I$(top_builddir)/src \
+	-DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(GLIB_CFLAGS)
+
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(GLIB_LIBS) $(CODE_COVERAGE_LDFLAGS)
+DEVICES_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
+@GNOME_CODE_COVERAGE_RULES@
+
+noinst_PROGRAMS = \
+	test-lldp
+
+test_lldp_SOURCES = \
+	test-lldp.c \
+	../nm-lldp-listener.c \
+	$(top_srcdir)/src/platform/tests/test-common.c
+
+test_lldp_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-I$(top_srcdir)/src/platform/tests \
+	-DSETUP=nm_linux_platform_setup
+
+test_lldp_LDADD = $(DEVICES_LDADD)
+
+@VALGRIND_RULES@
+TESTS = \
+	test-lldp
diff --git a/src/devices/tests/Makefile.in b/src/devices/tests/Makefile.in
new file mode 100644
index 00000000..1cfe46b6
--- /dev/null
+++ b/src/devices/tests/Makefile.in
@@ -0,0 +1,1193 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 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 = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+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@
+noinst_PROGRAMS = test-lldp$(EXEEXT)
+TESTS = test-lldp$(EXEEXT)
+subdir = src/devices/tests
+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)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_test_lldp_OBJECTS = test_lldp-test-lldp.$(OBJEXT) \
+	test_lldp-nm-lldp-listener.$(OBJEXT) \
+	test_lldp-test-common.$(OBJEXT)
+test_lldp_OBJECTS = $(am_test_lldp_OBJECTS)
+test_lldp_DEPENDENCIES = $(DEVICES_LDADD)
+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 = 
+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 = $(test_lldp_SOURCES)
+DIST_SOURCES = $(test_lldp_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
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+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__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp \
+	$(top_srcdir)/build-aux/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+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@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+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@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
+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_CHECK_PATH = @GTKDOC_CHECK_PATH@
+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@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
+LIBDL = @LIBDL@
+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@
+LOG_DRIVER = @LOG_DRIVER@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_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@
+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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_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@
+nmlibdir = @nmlibdir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+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)/shared \
+	-I$(top_builddir)/shared \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	-I$(top_srcdir)/src/platform \
+	-I$(top_srcdir)/src/devices \
+	-I$(top_srcdir)/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/libsystemd-network/ \
+	-I$(top_srcdir)/src \
+	-I$(top_builddir)/src \
+	-DG_LOG_DOMAIN=\""NetworkManager"\" \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(GLIB_CFLAGS)
+
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(GLIB_LIBS) $(CODE_COVERAGE_LDFLAGS)
+DEVICES_LDADD = \
+	$(top_builddir)/src/libNetworkManager.la
+
+test_lldp_SOURCES = \
+	test-lldp.c \
+	../nm-lldp-listener.c \
+	$(top_srcdir)/src/platform/tests/test-common.c
+
+test_lldp_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-I$(top_srcdir)/src/platform/tests \
+	-DSETUP=nm_linux_platform_setup
+
+test_lldp_LDADD = $(DEVICES_LDADD)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/devices/tests/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):
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test-lldp$(EXEEXT): $(test_lldp_OBJECTS) $(test_lldp_DEPENDENCIES) $(EXTRA_test_lldp_DEPENDENCIES) 
+	@rm -f test-lldp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_lldp_OBJECTS) $(test_lldp_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lldp-nm-lldp-listener.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lldp-test-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lldp-test-lldp.Po@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 -o $@ $<
+
+.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 -o $@ `$(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 $@ $<
+
+test_lldp-test-lldp.o: test-lldp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-test-lldp.o -MD -MP -MF $(DEPDIR)/test_lldp-test-lldp.Tpo -c -o test_lldp-test-lldp.o `test -f 'test-lldp.c' || echo '$(srcdir)/'`test-lldp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-test-lldp.Tpo $(DEPDIR)/test_lldp-test-lldp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-lldp.c' object='test_lldp-test-lldp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-test-lldp.o `test -f 'test-lldp.c' || echo '$(srcdir)/'`test-lldp.c
+
+test_lldp-test-lldp.obj: test-lldp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-test-lldp.obj -MD -MP -MF $(DEPDIR)/test_lldp-test-lldp.Tpo -c -o test_lldp-test-lldp.obj `if test -f 'test-lldp.c'; then $(CYGPATH_W) 'test-lldp.c'; else $(CYGPATH_W) '$(srcdir)/test-lldp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-test-lldp.Tpo $(DEPDIR)/test_lldp-test-lldp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test-lldp.c' object='test_lldp-test-lldp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-test-lldp.obj `if test -f 'test-lldp.c'; then $(CYGPATH_W) 'test-lldp.c'; else $(CYGPATH_W) '$(srcdir)/test-lldp.c'; fi`
+
+test_lldp-nm-lldp-listener.o: ../nm-lldp-listener.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-nm-lldp-listener.o -MD -MP -MF $(DEPDIR)/test_lldp-nm-lldp-listener.Tpo -c -o test_lldp-nm-lldp-listener.o `test -f '../nm-lldp-listener.c' || echo '$(srcdir)/'`../nm-lldp-listener.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-nm-lldp-listener.Tpo $(DEPDIR)/test_lldp-nm-lldp-listener.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-lldp-listener.c' object='test_lldp-nm-lldp-listener.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-nm-lldp-listener.o `test -f '../nm-lldp-listener.c' || echo '$(srcdir)/'`../nm-lldp-listener.c
+
+test_lldp-nm-lldp-listener.obj: ../nm-lldp-listener.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-nm-lldp-listener.obj -MD -MP -MF $(DEPDIR)/test_lldp-nm-lldp-listener.Tpo -c -o test_lldp-nm-lldp-listener.obj `if test -f '../nm-lldp-listener.c'; then $(CYGPATH_W) '../nm-lldp-listener.c'; else $(CYGPATH_W) '$(srcdir)/../nm-lldp-listener.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-nm-lldp-listener.Tpo $(DEPDIR)/test_lldp-nm-lldp-listener.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../nm-lldp-listener.c' object='test_lldp-nm-lldp-listener.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-nm-lldp-listener.obj `if test -f '../nm-lldp-listener.c'; then $(CYGPATH_W) '../nm-lldp-listener.c'; else $(CYGPATH_W) '$(srcdir)/../nm-lldp-listener.c'; fi`
+
+test_lldp-test-common.o: $(top_srcdir)/src/platform/tests/test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-test-common.o -MD -MP -MF $(DEPDIR)/test_lldp-test-common.Tpo -c -o test_lldp-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-test-common.Tpo $(DEPDIR)/test_lldp-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_lldp-test-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c
+
+test_lldp-test-common.obj: $(top_srcdir)/src/platform/tests/test-common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_lldp-test-common.obj -MD -MP -MF $(DEPDIR)/test_lldp-test-common.Tpo -c -o test_lldp-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_lldp-test-common.Tpo $(DEPDIR)/test_lldp-test-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_lldp-test-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_lldp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_lldp-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi`
+
+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
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	elif test -n "$$redo_logs"; then \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all 
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test-lldp.log: test-lldp$(EXEEXT)
+	@p='test-lldp$(EXEEXT)'; \
+	b='test-lldp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+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
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: 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:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+
+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."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	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-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:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+	clean-generic clean-libtool clean-noinstPROGRAMS 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-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 \
+	recheck tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+@GNOME_CODE_COVERAGE_RULES@
+
+@VALGRIND_RULES@
+
+# 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/tests/test-lldp.c b/src/devices/tests/test-lldp.c
new file mode 100644
index 00000000..c339d9ca
--- /dev/null
+++ b/src/devices/tests/test-lldp.c
@@ -0,0 +1,221 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * 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, 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) 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "nm-default.h"
+#include "nm-lldp-listener.h"
+#include "test-common.h"
+
+typedef struct {
+	int ifindex;
+	int fd;
+	guint8 mac[ETH_ALEN];
+} test_fixture;
+
+#define TEST_IFNAME "nm-tap-test0"
+
+static void
+fixture_setup (test_fixture *fixture, gconstpointer user_data)
+{
+	const NMPlatformLink *link;
+	struct ifreq ifr = { };
+	int fd, s;
+
+	fd = open ("/dev/net/tun", O_RDWR);
+	g_assert (fd >= 0);
+
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	strncpy (ifr.ifr_name, TEST_IFNAME, IFNAMSIZ);
+	g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
+
+	/* Bring the interface up */
+	s = socket (AF_INET, SOCK_DGRAM, 0);
+	g_assert (s >= 0);
+	ifr.ifr_flags |= IFF_UP;
+	g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
+	close (s);
+
+	nm_platform_process_events (NM_PLATFORM_GET);
+	link = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, TEST_IFNAME);
+	g_assert (link);
+	fixture->ifindex = link->ifindex;
+	fixture->fd = fd;
+	memcpy (fixture->mac, link->addr.data, ETH_ALEN);
+}
+
+typedef struct {
+	int num_called;
+} TestInfo;
+
+static gboolean
+loop_quit (gpointer user_data)
+{
+	g_main_loop_quit ((GMainLoop *) user_data);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
+                        gpointer user_data)
+{
+	TestInfo *info = user_data;
+
+	info->num_called++;
+}
+
+static GVariant *
+get_lldp_neighbor_attribute (GVariant *neighbors,
+                             const char *chassis, const char *port,
+                             const char *name)
+{
+	GVariantIter iter, attrs_iter;
+	GVariant *variant, *attr_variant;
+	const char *attr_name;
+	GVariant *chassis_v, *port_v, *attr_v;
+
+	g_return_val_if_fail (g_variant_is_of_type (neighbors,
+	                                            G_VARIANT_TYPE ("aa{sv}")),
+	                      NULL);
+	g_variant_iter_init (&iter, neighbors);
+
+	while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
+		g_variant_iter_init (&attrs_iter, variant);
+		chassis_v = NULL;
+		port_v = NULL;
+		attr_v = NULL;
+
+		while (g_variant_iter_next (&attrs_iter, "{&sv}", &attr_name, &attr_variant)) {
+			if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_CHASSIS_ID))
+				chassis_v = attr_variant;
+			else if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_PORT_ID))
+				port_v = attr_variant;
+			else if (!g_strcmp0 (attr_name, name))
+				attr_v = attr_variant;
+		}
+
+		if (   g_variant_is_of_type (chassis_v, G_VARIANT_TYPE_STRING)
+		    && g_variant_is_of_type (port_v, G_VARIANT_TYPE_STRING)
+		    && !g_strcmp0 (chassis, g_variant_get_string (chassis_v, NULL))
+		    && !g_strcmp0 (port, g_variant_get_string (port_v, NULL))) {
+
+			g_variant_ref (attr_v);
+			g_variant_unref (variant);
+
+			return attr_v;
+		}
+		g_variant_unref (variant);
+	}
+
+	/* neighbor not found */
+	return NULL;
+}
+
+static void
+test_receive_frame (test_fixture *fixture, gconstpointer user_data)
+{
+	NMLldpListener *listener;
+	GMainLoop *loop;
+	TestInfo info = { };
+	GVariant *neighbors, *attr;
+	uint8_t frame[] = {
+		/* Ethernet header */
+		0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
+		0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+		0x88, 0xcc,                             /* Ethertype */
+		/* LLDP mandatory TLVs */
+		0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+		0x03, 0x04, 0x05,
+		0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+		0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
+		/* LLDP optional TLVs */
+		0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+		0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+		0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+		0x00, 0x00                              /* End Of LLDPDU */
+	};
+
+	listener = nm_lldp_listener_new ();
+	g_assert (listener != NULL);
+	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, TEST_IFNAME, fixture->mac, ETH_ALEN, NULL));
+
+	g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+	                  (GCallback) lldp_neighbors_changed, &info);
+	loop = g_main_loop_new (NULL, FALSE);
+	g_timeout_add_seconds (1, loop_quit, loop);
+
+	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
+	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
+
+	g_main_loop_run (loop);
+
+	g_assert_cmpint (info.num_called, ==, 1);
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	g_assert (neighbors != NULL);
+
+	/* Check port description */
+	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
+	                                    NM_LLDP_ATTR_PORT_DESCRIPTION);
+	g_assert (attr != NULL);
+	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
+	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "Port");
+
+	/* Check system name */
+	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
+	                                    NM_LLDP_ATTR_SYSTEM_NAME);
+	g_assert (attr != NULL);
+	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
+	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "SYS");
+
+	/* Check destination */
+	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
+	                                    NM_LLDP_ATTR_DESTINATION);
+	g_assert (attr != NULL);
+	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
+	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==,
+	                 NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
+
+	g_clear_pointer (&loop, g_main_loop_unref);
+}
+
+static void
+fixture_teardown (test_fixture *fixture, gconstpointer user_data)
+{
+	nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
+}
+
+void
+init_tests (int *argc, char ***argv)
+{
+	nmtst_init_assert_logging (argc, argv, "WARN", "ALL");
+}
+
+void
+setup_tests (void)
+{
+	g_test_add ("/lldp/receive_frame", test_fixture, NULL, fixture_setup,
+	            test_receive_frame, fixture_teardown);
+}
diff --git a/src/devices/wifi/Makefile.am b/src/devices/wifi/Makefile.am
index 4ac59e3d..b0919ef7 100644
--- a/src/devices/wifi/Makefile.am
+++ b/src/devices/wifi/Makefile.am
@@ -2,7 +2,9 @@ include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
 
+if ENABLE_TESTS
 SUBDIRS=. tests
+endif
 
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
@@ -11,31 +13,15 @@ AM_CPPFLAGS = \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/supplicant-manager \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS)
-
-GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_wifi_enum_types_sources = \
-	$(srcdir)/nm-device-wifi.h \
-	$(srcdir)/nm-wifi-ap.h \
-	$(srcdir)/nm-device-olpc-mesh.h
-
-glue_sources = \
-	nm-device-wifi-glue.h \
-	nm-device-olpc-mesh-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)
+	$(GLIB_CFLAGS)
 
 pkglib_LTLIBRARIES = libnm-device-plugin-wifi.la
 
@@ -48,9 +34,7 @@ libnm_device_plugin_wifi_la_SOURCES = \
 	nm-wifi-ap-utils.c \
 	nm-wifi-ap-utils.h \
 	nm-device-olpc-mesh.c \
-	nm-device-olpc-mesh.h \
-	\
-	$(BUILT_SOURCES)
+	nm-device-olpc-mesh.h
 
 SYMBOL_VIS_FILE=$(srcdir)/exports.ver
 
@@ -59,11 +43,10 @@ libnm_device_plugin_wifi_la_LDFLAGS = \
 	 -Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_wifi_la_LIBADD = \
-	$(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(GLIB_LIBS) \
 	$(GUDEV_LIBS)
 
-CLEANFILES = $(BUILT_SOURCES)
-
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
 
 if ENABLE_TESTS
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
index 82ebad74..1286a480 100644
--- a/src/devices/wifi/Makefile.in
+++ b/src/devices/wifi/Makefile.in
@@ -140,14 +140,12 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-libnm_device_plugin_wifi_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1)
-am__objects_1 = nm-wifi-enum-types.lo
-am__objects_2 =
-am__objects_3 = $(am__objects_1) $(am__objects_2)
+libnm_device_plugin_wifi_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am_libnm_device_plugin_wifi_la_OBJECTS = nm-wifi-factory.lo \
 	nm-device-wifi.lo nm-wifi-ap.lo nm-wifi-ap-utils.lo \
-	nm-device-olpc-mesh.lo $(am__objects_3)
+	nm-device-olpc-mesh.lo
 libnm_device_plugin_wifi_la_OBJECTS =  \
 	$(am_libnm_device_plugin_wifi_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
@@ -234,7 +232,7 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-DIST_SUBDIRS = $(SUBDIRS)
+DIST_SUBDIRS = . tests
 am__DIST_COMMON = $(srcdir)/Makefile.in \
 	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -278,7 +276,6 @@ 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@
@@ -290,8 +287,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -301,6 +296,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -355,16 +351,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -401,6 +394,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -429,7 +424,6 @@ 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@
@@ -444,6 +438,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -504,6 +500,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -511,6 +508,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -526,7 +524,7 @@ with_dhcpcd = @with_dhcpcd@
 with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
-SUBDIRS = . tests
+@ENABLE_TESTS_TRUE@SUBDIRS = . tests
 AM_CPPFLAGS = \
 	-I${top_srcdir}/src \
 	-I${top_builddir}/src \
@@ -534,28 +532,16 @@ AM_CPPFLAGS = \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
 	-I${top_srcdir}/src/supplicant-manager \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_builddir}/libnm-core \
 	-I${top_srcdir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS)
+	$(GLIB_CFLAGS)
 
-GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_wifi_enum_types_sources = \
-	$(srcdir)/nm-device-wifi.h \
-	$(srcdir)/nm-wifi-ap.h \
-	$(srcdir)/nm-device-olpc-mesh.h
-
-glue_sources = \
-	nm-device-wifi-glue.h \
-	nm-device-olpc-mesh-glue.h
-
-BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources)
 pkglib_LTLIBRARIES = libnm-device-plugin-wifi.la
 libnm_device_plugin_wifi_la_SOURCES = \
 	nm-wifi-factory.c \
@@ -566,9 +552,7 @@ libnm_device_plugin_wifi_la_SOURCES = \
 	nm-wifi-ap-utils.c \
 	nm-wifi-ap-utils.h \
 	nm-device-olpc-mesh.c \
-	nm-device-olpc-mesh.h \
-	\
-	$(BUILT_SOURCES)
+	nm-device-olpc-mesh.h
 
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
 libnm_device_plugin_wifi_la_LDFLAGS = \
@@ -576,13 +560,12 @@ libnm_device_plugin_wifi_la_LDFLAGS = \
 	 -Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_wifi_la_LIBADD = \
-	$(DBUS_LIBS) \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(GLIB_LIBS) \
 	$(GUDEV_LIBS)
 
-CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = $(SYMBOL_VIS_FILE)
-all: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+all: all-recursive
 
 .SUFFIXES:
 .SUFFIXES: .c .lo .o .obj
@@ -664,7 +647,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-wifi.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap-utils.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-enum-types.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-factory.Plo@am__quote@
 
 .c.o:
@@ -851,16 +833,14 @@ distdir: $(DISTFILES)
 @ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) check-local
-check: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) check-recursive
+check: check-recursive
 all-am: Makefile $(LTLIBRARIES)
 installdirs: installdirs-recursive
 installdirs-am:
 	for dir in "$(DESTDIR)$(pkglibdir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
-install: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) install-recursive
+install: install-recursive
 install-exec: install-exec-recursive
 install-data: install-data-recursive
 uninstall: uninstall-recursive
@@ -882,7 +862,6 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -891,7 +870,6 @@ distclean-generic:
 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-recursive
 
 clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
@@ -963,8 +941,7 @@ ps-am:
 
 uninstall-am: uninstall-pkglibLTLIBRARIES
 
-.MAKE: $(am__recursive_targets) all check check-am install install-am \
-	install-strip
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
 	check-am check-local clean clean-generic clean-libtool \
@@ -988,9 +965,6 @@ 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-wifi.so $(SYMBOL_VIS_FILE)
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh-glue.h b/src/devices/wifi/nm-device-olpc-mesh-glue.h
deleted file mode 100644
index 04b5e83f..00000000
--- a/src/devices/wifi/nm-device-olpc-mesh-glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-
-#ifndef __dbus_glib_marshal_nm_device_olpc_mesh_MARSHAL_H__
-#define __dbus_glib_marshal_nm_device_olpc_mesh_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_olpc_mesh_MARSHAL_H__ */
-
-#include <dbus/dbus-glib.h>
-static const DBusGMethodInfo dbus_glib_nm_device_olpc_mesh_methods[] = {
-};
-
-const DBusGObjectInfo dbus_glib_nm_device_olpc_mesh_object_info = {  1,
-  dbus_glib_nm_device_olpc_mesh_methods,
-  0,
-"\0",
-"org.freedesktop.NetworkManager.Device.OlpcMesh\0PropertiesChanged\0\0",
-"org.freedesktop.NetworkManager.Device.OlpcMesh\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.OlpcMesh\0Companion\0companion\0read\0org.freedesktop.NetworkManager.Device.OlpcMesh\0ActiveChannel\0active_channel\0read\0\0"
-};
-
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index e67d1a20..23ebd9c8 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -26,9 +26,6 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -38,27 +35,24 @@
 #include <sys/ioctl.h>
 #include <errno.h>
 
+#include "nm-default.h"
 #include "nm-device.h"
 #include "nm-device-wifi.h"
 #include "nm-device-olpc-mesh.h"
 #include "nm-device-private.h"
 #include "nm-utils.h"
-#include "nm-logging.h"
 #include "NetworkManagerUtils.h"
 #include "nm-activation-request.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-olpc-mesh.h"
 #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 "nm-vpn-dbus-interface.h"
 
-
-#include "nm-device-olpc-mesh-glue.h"
+#include "nmdbus-device-olpc-mesh.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceOlpcMesh);
@@ -77,6 +71,7 @@ enum {
 
 struct _NMDeviceOlpcMeshPrivate {
 	NMDevice *companion;
+	NMManager *manager;
 	gboolean  stage1_waiting;
 };
 
@@ -213,7 +208,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	GBytes *ssid;
 	const char *anycast_addr;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 
 	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
@@ -345,7 +340,7 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other)
 	_LOGI (LOGD_OLPC, "found companion WiFi device %s",
 	       nm_device_get_iface (other));
 
-	g_signal_connect (G_OBJECT (other), "state-changed",
+	g_signal_connect (G_OBJECT (other), NM_DEVICE_STATE_CHANGED,
 	                  G_CALLBACK (companion_state_changed_cb), self);
 
 	g_signal_connect (G_OBJECT (other), "notify::scanning",
@@ -397,7 +392,7 @@ find_companion (NMDeviceOlpcMesh *self)
 	nm_device_add_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
 
 	/* Try to find the companion if it's already known to the NMManager */
-	for (list = nm_manager_get_devices (nm_manager_get ()); list ; list = g_slist_next (list)) {
+	for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) {
 		if (check_companion (self, NM_DEVICE (list->data))) {
 			nm_device_queue_recheck_available (NM_DEVICE (self),
 			                                   NM_DEVICE_STATE_REASON_NONE,
@@ -421,14 +416,13 @@ state_changed (NMDevice *device,
 /*******************************************************************/
 
 NMDevice *
-nm_device_olpc_mesh_new (NMPlatformLink *platform_device)
+nm_device_olpc_mesh_new (const char *iface)
 {
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
 	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_OLPC_MESH,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
+	                                  NM_DEVICE_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "802.11 OLPC Mesh",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_OLPC_MESH,
 	                                  NULL);
 }
 
@@ -437,35 +431,21 @@ nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self)
 {
 }
 
-static GObject*
-constructor (GType type,
-             guint n_construct_params,
-             GObjectConstructParam *construct_params)
+static void
+constructed (GObject *object)
 {
-	GObject *object;
-	GObjectClass *klass;
-	NMDeviceOlpcMesh *self;
-	NMDeviceWifiCapabilities caps;
-
-	klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class);
-	object = klass->constructor (type, n_construct_params, construct_params);
-	if (!object)
-		return NULL;
-
-	self = NM_DEVICE_OLPC_MESH (object);
-
-	if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &caps)) {
-		_LOGW (LOGD_HW | LOGD_OLPC, "failed to initialize WiFi driver");
-		g_object_unref (object);
-		return NULL;
-	}
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
-	g_signal_connect (nm_manager_get (), "device-added", G_CALLBACK (device_added_cb), self);
-	g_signal_connect (nm_manager_get (), "device-removed", G_CALLBACK (device_removed_cb), self);
+	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->constructed (object);
+
+	priv->manager = g_object_ref (nm_manager_get ());
+
+	g_signal_connect (priv->manager, "device-added", G_CALLBACK (device_added_cb), self);
+	g_signal_connect (priv->manager, "device-removed", G_CALLBACK (device_removed_cb), self);
 
 	/* shorter timeout for mesh connectivity */
 	nm_device_set_dhcp_timeout (NM_DEVICE (self), 20);
-	return object;
 }
 
 static void
@@ -477,10 +457,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_COMPANION:
-		if (priv->companion)
-			g_value_set_boxed (value, nm_device_get_path (priv->companion));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, priv->companion);
 		break;
 	case PROP_ACTIVE_CHANNEL:
 		g_value_set_uint (value, nm_platform_mesh_get_channel (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (device))));
@@ -506,10 +483,15 @@ static void
 dispose (GObject *object)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
 	companion_cleanup (self);
-	g_signal_handlers_disconnect_by_func (nm_manager_get (), G_CALLBACK (device_added_cb), self);
-	g_signal_handlers_disconnect_by_func (nm_manager_get (), G_CALLBACK (device_removed_cb), self);
+
+	if (priv->manager) {
+		g_signal_handlers_disconnect_by_func (priv->manager, G_CALLBACK (device_added_cb), self);
+		g_signal_handlers_disconnect_by_func (priv->manager, G_CALLBACK (device_removed_cb), self);
+		g_clear_object (&priv->manager);
+	}
 
 	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
 }
@@ -522,7 +504,9 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate));
 
-	object_class->constructor = constructor;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_OLPC_MESH_SETTING_NAME, NM_LINK_TYPE_OLPC_MESH)
+
+	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
@@ -540,10 +524,10 @@ 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, "", "",
-		                     DBUS_TYPE_G_OBJECT_PATH,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+		 g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, "", "",
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
 		(object_class, PROP_ACTIVE_CHANNEL,
@@ -552,8 +536,8 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 		                    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);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_OLPC_MESH_SKELETON,
+	                                        NULL);
 }
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h
index 67d83fb0..63895a88 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.h
+++ b/src/devices/wifi/nm-device-olpc-mesh.h
@@ -28,8 +28,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_OLPC_MESH_H__
 #define __NETWORKMANAGER_DEVICE_OLPC_MESH_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 
 G_BEGIN_DECLS
@@ -67,7 +65,7 @@ struct _NMDeviceOlpcMeshClass
 
 GType nm_device_olpc_mesh_get_type (void);
 
-NMDevice *nm_device_olpc_mesh_new (NMPlatformLink *platform_device);
+NMDevice *nm_device_olpc_mesh_new (const char *iface);
 
 G_END_DECLS
 
diff --git a/src/devices/wifi/nm-device-wifi-glue.h b/src/devices/wifi/nm-device-wifi-glue.h
deleted file mode 100644
index 8f8da2af..00000000
--- a/src/devices/wifi/nm-device-wifi-glue.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-
-#ifndef __dbus_glib_marshal_nm_device_wifi_MARSHAL_H__
-#define __dbus_glib_marshal_nm_device_wifi_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 */
-
-
-/* BOOLEAN:POINTER,POINTER */
-extern void dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER (GClosure     *closure,
-                                                                       GValue       *return_value,
-                                                                       guint         n_param_values,
-                                                                       const GValue *param_values,
-                                                                       gpointer      invocation_hint,
-                                                                       gpointer      marshal_data);
-void
-dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER (GClosure     *closure,
-                                                           GValue       *return_value G_GNUC_UNUSED,
-                                                           guint         n_param_values,
-                                                           const GValue *param_values,
-                                                           gpointer      invocation_hint G_GNUC_UNUSED,
-                                                           gpointer      marshal_data)
-{
-  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
-                                                             gpointer     arg_1,
-                                                             gpointer     arg_2,
-                                                             gpointer     data2);
-  GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
-  GCClosure *cc = (GCClosure*) closure;
-  gpointer data1, data2;
-  gboolean v_return;
-
-  g_return_if_fail (return_value != NULL);
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  v_return = callback (data1,
-                       g_marshal_value_peek_pointer (param_values + 1),
-                       g_marshal_value_peek_pointer (param_values + 2),
-                       data2);
-
-  g_value_set_boolean (return_value, v_return);
-}
-
-/* NONE:BOXED,POINTER */
-extern void dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER (GClosure     *closure,
-                                                                  GValue       *return_value,
-                                                                  guint         n_param_values,
-                                                                  const GValue *param_values,
-                                                                  gpointer      invocation_hint,
-                                                                  gpointer      marshal_data);
-void
-dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER (GClosure     *closure,
-                                                      GValue       *return_value G_GNUC_UNUSED,
-                                                      guint         n_param_values,
-                                                      const GValue *param_values,
-                                                      gpointer      invocation_hint G_GNUC_UNUSED,
-                                                      gpointer      marshal_data)
-{
-  typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer     data1,
-                                                    gpointer     arg_1,
-                                                    gpointer     arg_2,
-                                                    gpointer     data2);
-  GMarshalFunc_VOID__BOXED_POINTER callback;
-  GCClosure *cc = (GCClosure*) closure;
-  gpointer data1, data2;
-
-  g_return_if_fail (n_param_values == 3);
-
-  if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-      data1 = closure->data;
-      data2 = g_value_peek_pointer (param_values + 0);
-    }
-  else
-    {
-      data1 = g_value_peek_pointer (param_values + 0);
-      data2 = closure->data;
-    }
-  callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
-
-  callback (data1,
-            g_marshal_value_peek_boxed (param_values + 1),
-            g_marshal_value_peek_pointer (param_values + 2),
-            data2);
-}
-#define dbus_glib_marshal_nm_device_wifi_NONE__BOXED_POINTER	dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER
-
-G_END_DECLS
-
-#endif /* __dbus_glib_marshal_nm_device_wifi_MARSHAL_H__ */
-
-#include <dbus/dbus-glib.h>
-static const DBusGMethodInfo dbus_glib_nm_device_wifi_methods[] = {
-  { (GCallback) impl_device_get_access_points, dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER, 0 },
-  { (GCallback) impl_device_get_all_access_points, dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER, 89 },
-  { (GCallback) impl_device_request_scan, dbus_glib_marshal_nm_device_wifi_NONE__BOXED_POINTER, 181 },
-};
-
-const DBusGObjectInfo dbus_glib_nm_device_wifi_object_info = {  1,
-  dbus_glib_nm_device_wifi_methods,
-  3,
-"org.freedesktop.NetworkManager.Device.Wireless\0GetAccessPoints\0S\0access_points\0O\0F\0N\0ao\0\0org.freedesktop.NetworkManager.Device.Wireless\0GetAllAccessPoints\0S\0access_points\0O\0F\0N\0ao\0\0org.freedesktop.NetworkManager.Device.Wireless\0RequestScan\0A\0options\0I\0a{sv}\0\0\0",
-"org.freedesktop.NetworkManager.Device.Wireless\0PropertiesChanged\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPointAdded\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPointRemoved\0\0",
-"org.freedesktop.NetworkManager.Device.Wireless\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.Wireless\0PermHwAddress\0perm_hw_address\0read\0org.freedesktop.NetworkManager.Device.Wireless\0Mode\0mode\0read\0org.freedesktop.NetworkManager.Device.Wireless\0Bitrate\0bitrate\0read\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPoints\0access_points\0read\0org.freedesktop.NetworkManager.Device.Wireless\0ActiveAccessPoint\0active_access_point\0read\0org.freedesktop.NetworkManager.Device.Wireless\0WirelessCapabilities\0wireless_capabilities\0read\0\0"
-};
-
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 1bf64bcc..64da03e1 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -21,21 +21,16 @@
 
 #include "config.h"
 
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <dbus/dbus.h>
 #include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 
-#include "nm-glib-compat.h"
-#include "nm-dbus-manager.h"
+#include "nm-default.h"
 #include "nm-device.h"
 #include "nm-device-wifi.h"
 #include "nm-device-private.h"
 #include "nm-utils.h"
-#include "nm-logging.h"
 #include "NetworkManagerUtils.h"
 #include "nm-activation-request.h"
 #include "nm-supplicant-manager.h"
@@ -52,25 +47,11 @@
 #include "nm-auth-utils.h"
 #include "nm-settings-connection.h"
 #include "nm-enum-types.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-wifi-enum-types.h"
 #include "nm-connection-provider.h"
-#include "gsystem-local-alloc.h"
+#include "nm-core-internal.h"
+#include "nm-config.h"
 
-
-static gboolean impl_device_get_access_points (NMDeviceWifi *device,
-                                               GPtrArray **aps,
-                                               GError **err);
-
-static gboolean impl_device_get_all_access_points (NMDeviceWifi *device,
-                                                   GPtrArray **aps,
-                                                   GError **err);
-
-static void impl_device_request_scan (NMDeviceWifi *device,
-                                      GHashTable *options,
-                                      DBusGMethodInvocation *context);
-
-#include "nm-device-wifi-glue.h"
+#include "nmdbus-device-wifi.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceWifi);
@@ -111,29 +92,27 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 struct _NMDeviceWifiPrivate {
-	gboolean disposed;
-
 	gint8             invalid_strength_counter;
 
-	GSList *          ap_list;
+	GHashTable *      aps;
 	NMAccessPoint *   current_ap;
 	guint32           rate;
 	gboolean          enabled; /* rfkilled or not */
 
+	gint32            last_scan;
 	gint32            scheduled_scan_time;
 	guint8            scan_interval; /* seconds */
 	guint             pending_scan_id;
-	guint             scanlist_cull_id;
-	gboolean          requested_scan;
+	guint             ap_dump_id;
+	bool              requested_scan;
 
 	NMSupplicantManager   *sup_mgr;
 	NMSupplicantInterface *sup_iface;
 	guint                  sup_timeout_id; /* supplicant association timeout */
 
-	gboolean          ssid_found;
+	bool              ssid_found;
 	NM80211Mode       mode;
 
-	guint32           failed_link_count;
 	guint             periodic_source_id;
 	guint             link_timeout_id;
 	guint32           failed_iface_count;
@@ -164,7 +143,7 @@ static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
 
 static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
                                              const char *object_path,
-                                             GHashTable *properties,
+                                             GVariant *properties,
                                              NMDeviceWifi *self);
 
 static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
@@ -179,49 +158,34 @@ static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
                                                  GParamSpec * pspec,
                                                  NMDeviceWifi * self);
 
-static void schedule_scanlist_cull (NMDeviceWifi *self);
+static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
+                                                 GParamSpec *pspec,
+                                                 NMDeviceWifi *self);
 
-static void request_wireless_scan (NMDeviceWifi *self, GHashTable *scan_options);
+static void request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options);
 
-static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap);
+static void ap_add_remove (NMDeviceWifi *self,
+                           guint signum,
+                           NMAccessPoint *ap,
+                           gboolean recheck_available_connections);
 
 static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
 
 /*****************************************************************/
 
-static GObject*
-constructor (GType type,
-             guint n_construct_params,
-             GObjectConstructParam *construct_params)
+static void
+constructed (GObject *object)
 {
-	GObject *object;
-	GObjectClass *klass;
-	NMDeviceWifi *self;
-	NMDeviceWifiPrivate *priv;
+	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	klass = G_OBJECT_CLASS (nm_device_wifi_parent_class);
-	object = klass->constructor (type, n_construct_params, construct_params);
-	if (!object)
-		return NULL;
-
-	self = NM_DEVICE_WIFI (object);
-	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET,
-	                                        nm_device_get_ifindex (NM_DEVICE (self)),
-	                                        &priv->capabilities)) {
-		_LOGW (LOGD_HW | LOGD_WIFI, "failed to initialize WiFi driver");
-		g_object_unref (object);
-		return NULL;
-	}
+	G_OBJECT_CLASS (nm_device_wifi_parent_class)->constructed (object);
 
 	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 = g_object_ref (nm_supplicant_manager_get ());
-
-	return object;
 }
 
 static gboolean
@@ -268,6 +232,10 @@ supplicant_interface_acquire (NMDeviceWifi *self)
 	                  "notify::scanning",
 	                  G_CALLBACK (supplicant_iface_notify_scanning_cb),
 	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  "notify::" NM_SUPPLICANT_INTERFACE_CURRENT_BSS,
+	                  G_CALLBACK (supplicant_iface_notify_current_bss),
+	                  self);
 
 	return TRUE;
 }
@@ -288,10 +256,7 @@ supplicant_interface_release (NMDeviceWifi *self)
 	_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);
-		priv->scanlist_cull_id = 0;
-	}
+	nm_clear_g_source (&priv->ap_dump_id);
 
 	if (priv->sup_iface) {
 		remove_supplicant_interface_error_handler (self);
@@ -310,177 +275,30 @@ supplicant_interface_release (NMDeviceWifi *self)
 static NMAccessPoint *
 get_ap_by_path (NMDeviceWifi *self, const char *path)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList *iter;
+	g_return_val_if_fail (path != NULL, NULL);
+	return g_hash_table_lookup (NM_DEVICE_WIFI_GET_PRIVATE (self)->aps, path);
 
-	if (!path)
-		return NULL;
-
-	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0)
-			return NM_AP (iter->data);
-	}
-	return NULL;
 }
 
 static NMAccessPoint *
 get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList *iter;
+	GHashTableIter iter;
+	NMAccessPoint *ap;
 
-	if (!path)
-		return NULL;
+	g_return_val_if_fail (path != NULL, NULL);
 
-	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
-		if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0)
-			return NM_AP (iter->data);
+	g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+		if (g_strcmp0 (path, nm_ap_get_supplicant_path (ap)) == 0)
+			return ap;
 	}
 	return NULL;
 }
 
-static NMAccessPoint *
-find_active_ap (NMDeviceWifi *self,
-                NMAccessPoint *ignore_ap,
-                gboolean match_hidden)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
-	guint8 bssid[ETH_ALEN];
-	GByteArray *ssid;
-	GSList *iter;
-	int i = 0;
-	NMAccessPoint *match_nofreq = NULL, *active_ap = NULL;
-	gboolean found_a_band = FALSE;
-	gboolean found_bg_band = FALSE;
-	NM80211Mode devmode;
-	guint32 devfreq;
-
-	nm_platform_wifi_get_bssid (NM_PLATFORM_GET, 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, ETH_ALEN))
-		return NULL;
-
-	ssid = nm_platform_wifi_get_ssid (NM_PLATFORM_GET, ifindex);
-	_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 (NM_PLATFORM_GET, ifindex);
-	devfreq = nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex);
-
-	/* When matching hidden APs, do a second pass that ignores the SSID check,
-	 * because NM might not yet know the SSID of the hidden AP in the scan list
-	 * and therefore it won't get matched the first time around.
-	 */
-	while (i++ < (match_hidden ? 2 : 1)) {
-		_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 char *ap_bssid = nm_ap_get_address (ap);
-			const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
-			NM80211Mode apmode;
-			guint32 apfreq;
-
-			_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) {
-				_LOGD (LOGD_WIFI, "      ignored");
-				continue;
-			}
-
-			if (!nm_utils_hwaddr_matches (bssid, ETH_ALEN, ap_bssid, -1)) {
-				_LOGD (LOGD_WIFI, "      BSSID 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) {
-				_LOGD (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
-				       devmode, apmode);
-				continue;
-			}
-
-			apfreq = nm_ap_get_freq (ap);
-			if (devfreq != apfreq) {
-				_LOGD (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
-				       devfreq, apfreq);
-
-				if (match_nofreq == NULL)
-					match_nofreq = ap;
-
-				if (apfreq > 4000)
-					found_a_band = TRUE;
-				else if (apfreq > 2000)
-					found_bg_band = TRUE;
-				continue;
-			}
-
-			// FIXME: handle security settings here too
-			_LOGD (LOGD_WIFI, "      matched");
-			active_ap = ap;
-			goto done;
-		}
-	}
-
-	/* Some proprietary drivers (wl.o) report tuned frequency (like when
-	 * scanning) instead of the associated AP's frequency.  This is a great
-	 * example of how WEXT is underspecified.  We use frequency to find the
-	 * active AP in the scan list because some configurations use the same
-	 * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to
-	 * make sure we get the right AP in the right band.  This configuration
-	 * is uncommon though, and the frequency check penalizes closed drivers we
-	 * can't fix.  Because we're not total dicks, ignore the frequency condition
-	 * if the associated BSSID/SSID exists only in one band since that's most
-	 * likely the AP we want.  Sometimes wl.o returns a frequency of 0, so if
-	 * 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 GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
-
-		_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;
-	}
-
-	_LOGD (LOGD_WIFI, "  No matching AP found.");
-
-done:
-	if (ssid)
-		g_byte_array_free (ssid, TRUE);
-	return active_ap;
-}
-
 static void
 update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
 {
-	NMConnection *connection;
-
 	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
 
 	if (ap == NULL)
@@ -490,12 +308,10 @@ update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
 	if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA)
 		return;
 
-	if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) {
-		connection = nm_device_get_connection (NM_DEVICE (self));
-		if (connection) {
-			nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection),
-			                                       nm_ap_get_address (ap));
-		}
+	if (   nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED
+	    && nm_device_has_unmodified_applied_connection (NM_DEVICE (self), NM_SETTING_COMPARE_FLAG_NONE)) {
+		nm_settings_connection_add_seen_bssid (nm_device_get_settings_connection (NM_DEVICE (self)),
+		                                       nm_ap_get_address (ap));
 	}
 }
 
@@ -516,13 +332,6 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
 	if (new_ap) {
 		priv->current_ap = g_object_ref (new_ap);
 
-		/* Move the current AP to the front of the scan list.  Since we
-		 * do a lot of searches looking for the current AP, it saves
-		 * time to have it in front.
-		 */
-		priv->ap_list = g_slist_remove (priv->ap_list, new_ap);
-		priv->ap_list = g_slist_prepend (priv->ap_list, new_ap);
-
 		/* Update seen BSSIDs cache */
 		update_seen_bssids_cache (self, priv->current_ap);
 	} else
@@ -531,11 +340,9 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
 	if (old_ap) {
 		NM80211Mode mode = nm_ap_get_mode (old_ap);
 
-		if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) {
-			remove_access_point (self, old_ap);
-			if (recheck_available_connections)
-				nm_device_recheck_available_connections (NM_DEVICE (self));
-		}
+		/* Remove any AP from the internal list if it was created by NM or isn't known to the supplicant */
+		if (mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap))
+			ap_add_remove (self, ACCESS_POINT_REMOVED, old_ap, recheck_available_connections);
 		g_object_unref (old_ap);
 	}
 
@@ -543,11 +350,10 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
 }
 
 static void
-periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
+periodic_update (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
-	NMAccessPoint *new_ap;
 	guint32 new_rate;
 	int percent;
 	NMDeviceState state;
@@ -574,65 +380,15 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 	if (priv->mode == NM_802_11_MODE_AP)
 		return;
 
-	/* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where
-	 * multiple IBSS stations using the same SSID will eventually switch to
-	 * using the same BSSID to avoid network segmentation.  When this happens,
-	 * the card's reported BSSID will change, but the new BSS may not
-	 * be in the scan list, since scanning isn't done in ad-hoc mode for
-	 * various reasons.  So pull the BSSID from the card and update the
-	 * 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)) {
-		guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
-
-		nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid);
-		/* 0x02 means "locally administered" and should be OR-ed into
-		 * the first byte of IBSS BSSIDs.
-		 */
-		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);
-	if (new_ap) {
-		/* Try to smooth out the strength.  Atmel cards, for example, will give no strength
-		 * one second and normal strength the next.
-		 */
+	if (priv->current_ap) {
+		/* Smooth out the strength to work around crappy drivers */
 		percent = nm_platform_wifi_get_quality (NM_PLATFORM_GET, ifindex);
 		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
-			nm_ap_set_strength (new_ap, (gint8) percent);
+			nm_ap_set_strength (priv->current_ap, (gint8) percent);
 			priv->invalid_strength_counter = 0;
 		}
 	}
 
-	if (new_ap != priv->current_ap) {
-		const char *new_bssid = NULL;
-		const GByteArray *new_ssid = NULL;
-		const char *old_bssid = NULL;
-		const GByteArray *old_ssid = NULL;
-
-		if (new_ap) {
-			new_bssid = nm_ap_get_address (new_ap);
-			new_ssid = nm_ap_get_ssid (new_ap);
-		}
-
-		if (priv->current_ap) {
-			old_bssid = nm_ap_get_address (priv->current_ap);
-			old_ssid = nm_ap_get_ssid (priv->current_ap);
-		}
-
-		_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);
-	}
-
 	new_rate = nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex);
 	if (new_rate != priv->rate) {
 		priv->rate = new_rate;
@@ -643,10 +399,18 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
 static gboolean
 periodic_update_cb (gpointer user_data)
 {
-	periodic_update (NM_DEVICE_WIFI (user_data), NULL);
+	periodic_update (NM_DEVICE_WIFI (user_data));
 	return TRUE;
 }
 
+static void
+realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
+{
+	NM_DEVICE_CLASS (nm_device_wifi_parent_class)->realize_start_notify (device, plink);
+
+	g_object_notify (G_OBJECT (device), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
+}
+
 static gboolean
 bring_up (NMDevice *device, gboolean *no_firmware)
 {
@@ -657,47 +421,55 @@ bring_up (NMDevice *device, gboolean *no_firmware)
 }
 
 static void
-emit_ap_added_removed (NMDeviceWifi *self,
-                       guint signum,
-                       NMAccessPoint *ap,
-                       gboolean recheck_available_connections)
+ap_add_remove (NMDeviceWifi *self,
+               guint signum,
+               NMAccessPoint *ap,
+               gboolean recheck_available_connections)
 {
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_assert (NM_IN_SET (signum, ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED));
+
+	if (signum == ACCESS_POINT_ADDED) {
+		g_hash_table_insert (priv->aps,
+		                     (gpointer) nm_exported_object_export ((NMExportedObject *) ap),
+		                     g_object_ref (ap));
+	}
+
 	g_signal_emit (self, signals[signum], 0, ap);
 	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACCESS_POINTS);
+
+	if (signum == ACCESS_POINT_REMOVED) {
+		g_hash_table_remove (priv->aps, nm_exported_object_get_path ((NMExportedObject *) ap));
+		nm_exported_object_unexport ((NMExportedObject *) ap);
+		g_object_unref (ap);
+	}
+
 	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
 	if (recheck_available_connections)
 		nm_device_recheck_available_connections (NM_DEVICE (self));
 }
 
 static void
-remove_access_point (NMDeviceWifi *device,
-                     NMAccessPoint *ap)
-{
-	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	g_return_if_fail (ap);
-	g_return_if_fail (ap != priv->current_ap);
-	g_return_if_fail (g_slist_find (priv->ap_list, ap));
-
-	priv->ap_list = g_slist_remove (priv->ap_list, ap);
-	emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
-	g_object_unref (ap);
-}
-
-static void
 remove_all_aps (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GHashTableIter iter;
+	NMAccessPoint *ap;
 
-	if (priv->ap_list) {
-		set_current_ap (self, NULL, FALSE);
+	if (!g_hash_table_size (priv->aps))
+		return;
 
-		while (priv->ap_list)
-			remove_access_point (self, NM_AP (priv->ap_list->data));
+	set_current_ap (self, NULL, FALSE);
 
-		nm_device_recheck_available_connections (NM_DEVICE (self));
+again:
+	g_hash_table_iter_init (&iter, priv->aps);
+	if (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+		ap_add_remove (self, ACCESS_POINT_REMOVED, ap, FALSE);
+		goto again;
 	}
+
+	nm_device_recheck_available_connections (NM_DEVICE (self));
 }
 
 static void
@@ -706,29 +478,14 @@ deactivate (NMDevice *device)
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ifindex (device);
-	NMConnection *connection;
 	NM80211Mode old_mode = priv->mode;
 
-	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);
-	}
-
-	if (priv->periodic_source_id) {
-		g_source_remove (priv->periodic_source_id);
-		priv->periodic_source_id = 0;
-	}
+	nm_clear_g_source (&priv->periodic_source_id);
 
 	cleanup_association_attempt (self, TRUE);
 
 	priv->rate = 0;
 
-	/* If the AP is 'fake', i.e. it wasn't actually found from
-	 * a scan but the user tried to connect to it manually (maybe it
-	 * was non-broadcasting or something) get rid of it, because 'fake'
-	 * APs should only live for as long as we're connected to them.
-	 **/
 	set_current_ap (self, NULL, TRUE);
 
 	/* Clear any critical protocol notification in the Wi-Fi stack */
@@ -738,6 +495,8 @@ deactivate (NMDevice *device)
 	if (nm_device_get_initial_hw_address (device))
 		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_WIFI);
 
+	nm_platform_wifi_set_powersave (NM_PLATFORM_GET, ifindex, 0);
+
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
 	 */
@@ -848,7 +607,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 			return FALSE;
 
 		if (priv->sup_iface) {
-			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == AP_SUPPORT_NO)
+			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO)
 				return FALSE;
 		}
 	}
@@ -860,6 +619,28 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	return TRUE;
 }
 
+static NMAccessPoint *
+find_first_compatible_ap (NMDeviceWifi *self,
+                          NMConnection *connection,
+                          gboolean allow_unstable_order)
+{
+	GHashTableIter iter;
+	NMAccessPoint *ap;
+	NMAccessPoint *cand_ap = NULL;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+		if (!nm_ap_check_compatible (ap, connection))
+			continue;
+		if (allow_unstable_order)
+			return ap;
+		if (!cand_ap || (nm_ap_get_id (cand_ap) < nm_ap_get_id (ap)))
+			cand_ap = ap;
+	}
+	return cand_ap;
+}
 
 static gboolean
 check_connection_available (NMDevice *device,
@@ -867,10 +648,8 @@ check_connection_available (NMDevice *device,
                             NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
 	NMSettingWireless *s_wifi;
 	const char *mode;
-	GSList *ap_iter = NULL;
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wifi, FALSE);
@@ -904,13 +683,8 @@ check_connection_available (NMDevice *device,
 	if (nm_setting_wireless_get_hidden (s_wifi) || NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
 		return TRUE;
 
-	/* check if its visible */
-	for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
-		if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection))
-			return TRUE;
-	}
-
-	return FALSE;
+	/* check at least one AP is compatible with this connection */
+	return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE);
 }
 
 /*
@@ -957,7 +731,6 @@ complete_connection (NMDevice *device,
                      GError **error)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
 	const char *setting_mac;
 	char *str_ssid = NULL;
@@ -965,7 +738,6 @@ complete_connection (NMDevice *device,
 	const GByteArray *ssid = NULL;
 	GByteArray *tmp_ssid = NULL;
 	GBytes *setting_ssid = NULL;
-	GSList *iter;
 	gboolean hidden = FALSE;
 	const char *perm_hw_addr;
 
@@ -991,12 +763,7 @@ complete_connection (NMDevice *device,
 		}
 
 		/* Find a compatible AP in the scan list */
-		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
-			if (nm_ap_check_compatible (NM_AP (iter->data), connection)) {
-				ap = NM_AP (iter->data);
-				break;
-			}
-		}
+		ap = find_first_compatible_ap (self, connection, FALSE);
 
 		/* If we still don't have an AP, then the WiFI settings needs to be
 		 * fully specified by the client.  Might not be able to find an AP
@@ -1159,8 +926,7 @@ can_auto_connect (NMDevice *device,
                   char **specific_object)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList *ap_iter;
+	NMAccessPoint *ap;
 	const char *method = NULL;
 	guint64 timestamp = 0;
 
@@ -1181,130 +947,141 @@ can_auto_connect (NMDevice *device,
 	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
 		return TRUE;
 
-	for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
-		NMAccessPoint *ap = NM_AP (ap_iter->data);
-
-		if (nm_ap_check_compatible (ap, connection)) {
-			/* All good; connection is usable */
-			*specific_object = (char *) nm_ap_get_dbus_path (ap);
-			return TRUE;
-		}
+	ap = find_first_compatible_ap (self, connection, FALSE);
+	if (ap) {
+		/* All good; connection is usable */
+		*specific_object = (char *) nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap));
+		return TRUE;
 	}
 
 	return FALSE;
 }
 
-static void
-ap_list_dump (NMDeviceWifi *self)
+static gint
+ap_id_compare (NMAccessPoint *a, NMAccessPoint *b)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList * elt;
-	int i = 0;
+	guint32 a_id = nm_ap_get_id (a);
+	guint32 b_id = nm_ap_get_id (b);
 
-	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+	return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1);
+}
 
-	_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: ");
-	}
-	_LOGD (LOGD_WIFI_SCAN, "Current AP list: done");
+static GSList *
+get_sorted_ap_list (NMDeviceWifi *self)
+{
+	GSList *sorted = NULL;
+	GHashTableIter iter;
+	NMAccessPoint *ap;
+
+	g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap))
+		sorted = g_slist_prepend (sorted, ap);
+	return g_slist_sort (sorted, (GCompareFunc) ap_id_compare);
 }
 
-static gboolean
-impl_device_get_access_points (NMDeviceWifi *self,
-                               GPtrArray **aps,
-                               GError **err)
+static void
+impl_device_wifi_get_access_points (NMDeviceWifi *self,
+                                    GDBusMethodInvocation *context)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList *elt;
+	GSList *sorted, *iter;
+	GPtrArray *paths;
 
-	*aps = g_ptr_array_new ();
-	for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) {
-		NMAccessPoint *ap = NM_AP (elt->data);
+	paths = g_ptr_array_new ();
+	sorted = get_sorted_ap_list (self);
+	for (iter = sorted; iter; iter = iter->next) {
+		NMAccessPoint *ap = NM_AP (iter->data);
 
 		if (nm_ap_get_ssid (ap))
-			g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap)));
+			g_ptr_array_add (paths, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap))));
 	}
-	return TRUE;
+	g_ptr_array_add (paths, NULL);
+	g_slist_free (sorted);
+
+	g_dbus_method_invocation_return_value (context, g_variant_new ("(^ao)", (char **) paths->pdata));
+	g_ptr_array_unref (paths);
 }
 
-static gboolean
-impl_device_get_all_access_points (NMDeviceWifi *self,
-                                   GPtrArray **aps,
-                                   GError **err)
+static void
+impl_device_wifi_get_all_access_points (NMDeviceWifi *self,
+                                        GDBusMethodInvocation *context)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	GSList *elt;
+	GSList *sorted, *iter;
+	GPtrArray *paths;
 
-	*aps = g_ptr_array_new ();
-	for (elt = priv->ap_list; elt; elt = g_slist_next (elt))
-		g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (elt->data))));
-	return TRUE;
+	paths = g_ptr_array_new ();
+	sorted = get_sorted_ap_list (self);
+	for (iter = sorted; iter; iter = iter->next)
+		g_ptr_array_add (paths, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data))));
+	g_ptr_array_add (paths, NULL);
+	g_slist_free (sorted);
+
+	g_dbus_method_invocation_return_value (context, g_variant_new ("(^ao)", (char **) paths->pdata));
+	g_ptr_array_unref (paths);
 }
 
 static void
 request_scan_cb (NMDevice *device,
-                 DBusGMethodInvocation *context,
+                 GDBusMethodInvocation *context,
+                 NMAuthSubject *subject,
                  GError *error,
                  gpointer user_data)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	GError *local = NULL;
-	gs_unref_hashtable GHashTable *new_scan_options = user_data;
+	gs_unref_variant GVariant *new_scan_options = user_data;
 
 	if (error) {
-		dbus_g_method_return_error (context, error);
+		g_dbus_method_invocation_return_gerror (context, error);
 		return;
 	}
 
 	if (!check_scanning_allowed (self)) {
-		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);
+		g_dbus_method_invocation_return_error_literal (context,
+		                                               NM_DEVICE_ERROR,
+		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
+		                                               "Scanning not allowed at this time");
 		return;
 	}
 
 	cancel_pending_scan (self);
 	request_wireless_scan (self, new_scan_options);
-	dbus_g_method_return (context);
+	g_dbus_method_invocation_return_value (context, NULL);
 }
 
 static void
-impl_device_request_scan (NMDeviceWifi *self,
-                          GHashTable *options,
-                          DBusGMethodInvocation *context)
+impl_device_wifi_request_scan (NMDeviceWifi *self,
+                               GDBusMethodInvocation *context,
+                               GVariant *options)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 	gint32 last_scan;
-	GError *error;
 
 	if (   !priv->enabled
 	    || !priv->sup_iface
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
-		error = g_error_new_literal (NM_DEVICE_ERROR,
-		                             NM_DEVICE_ERROR_NOT_ALLOWED,
-		                             "Scanning not allowed while unavailable or activating");
-		goto error;
+		g_dbus_method_invocation_return_error_literal (context,
+		                                               NM_DEVICE_ERROR,
+		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
+		                                               "Scanning not allowed while unavailable or activating");
+		return;
 	}
 
 	if (nm_supplicant_interface_get_scanning (priv->sup_iface)) {
-		error = g_error_new_literal (NM_DEVICE_ERROR,
-		                             NM_DEVICE_ERROR_NOT_ALLOWED,
-		                             "Scanning not allowed while already scanning");
-		goto error;
+		g_dbus_method_invocation_return_error_literal (context,
+		                                               NM_DEVICE_ERROR,
+		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
+		                                               "Scanning not allowed while already scanning");
+		return;
 	}
 
 	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_DEVICE_ERROR,
-		                             NM_DEVICE_ERROR_NOT_ALLOWED,
-		                             "Scanning not allowed immediately following previous scan");
-		goto error;
+		g_dbus_method_invocation_return_error_literal (context,
+		                                               NM_DEVICE_ERROR,
+		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
+		                                               "Scanning not allowed immediately following previous scan");
+		return;
 	}
 
 	/* Ask the manager to authenticate this request for us */
@@ -1315,12 +1092,7 @@ impl_device_request_scan (NMDeviceWifi *self,
 	                       NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	                       TRUE,
 	                       request_scan_cb,
-	                       options ? g_hash_table_ref (options) : NULL);
-	return;
-
-error:
-	dbus_g_method_return_error (context, error);
-	g_error_free (error);
+	                       options ? g_variant_ref (options) : NULL);
 }
 
 static gboolean
@@ -1367,7 +1139,7 @@ scanning_allowed (NMDeviceWifi *self)
 	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
 		return FALSE;
 
-	connection = nm_device_get_connection (NM_DEVICE (self));
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	if (connection) {
 		NMSettingWireless *s_wifi;
 		const char *ip4_method = NULL;
@@ -1481,8 +1253,32 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	return ssids;
 }
 
+static GPtrArray *
+ssids_options_to_ptrarray (GVariant *value)
+{
+	GPtrArray *ssids = NULL;
+	GByteArray *ssid_array;
+	GVariant *v;
+	const guint8 *bytes;
+	gsize len;
+	int num_ssids, i;
+
+	num_ssids = g_variant_n_children (value);
+	if (num_ssids) {
+		ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref);
+		for (i = 0; i < num_ssids; i++) {
+			v = g_variant_get_child_value (value, i);
+			bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8));
+			ssid_array = g_byte_array_new ();
+			g_byte_array_append (ssid_array, bytes, len);
+			g_ptr_array_add (ssids, ssid_array);
+		}
+	}
+	return ssids;
+}
+
 static void
-request_wireless_scan (NMDeviceWifi *self, GHashTable *scan_options)
+request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	gboolean backoff = FALSE;
@@ -1496,13 +1292,16 @@ request_wireless_scan (NMDeviceWifi *self, GHashTable *scan_options)
 	if (check_scanning_allowed (self)) {
 		_LOGD (LOGD_WIFI_SCAN, "scanning requested");
 
-		if (scan_options && g_hash_table_size (scan_options)) {
-			GValue *val = g_hash_table_lookup (scan_options, "ssids");
+		if (scan_options) {
+			GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL);
 
-			if (val && G_VALUE_HOLDS (val, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR))
-				ssids = g_ptr_array_ref (g_value_get_boxed (val));
-			else
-				_LOGD (LOGD_WIFI_SCAN, "ignoring invalid scan options");
+			if (val) {
+				if (g_variant_is_of_type (val, G_VARIANT_TYPE ("aay")))
+					ssids = ssids_options_to_ptrarray (val);
+				else
+					_LOGD (LOGD_WIFI_SCAN, "ignoring invalid 'ssids' scan option");
+				g_variant_unref (val);
+			}
 		}
 		if (!ssids)
 			ssids = build_hidden_probe_list (self);
@@ -1606,10 +1405,7 @@ cancel_pending_scan (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	if (priv->pending_scan_id) {
-		g_source_remove (priv->pending_scan_id);
-		priv->pending_scan_id = 0;
-	}
+	nm_clear_g_source (&priv->pending_scan_id);
 }
 
 static void
@@ -1621,13 +1417,9 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
 
 	_LOGD (LOGD_WIFI_SCAN, "scan %s", success ? "successful" : "failed");
 
+	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
 	schedule_scan (self, success);
 
-	/* Ensure that old APs get removed, which otherwise only
-	 * happens when there are new BSSes.
-	 */
-	schedule_scanlist_cull (self);
-
 	if (priv->requested_scan) {
 		priv->requested_scan = FALSE;
 		nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE);
@@ -1639,7 +1431,35 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
  *
  */
 
-#define WPAS_REMOVED_TAG "supplicant-removed"
+static gboolean
+ap_list_dump (gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *sorted, *iter;
+
+	priv->ap_dump_id = 0;
+	_LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]",
+	       nm_utils_get_monotonic_timestamp_s (),
+	       priv->last_scan,
+	       priv->scheduled_scan_time);
+	sorted = get_sorted_ap_list (self);
+	for (iter = sorted; iter; iter = iter->next)
+		nm_ap_dump (NM_AP (iter->data), "dump    ", nm_device_get_iface (NM_DEVICE (self)));
+	g_slist_free (sorted);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+schedule_ap_list_dump (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN))
+		return;
+	nm_clear_g_source (&priv->ap_dump_id);
+	priv->ap_dump_id = g_timeout_add_seconds (1, ap_list_dump, self);
+}
 
 static void
 try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
@@ -1673,213 +1493,77 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
 	}
 }
 
-/*
- * merge_scanned_ap
- *
- * If there is already an entry that matches the BSSID and ESSID of the
- * AP to merge, replace that entry with the scanned AP.  Otherwise, add
- * the scanned AP to the list.
- *
- * TODO: possibly need to differentiate entries based on security too; i.e. if
- * there are two scan results with the same BSSID and SSID but different
- * security options?
- *
- */
 static void
-merge_scanned_ap (NMDeviceWifi *self,
-                  NMAccessPoint *merge_ap)
+supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+                             const char *object_path,
+                             GVariant *properties,
+                             NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDeviceState state;
+	NMAccessPoint *ap;
 	NMAccessPoint *found_ap = NULL;
 	const GByteArray *ssid;
-	gboolean strict_match = TRUE;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (properties != NULL);
+	g_return_if_fail (iface != NULL);
+
+	/* Ignore new APs when unavailable, unmanaged, or in AP mode */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
+		return;
+	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
+		return;
+
+	ap = nm_ap_new_from_properties (object_path, properties);
+	if (!ap) {
+		_LOGD (LOGD_WIFI_SCAN, "invalid AP properties received for %s", object_path);
+		return;
+	}
 
 	/* Let the manager try to fill in the SSID from seen-bssids lists */
-	ssid = nm_ap_get_ssid (merge_ap);
+	ssid = nm_ap_get_ssid (ap);
 	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
 		/* Try to fill the SSID from the AP database */
-		try_fill_ssid_for_hidden_ap (merge_ap);
+		try_fill_ssid_for_hidden_ap (ap);
 
-		ssid = nm_ap_get_ssid (merge_ap);
+		ssid = nm_ap_get_ssid (ap);
 		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
 			/* Yay, matched it, no longer treat as hidden */
 			_LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'",
-			       nm_ap_get_address (merge_ap), nm_utils_escape_ssid (ssid->data, ssid->len));
-			nm_ap_set_broadcast (merge_ap, FALSE);
+			       nm_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
 		} else {
 			/* Didn't have an entry for this AP in the database */
 			_LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s",
-			       nm_ap_get_address (merge_ap));
+			       nm_ap_get_address (ap));
 		}
 	}
 
-	/* If the incoming scan result matches the hidden AP that NM is currently
-	 * connected to but hasn't been seen in the scan list yet, don't use
-	 * strict matching.  Because the capabilities of the fake AP have to be
-	 * constructed from the NMConnection of the activation request, they won't
-	 * always be the same as the capabilities of the real AP from the scan.
-	 */
-	if (priv->current_ap && nm_ap_get_fake (priv->current_ap))
-		strict_match = FALSE;
-
-	found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap));
-	if (!found_ap)
-		found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
+	found_ap = get_ap_by_supplicant_path (self, object_path);
 	if (found_ap) {
-		_LOGD (LOGD_WIFI_SCAN, "merging AP '%s' %s (%p) with existing (%p)",
-		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-		            nm_ap_get_address (merge_ap),
-		            merge_ap,
-		            found_ap);
-
-		nm_ap_set_supplicant_path (found_ap, nm_ap_get_supplicant_path (merge_ap));
-		nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap));
-		nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap));
-		nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap));
-		nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap));
-		nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap));
-		nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap));
-		nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap));
-		nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap));
-
-		/* If the AP is noticed in a scan, it's automatically no longer
-		 * fake, since it clearly exists somewhere.
-		 */
-		nm_ap_set_fake (found_ap, FALSE);
-		g_object_set_data (G_OBJECT (found_ap), WPAS_REMOVED_TAG, NULL);
+		nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self)));
+		nm_ap_update_from_properties (found_ap, object_path, properties);
 	} else {
-		/* New entry in the list */
-		_LOGD (LOGD_WIFI_SCAN, "adding new AP '%s' %s (%p)",
-		       ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
-		       nm_ap_get_address (merge_ap), merge_ap);
-
-		g_object_ref (merge_ap);
-		priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
-		nm_ap_export_to_dbus (merge_ap);
-		emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE);
+		nm_ap_dump (ap, "added   ", nm_device_get_iface (NM_DEVICE (self)));
+		ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
 	}
-}
 
-static gboolean
-cull_scan_list (NMDeviceWifi *self)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	gint32 now = nm_utils_get_monotonic_timestamp_s ();
-	GSList *outdated_list = NULL;
-	GSList *elt;
-	guint32 removed = 0, total = 0;
-
-	priv->scanlist_cull_id = 0;
-
-	_LOGD (LOGD_WIFI_SCAN, "checking scan list for outdated APs");
+	g_object_unref (ap);
 
-	/* Walk the access point list and remove any access points older than
-	 * three times the inactive scan interval.
+	/* Update the current AP if the supplicant notified a current BSS change
+	 * before it sent the current BSS's scan result.
 	 */
-	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) {
-		NMAccessPoint *ap = elt->data;
-		const guint prune_interval_s = SCAN_INTERVAL_MAX * 3;
-		gint32 last_seen;
-
-		/* Don't cull the associated AP or manually created APs */
-		if (ap == priv->current_ap)
-			continue;
-		g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */
-
-		/* Don't cull APs still known to the supplicant.  Since the supplicant
-		 * doesn't yet emit property updates for "last seen" we have to rely
-		 * on changing signal strength for updating "last seen".  But if the
-		 * AP's strength doesn't change we won't get any updates for the AP,
-		 * and we'll end up here even if the AP was still found by the
-		 * supplicant in the last scan.
-		 */
-		if (   nm_ap_get_supplicant_path (ap)
-		    && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL)
-			continue;
-
-		last_seen = nm_ap_get_last_seen (ap);
-		if (!last_seen || last_seen + prune_interval_s < now)
-			outdated_list = g_slist_prepend (outdated_list, ap);
-	}
-
-	/* Remove outdated APs */
-	for (elt = outdated_list; elt; elt = g_slist_next (elt)) {
-		NMAccessPoint *outdated_ap = NM_AP (elt->data);
-		const GByteArray *ssid;
-
-		ssid = nm_ap_get_ssid (outdated_ap);
-		_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);
-
-	_LOGD (LOGD_WIFI_SCAN, "removed %d APs (of %d)",
-	       removed, total);
-
-	ap_list_dump (self);
-
-	if(removed > 0)
-	    nm_device_recheck_available_connections (NM_DEVICE (self));
-
-	return FALSE;
-}
+	if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0)
+		supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self);
 
-static void
-schedule_scanlist_cull (NMDeviceWifi *self)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	/* Cull the scan list after the last request for it has come in */
-	if (priv->scanlist_cull_id)
-		g_source_remove (priv->scanlist_cull_id);
-	priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self);
-}
-
-static void
-supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
-                             const char *object_path,
-                             GVariant *properties,
-                             NMDeviceWifi *self)
-{
-	NMDeviceState state;
-	NMAccessPoint *ap;
-
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (properties != NULL);
-	g_return_if_fail (iface != NULL);
-
-	/* Ignore new APs when unavailable, unmanaged, or in AP mode */
-	state = nm_device_get_state (NM_DEVICE (self));
-	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
-		return;
-	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
-		return;
-
-	ap = nm_ap_new_from_properties (object_path, properties);
-	if (ap) {
-		nm_ap_dump (ap, "New AP: ");
-
-		/* Add the AP to the device's AP list */
-		merge_scanned_ap (self, ap);
-		g_object_unref (ap);
-	} else
-		_LOGD (LOGD_WIFI_SCAN, "invalid AP properties received for %s", object_path);
-
-	/* Remove outdated access points */
-	schedule_scanlist_cull (self);
+	schedule_ap_list_dump (self);
 }
 
 static void
 supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
                                  const char *object_path,
-                                 GHashTable *properties,
+                                 GVariant *properties,
                                  NMDeviceWifi *self)
 {
 	NMDeviceState state;
@@ -1889,18 +1573,17 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 	g_return_if_fail (object_path != NULL);
 	g_return_if_fail (properties != NULL);
 
-	/* Ignore new APs when unavailable or unamnaged */
+	/* Ignore new APs when unavailable or unmanaged */
 	state = nm_device_get_state (NM_DEVICE (self));
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
 		return;
 
-	/* Update the AP's last-seen property */
 	ap = get_ap_by_supplicant_path (self, object_path);
-	if (ap)
-		nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
-
-	/* Remove outdated access points */
-	schedule_scanlist_cull (self);
+	if (ap) {
+		nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self)));
+		nm_ap_update_from_properties (ap, object_path, properties);
+		schedule_ap_list_dump (self);
+	}
 }
 
 static void
@@ -1908,28 +1591,28 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
                                  const char *object_path,
                                  NMDeviceWifi *self)
 {
+	NMDeviceWifiPrivate *priv;
 	NMAccessPoint *ap;
 
 	g_return_if_fail (self != NULL);
 	g_return_if_fail (object_path != NULL);
 
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	ap = get_ap_by_supplicant_path (self, object_path);
 	if (ap) {
-		gint32 now = nm_utils_get_monotonic_timestamp_s ();
-		gint32 last_seen = nm_ap_get_last_seen (ap);
-
-		/* We don't know when the supplicant last saw the AP's beacons,
-		 * it could be two minutes or it could be 2 seconds.  Because the
-		 * supplicant doesn't send property change notifications if the
-		 * AP's other properties don't change, our last-seen time may be
-		 * much older the supplicant's, and the AP would be immediately
-		 * removed from the list on the next cleanup.  So update the
-		 * last-seen time to ensure the AP sticks around for at least
-		 * one more periodic scan.
-		 */
-		nm_ap_set_last_seen (ap, MAX (last_seen, now - SCAN_INTERVAL_MAX));
-		g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE));
-}
+		if (ap == priv->current_ap) {
+			/* The current AP cannot be removed (to prevent NM indicating that
+			 * it is connected, but to nothing), but it must be removed later
+			 * when the current AP is changed or cleared.  Set 'fake' to
+			 * indicate that this AP is now unknown to the supplicant.
+			 */
+			nm_ap_set_fake (ap, TRUE);
+		} else {
+			nm_ap_dump (ap, "removed ", nm_device_get_iface (NM_DEVICE (self)));
+			ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
+			schedule_ap_list_dump (self);
+		}
+	}
 }
 
 static void
@@ -1937,15 +1620,8 @@ remove_supplicant_timeouts (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	if (priv->sup_timeout_id) {
-		g_source_remove (priv->sup_timeout_id);
-		priv->sup_timeout_id = 0;
-	}
-
-	if (priv->link_timeout_id) {
-		g_source_remove (priv->link_timeout_id);
-		priv->link_timeout_id = 0;
-	}
+	nm_clear_g_source (&priv->sup_timeout_id);
+	nm_clear_g_source (&priv->link_timeout_id);
 }
 
 static void
@@ -1970,17 +1646,19 @@ cleanup_supplicant_failures (NMDeviceWifi *self)
 
 static void
 wifi_secrets_cb (NMActRequest *req,
-                 guint32 call_id,
-                 NMConnection *connection,
+                 NMActRequestGetSecretsCallId call_id,
+                 NMSettingsConnection *connection,
                  GError *error,
                  gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 
-	g_return_if_fail (req == nm_device_get_act_request (device));
+	if (req != nm_device_get_act_request (device))
+		return;
+
 	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);
+	g_return_if_fail (nm_act_request_get_settings_connection (req) == connection);
 
 	if (error) {
 		_LOGW (LOGD_WIFI, "%s", error->message);
@@ -2015,23 +1693,7 @@ link_timeout_cb (gpointer user_data)
 	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
-	 * attempted to reconnect, then it's probably out of range or turned off.
-	 */
-	if (priv->ssid_found == FALSE && priv->current_ap) {
-		NMAccessPoint *old_ap = g_object_ref (priv->current_ap);
-
-		set_current_ap (self, NULL, TRUE);
-
-		/* If it was an external entity and the supplicant doesn't know about
-		 * it, remove it from the scan list.  If it's still around, it'll get
-		 * found in the next scan.
-		 */
-		if (    nm_ap_get_mode (old_ap) == NM_802_11_MODE_INFRA
-		    && g_object_get_data (G_OBJECT (old_ap), WPAS_REMOVED_TAG))
-			remove_access_point (self, old_ap);
-		g_object_unref (old_ap);
-	}
+	set_current_ap (self, NULL, TRUE);
 
 	nm_device_state_changed (device,
 	                         NM_DEVICE_STATE_FAILED,
@@ -2052,7 +1714,7 @@ need_new_8021x_secrets (NMDeviceWifi *self,
 
 	g_assert (setting_name != NULL);
 
-	connection = nm_device_get_connection (NM_DEVICE (self));
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_return_val_if_fail (connection != NULL, FALSE);
 
 	/* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's
@@ -2105,7 +1767,7 @@ need_new_wpa_psk (NMDeviceWifi *self,
 
 	g_assert (setting_name != NULL);
 
-	connection = nm_device_get_connection (NM_DEVICE (self));
+	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_return_val_if_fail (connection != NULL, FALSE);
 
 	/* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */
@@ -2133,7 +1795,6 @@ handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
 {
 	NMDevice *device = NM_DEVICE (self);
 	NMActRequest *req;
-	NMConnection *connection;
 	const char *setting_name = NULL;
 	gboolean handled = FALSE;
 
@@ -2142,13 +1803,10 @@ handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_return_val_if_fail (req != NULL, FALSE);
 
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
-
 	if (   need_new_8021x_secrets (self, old_state, &setting_name)
 	    || need_new_wpa_psk (self, old_state, &setting_name)) {
 
-		nm_connection_clear_secrets (connection);
+		nm_act_request_clear_secrets (req);
 
 		_LOGI (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: (wifi) disconnected during association, asking for new key");
@@ -2236,7 +1894,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			NMSettingWireless *s_wifi;
 			GBytes *ssid;
 
-			connection = nm_device_get_connection (NM_DEVICE (self));
+			connection = nm_device_get_applied_connection (NM_DEVICE (self));
 			g_return_if_fail (connection);
 
 			s_wifi = nm_connection_get_setting_wireless (connection);
@@ -2253,7 +1911,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			                                    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);
+			periodic_update (self);
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
 		if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
@@ -2359,7 +2017,53 @@ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
 	/* Run a quick update of current AP when coming out of a scan */
 	state = nm_device_get_state (NM_DEVICE (self));
 	if (!scanning && state == NM_DEVICE_STATE_ACTIVATED)
-		periodic_update (self, NULL);
+		periodic_update (self);
+}
+
+static void
+supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
+                                     GParamSpec *pspec,
+                                     NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	const char *current_bss;
+	NMAccessPoint *new_ap = NULL;
+
+	current_bss = nm_supplicant_interface_get_current_bss (iface);
+	if (current_bss)
+		new_ap = get_ap_by_supplicant_path (self, current_bss);
+
+	if (new_ap != priv->current_ap) {
+		const char *new_bssid = NULL;
+		const GByteArray *new_ssid = NULL;
+		const char *old_bssid = NULL;
+		const GByteArray *old_ssid = NULL;
+
+		/* Don't ever replace a "fake" current AP if we don't know about the
+		 * supplicant's current BSS yet.  It'll get replaced when we receive
+		 * the current BSS's scan result.
+		 */
+		if (new_ap == NULL && nm_ap_get_fake (priv->current_ap))
+			return;
+
+		if (new_ap) {
+			new_bssid = nm_ap_get_address (new_ap);
+			new_ssid = nm_ap_get_ssid (new_ap);
+		}
+
+		if (priv->current_ap) {
+			old_bssid = nm_ap_get_address (priv->current_ap);
+			old_ssid = nm_ap_get_ssid (priv->current_ap);
+		}
+
+		_LOGD (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);
+	}
 }
 
 static NMActStageReturn
@@ -2369,7 +2073,7 @@ handle_auth_or_fail (NMDeviceWifi *self,
 {
 	const char *setting_name;
 	guint32 tries;
-	NMConnection *connection;
+	NMConnection *applied_connection;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
 	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE);
@@ -2379,17 +2083,16 @@ handle_auth_or_fail (NMDeviceWifi *self,
 		g_assert (req);
 	}
 
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
+	applied_connection = nm_act_request_get_applied_connection (req);
 
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES));
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES));
 	if (tries > 3)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
-	nm_connection_clear_secrets (connection);
-	setting_name = nm_connection_need_secrets (connection, NULL);
+	nm_act_request_clear_secrets (req);
+	setting_name = nm_connection_need_secrets (applied_connection, NULL);
 	if (setting_name) {
 		NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
@@ -2397,7 +2100,7 @@ handle_auth_or_fail (NMDeviceWifi *self,
 			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));
+		g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else
 		_LOGW (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
@@ -2434,7 +2137,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	if (   priv->mode == NM_802_11_MODE_ADHOC
@@ -2468,7 +2171,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 		 * dialogs, just retry or fail, and if we never connect the user can
 		 * fix the password somewhere else.
 		 */
-		if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp))
+		if (nm_settings_connection_get_timestamp (nm_act_request_get_settings_connection (req), &timestamp))
 			new_secrets = !timestamp;
 
 		if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -2491,12 +2194,16 @@ supplicant_connection_timeout_cb (gpointer user_data)
 static NMSupplicantConfig *
 build_supplicant_config (NMDeviceWifi *self,
                          NMConnection *connection,
-                         guint32 fixed_freq)
+                         guint32 fixed_freq,
+                         GError **error)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSupplicantConfig *config = NULL;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
+	NMSupplicantFeature mac_randomization_support;
+	NMSettingMacRandomization mac_randomization_fallback;
+	gs_free char *svalue = NULL;
 
 	g_return_val_if_fail (self != NULL, NULL);
 
@@ -2504,19 +2211,29 @@ build_supplicant_config (NMDeviceWifi *self,
 	g_return_val_if_fail (s_wireless != NULL, NULL);
 
 	config = nm_supplicant_config_new ();
-	if (!config)
-		return NULL;
 
 	/* 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_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_UNKNOWN) {
 		_LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
 	}
 
+	mac_randomization_support = nm_supplicant_interface_get_mac_randomization_support (priv->sup_iface);
+	svalue = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                                "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
+	                                                NM_DEVICE (self));
+	mac_randomization_fallback = _nm_utils_ascii_str_to_int64 (svalue, 10,
+	                                                           NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
+	                                                           NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
+	                                                           NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
+
 	if (!nm_supplicant_config_add_setting_wireless (config,
 	                                                s_wireless,
-	                                                fixed_freq)) {
-		_LOGE (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
+	                                                fixed_freq,
+	                                                mac_randomization_support,
+	                                                mac_randomization_fallback,
+	                                                error)) {
+		g_prefix_error (error, "802-11-wireless: ");
 		goto error;
 	}
 
@@ -2533,13 +2250,14 @@ build_supplicant_config (NMDeviceWifi *self,
 		                                                         s_wireless_sec,
 		                                                         s_8021x,
 		                                                         con_uuid,
-		                                                         mtu)) {
-			_LOGE (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to supplicant config.");
+		                                                         mtu,
+		                                                         error)) {
+			g_prefix_error (error, "802-11-wireless-security: ");
 			goto error;
 		}
 	} else {
-		if (!nm_supplicant_config_add_no_security (config)) {
-			_LOGE (LOGD_WIFI, "Couldn't add unsecured option to supplicant config.");
+		if (!nm_supplicant_config_add_no_security (config, error)) {
+			g_prefix_error (error, "unsecured-option: ");
 			goto error;
 		}
 	}
@@ -2564,7 +2282,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
 	const char *cloned_mac;
-	GSList *iter;
 	const char *mode;
 	const char *ap_path;
 
@@ -2575,7 +2292,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
@@ -2611,25 +2328,17 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 
 	/* AP mode never uses a specific object or existing scanned AP */
 	if (priv->mode != NM_802_11_MODE_AP) {
-
 		ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
 		ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
 		if (ap)
 			goto done;
 
-		/* Find a compatible AP in the scan list */
-		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
-			NMAccessPoint *candidate = NM_AP (iter->data);
-
-			if (nm_ap_check_compatible (candidate, connection)) {
-				ap = candidate;
-				break;
-			}
-		}
+		ap = find_first_compatible_ap (self, connection, FALSE);
 	}
 
 	if (ap) {
-		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap));
+		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+		                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)));
 		goto done;
 	}
 
@@ -2642,18 +2351,15 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	ap = nm_ap_new_fake_from_connection (connection);
 	g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA)
-		nm_ap_set_broadcast (ap, FALSE);
-	else if (nm_ap_is_hotspot (ap))
+	if (nm_ap_is_hotspot (ap))
 		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);
 	g_object_freeze_notify (G_OBJECT (self));
-	set_current_ap (self, ap, FALSE);
-	emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE);
+	ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
 	g_object_thaw_notify (G_OBJECT (self));
-	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap));
+	set_current_ap (self, ap, FALSE);
+	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+	                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)));
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 
 done:
@@ -2699,6 +2405,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	const char *setting_name;
 	NMSettingWireless *s_wireless;
+	GError *error = NULL;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
@@ -2713,7 +2420,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		goto out;
 	}
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
@@ -2752,11 +2459,19 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_ap_is_hotspot (ap))
 		ensure_hotspot_frequency (self, s_wireless, ap);
 
+	if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA) {
+		nm_platform_wifi_set_powersave (NM_PLATFORM_GET,
+		                                nm_device_get_ifindex (device),
+		                                nm_setting_wireless_get_powersave (s_wireless));
+	}
+
 	/* Build up the supplicant configuration */
-	config = build_supplicant_config (self, connection, nm_ap_get_freq (ap));
+	config = build_supplicant_config (self, connection, nm_ap_get_freq (ap), &error);
 	if (config == NULL) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) couldn't build wireless configuration.");
+		       "Activation: (wifi) couldn't build wireless configuration: %s",
+		       error ? error->message : "<BUG>");
+		g_clear_error (&error);
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
 	}
@@ -2767,9 +2482,11 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	                  G_CALLBACK (supplicant_iface_connection_error_cb),
 	                  self);
 
-	if (!nm_supplicant_interface_set_config (priv->sup_iface, config)) {
+	if (!nm_supplicant_interface_set_config (priv->sup_iface, config, &error)) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) couldn't send wireless configuration to the supplicant.");
+		       "Activation: (wifi) couldn't send wireless configuration to the supplicant: %s",
+		       error ? error->message : "<BUG>");
+		g_clear_error (&error);
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
 	}
@@ -2805,7 +2522,7 @@ act_stage3_ip4_config_start (NMDevice *device,
 	NMSettingIPConfig *s_ip4;
 	const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (s_ip4)
@@ -2827,7 +2544,7 @@ act_stage3_ip6_config_start (NMDevice *device,
 	NMSettingIPConfig *s_ip6;
 	const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	if (s_ip6)
@@ -2848,7 +2565,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	NMSettingWireless *s_wifi;
 	guint32 mtu;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
@@ -2935,7 +2652,7 @@ act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 	gboolean may_fail = FALSE, chain_up = FALSE;
 	NMActStageReturn ret;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
@@ -2956,7 +2673,7 @@ act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 	gboolean may_fail = FALSE, chain_up = FALSE;
 	NMActStageReturn ret;
 
-	connection = nm_device_get_connection (device);
+	connection = nm_device_get_applied_connection (device);
 	g_assert (connection);
 
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
@@ -2975,78 +2692,53 @@ activation_success_handler (NMDevice *device)
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ifindex (device);
-	NMAccessPoint *ap;
-	guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
-	NMAccessPoint *tmp_ap = NULL;
 	NMActRequest *req;
-	NMConnection *connection;
+	NMConnection *applied_connection;
 
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
+	applied_connection = nm_act_request_get_applied_connection (req);
 
 	/* Clear any critical protocol notification in the wifi stack */
 	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
 
 	/* Clear wireless secrets tries on success */
-	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
-
-	ap = priv->current_ap;
-
-	/* If the AP isn't fake, it was found in the scan list and all its
-	 * details are known.
-	 */
-	if (!ap || !nm_ap_get_fake (ap)){
-		ap = NULL;
-		goto done;
-	}
+	g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, NULL);
 
-	/* If the activate AP was fake, it probably won't have a BSSID at all.
-	 * 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.
+	/* There should always be a current AP, either a fake one because we haven't
+	 * seen a scan result for the activated AP yet, or a real one from the
+	 * supplicant's scan list.
 	 */
-	nm_platform_wifi_get_bssid (NM_PLATFORM_GET, 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 (NM_PLATFORM_GET, ifindex));
-	if (!nm_ap_get_max_bitrate (ap))
-		nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
-
-	tmp_ap = find_active_ap (self, ap, TRUE);
-	if (tmp_ap) {
-		const GByteArray *ssid = nm_ap_get_ssid (tmp_ap);
-
-		/* Found a better match in the scan list than the fake AP.  Use it
-		 * instead.
-		 */
-
-		/* 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);
+	g_warn_if_fail (priv->current_ap);
+	if (priv->current_ap) {
+		if (nm_ap_get_fake (priv->current_ap)) {
+			/* If the activation AP hasn't been seen by the supplicant in a scan
+			 * yet, it will be "fake".  This usually happens for Ad-Hoc and
+			 * AP-mode connections.  Fill in the details from the device itself
+			 * until the supplicant sends the scan result.
+			 */
+			if (!nm_ap_get_address (priv->current_ap)) {
+				guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+				gs_free char *bssid_str = NULL;
+
+				if (   nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid)
+				    && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
+					bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
+					nm_ap_set_address (priv->current_ap, bssid_str);
+				}
+			}
+			if (!nm_ap_get_freq (priv->current_ap))
+				nm_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex));
+			if (!nm_ap_get_max_bitrate (priv->current_ap))
+				nm_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
 		}
 
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-		                                          nm_ap_get_dbus_path (tmp_ap));
+		                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (priv->current_ap)));
 	}
 
-done:
-	periodic_update (self, ap);
-
-	/* ap might be already unrefed, because it was a fake_ap. But we don't touch it... */
-	if (tmp_ap && ap == priv->current_ap) {
-		/* Strange, we would expect periodic_update() to find a better AP
-		 * then the fake one and reset it. Reset the fake current_ap to NULL
-		 * now, which will remove the fake ap.
-		 **/
-		set_current_ap (self, NULL, TRUE);
-	}
+	periodic_update (self);
 
 	update_seen_bssids_cache (self, priv->current_ap);
 
@@ -3057,13 +2749,13 @@ done:
 static void
 activation_failure_handler (NMDevice *device)
 {
-	NMConnection *connection;
+	NMConnection *applied_connection;
 
-	connection = nm_device_get_connection (device);
-	g_assert (connection);
+	applied_connection = nm_device_get_applied_connection (device);
+	g_assert (applied_connection);
 
 	/* Clear wireless secrets tries on failure */
-	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+	g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, NULL);
 
 	/* Clear any critical protocol notification in the wifi stack */
 	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE);
@@ -3086,10 +2778,7 @@ device_state_changed (NMDevice *device,
 		if (priv->sup_iface)
 			supplicant_interface_release (self);
 
-		if (priv->periodic_source_id) {
-			g_source_remove (priv->periodic_source_id);
-			priv->periodic_source_id = 0;
-		}
+		nm_clear_g_source (&priv->periodic_source_id);
 
 		cleanup_association_attempt (self, TRUE);
 		cleanup_supplicant_failures (self);
@@ -3196,22 +2885,25 @@ set_enabled (NMDevice *device, gboolean enabled)
 /********************************************************************/
 
 NMDevice *
-nm_device_wifi_new (NMPlatformLink *platform_device)
+nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities)
 {
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI,
-	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                  NM_DEVICE_TYPE_DESC, "802.11 WiFi",
-	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
-	                                  NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
-	                                  NULL);
+	return g_object_new (NM_TYPE_DEVICE_WIFI,
+	                     NM_DEVICE_IFACE, iface,
+	                     NM_DEVICE_TYPE_DESC, "802.11 WiFi",
+	                     NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
+	                     NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI,
+	                     NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
+	                     NM_DEVICE_WIFI_CAPABILITIES, (guint) capabilities,
+	                     NULL);
 }
 
 static void
 nm_device_wifi_init (NMDeviceWifi *self)
 {
-	NM_DEVICE_WIFI_GET_PRIVATE (self)->mode = NM_802_11_MODE_INFRA;
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	priv->mode = NM_802_11_MODE_INFRA;
+	priv->aps = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 static void
@@ -3220,17 +2912,7 @@ dispose (GObject *object)
 	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	if (priv->disposed) {
-		G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
-		return;
-	}
-
-	priv->disposed = TRUE;
-
-	if (priv->periodic_source_id) {
-		g_source_remove (priv->periodic_source_id);
-		priv->periodic_source_id = 0;
-	}
+	nm_clear_g_source (&priv->periodic_source_id);
 
 	cleanup_association_attempt (self, TRUE);
 	supplicant_interface_release (self);
@@ -3246,6 +2928,13 @@ dispose (GObject *object)
 static void
 finalize (GObject *object)
 {
+	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_assert (g_hash_table_size (priv->aps) == 0);
+
+	g_hash_table_unref (priv->aps);
+
 	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
 }
 
@@ -3255,8 +2944,9 @@ get_property (GObject *object, guint prop_id,
 {
 	NMDeviceWifi *device = NM_DEVICE_WIFI (object);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+	GHashTableIter iter;
+	const char *dbus_path;
 	GPtrArray *array;
-	GSList *iter;
 
 	switch (prop_id) {
 	case PROP_PERM_HW_ADDRESS:
@@ -3272,16 +2962,15 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->capabilities);
 		break;
 	case PROP_ACCESS_POINTS:
-		array = g_ptr_array_sized_new (4);
-		for (iter = priv->ap_list; iter; iter = g_slist_next (iter))
-			g_ptr_array_add (array, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data))));
-		g_value_take_boxed (value, array);
+		array = g_ptr_array_sized_new (g_hash_table_size (priv->aps) + 1);
+		g_hash_table_iter_init (&iter, priv->aps);
+		while (g_hash_table_iter_next (&iter, (gpointer) &dbus_path, NULL))
+			g_ptr_array_add (array, g_strdup (dbus_path));
+		g_ptr_array_add (array, NULL);
+		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
 		break;
 	case PROP_ACTIVE_ACCESS_POINT:
-		if (priv->current_ap)
-			g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, priv->current_ap);
 		break;
 	case PROP_SCANNING:
 		g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->sup_iface));
@@ -3296,7 +2985,14 @@ static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
+	NMDeviceWifi *device = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+
 	switch (prop_id) {
+	case PROP_CAPABILITIES:
+		/* construct-only */
+		priv->capabilities = g_value_get_uint (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -3312,12 +3008,15 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate));
 
-	object_class->constructor = constructor;
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
+
+	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
+	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->bring_up = bring_up;
 	parent_class->can_auto_connect = can_auto_connect;
 	parent_class->is_available = is_available;
@@ -3366,22 +3065,23 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	g_object_class_install_property
 		(object_class, PROP_ACCESS_POINTS,
 		 g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_TYPE_STRV,
 		                     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_param_spec_string (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
+		                      NULL,
+		                      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_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
 		                    G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
@@ -3399,7 +3099,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 		              G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added),
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1,
-		              G_TYPE_OBJECT);
+		              NM_TYPE_AP);
 
 	signals[ACCESS_POINT_REMOVED] =
 		g_signal_new ("access-point-removed",
@@ -3408,7 +3108,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 		              0,
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1,
-		              G_TYPE_OBJECT);
+		              NM_TYPE_AP);
 
 	signals[SCANNING_ALLOWED] =
 		g_signal_new ("scanning-allowed",
@@ -3418,9 +3118,12 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 		              scanning_allowed_accumulator, NULL, NULL,
 		              G_TYPE_BOOLEAN, 0);
 
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_device_wifi_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_WIFI_SKELETON,
+	                                        "GetAccessPoints", impl_device_wifi_get_access_points,
+	                                        "GetAllAccessPoints", impl_device_wifi_get_all_access_points,
+	                                        "RequestScan", impl_device_wifi_request_scan,
+	                                        NULL);
 }
 
 
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
index bcba91da..7e0d06f5 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -22,8 +22,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_WIFI_H__
 #define __NETWORKMANAGER_DEVICE_WIFI_H__
 
-#include <glib-object.h>
-
 #include "nm-device.h"
 #include "nm-wifi-ap.h"
 
@@ -75,7 +73,7 @@ struct _NMDeviceWifiClass
 
 GType nm_device_wifi_get_type (void);
 
-NMDevice *nm_device_wifi_new (NMPlatformLink *platform_device);
+NMDevice * nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities);
 
 G_END_DECLS
 
diff --git a/src/devices/wifi/nm-wifi-ap-utils.c b/src/devices/wifi/nm-wifi-ap-utils.c
index 1a772fb7..d7916aa0 100644
--- a/src/devices/wifi/nm-wifi-ap-utils.c
+++ b/src/devices/wifi/nm-wifi-ap-utils.c
@@ -23,8 +23,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-#include <glib/gi18n.h>
-
+#include "nm-default.h"
 #include "nm-wifi-ap-utils.h"
 #include "nm-utils.h"
 
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index 72430021..771d6704 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -24,32 +24,29 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "nm-default.h"
 #include "nm-wifi-ap.h"
 #include "nm-wifi-ap-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
-#include "nm-logging.h"
-#include "nm-dbus-manager.h"
 #include "nm-core-internal.h"
 
 #include "nm-setting-wireless.h"
-#include "nm-glib-compat.h"
 
-#include "nm-access-point-glue.h"
+#include "nmdbus-access-point.h"
 
 /*
  * Encapsulates Access Point information
  */
 typedef struct
 {
-	char *dbus_path;
 	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
 
 	/* Scanned or cached values */
 	GByteArray *	ssid;
 	char *          address;
 	NM80211Mode		mode;
-	gint8			strength;
+	guint8			strength;
 	guint32			freq;		/* Frequency in MHz; ie 2412 (== 2.412 GHz) */
 	guint32			max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
 
@@ -60,13 +57,12 @@ typedef struct
 	/* Non-scanned attributes */
 	gboolean			fake;	/* Whether or not the AP is from a scan */
 	gboolean            hotspot;    /* Whether the AP is a local device's hotspot network */
-	gboolean			broadcast;	/* Whether or not the AP is broadcasting (hidden) */
 	gint32              last_seen;  /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
 } NMAccessPointPrivate;
 
 #define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate))
 
-G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT)
+G_DEFINE_TYPE (NMAccessPoint, nm_ap, NM_TYPE_EXPORTED_OBJECT)
 
 enum {
 	PROP_0,
@@ -83,267 +79,267 @@ enum {
 	LAST_PROP
 };
 
-static void
-nm_ap_init (NMAccessPoint *ap)
+/*****************************************************************/
+
+const char *
+nm_ap_get_supplicant_path (NMAccessPoint *ap)
 {
-	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
 
-	priv->dbus_path = NULL;
-	priv->mode = NM_802_11_MODE_INFRA;
-	priv->flags = NM_802_11_AP_FLAGS_NONE;
-	priv->wpa_flags = NM_802_11_AP_SEC_NONE;
-	priv->rsn_flags = NM_802_11_AP_SEC_NONE;
-	priv->broadcast = TRUE;
+	return NM_AP_GET_PRIVATE (ap)->supplicant_path;
 }
 
-static void
-finalize (GObject *object)
+guint32
+nm_ap_get_id (NMAccessPoint *ap)
 {
-	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+	g_return_val_if_fail (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (ap)), 0);
 
-	g_free (priv->dbus_path);
-	g_free (priv->supplicant_path);
-	if (priv->ssid)
+	return atoi (strrchr (nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)), '/') + 1);
+}
+
+const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->ssid;
+}
+
+void
+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);
+
+	/* same SSID */
+	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
+		if (!memcmp (ssid, priv->ssid->data, len))
+			return;
+	}
+
+	if (priv->ssid) {
 		g_byte_array_free (priv->ssid, TRUE);
-	g_free (priv->address);
+		priv->ssid = NULL;
+	}
 
-	G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object);
+	if (ssid) {
+		priv->ssid = g_byte_array_new ();
+		g_byte_array_append (priv->ssid, ssid, len);
+	}
+
+	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
 }
 
 static void
-set_property (GObject *object, guint prop_id,
-		    const GValue *value, GParamSpec *pspec)
+nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags)
 {
-	NMAccessPoint *ap = NM_AP (object);
-	GByteArray *ssid;
+	NMAccessPointPrivate *priv;
 
-	switch (prop_id) {
-	case PROP_FLAGS:
-		nm_ap_set_flags (ap, g_value_get_uint (value));
-		break;
-	case PROP_WPA_FLAGS:
-		nm_ap_set_wpa_flags (ap, g_value_get_uint (value));
-		break;
-	case PROP_RSN_FLAGS:
-		nm_ap_set_rsn_flags (ap, g_value_get_uint (value));
-		break;
-	case PROP_SSID:
-		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));
-		break;
-	case PROP_MODE:
-		nm_ap_set_mode (ap, g_value_get_uint (value));
-		break;
-	case PROP_MAX_BITRATE:
-		nm_ap_set_max_bitrate (ap, g_value_get_uint (value));
-		break;
-	case PROP_STRENGTH:
-		nm_ap_set_strength (ap, g_value_get_schar (value));
-		break;
-	case PROP_HW_ADDRESS:
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->flags != flags) {
+		priv->flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_FLAGS);
 	}
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
 {
-	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
-	GArray * ssid;
-	int len;
-	int i;
+	NMAccessPointPrivate *priv;
 
-	switch (prop_id) {
-	case PROP_FLAGS:
-		g_value_set_uint (value, priv->flags);
-		break;
-	case PROP_WPA_FLAGS:
-		g_value_set_uint (value, priv->wpa_flags);
-		break;
-	case PROP_RSN_FLAGS:
-		g_value_set_uint (value, priv->rsn_flags);
-		break;
-	case PROP_SSID:
-		len = priv->ssid ? priv->ssid->len : 0;
-		ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len);
-		for (i = 0; i < len; i++)
-			g_array_append_val (ssid, priv->ssid->data[i]);
-		g_value_set_boxed (value, ssid);
-		g_array_free (ssid, TRUE);
-		break;
-	case PROP_FREQUENCY:
-		g_value_set_uint (value, priv->freq);
-		break;
-	case PROP_HW_ADDRESS:
-		g_value_set_string (value, priv->address);
-		break;
-	case PROP_MODE:
-		g_value_set_uint (value, priv->mode);
-		break;
-	case PROP_MAX_BITRATE:
-		g_value_set_uint (value, priv->max_bitrate);
-		break;
-	case PROP_STRENGTH:
-		g_value_set_schar (value, priv->strength);
-		break;
-	case PROP_LAST_SEEN:
-		g_value_set_int (value,
-		                 priv->last_seen > 0
-		                     ? (gint) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
-		                     : -1);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+	if (priv->wpa_flags != flags) {
+		priv->wpa_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS);
 	}
 }
 
 static void
-nm_ap_class_init (NMAccessPointClass *ap_class)
+nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
-	const NM80211ApSecurityFlags all_sec_flags =   NM_802_11_AP_SEC_NONE
-	                                             | NM_802_11_AP_SEC_PAIR_WEP40
-	                                             | NM_802_11_AP_SEC_PAIR_WEP104
-	                                             | NM_802_11_AP_SEC_PAIR_TKIP
-	                                             | NM_802_11_AP_SEC_PAIR_CCMP
-	                                             | NM_802_11_AP_SEC_GROUP_WEP40
-	                                             | NM_802_11_AP_SEC_GROUP_WEP104
-	                                             | NM_802_11_AP_SEC_GROUP_TKIP
-	                                             | NM_802_11_AP_SEC_GROUP_CCMP
-	                                             | NM_802_11_AP_SEC_KEY_MGMT_PSK
-	                                             | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+	NMAccessPointPrivate *priv;
 
-	g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
+	g_return_if_fail (NM_IS_AP (ap));
 
-	/* virtual methods */
-	object_class->set_property = set_property;
-	object_class->get_property = get_property;
-	object_class->finalize = finalize;
+	priv = NM_AP_GET_PRIVATE (ap);
+	if (priv->rsn_flags != flags) {
+		priv->rsn_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS);
+	}
+}
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_FLAGS,
-		 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));
+const char *
+nm_ap_get_address (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
 
-	g_object_class_install_property
-		(object_class, PROP_WPA_FLAGS,
-		 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));
+	return NM_AP_GET_PRIVATE (ap)->address;
+}
 
-	g_object_class_install_property
-		(object_class, PROP_RSN_FLAGS,
-		 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));
+void
+nm_ap_set_address (NMAccessPoint *ap, const char *addr)
+{
+	NMAccessPointPrivate *priv;
 
-	g_object_class_install_property
-		(object_class, PROP_SSID,
-	     g_param_spec_boxed (NM_AP_SSID, "", "",
-	                         DBUS_TYPE_G_UCHAR_ARRAY,
-	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-	                         G_PARAM_STATIC_STRINGS));
+	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));
 
-	g_object_class_install_property
-		(object_class, PROP_FREQUENCY,
-		 g_param_spec_uint (NM_AP_FREQUENCY, "", "",
-		                    0, 10000, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
+	priv = NM_AP_GET_PRIVATE (ap);
 
-	g_object_class_install_property
-		(object_class, PROP_HW_ADDRESS,
-		 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, "", "",
-		                    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));
+	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);
+	}
+}
 
-	g_object_class_install_property
-		(object_class, PROP_MAX_BITRATE,
-		 g_param_spec_uint (NM_AP_MAX_BITRATE, "", "",
-		                    0, G_MAXUINT16, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
+NM80211Mode
+nm_ap_get_mode (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_MODE_UNKNOWN);
 
-	g_object_class_install_property
-		(object_class, PROP_STRENGTH,
-		 g_param_spec_char (NM_AP_STRENGTH, "", "",
-		                    G_MININT8, G_MAXINT8, 0,
-		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
+	return NM_AP_GET_PRIVATE (ap)->mode;
+}
 
-	g_object_class_install_property
-		(object_class, PROP_LAST_SEEN,
-		 g_param_spec_int (NM_AP_LAST_SEEN, "", "",
-		                   -1, G_MAXINT, -1,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (ap_class),
-	                                        &dbus_glib_nm_access_point_object_info);
+static void
+nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (   mode == NM_802_11_MODE_ADHOC
+	                  || mode == NM_802_11_MODE_INFRA);
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->mode != mode) {
+		priv->mode = mode;
+		g_object_notify (G_OBJECT (ap), NM_AP_MODE);
+	}
+}
+
+gboolean
+nm_ap_is_hotspot (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->hotspot;
+}
+
+gint8
+nm_ap_get_strength (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	return NM_AP_GET_PRIVATE (ap)->strength;
 }
 
 void
-nm_ap_export_to_dbus (NMAccessPoint *ap)
+nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength)
 {
 	NMAccessPointPrivate *priv;
-	static guint32 counter = 0;
 
 	g_return_if_fail (NM_IS_AP (ap));
 
 	priv = NM_AP_GET_PRIVATE (ap);
 
-	if (priv->dbus_path) {
-		nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path);
-		return;
+	if (priv->strength != strength) {
+		priv->strength = strength;
+		g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH);
 	}
+}
+
+guint32
+nm_ap_get_freq (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
 
-	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++);
-	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, ap);
+	return NM_AP_GET_PRIVATE (ap)->freq;
 }
 
-/*
- * nm_ap_new
- *
- * Create a new, blank user access point info structure
- *
- */
-static NMAccessPoint *
-nm_ap_new (void)
+void
+nm_ap_set_freq (NMAccessPoint *ap,
+                const guint32 freq)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->freq != freq) {
+		priv->freq = freq;
+		g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY);
+	}
+}
+
+guint32
+nm_ap_get_max_bitrate (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+	g_return_val_if_fail (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (ap)), 0);
+
+	return NM_AP_GET_PRIVATE (ap)->max_bitrate;
+}
+
+void
+nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->max_bitrate != bitrate) {
+		priv->max_bitrate = bitrate;
+		g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE);
+	}
+}
+
+gboolean
+nm_ap_get_fake (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->fake;
+}
+
+void
+nm_ap_set_fake (NMAccessPoint *ap, gboolean fake)
 {
-	return (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL);
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->fake = fake;
 }
 
+static void
+nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->last_seen != last_seen) {
+		priv->last_seen = last_seen;
+		g_object_notify (G_OBJECT (ap), NM_AP_LAST_SEEN);
+	}
+}
+
+/*****************************************************************/
+
 static NM80211ApSecurityFlags
 security_from_vardict (GVariant *security)
 {
@@ -382,14 +378,14 @@ security_from_vardict (GVariant *security)
 	return flags;
 }
 
-NMAccessPoint *
-nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+void
+nm_ap_update_from_properties (NMAccessPoint *ap,
+                              const char *supplicant_path,
+                              GVariant *properties)
 {
-	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-	const char *addr;
+	NMAccessPointPrivate *priv;
+	char *addr;
 	const guint8 *bytes;
-	NMAccessPoint *ap;
 	GVariant *v;
 	gsize len;
 	gboolean b = FALSE;
@@ -397,14 +393,14 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 	gint16 i16;
 	guint16 u16;
 
-	g_return_val_if_fail (properties != NULL, NULL);
-
-	ap = nm_ap_new ();
+	g_return_if_fail (ap != NULL);
+	g_return_if_fail (properties != NULL);
+	priv = NM_AP_GET_PRIVATE (ap);
 
 	g_object_freeze_notify (G_OBJECT (ap));
 
 	if (g_variant_lookup (properties, "Privacy", "b", &b) && b)
-		nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY);
+		nm_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
 
 	if (g_variant_lookup (properties, "Mode", "&s", &s)) {
 		if (!g_strcmp0 (s, "infrastructure"))
@@ -437,11 +433,9 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 	if (v) {
 		bytes = g_variant_get_fixed_array (v, &len, 1);
 		if (len == ETH_ALEN) {
-			char *a;
-
-			a = nm_utils_hwaddr_ntoa (bytes, len);
-			nm_ap_set_address (ap, a);
-			g_free (a);
+			addr = nm_utils_hwaddr_ntoa (bytes, len);
+			nm_ap_set_address (ap, addr);
+			g_free (addr);
 		}
 		g_variant_unref (v);
 	}
@@ -464,17 +458,38 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 
 	v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT);
 	if (v) {
-		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | security_from_vardict (v));
+		nm_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
 		g_variant_unref (v);
 	}
 
 	v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT);
 	if (v) {
-		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | security_from_vardict (v));
+		nm_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
 		g_variant_unref (v);
 	}
 
-	nm_ap_set_supplicant_path (ap, supplicant_path);
+	if (!priv->supplicant_path)
+		priv->supplicant_path = g_strdup (supplicant_path);
+
+	nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+	priv->fake = FALSE;
+
+	g_object_thaw_notify (G_OBJECT (ap));
+}
+
+NMAccessPoint *
+nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+{
+	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	NMAccessPoint *ap;
+	const char *addr;
+
+	g_return_val_if_fail (supplicant_path != NULL, NULL);
+	g_return_val_if_fail (properties != NULL, NULL);
+
+	ap = (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL);
+	nm_ap_update_from_properties (ap, supplicant_path, properties);
 
 	/* ignore APs with invalid or missing BSSIDs */
 	addr = nm_ap_get_address (ap);
@@ -485,13 +500,6 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 		return NULL;
 	}
 
-	nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
-
-	if (!nm_ap_get_ssid (ap))
-		nm_ap_set_broadcast (ap, FALSE);
-
-	g_object_thaw_notify (G_OBJECT (ap));
-
 	return ap;
 }
 
@@ -517,6 +525,7 @@ has_proto (NMSettingWirelessSecurity *sec, const char *proto)
 static void
 add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
 {
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
 	guint32 num = nm_setting_wireless_security_get_num_pairwise (sec);
 	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
 	guint32 i;
@@ -536,14 +545,15 @@ add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
 	}
 
 	if (has_proto (sec, PROTO_WPA))
-		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+		nm_ap_set_wpa_flags (ap, priv->wpa_flags | flags);
 	if (has_proto (sec, PROTO_RSN))
-		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+		nm_ap_set_rsn_flags (ap, priv->rsn_flags | flags);
 }
 
 static void
 add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
 {
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
 	guint32 num = nm_setting_wireless_security_get_num_groups (sec);
 	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
 	guint32 i;
@@ -567,15 +577,16 @@ add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
 	}
 
 	if (has_proto (sec, PROTO_WPA))
-		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+		nm_ap_set_wpa_flags (ap, priv->wpa_flags | flags);
 	if (has_proto (sec, PROTO_RSN))
-		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+		nm_ap_set_rsn_flags (ap, priv->rsn_flags | flags);
 }
 
 NMAccessPoint *
 nm_ap_new_fake_from_connection (NMConnection *connection)
 {
 	NMAccessPoint *ap;
+	NMAccessPointPrivate *priv;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
 	GBytes *ssid;
@@ -593,8 +604,9 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 	g_return_val_if_fail (ssid != NULL, NULL);
 	g_return_val_if_fail (g_bytes_get_size (ssid) > 0, NULL);
 
-	ap = nm_ap_new ();
-	nm_ap_set_fake (ap, TRUE);
+	ap = (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL);
+	priv = NM_AP_GET_PRIVATE (ap);
+	priv->fake = TRUE;
 	nm_ap_set_ssid (ap, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
 
 	// FIXME: bssid too?
@@ -634,7 +646,7 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
 
 	/* Everything below here uses encryption */
-	nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY);
+	nm_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
 
 	/* Static & Dynamic WEP */
 	if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x"))
@@ -644,13 +656,11 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 	eap = !strcmp (key_mgmt, "wpa-eap");
 	if (psk || eap) {
 		if (has_proto (s_wireless_sec, PROTO_WPA)) {
-			flags = nm_ap_get_wpa_flags (ap);
-			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			flags = priv->wpa_flags | (eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK);
 			nm_ap_set_wpa_flags (ap, flags);
 		}
 		if (has_proto (s_wireless_sec, PROTO_RSN)) {
-			flags = nm_ap_get_rsn_flags (ap);
-			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			flags = priv->rsn_flags | (eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK);
 			nm_ap_set_rsn_flags (ap, flags);
 		}
 
@@ -663,8 +673,7 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
 		 * group=TKIP/CCMP (but not both).
 		 */
 
-		flags = nm_ap_get_wpa_flags (ap);
-		flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+		flags = priv->wpa_flags | NM_802_11_AP_SEC_KEY_MGMT_PSK;
 
 		/* Clear ciphers; pairwise must be unset anyway, and group gets set below */
 		flags &= ~(  NM_802_11_AP_SEC_PAIR_WEP40
@@ -701,394 +710,60 @@ error:
 	return NULL;
 }
 
-void
-nm_ap_dump (NMAccessPoint *ap, const char *prefix)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)",
-	            prefix,
-	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
-	            ap);
-	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);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    rsn flags 0x%X", priv->rsn_flags);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    quality   %d", priv->strength);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    frequency %d", priv->freq);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    max rate  %d", priv->max_bitrate);
-	nm_log_dbg (LOGD_WIFI_SCAN, "    last-seen %d", (int) priv->last_seen);
-}
-
-const char *
-nm_ap_get_dbus_path (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), NULL);
-
-	return NM_AP_GET_PRIVATE (ap)->dbus_path;
-}
-
-const char *
-nm_ap_get_supplicant_path (NMAccessPoint *ap)
+static char
+mode_to_char (NMAccessPoint *self)
 {
-	g_return_val_if_fail (NM_IS_AP (ap), NULL);
-
-	return NM_AP_GET_PRIVATE (ap)->supplicant_path;
-}
-
-void
-nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path)
-{
-	g_return_if_fail (NM_IS_AP (ap));
-	g_return_if_fail (path != NULL);
-
-	g_free (NM_AP_GET_PRIVATE (ap)->supplicant_path);
-	NM_AP_GET_PRIVATE (ap)->supplicant_path = g_strdup (path);
-}
-
-/*
- * Get/set functions for ssid
- *
- */
-const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), NULL);
-
-	return NM_AP_GET_PRIVATE (ap)->ssid;
-}
-
-void
-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);
-
-	/* same SSID */
-	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
-		if (!memcmp (ssid, priv->ssid->data, len))
-			return;
-	}
-
-	if (priv->ssid) {
-		g_byte_array_free (priv->ssid, TRUE);
-		priv->ssid = NULL;
-	}
-
-	if (ssid) {
-		priv->ssid = g_byte_array_new ();
-		g_byte_array_append (priv->ssid, ssid, len);
-	}
-
-	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
-}
-
-
-NM80211ApFlags
-nm_ap_get_flags (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_FLAGS_NONE);
-
-	return NM_AP_GET_PRIVATE (ap)->flags;
-}
-
-
-void
-nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->flags != flags) {
-		priv->flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_FLAGS);
-	}
-}
-
-NM80211ApSecurityFlags
-nm_ap_get_wpa_flags (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
-
-	return NM_AP_GET_PRIVATE (ap)->wpa_flags;
-}
-
-
-void
-nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-	if (priv->wpa_flags != flags) {
-		priv->wpa_flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS);
-	}
-}
-
-NM80211ApSecurityFlags
-nm_ap_get_rsn_flags (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
-
-	return NM_AP_GET_PRIVATE (ap)->rsn_flags;
-}
-
-
-void
-nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-	if (priv->rsn_flags != flags) {
-		priv->rsn_flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS);
-	}
-}
-
-/*
- * Get/set functions for address
- *
- */
-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;
-}
-
-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 (!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);
-	}
-}
-
-
-/*
- * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc)
- *
- */
-NM80211Mode nm_ap_get_mode (NMAccessPoint *ap)
-{
-	NM80211Mode mode;
-
-	g_return_val_if_fail (NM_IS_AP (ap), -1);
-
-	g_object_get (ap, NM_AP_MODE, &mode, NULL);
-
-	return mode;
-}
-
-void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-	g_return_if_fail (   mode == NM_802_11_MODE_ADHOC
-	                  || mode == NM_802_11_MODE_INFRA);
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->mode != mode) {
-		priv->mode = mode;
-		g_object_notify (G_OBJECT (ap), NM_AP_MODE);
-	}
-}
-
-gboolean
-nm_ap_is_hotspot (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
-
-	return NM_AP_GET_PRIVATE (ap)->hotspot;
-}
-
-/*
- * Get/set functions for strength
- *
- */
-gint8 nm_ap_get_strength (NMAccessPoint *ap)
-{
-	gint8 strength;
-
-	g_return_val_if_fail (NM_IS_AP (ap), 0);
-
-	g_object_get (ap, NM_AP_STRENGTH, &strength, NULL);
-
-	return strength;
-}
-
-void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->strength != strength) {
-		priv->strength = strength;
-		g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH);
-	}
-}
-
-
-/*
- * Get/set functions for frequency
- *
- */
-guint32
-nm_ap_get_freq (NMAccessPoint *ap)
-{
-	guint32 freq;
-
-	g_return_val_if_fail (NM_IS_AP (ap), 0);
-
-	g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL);
-
-	return freq;
-}
-
-void
-nm_ap_set_freq (NMAccessPoint *ap,
-                const guint32 freq)
-{
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->freq != freq) {
-		priv->freq = freq;
-		g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY);
-	}
-}
-
-
-/*
- * Get/set functions for max bitrate (in kbit/s)
- *
- */
-guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap)
-{
-	guint32 rate;
-
-	g_return_val_if_fail (NM_IS_AP (ap), 0);
-
-	g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL);
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (self);
 
-	return rate;
+	if (priv->mode == NM_802_11_MODE_ADHOC)
+		return '*';
+	if (priv->hotspot)
+		return '#';
+	if (priv->fake)
+		return '-';
+	return ' ';
 }
 
 void
-nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate)
+nm_ap_dump (NMAccessPoint *self,
+            const char *prefix,
+            const char *ifname)
 {
 	NMAccessPointPrivate *priv;
+	const char *supplicant_id = "-";
+	guint32 chan;
 
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->max_bitrate != bitrate) {
-		priv->max_bitrate = bitrate;
-		g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE);
-	}
-}
-
-/*
- * Get/Set functions to indicate that an access point is 'fake', ie whether
- * or not it was created from scan results
- */
-gboolean nm_ap_get_fake (const NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
-
-	return NM_AP_GET_PRIVATE (ap)->fake;
-}
-
-void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake)
-{
-	g_return_if_fail (NM_IS_AP (ap));
-
-	NM_AP_GET_PRIVATE (ap)->fake = fake;
-}
-
-
-/*
- * Get/Set functions to indicate whether an AP broadcasts its SSID.
- */
-gboolean nm_ap_get_broadcast (NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), TRUE);
-
-	return NM_AP_GET_PRIVATE (ap)->broadcast;
-}
-
-
-void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast)
-{
-	g_return_if_fail (NM_IS_AP (ap));
-
-	NM_AP_GET_PRIVATE (ap)->broadcast = broadcast;
-}
-
+	g_return_if_fail (NM_IS_AP (self));
 
-/*
- * Get/Set functions for how long ago the AP was last seen in a scan.
- * APs older than a certain date are dropped from the list.
- *
- */
-gint32
-nm_ap_get_last_seen (const NMAccessPoint *ap)
-{
-	g_return_val_if_fail (NM_IS_AP (ap), 0);
+	priv = NM_AP_GET_PRIVATE (self);
+	chan = nm_utils_wifi_freq_to_channel (priv->freq);
+	if (priv->supplicant_path)
+		supplicant_id = strrchr (priv->supplicant_path, '/');
 
-	return NM_AP_GET_PRIVATE (ap)->last_seen;
+	nm_log_dbg (LOGD_WIFI_SCAN, "%s[%s%c] %-32s[%s%u %3u%% %c W:%04X R:%04X] [%3u] %s%s",
+	            prefix,
+	            str_if_set (priv->address, "(none)"),
+	            mode_to_char (self),
+	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
+	            chan > 99 ? "" : (chan > 9 ? " " : "  "),
+	            chan,
+	            priv->strength,
+	            priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : ' ',
+	            priv->wpa_flags & 0xFFFF,
+	            priv->rsn_flags & 0xFFFF,
+	            priv->last_seen > 0 ? (nm_utils_get_monotonic_timestamp_s () - priv->last_seen) : -1,
+	            ifname,
+	            supplicant_id);
 }
 
-void
-nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
+static guint
+freq_to_band (guint32 freq)
 {
-	NMAccessPointPrivate *priv;
-
-	g_return_if_fail (NM_IS_AP (ap));
-
-	priv = NM_AP_GET_PRIVATE (ap);
-
-	if (priv->last_seen != last_seen) {
-		priv->last_seen = last_seen;
-		g_object_notify (G_OBJECT (ap), NM_AP_LAST_SEEN);
-	}
+	if (freq >= 4915 && freq <= 5825)
+		return 5;
+	else if (freq >= 2412 && freq <= 2484)
+		return 2;
+	return 0;
 }
 
 gboolean
@@ -1141,13 +816,12 @@ nm_ap_check_compatible (NMAccessPoint *self,
 
 	band = nm_setting_wireless_get_band (s_wireless);
 	if (band) {
-		if (!strcmp (band, "a")) {
-			if (priv->freq < 4915 || priv->freq > 5825)
-				return FALSE;
-		} else if (!strcmp (band, "bg")) {
-			if (priv->freq < 2412 || priv->freq > 2484)
-				return FALSE;
-		}
+		guint ap_band = freq_to_band (priv->freq);
+
+		if (!strcmp (band, "a") && ap_band != 5)
+			return FALSE;
+		else if (!strcmp (band, "bg") && ap_band != 2)
+			return FALSE;
 	}
 
 	channel = nm_setting_wireless_get_channel (s_wireless);
@@ -1162,10 +836,10 @@ nm_ap_check_compatible (NMAccessPoint *self,
 
 	return nm_setting_wireless_ap_security_compatible (s_wireless,
 	                                                   s_wireless_sec,
-	                                                   nm_ap_get_flags (self),
-	                                                   nm_ap_get_wpa_flags (self),
-	                                                   nm_ap_get_rsn_flags (self),
-	                                                   nm_ap_get_mode (self));
+	                                                   priv->flags,
+	                                                   priv->wpa_flags,
+	                                                   priv->rsn_flags,
+	                                                   priv->mode);
 }
 
 gboolean
@@ -1189,96 +863,188 @@ nm_ap_complete_connection (NMAccessPoint *self,
 	                                        error);
 }
 
-static gboolean
-capabilities_compatible (NM80211ApSecurityFlags a_flags, NM80211ApSecurityFlags b_flags)
+/*****************************************************************/
+
+static void
+nm_ap_init (NMAccessPoint *ap)
 {
-	if (a_flags == b_flags)
-		return TRUE;
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
 
-	/* Make sure there's a common key management method */
-	if (!((a_flags & 0x300) & (b_flags & 0x300)))
-		return FALSE;
+	priv->mode = NM_802_11_MODE_INFRA;
+	priv->flags = NM_802_11_AP_FLAGS_NONE;
+	priv->wpa_flags = NM_802_11_AP_SEC_NONE;
+	priv->rsn_flags = NM_802_11_AP_SEC_NONE;
+	priv->last_seen = -1;
+}
 
-	/* Ensure common pairwise ciphers */
-	if (!((a_flags & 0xF) & (b_flags & 0xF)))
-		return FALSE;
+static void
+finalize (GObject *object)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
 
-	/* Ensure common group ciphers */
-	if (!((a_flags & 0xF0) & (b_flags & 0xF0)))
-		return FALSE;
+	g_free (priv->supplicant_path);
+	if (priv->ssid)
+		g_byte_array_free (priv->ssid, TRUE);
+	g_free (priv->address);
 
-	return TRUE;
+	G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object);
 }
 
-NMAccessPoint *
-nm_ap_match_in_list (NMAccessPoint *find_ap,
-                     GSList *ap_list,
-                     gboolean strict_match)
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
 {
-	GSList *iter;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
 
-	g_return_val_if_fail (find_ap != NULL, NULL);
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	GVariant *ssid;
 
-	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 char * list_addr = nm_ap_get_address (list_ap);
+	switch (prop_id) {
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_WPA_FLAGS:
+		g_value_set_uint (value, priv->wpa_flags);
+		break;
+	case PROP_RSN_FLAGS:
+		g_value_set_uint (value, priv->rsn_flags);
+		break;
+	case PROP_SSID:
+		if (priv->ssid) {
+			ssid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+			                                  priv->ssid->data, priv->ssid->len, 1);
+		} else
+			ssid = g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0);
+		g_value_take_variant (value, ssid);
+		break;
+	case PROP_FREQUENCY:
+		g_value_set_uint (value, priv->freq);
+		break;
+	case PROP_HW_ADDRESS:
+		g_value_set_string (value, priv->address);
+		break;
+	case PROP_MODE:
+		g_value_set_uint (value, priv->mode);
+		break;
+	case PROP_MAX_BITRATE:
+		g_value_set_uint (value, priv->max_bitrate);
+		break;
+	case PROP_STRENGTH:
+		g_value_set_uchar (value, priv->strength);
+		break;
+	case PROP_LAST_SEEN:
+		g_value_set_int (value,
+		                 priv->last_seen > 0
+		                     ? (gint) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
+		                     : -1);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
 
-		const GByteArray * find_ssid = nm_ap_get_ssid (find_ap);
-		const char * find_addr = nm_ap_get_address (find_ap);
+static void
+nm_ap_class_init (NMAccessPointClass *ap_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (ap_class);
+	const NM80211ApSecurityFlags all_sec_flags =   NM_802_11_AP_SEC_NONE
+	                                             | NM_802_11_AP_SEC_PAIR_WEP40
+	                                             | NM_802_11_AP_SEC_PAIR_WEP104
+	                                             | NM_802_11_AP_SEC_PAIR_TKIP
+	                                             | NM_802_11_AP_SEC_PAIR_CCMP
+	                                             | NM_802_11_AP_SEC_GROUP_WEP40
+	                                             | NM_802_11_AP_SEC_GROUP_WEP104
+	                                             | NM_802_11_AP_SEC_GROUP_TKIP
+	                                             | NM_802_11_AP_SEC_GROUP_CCMP
+	                                             | NM_802_11_AP_SEC_KEY_MGMT_PSK
+	                                             | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
 
-		/* 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))
-			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, -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))
-			continue;
-
-		/* Frequency match */
-		if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap))
-			continue;
-
-		/* AP flags */
-		if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap))
-			continue;
-
-		if (strict_match) {
-			if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap))
-				continue;
-
-			if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap))
-				continue;
-		} else {
-			NM80211ApSecurityFlags list_wpa_flags = nm_ap_get_wpa_flags (list_ap);
-			NM80211ApSecurityFlags find_wpa_flags = nm_ap_get_wpa_flags (find_ap);
-			NM80211ApSecurityFlags list_rsn_flags = nm_ap_get_rsn_flags (list_ap);
-			NM80211ApSecurityFlags find_rsn_flags = nm_ap_get_rsn_flags (find_ap);
-
-			/* Just ensure that there is overlap in the capabilities */
-			if (   !capabilities_compatible (list_wpa_flags, find_wpa_flags)
-			    && !capabilities_compatible (list_rsn_flags, find_rsn_flags))
-				continue;
-		}
+	g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
 
-		return list_ap;
-	}
+	exported_object_class->export_path = NM_DBUS_PATH_ACCESS_POINT "/%u";
 
-	return NULL;
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+	    (object_class, PROP_FLAGS,
+	     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_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_WPA_FLAGS,
+	     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_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_RSN_FLAGS,
+	     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_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_SSID,
+	     g_param_spec_variant (NM_AP_SSID, "", "",
+	                           G_VARIANT_TYPE ("ay"),
+	                           NULL,
+	                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_FREQUENCY,
+	     g_param_spec_uint (NM_AP_FREQUENCY, "", "",
+	                        0, 10000, 0,
+	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_HW_ADDRESS,
+	     g_param_spec_string (NM_AP_HW_ADDRESS, "", "",
+	                          NULL,
+	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_MODE,
+	     g_param_spec_uint (NM_AP_MODE, "", "",
+	                        NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_MAX_BITRATE,
+	     g_param_spec_uint (NM_AP_MAX_BITRATE, "", "",
+	                        0, G_MAXUINT16, 0,
+	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_STRENGTH,
+	     g_param_spec_uchar (NM_AP_STRENGTH, "", "",
+	                         0, G_MAXINT8, 0,
+	                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
+	    (object_class, PROP_LAST_SEEN,
+	     g_param_spec_int (NM_AP_LAST_SEEN, "", "",
+	                       -1, G_MAXINT, -1,
+	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ap_class),
+	                                        NMDBUS_TYPE_ACCESS_POINT_SKELETON,
+	                                        NULL);
 }
 
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 67d30769..84aff983 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -22,8 +22,7 @@
 #ifndef __NETWORKMANAGER_ACCESS_POINT_H__
 #define __NETWORKMANAGER_ACCESS_POINT_H__
 
-#include <glib.h>
-#include <glib-object.h>
+#include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 
@@ -46,77 +45,58 @@
 #define NM_AP_LAST_SEEN "last-seen"
 
 typedef struct {
-	GObject parent;
+	NMExportedObject parent;
 } NMAccessPoint;
 
 typedef struct {
-	GObjectClass parent;
+	NMExportedObjectClass parent;
 
 } NMAccessPointClass;
 
 GType nm_ap_get_type (void);
 
-NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
-                                           GVariant *properties);
-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_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 guint8 * ssid, gsize len);
-
-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);
-
-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);
-
-gboolean nm_ap_is_hotspot (NMAccessPoint *ap);
-
-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_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_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);
-
-gboolean nm_ap_check_compatible (NMAccessPoint *self,
-                                 NMConnection *connection);
-
-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);
+NMAccessPoint *   nm_ap_new_from_properties      (const char *supplicant_path,
+                                                  GVariant *properties);
+NMAccessPoint *   nm_ap_new_fake_from_connection (NMConnection *connection);
+
+void              nm_ap_update_from_properties   (NMAccessPoint *ap,
+                                                  const char *supplicant_path,
+                                                  GVariant *properties);
+
+gboolean          nm_ap_check_compatible         (NMAccessPoint *self,
+                                                  NMConnection *connection);
+
+gboolean          nm_ap_complete_connection      (NMAccessPoint *self,
+                                                  NMConnection *connection,
+                                                  gboolean lock_bssid,
+                                                  GError **error);
+
+const char *      nm_ap_get_supplicant_path      (NMAccessPoint *ap);
+guint32           nm_ap_get_id                   (NMAccessPoint *ap);
+const GByteArray *nm_ap_get_ssid                 (const NMAccessPoint *ap);
+void              nm_ap_set_ssid                 (NMAccessPoint *ap,
+                                                  const guint8 *ssid,
+                                                  gsize len);
+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);
+gboolean          nm_ap_is_hotspot               (NMAccessPoint *ap);
+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_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);
+
+void              nm_ap_dump                     (NMAccessPoint *self,
+                                                  const char *prefix,
+                                                  const char *ifname);
 
 #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
deleted file mode 100644
index 7cce51cd..00000000
--- a/src/devices/wifi/nm-wifi-enum-types.c
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-/* Generated by glib-mkenums. Do not edit */
-
-#include "nm-wifi-enum-types.h"
-
-#include "nm-device-wifi.h" 
-#include "nm-wifi-ap.h" 
-#include "nm-device-olpc-mesh.h"
-
-
-
-
diff --git a/src/devices/wifi/nm-wifi-enum-types.h b/src/devices/wifi/nm-wifi-enum-types.h
deleted file mode 100644
index 7fc64cd1..00000000
--- a/src/devices/wifi/nm-wifi-enum-types.h
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-/* Generated by glib-mkenums. Do not edit */
-
-#ifndef __NM_WIFI_ENUM_TYPES_H__
-#define __NM_WIFI_ENUM_TYPES_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-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 c4b4042e..7f56dc74 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -43,7 +43,7 @@ typedef struct {
 
 static GType nm_wifi_factory_get_type (void);
 
-static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+static void device_factory_interface_init (NMDeviceFactoryInterface *factory_iface);
 
 G_DEFINE_TYPE_EXTENDED (NMWifiFactory, nm_wifi_factory, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
@@ -59,13 +59,30 @@ nm_device_factory_create (GError **error)
 /**************************************************************************/
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
+	NMDeviceWifiCapabilities capabilities;
+
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (plink != NULL, NULL);
+	g_return_val_if_fail (g_strcmp0 (iface, plink->name) == 0, NULL);
+	g_return_val_if_fail (NM_IN_SET (plink->type, NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH), NULL);
+
+	if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET,
+	                                        plink->ifindex,
+	                                        &capabilities)) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s) failed to initialize Wi-Fi driver for ifindex %d", iface, plink->ifindex);
+		return NULL;
+	}
+
 	if (plink->type == NM_LINK_TYPE_WIFI)
-		return nm_device_wifi_new (plink);
-	else if (plink->type == NM_LINK_TYPE_OLPC_MESH)
-		return nm_device_olpc_mesh_new (plink);
-	g_assert_not_reached ();
+		return nm_device_wifi_new (iface, capabilities);
+	else
+		return nm_device_olpc_mesh_new (iface);
 }
 
 NM_DEVICE_FACTORY_DECLARE_TYPES (
@@ -74,9 +91,9 @@ NM_DEVICE_FACTORY_DECLARE_TYPES (
 )
 
 static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
+device_factory_interface_init (NMDeviceFactoryInterface *factory_iface)
 {
-	factory_iface->new_link = new_link;
+	factory_iface->create_device = create_device;
 	factory_iface->get_supported_types = get_supported_types;
 }
 
diff --git a/src/devices/wifi/tests/Makefile.am b/src/devices/wifi/tests/Makefile.am
index f76bdbf5..dee8e799 100644
--- a/src/devices/wifi/tests/Makefile.am
+++ b/src/devices/wifi/tests/Makefile.am
@@ -1,6 +1,7 @@
 AM_CPPFLAGS = \
-	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
+	-I$(top_builddir)/introspection \
+	-I$(top_srcdir)/shared \
+	-I$(top_builddir)/shared \
 	-I$(top_srcdir)/libnm-core \
 	-I$(top_builddir)/libnm-core \
 	-I${top_srcdir}/src/platform \
@@ -8,10 +9,9 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/devices/wifi \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(GLIB_CFLAGS) \
-	$(DBUS_CFLAGS)
+	$(GLIB_CFLAGS)
 
 noinst_PROGRAMS = test-wifi-ap-utils
 
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
index f2488c7d..90900f00 100644
--- a/src/devices/wifi/tests/Makefile.in
+++ b/src/devices/wifi/tests/Makefile.in
@@ -404,7 +404,6 @@ 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@
@@ -416,8 +415,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -427,6 +424,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -481,16 +479,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -527,6 +522,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -555,7 +552,6 @@ 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@
@@ -570,6 +566,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -630,6 +628,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -637,6 +636,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -653,8 +653,9 @@ with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
 with_valgrind = @with_valgrind@
 AM_CPPFLAGS = \
-	-I$(top_srcdir)/include \
-	-I$(top_builddir)/include \
+	-I$(top_builddir)/introspection \
+	-I$(top_srcdir)/shared \
+	-I$(top_builddir)/shared \
 	-I$(top_srcdir)/libnm-core \
 	-I$(top_builddir)/libnm-core \
 	-I${top_srcdir}/src/platform \
@@ -662,10 +663,9 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/devices/wifi \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(GLIB_CFLAGS) \
-	$(DBUS_CFLAGS)
+	$(GLIB_CFLAGS)
 
 test_wifi_ap_utils_SOURCES = \
 	test-wifi-ap-utils.c \
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
index 26e130c4..b5642fe2 100644
--- a/src/devices/wifi/tests/test-wifi-ap-utils.c
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -20,14 +20,15 @@
 
 #include "config.h"
 
-#include <glib.h>
 #include <string.h>
 
+#include "nm-default.h"
 #include "nm-wifi-ap-utils.h"
-#include "nm-dbus-glib-types.h"
 
 #include "nm-core-internal.h"
 
+#include "nm-test-utils.h"
+
 #define DEBUG 1
 
 /*******************************************/
@@ -1336,16 +1337,14 @@ test_strength_wext (void)
 
 /*******************************************/
 
+NMTST_DEFINE ();
+
 int
 main (int argc, char **argv)
 {
 	gsize i;
 
-#if !GLIB_CHECK_VERSION (2, 35, 0)
-	g_type_init ();
-#endif
-
-	g_test_init (&argc, &argv, NULL);
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
 	g_test_add_func ("/wifi/lock_bssid",
 	                 test_lock_bssid);
diff --git a/src/devices/wimax/Makefile.am b/src/devices/wimax/Makefile.am
deleted file mode 100644
index 0ecd1158..00000000
--- a/src/devices/wimax/Makefile.am
+++ /dev/null
@@ -1,60 +0,0 @@
-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}/include \
-	-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) \
-	$(IWMX_SDK_CFLAGS)
-
-pkglib_LTLIBRARIES = libnm-device-plugin-wimax.la
-
-SYMBOL_VIS_FILE=$(srcdir)/exports.ver
-
-libnm_device_plugin_wimax_la_SOURCES = \
-	nm-wimax-factory.c \
-	nm-device-wimax.c \
-	nm-device-wimax.h \
-	nm-wimax-nsp.c \
-	nm-wimax-nsp.h \
-	nm-wimax-types.h \
-	nm-wimax-util.c \
-	nm-wimax-util.h \
-	iwmxsdk.c \
-	iwmxsdk.h
-
-libnm_device_plugin_wimax_la_LDFLAGS = \
-	-module -avoid-version \
-	-Wl,--version-script=$(SYMBOL_VIS_FILE)
-
-libnm_device_plugin_wimax_la_LIBADD = \
-	$(DBUS_LIBS) \
-	$(IWMX_SDK_LIBS) \
-	$(GUDEV_LIBS)
-
-nm-wimax-nsp-glue.h: $(top_srcdir)/introspection/nm-wimax-nsp.xml
-	dbus-binding-tool --prefix=nm_wimax_nsp --mode=glib-server --output=$@ $<
-
-nm-device-wimax-glue.h: $(top_srcdir)/introspection/nm-device-wimax.xml
-	dbus-binding-tool --prefix=nm_device_wimax --mode=glib-server --output=$@ $<
-
-BUILT_SOURCES = \
-	nm-wimax-nsp-glue.h \
-	nm-device-wimax-glue.h
-
-CLEANFILES = $(BUILT_SOURCES)
-EXTRA_DIST = $(SYMBOL_VIS_FILE)
-
-if ENABLE_TESTS
-
-check-local:
-	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wimax.so $(SYMBOL_VIS_FILE)
-
-endif
-
diff --git a/src/devices/wimax/exports.ver b/src/devices/wimax/exports.ver
deleted file mode 100644
index 24cd848c..00000000
--- a/src/devices/wimax/exports.ver
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-global:
-	nm_device_factory_create;
-local:
-	*;
-};
diff --git a/src/devices/wimax/iwmxsdk.c b/src/devices/wimax/iwmxsdk.c
deleted file mode 100644
index 1c2c6821..00000000
--- a/src/devices/wimax/iwmxsdk.c
+++ /dev/null
@@ -1,1515 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- *
- *  Copyright (C) 2011 Red Hat, Inc. All rights reserved.
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/socket.h>
-
-#include <glib.h>
-
-#include "nm-wimax-util.h"
-
-#include <WiMaxAPI.h>
-#include <WiMaxAPIEx.h>
-
-#include "nm-logging.h"
-#include "iwmxsdk.h"
-
-static WIMAX_API_DEVICE_ID g_api;
-static GMutex add_remove_mutex;
-
-/* Misc utilities */
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-/* Misc values */
-enum {
-	/*
-	 * WARNING!!!!!
-	 *
-	 * ONLY ONE DEVICE SUPPORTED
-	 *
-	 * - on removal, there is no way to know which device was
-	 *   removed (the removed device is removed from the list and
-	 *   the callback doesn't have any more information than the
-	 *   index in the list that getlistdevice would return -- racy
-	 *   as hell).
-	 *
-	 * - on insertion, there is not enough information provided.
-	 */
-	IWMX_SDK_DEV_MAX = 1,
-};
-
-/* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX */
-static struct wmxsdk *g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
-
-static struct wmxsdk *deviceid_to_wmxsdk(WIMAX_API_DEVICE_ID *device_id)
-{
-	unsigned cnt;
-	for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
-		struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
-		if (wmxsdk &&
-		    wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
-			return wmxsdk;
-	}
-	return NULL;
-}
-
-static int deviceid_to_index(WIMAX_API_DEVICE_ID *device_id)
-{
-	unsigned cnt;
-
-	for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
-		struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
-		if (wmxsdk && wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
-			return cnt;
-	}
-	return -1;
-}
-
-struct wmxsdk *iwmx_sdk_get_wmxsdk_for_iface(const char *iface)
-{
-	unsigned cnt;
-
-	for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
-		struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
-		if (wmxsdk && !strcmp(wmxsdk->ifname, iface))
-			return wmxsdk;
-	}
-	return NULL;
-}
-
-/*
- * FIXME: pulled it it out of some hole
- *
- * the cinr to percentage computation comes from the L3/L4 doc
- *
- * But some other places (L4 code) have a more complex, seemingly
- * logarithmical computation.
- *
- * Oh well...
- *
- */
-static int cinr_to_percentage(int cinr)
-{
-	int strength;
-	if (cinr <= -5)
-		strength = 0;
-	else if (cinr >= 25)
-		strength = 100;
-	else	/* Calc percentage on the value from -5 to 25 */
-		strength = ((100UL * (cinr - -5)) / (25 - -5));
-	return strength;
-}
-
-/**************************************************************/
-
-typedef struct {
-	WimaxNewWmxsdkFunc callback;
-	void *user_data;
-} NewSdkCallback;
-
-static GMutex new_callbacks_mutex;
-static GSList *new_callbacks = NULL;
-
-void iwmx_sdk_new_callback_register(WimaxNewWmxsdkFunc callback, void *user_data)
-{
-	NewSdkCallback *cb;
-
-	cb = g_malloc0 (sizeof (NewSdkCallback));
-	g_assert (cb);
-	cb->callback = callback;
-	cb->user_data = user_data;
-
-	g_mutex_lock (&new_callbacks_mutex);
-	new_callbacks = g_slist_append (new_callbacks, cb);
-	g_mutex_unlock (&new_callbacks_mutex);
-}
-
-void iwmx_sdk_new_callback_unregister(WimaxNewWmxsdkFunc callback, void *user_data)
-{
-	GSList *iter;
-	NewSdkCallback *found = NULL;
-
-	g_mutex_lock (&new_callbacks_mutex);
-	for (iter = new_callbacks; iter; iter = g_slist_next (iter)) {
-		NewSdkCallback *cb = iter->data;
-
-		if (cb->callback == callback && cb->user_data == user_data) {
-			found = cb;
-			break;
-		}
-	}
-
-	if (found) {
-		new_callbacks = g_slist_remove (new_callbacks, found);
-		g_free (found);
-	}
-	g_mutex_unlock (&new_callbacks_mutex);
-}
-
-static void iwmx_sdk_call_new_callbacks(struct wmxsdk *wmxsdk)
-{
-	GSList *iter;
-
-	g_mutex_lock (&new_callbacks_mutex);
-	for (iter = new_callbacks; iter; iter = g_slist_next (iter)) {
-		NewSdkCallback *cb = iter->data;
-
-		cb->callback (wmxsdk, cb->user_data);
-	}
-	g_mutex_unlock (&new_callbacks_mutex);
-}
-
-/****************************************************************/
-
-typedef struct {
-	struct wmxsdk *wmxsdk;
-	WIMAX_API_DEVICE_STATUS new_status;
-	WIMAX_API_DEVICE_STATUS old_status;
-	WIMAX_API_STATUS_REASON reason;
-	WIMAX_API_CONNECTION_PROGRESS_INFO progress;
-} StateChangeInfo;
-
-static gboolean
-state_change_handler(gpointer user_data)
-{
-	StateChangeInfo *info = user_data;
-
-	if (info->wmxsdk->state_change_cb) {
-		info->wmxsdk->state_change_cb(info->wmxsdk,
-		                              info->new_status,
-		                              info->old_status,
-		                              info->reason,
-		                              info->progress,
-		                              info->wmxsdk->callback_data);
-	}
-	wmxsdk_unref(info->wmxsdk);
-	memset(info, 0, sizeof(*info));
-	free(info);
-	return FALSE;
-}
-
-static void
-_schedule_state_change(struct wmxsdk *wmxsdk,
-                       WIMAX_API_DEVICE_STATUS new_status,
-                       WIMAX_API_DEVICE_STATUS old_status,
-                       WIMAX_API_STATUS_REASON reason,
-                       WIMAX_API_CONNECTION_PROGRESS_INFO progress)
-{
-	StateChangeInfo *info;
-
-	info = malloc(sizeof (*info));
-	if (!info)
-		return;
-
-	memset(info, 0, sizeof(*info));
-	info->wmxsdk = wmxsdk;
-	info->new_status = new_status;
-	info->old_status = old_status;
-	info->reason = reason;
-	info->progress = progress;
-
-	wmxsdk_ref(wmxsdk);
-	g_idle_add(state_change_handler, info);
-}
-
-typedef struct {
-	struct wmxsdk *wmxsdk;
-	WIMAX_API_MEDIA_STATUS media_status;
-} MediaStatusInfo;
-
-static gboolean
-media_status_change_handler(gpointer user_data)
-{
-	MediaStatusInfo *info = user_data;
-
-	if (info->wmxsdk->media_status_cb) {
-		info->wmxsdk->media_status_cb(info->wmxsdk,
-		                              info->media_status,
-		                              info->wmxsdk->callback_data);
-	}
-	wmxsdk_unref(info->wmxsdk);
-	memset(info, 0, sizeof(*info));
-	free(info);
-	return FALSE;
-}
-
-static void
-_schedule_media_status_change(struct wmxsdk *wmxsdk,
-                              WIMAX_API_MEDIA_STATUS media_status)
-{
-	MediaStatusInfo *info;
-
-	info = malloc(sizeof (*info));
-	if (!info)
-		return;
-
-	memset(info, 0, sizeof(*info));
-	info->wmxsdk = wmxsdk;
-	info->media_status = media_status;
-
-	wmxsdk_ref(wmxsdk);
-	g_idle_add(media_status_change_handler, info);
-}
-
-typedef struct {
-	struct wmxsdk *wmxsdk;
-	WIMAX_API_NETWORK_CONNECTION_RESP result;
-} ConnectResultInfo;
-
-static gboolean
-connect_result_handler(gpointer user_data)
-{
-	ConnectResultInfo *info = user_data;
-
-	if (info->wmxsdk->connect_result_cb) {
-		info->wmxsdk->connect_result_cb(info->wmxsdk,
-		                                info->result,
-		                                info->wmxsdk->callback_data);
-	}
-	wmxsdk_unref(info->wmxsdk);
-	memset(info, 0, sizeof(*info));
-	free(info);
-	return FALSE;
-}
-
-static void
-_schedule_connect_result(struct wmxsdk *wmxsdk,
-                         WIMAX_API_NETWORK_CONNECTION_RESP resp)
-{
-	ConnectResultInfo *info;
-
-	info = malloc(sizeof (*info));
-	if (!info)
-		return;
-
-	memset(info, 0, sizeof(*info));
-	info->wmxsdk = wmxsdk;
-	info->result = resp;
-
-	wmxsdk_ref(wmxsdk);
-	g_idle_add(connect_result_handler, info);
-}
-
-typedef struct {
-	struct wmxsdk *wmxsdk;
-	WIMAX_API_NSP_INFO_EX *nsps;
-	guint num_nsps;
-} ScanResultInfo;
-
-static gboolean
-scan_result_handler(gpointer user_data)
-{
-	ScanResultInfo *info = user_data;
-
-	if (info->wmxsdk->scan_result_cb) {
-		info->wmxsdk->scan_result_cb(info->wmxsdk,
-		                             info->nsps,
-		                             info->num_nsps,
-		                             info->wmxsdk->callback_data);
-	}
-	wmxsdk_unref(info->wmxsdk);
-	free(info->nsps);
-	memset(info, 0, sizeof(*info));
-	free(info);
-	return FALSE;
-}
-
-static void
-_schedule_scan_result(struct wmxsdk *wmxsdk,
-                      WIMAX_API_NSP_INFO_EX *nsps,
-                      guint num_nsps)
-{
-	ScanResultInfo *info;
-	size_t nsps_size;
-	int i, tmp;
-
-	info = malloc(sizeof (*info));
-	if (!info)
-		return;
-
-	memset(info, 0, sizeof(*info));
-	info->wmxsdk = wmxsdk;
-
-	nsps_size = num_nsps * sizeof (WIMAX_API_NSP_INFO_EX);
-	info->nsps = malloc(nsps_size);
-	memcpy(info->nsps, nsps, nsps_size);
-	info->num_nsps = num_nsps;
-
-	/* CAPI may report link quality as zero -- if it does check if it is a bug
-	 * by computing it based on CINR. If it is different, use the computed one.
-	 */
-	for (i = 0; i < num_nsps; i++) {
-		WIMAX_API_NSP_INFO_EX *nsp = &info->nsps[i];
-
-		if (nsp->linkQuality == 0) {
-			tmp = cinr_to_percentage(nsp->CINR - 10);
-			if (tmp != nsp->linkQuality)
-				nsp->linkQuality = tmp;
-		}
-	}
-
-	wmxsdk_ref(wmxsdk);
-	g_idle_add(scan_result_handler, info);
-}
-
-static gboolean
-removed_handler(gpointer user_data)
-{
-	struct wmxsdk *wmxsdk = user_data;
-
-	if (wmxsdk->removed_cb)
-		wmxsdk->removed_cb(wmxsdk, wmxsdk->callback_data);
-	wmxsdk_unref(wmxsdk);
-	return FALSE;
-}
-
-static void
-_schedule_removed(struct wmxsdk *wmxsdk)
-{
-	wmxsdk_ref(wmxsdk);
-	g_idle_add(removed_handler, wmxsdk);
-}
-
-/****************************************************************/
-
-/*
- * Convert a WiMAX API status to an string.
- */
-const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
-{
-	switch (status) {
-	case WIMAX_API_DEVICE_STATUS_UnInitialized:
-		return "uninitialized";
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-		return "rf off";
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-		return "rf off (hard-block)";
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-		return "rf off (soft-block)";
-	case WIMAX_API_DEVICE_STATUS_Ready:
-		return "ready";
-	case WIMAX_API_DEVICE_STATUS_Scanning:
-		return "scanning";
-	case WIMAX_API_DEVICE_STATUS_Connecting:
-		return "connecting";
-	case WIMAX_API_DEVICE_STATUS_Data_Connected:
-		return "connected";
-	default:
-		return "unknown";
-	}
-}
-
-const char *iwmx_sdk_reason_to_str(WIMAX_API_STATUS_REASON reason)
-{
-	switch (reason) {
-	case WIMAX_API_STATUS_REASON_Normal:
-		return "normal";
-
-	/**< Failed to complete NW entry with the selected operator (unspecified reason).  */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_to_NW:
-		return "unspecified failure";
-
-	/**< Failed to complete ranging */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_Ranging:
-		return "ranging failed";
-
-	/**< SBC phase failed */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_SBC:
-		return "sbc failed";
-
-	/**< Security error. EAP authentication failed device level */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_EAP_AUTH_Device:
-		return "EAP device auth failed";
-
-	/**< Security error. EAP authentication failed user level */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_EAP_AUTH_user:
-		return "EAP user auth failed";
-
-	/**< Security error. Handshake failed */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_3_Way_Handshake:
-		return "3 way handshake failed";
-
-	/**< Registration failed */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_REG:
-		return "registration failed";
-
-	/**< Failed to initialize the data path (failed to perform DSA to one UL and one DL SFs). */
-	case WIMAX_API_STATUS_REASON_Fail_to_connect_datapath:
-		return "datapath failed";
-
-	default:
-		return "unknown";
-	}
-}
-
-const char *iwmx_sdk_media_status_to_str(WIMAX_API_MEDIA_STATUS status)
-{
-	switch (status) {
-	case WIMAX_API_MEDIA_STATUS_LINK_UP:
-		return "link-up";
-	case WIMAX_API_MEDIA_STATUS_LINK_DOWN:
-		return "link-down";
-	case WIMAX_API_MEDIA_STATUS_LINK_RENEW:
-		return "link-renew";
-	default:
-		return "unknown";
-	}
-}
-
-const char *
-iwmx_sdk_con_progress_to_str(WIMAX_API_CONNECTION_PROGRESS_INFO progress)
-{
-	switch (progress) {
-
-	/**< Device is in Ranging */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Ranging:
-		return "ranging";
-
-	/**< Device is in SBC */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_SBC:
-		return "sbc";
-
-	/**< Device is in EAP authentication Device */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_Device:
-		return "eap-auth-device";
-
-	/**< Device is in EAP authentication User */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_User:
-		return "eap-auth-user";
-
-	/**< Device is in 3-way-handshake */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_3_way_handshake:
-		return "3way-handshake";
-
-	/**< Device is in Registration */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registration:
-		return "registration";
-
-	/**< Device is in De-registration */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_De_registration:
-		return "deregistration";
-
-	/**< Device is registered (operational) */
-	case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registered:
-		return "registered";
-
-	default:
-		return "unknown";
-	}
-}
-
-/*
- * Get the device's status from the device
- *
- * Does NOT cache the result
- * Does NOT trigger a state change in NetworkManager
- *
- * Returns < 0 errno code on error, status code if ok.
- */
-static WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
-{
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	WIMAX_API_DEVICE_STATUS dev_status;
-	WIMAX_API_CONNECTION_PROGRESS_INFO pi;
-
-	r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot read device state: %d (%s)", r, errstr);
-		dev_status = -EIO;
-	}
-	return dev_status;
-}
-
-/*
- * Get the device's status from the device but return a string describing it
- *
- * Same conditions as iwmx_sdk_get_device_status().
- */
-static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
-{
-	const char *result;
-	WIMAX_API_DEVICE_STATUS dev_status;
-
-	dev_status = iwmx_sdk_get_device_status(wmxsdk);
-	if ((int) dev_status < 0)
-		result = "cannot read device state";
-	else
-		result = iwmx_sdk_dev_status_to_str(dev_status);
-	return result;
-}
-
-/*
- * If the device is connected but we don't know about the network,
- * create the knowledge of it.
- *
- * Asks the WiMAX API to report which NSP we are connected to and we
- * create/update a network_el in the device's network list. Then
- * return it.
- *
- * Returns NULL on error.
- *
- */
-WIMAX_API_CONNECTED_NSP_INFO_EX *iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
-{
-	WIMAX_API_CONNECTED_NSP_INFO_EX *nsp_info = NULL;
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	nsp_info = malloc(sizeof (*nsp_info));
-	if (!nsp_info) {
-		nm_log_err(LOGD_WIMAX, "wmxsdk: cannot allocate NSP info");
-		return NULL;
-	}
-
-	r = GetConnectedNSPEx(&wmxsdk->device_id, nsp_info);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get connected NSP info: %d (%s)", r, errstr);
-		free (nsp_info);
-		nsp_info = NULL;
-	} else {
-		/* Migth be 0 sometimes; fix that up */
-		if (nsp_info->linkQuality == 0) {
-			int linkq_expected = cinr_to_percentage(nsp_info->CINR - 10);
-			if (linkq_expected != nsp_info->linkQuality)
-				nsp_info->linkQuality = linkq_expected;
-		}
-	}
-
-	return nsp_info;
-}
-
-/*
- * Asks the WiMAX API to report current link statistics.
- *
- * Returns NULL on error.
- *
- */
-WIMAX_API_LINK_STATUS_INFO_EX *iwmx_sdk_get_link_status_info(struct wmxsdk *wmxsdk)
-{
-	WIMAX_API_LINK_STATUS_INFO_EX *stats = NULL;
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	/* Only report if connected */
-	if (iwmxsdk_status_get(wmxsdk) < WIMAX_API_DEVICE_STATUS_Connecting) {
-		nm_log_err(LOGD_WIMAX, "wmxsdk: cannot get link status info unless connected");
-		return NULL;
-	}
-
-	stats = malloc(sizeof (*stats));
-	if (!stats) {
-		nm_log_err(LOGD_WIMAX, "wmxsdk: cannot allocate links status info");
-		return NULL;
-	}
-
-	r = GetLinkStatusEx(&wmxsdk->device_id, stats);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get link status info: %d (%s)", r, errstr);
-		free (stats);
-		stats = NULL;
-	}
-
-	return stats;
-}
-
-/*
- * Callback for a RF State command
- *
- * Called by the WiMAX API when a command sent to change the RF state
- * is completed. This is just a confirmation of what happened with the
- * command.
- *
- * We don't do anything, as when the device changes state, the state
- * change callback is called and that will fiddle with the NetworkManager
- * internals.
- */
-static void __iwmx_sdk_rf_state_cb(WIMAX_API_DEVICE_ID *device_id,
-				   WIMAX_API_RF_STATE rf_state)
-{
-	nm_log_dbg(LOGD_WIMAX, "rf_state changed to %d", rf_state);
-}
-
-/*
- * Turn the radio on or off
- *
- * First it checks that we are in the right state before doing
- * anything; there might be no need to do anything.
- *
- * Issue a command to the WiMAX API, wait for a callback confirming it
- * is done. Sometimes the callback is missed -- in that case, do force
- * a state change evaluation.
- *
- * Frustration note:
- *
- *      Geezoos efing Xist, they make difficult even the most simple
- *      of the operations
- *
- *      This thing is definitely a pain. If the radio is ON already
- *      and you switch it on again...well, there is no way to tell
- *      because you don't get a callback saying it basically
- *      suceeded. But on the other hand, if the thing was in a
- *      different state and action needs to be taken, you have to wait
- *      for a callback to confirm it's done. However, there is also an
- *      state change callback, which is almost the same, so now you
- *      have to handle things in two "unrelated" threads of execution.
- *
- *      How the shpx are you expected to tell the difference? Check
- *      status first? On timeout? Nice gap (eighteen wheeler size) for
- *      race conditions.
- */
-int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
-{
-	int result;
-
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-	WIMAX_API_DEVICE_STATUS dev_status;
-
-	g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
-
-	/* Guess what the current radio state is; if it is ON
-	 * already, don't redo it. */
-	dev_status = iwmx_sdk_get_device_status(wmxsdk);
-	if ((int) dev_status < 0) {
-		result = dev_status;
-		goto error_get_status;
-	}
-	switch (dev_status) {
-	case WIMAX_API_DEVICE_STATUS_UnInitialized:
-		result = -EINVAL;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-		nm_log_err(LOGD_WIMAX, "wmxsdk: cannot turn on radio: hw switch is off");
-		result = -EPERM;
-		goto error_cant_do;
-		break;
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-		if (rf_state == WIMAX_API_RF_OFF) {
-			result = 0;
-			nm_log_dbg(LOGD_WIMAX, "radio is already off");
-			goto out_done;
-		}
-		break;
-	case WIMAX_API_DEVICE_STATUS_Ready:
-	case WIMAX_API_DEVICE_STATUS_Scanning:
-	case WIMAX_API_DEVICE_STATUS_Connecting:
-	case WIMAX_API_DEVICE_STATUS_Data_Connected:
-		if (rf_state == WIMAX_API_RF_ON) {
-			result = 0;
-			nm_log_dbg(LOGD_WIMAX, "radio is already on");
-			goto out_done;
-		}
-		break;
-	default:
-		g_assert(1);
-	}
-	/* Ok, flip the radio */
-	r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot flip radio to %d: %d (%s) [device is in state %s]",
-		           rf_state, r, errstr, iwmx_sdk_get_device_status_str(wmxsdk));
-		result = -EIO;
-	} else
-		result = -EINPROGRESS;
-out_done:
-error_cant_do:
-error_get_status:
-	return result;
-}
-
-/*
- * Read the cached device status
- */
-WIMAX_API_DEVICE_STATUS iwmxsdk_status_get(struct wmxsdk *wmxsdk)
-{
-	WIMAX_API_DEVICE_STATUS status;
-
-	g_mutex_lock(&wmxsdk->status_mutex);
-	status = wmxsdk->status;
-	g_mutex_unlock(&wmxsdk->status_mutex);
-	return status;
-}
-
-/*
- * Callback for a Connect command
- *
- * Called by the WiMAX API when a command sent to connect is
- * completed. This is just a confirmation of what happened with the
- * command.
- *
- * WE DON'T DO MUCH HERE -- the real meat happens when a state change
- * callback is sent, where we detect we move to connected state (or
- * from disconnecting to something else); the state change callback is
- * called and that will fiddle with the NetworkManager internals.
- */
-static void __iwmx_sdk_connect_cb(WIMAX_API_DEVICE_ID *device_id,
-				  WIMAX_API_NETWORK_CONNECTION_RESP resp)
-{
-	WIMAX_API_DEVICE_STATUS status;
-	struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-
-	status = iwmxsdk_status_get(wmxsdk);
-	if (resp == WIMAX_API_CONNECTION_SUCCESS) {
-		if (status != WIMAX_API_DEVICE_STATUS_Data_Connected) {
-			nm_log_err(LOGD_WIMAX, "wmxsdk: error: connect worked, but state"
-			           " didn't change (now it is %d [%s])",
-			           status,
-			           iwmx_sdk_dev_status_to_str(status));
-		}
-	} else {
-		nm_log_err(LOGD_WIMAX, "wmxsdk: failed to connect (status %d: %s)",
-		           status, iwmx_sdk_dev_status_to_str(status));
-	}
-
-	_schedule_connect_result(wmxsdk, resp);
-}
-
-/*
- * Connect to a network
- *
- * This function starts the connection process to a given network;
- * when the device changes status, the status change callback will
- * tell NetworkManager if the network is finally connected or not.
- *
- * One of the reasons it is done like that is to allow external tools
- * to control the device and the plugin just passing the status so
- * NetworkManager displays the right info.
- */
-int iwmx_sdk_connect(struct wmxsdk *wmxsdk, const char *nsp_name)
-{
-	int result = 0;
-
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-	WIMAX_API_DEVICE_STATUS dev_status;
-	char sdk_name[MAX_SIZE_OF_NSP_NAME];
-
-	g_mutex_lock(&wmxsdk->connect_mutex);
-	/* Guess what the current radio state is; if it is ON
-	 * already, don't redo it. */
-	dev_status = iwmxsdk_status_get(wmxsdk);
-	if ((int) dev_status < 0) {
-		result = dev_status;
-		goto error_get_status;
-	}
-	switch (dev_status) {
-	case WIMAX_API_DEVICE_STATUS_UnInitialized:
-		nm_log_err(LOGD_WIMAX, "wmxsdk: SW BUG? HW is uninitialized");
-		result = -EINVAL;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot connect: radio is off");
-		result = -EPERM;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_Ready:
-	case WIMAX_API_DEVICE_STATUS_Scanning:
-		break;
-	case WIMAX_API_DEVICE_STATUS_Connecting:
-		nm_log_dbg(LOGD_WIMAX, "Connect already pending, waiting for it");
-		result = -EINPROGRESS;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_Data_Connected:
-		nm_log_err(LOGD_WIMAX, "wmxsdk: BUG? need to disconnect?");
-		result = -EINVAL;
-		goto error_cant_do;
-	default:
-		g_assert(1);
-	}
-
-	/* The SDK treats the network name as wchar_t* while the contents are
-	 * actually just UTF-8...  WTF?  Hand it a full buffer to work around
-	 * boundary cases where the NSP name contains an odd # of characters.
-	 */
-	memset(sdk_name, 0, sizeof (sdk_name));
-	memcpy(sdk_name, nsp_name, strlen (nsp_name));
-
-	/* Ok, do the connection, wait for a callback */
-	r = CmdConnectToNetwork(&wmxsdk->device_id, &sdk_name[0], 0, 0);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot connect to network %s: %d (%s) - device is in state '%s'",
-		           nsp_name, r, errstr,
-		           iwmx_sdk_get_device_status_str(wmxsdk));
-		result = -EIO;
-	}
-
-error_cant_do:
-error_get_status:
-	g_mutex_unlock(&wmxsdk->connect_mutex);
-	return result;
-}
-
-/*
- * Callback for a Disconnect command
- *
- * Called by the WiMAX API when a command sent to connect is
- * completed. This is just a confirmation of what happened with the
- * command.
- *
- * When the device changes state, the state change callback is called
- * and that will fiddle with the NetworkManager internals.
- *
- * We just update the result of the command and wake up anybody who is
- * waiting for this conditional variable.
- */
-static void __iwmx_sdk_disconnect_cb(WIMAX_API_DEVICE_ID *device_id,
-				     WIMAX_API_NETWORK_CONNECTION_RESP resp)
-{
-	struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-	WIMAX_API_DEVICE_STATUS status;
-
-	status = iwmxsdk_status_get(wmxsdk);
-	if (resp == WIMAX_API_CONNECTION_SUCCESS) {
-		if (status == WIMAX_API_DEVICE_STATUS_Data_Connected) {
-			nm_log_err(LOGD_WIMAX, "wmxsdk: error: disconnect worked, "
-				      "but state didn't change (now it is %d [%s])", status,
-				      iwmx_sdk_dev_status_to_str(status));
-		}
-	} else
-		nm_log_err(LOGD_WIMAX, "wmxsdk: failed to disconnect (status %d: %s)",
-			      status, iwmx_sdk_dev_status_to_str(status));
-}
-
-/*
- * Disconnect from a network
- *
- * This function tells the device to disconnect; the state change
- * callback will take care of inform NetworkManager's internals.
- */
-int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
-{
-	int result;
-
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-	WIMAX_API_DEVICE_STATUS dev_status;
-
-	g_mutex_lock(&wmxsdk->connect_mutex);
-	/* Guess what the current radio state is; if it is ON
-	 * already, don't redo it. */
-	dev_status = iwmx_sdk_get_device_status(wmxsdk);
-	if ((int) dev_status < 0) {
-		result = dev_status;
-		goto error_get_status;
-	}
-	switch (dev_status) {
-	case WIMAX_API_DEVICE_STATUS_UnInitialized:
-		nm_log_err(LOGD_WIMAX, "wmxsdk: SW BUG? HW is uninitialized");
-		result = -EINVAL;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-		nm_log_dbg(LOGD_WIMAX, "Cannot disconnect, radio is off; ignoring");
-		result = 0;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_Ready:
-	case WIMAX_API_DEVICE_STATUS_Scanning:
-		nm_log_dbg(LOGD_WIMAX, "Cannot disconnect, already disconnected; ignoring");
-		result = 0;
-		goto error_cant_do;
-	case WIMAX_API_DEVICE_STATUS_Connecting:
-	case WIMAX_API_DEVICE_STATUS_Data_Connected:
-		break;
-	default:
-		g_assert(1);
-	}
-	/* Ok, flip the radio */
-	r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot disconnect from network: %d (%s)", r, errstr);
-		result = -EIO;
-	} else
-		result = -EINPROGRESS;
-error_cant_do:
-error_get_status:
-	g_mutex_unlock(&wmxsdk->connect_mutex);
-	return result;
-}
-
-/*
- * Turn fast reconnect capability on/off
- *
- * This function tells wimaxd to turn fast reconnect on or off.
- */
-int iwmx_sdk_set_fast_reconnect_enabled(struct wmxsdk *wmxsdk, int enabled)
-{
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	r = SetFastReconnectCapabilityStatus(&wmxsdk->device_id, !!enabled);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot set fast reconnect to %d: %d (%s)",
-		           enabled, r, errstr);
-		return -EIO;
-	}
-	return 0;
-}
-
-static void __iwmx_sdk_media_status_update_cb (WIMAX_API_DEVICE_ID_P device_id,
-					WIMAX_API_MEDIA_STATUS mediaStatus)
-{
-	struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-
-	/* Ignore redundant LINK_UP events */
-	if (   mediaStatus == WIMAX_API_MEDIA_STATUS_LINK_UP
-	    && wmxsdk->media_status == WIMAX_API_MEDIA_STATUS_LINK_UP)
-	    return;
-
-	wmxsdk->media_status = mediaStatus;
-
-	nm_log_dbg(LOGD_WIMAX, "wmxsdk: media status change to (%d) %s",
-	           mediaStatus, iwmx_sdk_media_status_to_str (mediaStatus));
-
-	_schedule_media_status_change(wmxsdk, mediaStatus);
-}
-
-/*
- * Callback for state change messages
- *
- * Just pass them to the state transition handler
- */
-static void __iwmx_sdk_state_change_cb(WIMAX_API_DEVICE_ID *device_id,
-					WIMAX_API_DEVICE_STATUS status,
-					WIMAX_API_STATUS_REASON reason,
-					WIMAX_API_CONNECTION_PROGRESS_INFO pi)
-{
-	struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-	WIMAX_API_DEVICE_STATUS old_status;
-
-	nm_log_dbg(LOGD_WIMAX, "wmxsdk: state change to (%d) %s reason (%d) %s",
-	           status, iwmx_sdk_dev_status_to_str (status),
-	           reason, iwmx_sdk_reason_to_str (reason));
-
-	g_mutex_lock(&wmxsdk->status_mutex);
-	old_status = wmxsdk->status;
-	wmxsdk->status = status;
-	g_mutex_unlock(&wmxsdk->status_mutex);
-
-	_schedule_state_change(wmxsdk, status, old_status, reason, pi);
-}
-
-/*
- * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
- * are available.
- *
- * From here we update NetworkManager's idea of which networks are available.
- */
-static void __iwmx_sdk_scan_common_cb(WIMAX_API_DEVICE_ID *device_id,
-				      WIMAX_API_NSP_INFO_EX *nsp_list,
-				      UINT32 nsp_list_size)
-{
-	struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
-
-	g_mutex_lock(&wmxsdk->network_mutex);
-	_schedule_scan_result(wmxsdk, nsp_list, nsp_list_size);
-	g_mutex_unlock(&wmxsdk->network_mutex);
-}
-
-/*
- * Called by the WiMAX API when we get a wide scan result
- *
- * We treat them same as wide, so we just call that.
- */
-static void __iwmx_sdk_wide_scan_cb(WIMAX_API_DEVICE_ID *device_id,
-				    WIMAX_API_NSP_INFO_EX *nsp_list,
-				    UINT32 nsp_list_size)
-{
-	__iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
-}
-
-/*
- * Called by the WiMAX API when we get a normal (non wide) scan result
- *
- * We treat them same as wide, so we just call that.
- */
-static void __iwmx_sdk_scan_cb(WIMAX_API_DEVICE_ID *device_id,
-				WIMAX_API_NSP_INFO_EX *nsp_list,
-				UINT32 nsp_list_size, UINT32 searchProgress)
-{
-	__iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
-}
-
-/*
- * Called to ask the device to scan for networks
- *
- * We don't really scan as the WiMAX SDK daemon scans in the
- * background for us. We just get the results and hand them back via
- * the scan_result_cb callback.
- */
-int iwmx_sdk_get_networks(struct wmxsdk *wmxsdk)
-{
-	int result;
-
-	UINT32 nsp_list_length = 10;
-	WIMAX_API_NSP_INFO_EX nsp_list[10];	/* FIXME: up to 32? */
-
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot get network list: %d (%s)", r, errstr);
-		result = -EIO;
-		goto error_scan;
-	}
-
-	if (nsp_list_length == 0) {
-		nm_log_dbg(LOGD_WIMAX, "no networks");
-	} else
-		__iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
-					nsp_list_length);
-	result = 0;
-error_scan:
-	return result;
-}
-
-/*
- * Initialize the WiMAX API, register with it, setup callbacks
- *
- */
-static int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
-{
-	int result, status;
-
-	WIMAX_API_RET r;
-
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	result = -ENFILE;
-
-	/* device_id initialized by iwmx_sdk_dev_add */
-
-	r = WiMaxDeviceOpen(&wmxsdk->device_id);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot open device: %d (%s)", r, errstr);
-		goto error_wimaxdeviceopen;
-	}
-
-	/*
-	 * We scan in auto mode (in the background)
-	 *
-	 * Otherwise is messy -- if we have NetworkManager triggering a scan
-	 * when we call iwmx_nm_scan() -> iwmx_sdk_scan(), most of the
-	 * times that causes a race condition when the UI asks for a
-	 * scan right before displaying the network menu. As there is
-	 * no way to cancel an ongoing scan before connecting, we are
-	 * stuck. So we do auto bg and have iwmx_sdk_scan() just return
-	 * the current network list.
-	 */
-	r = SetConnectionMode(&wmxsdk->device_id,
-			      WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot set connectin mode to manual: %d (%s)", r, errstr);
-		goto error_connection_mode;
-	}
-
-	r = SubscribeControlPowerManagement(&wmxsdk->device_id,
-					    __iwmx_sdk_rf_state_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to radio change events: %u (%s)", r, errstr);
-		result = -EIO;
-		goto error_subscribe_rf_state;
-	}
-
-	r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
-					__iwmx_sdk_state_change_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to state chaneg events: %d (%s)", r, errstr);
-		goto error_subscribe_state_change;
-	}
-
-	r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
-					     __iwmx_sdk_wide_scan_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to wide scan events: %d (%s)", r, errstr);
-		goto error_subscribe_wide_scan;
-	}
-	r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to scan events: %d (%s)", r, errstr);
-		goto error_subscribe_scan;
-	}
-
-	r = SubscribeConnectToNetwork(&wmxsdk->device_id,
-				      __iwmx_sdk_connect_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to connect events: %d (%s)", r, errstr);
-		goto error_subscribe_connect;
-	}
-
-	r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
-					 __iwmx_sdk_disconnect_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to disconnect events: %d (%s)", r, errstr);
-		goto error_subscribe_disconnect;
-	}
-
-	r = SubscribeMediaStatusUpdate(&wmxsdk->device_id, __iwmx_sdk_media_status_update_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot subscribe to media status events: %d (%s)", r, errstr);
-		goto error_subscribe_media_status;
-	}
-
-	status = iwmx_sdk_get_device_status(wmxsdk);
-	if ((int) status < 0)
-		status = WIMAX_API_DEVICE_STATUS_UnInitialized;
-
-	g_mutex_lock(&wmxsdk->status_mutex);
-	wmxsdk->status = status;
-	g_mutex_unlock(&wmxsdk->status_mutex);
-
-	_schedule_state_change(wmxsdk,
-	                       status,
-	                       WIMAX_API_DEVICE_STATUS_UnInitialized,
-	                       WIMAX_API_STATUS_REASON_Normal,
-	                       WIMAX_API_DEVICE_CONNECTION_PROGRESS_Ranging);
-
-	return 0;
-
-	UnsubscribeMediaStatusUpdate(&wmxsdk->device_id);
-error_subscribe_media_status:
-	UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
-error_subscribe_disconnect:
-	UnsubscribeConnectToNetwork(&wmxsdk->device_id);
-error_subscribe_connect:
-	UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
-error_subscribe_scan:
-	UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
-error_subscribe_wide_scan:
-	UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
-error_subscribe_state_change:
-	UnsubscribeControlPowerManagement(&wmxsdk->device_id);
-error_subscribe_rf_state:
-error_connection_mode:
-	WiMaxDeviceClose(&wmxsdk->device_id);
-error_wimaxdeviceopen:
-	return result;
-}
-
-/*
- * Called when a device is torn down
- *
- * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
- * unregister from the WiMAX API.
- */
-static void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
-{
-	UnsubscribeMediaStatusUpdate(&wmxsdk->device_id);
-	UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
-	UnsubscribeConnectToNetwork(&wmxsdk->device_id);
-	UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
-	UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
-	UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
-	UnsubscribeControlPowerManagement(&wmxsdk->device_id);
-	WiMaxDeviceClose(&wmxsdk->device_id);
-}
-
-void iwmx_sdk_set_callbacks(struct wmxsdk *wmxsdk,
-                            WimaxStateChangeFunc state_change_cb,
-                            WimaxMediaStatusFunc media_status_cb,
-                            WimaxConnectResultFunc connect_result_cb,
-                            WimaxScanResultFunc scan_result_cb,
-                            WimaxRemovedFunc removed_cb,
-                            void *user_data)
-{
-	wmxsdk->state_change_cb = state_change_cb;
-	wmxsdk->media_status_cb = media_status_cb;
-	wmxsdk->connect_result_cb = connect_result_cb;
-	wmxsdk->scan_result_cb = scan_result_cb;
-	wmxsdk->removed_cb = removed_cb;
-	wmxsdk->callback_data = user_data;
-}
-
-/* Initialize a [zeroed] struct wmxsdk */
-static struct wmxsdk *wmxsdk_new(void)
-{
-	struct wmxsdk *wmxsdk;
-
-	wmxsdk = malloc(sizeof(*wmxsdk));
-	if (wmxsdk) {
-		memset(wmxsdk, 0, sizeof(*wmxsdk));
-
-		wmxsdk->refcount = 1;
-		g_mutex_init(&wmxsdk->network_mutex);
-
-		wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
-		g_mutex_init(&wmxsdk->status_mutex);
-
-		g_mutex_init(&wmxsdk->connect_mutex);
-	}
-	return wmxsdk;
-}
-
-struct wmxsdk *wmxsdk_ref(struct wmxsdk *wmxsdk)
-{
-	g_atomic_int_add(&wmxsdk->refcount, 1);
-	return wmxsdk;
-}
-
-void wmxsdk_unref(struct wmxsdk *wmxsdk)
-{
-	if (g_atomic_int_dec_and_test(&wmxsdk->refcount)) {
-		g_mutex_clear(&wmxsdk->status_mutex);
-		g_mutex_clear(&wmxsdk->connect_mutex);
-		memset(wmxsdk, 0, sizeof(*wmxsdk));
-		free(wmxsdk);
-	}
-}
-
-static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
-{
-	struct wmxsdk *wmxsdk;
-	const char *s;
-
-	if (idx >= IWMX_SDK_DEV_MAX) {
-		nm_log_err(LOGD_WIMAX, "BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)", idx, IWMX_SDK_DEV_MAX);
-		return;
-	}
-	if (g_iwmx_sdk_devs[idx] != NULL) {
-		nm_log_err(LOGD_WIMAX, "BUG! device index %u already enumerated?", idx);
-		return;
-	}
-
-	wmxsdk = wmxsdk_new();
-	if (wmxsdk == NULL) {
-		nm_log_err(LOGD_WIMAX, "Can't allocate %zu bytes", sizeof(*wmxsdk));
-		return;
-	}
-
-	/*
-	 * This depends on a hack in the WiMAX Network Service; it has
-	 * to return, as part of the device name, a string "if:IFNAME"
-	 * where the OS's device name is stored.
-	 */
-	s = strstr(name, "if:");
-	if (s == NULL
-	    || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
-		nm_log_err(LOGD_WIMAX, "Cannot extract network interface name off '%s'",
-			      name);
-		goto error;
-	}
-	nm_log_dbg(LOGD_WIMAX, "network interface name: '%s'", wmxsdk->ifname);
-
-	strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
-	wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
-	wmxsdk->device_id.deviceIndex = api_idx;
-
-	if (iwmx_sdk_setup(wmxsdk) != 0) {
-		nm_log_err(LOGD_WIMAX, "wxmsdk: %s: cannot set up interface", wmxsdk->ifname);
-		goto error;
-	}
-
-	g_iwmx_sdk_devs[idx] = wmxsdk;
-
-	/* Notify listeners of new devices */
-	iwmx_sdk_call_new_callbacks (wmxsdk);
-	return;
-
-error:
-	wmxsdk_unref(wmxsdk);
-	return;
-}
-
-static void iwmx_sdk_dev_rm(unsigned idx)
-{
-	struct wmxsdk *wmxsdk;
-
-	if (idx >= IWMX_SDK_DEV_MAX) {
-		nm_log_err(LOGD_WIMAX, "BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)", idx, IWMX_SDK_DEV_MAX);
-		return;
-	}
-
-	wmxsdk = g_iwmx_sdk_devs[idx];
-	_schedule_removed(wmxsdk);
-	iwmx_sdk_remove(wmxsdk);
-	wmxsdk_unref(wmxsdk);
-	g_iwmx_sdk_devs[idx] = NULL;
-}
-
-static void iwmx_sdk_addremove_cb(WIMAX_API_DEVICE_ID *devid,
-				  BOOL presence)
-{
-	unsigned int cnt;
-	WIMAX_API_RET r;
-	WIMAX_API_HW_DEVICE_ID device_id_list[5];
-	UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	g_mutex_lock(&add_remove_mutex);
-
-	nm_log_dbg(LOGD_WIMAX, "cb: handle %u index #%u is %d", devid->sdkHandle,
-	           devid->deviceIndex, presence);
-
-	r = GetListDevice(devid, device_id_list, &device_id_list_size);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(devid, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot obtain list of devices: %d (%s)", r, errstr);
-		goto out;
-	}
-
-	if (device_id_list_size == 0) {
-		nm_log_dbg(LOGD_WIMAX, "No WiMAX devices reported");
-	} else {
-		for (cnt = 0; cnt < device_id_list_size; cnt++) {
-			WIMAX_API_HW_DEVICE_ID *dev =
-				device_id_list + cnt;
-			nm_log_dbg(LOGD_WIMAX, "#%u index #%u device %s", cnt,
-			           dev->deviceIndex, dev->deviceName);
-		}
-	}
-
-	if (presence) {
-		WIMAX_API_HW_DEVICE_ID *dev;
-
-		/* Make sure the wimax NS isn't lying to us */
-		if (device_id_list_size < devid->deviceIndex) {
-			nm_log_err(LOGD_WIMAX, "wmxsdk: changed device (%u) not in the list? (%u items)",
-			           devid->deviceIndex, device_id_list_size);
-			goto out;
-		}
-
-		/* Add the device to our internal list */
-		dev = device_id_list + devid->deviceIndex;
-		iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex, dev->deviceName);
-	} else {
-		/* Remove the device from our internal list */
-		int idx = deviceid_to_index(devid);
-
-		if (idx >= 0)
-			iwmx_sdk_dev_rm(idx);
-	}
-
-out:
-	g_mutex_unlock(&add_remove_mutex);
-}
-
-/*
- * Initialize the WiMAX API, register with it, setup callbacks for
- * device coming up / dissapearing
- */
-int iwmx_sdk_api_init(void)
-{
-	int result;
-	unsigned int cnt;
-	WIMAX_API_RET r;
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	WIMAX_API_HW_DEVICE_ID device_id_list[5];
-	UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
-
-	memset(&g_api, 0, sizeof(g_api));
-	g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
-
-	result = -EIO;
-	r = WiMaxAPIOpen(&g_api);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&g_api, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: WiMaxAPIOpen failed with %d (%s)", r, errstr);
-		goto error_wimaxapiopen;
-	}
-
-	r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&g_api, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: insert/remove subscribe failed with %d (%s)", r, errstr);
-		goto error_close;
-	}
-
-	r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&g_api, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: Cannot obtain list of devices: %d (%s)", r, errstr);
-		goto error_close;
-	}
-	if (device_id_list_size < g_api.deviceIndex) {
-		nm_log_err(LOGD_WIMAX, "wmxsdk: changed device (%u) not in the list? (%u items)",
-		           g_api.deviceIndex, device_id_list_size);
-	}
-
-	if (device_id_list_size == 0) {
-		nm_log_dbg(LOGD_WIMAX, "No WiMAX devices reported");
-	} else {
-		for (cnt = 0; cnt < device_id_list_size; cnt++) {
-			WIMAX_API_HW_DEVICE_ID *dev = device_id_list + cnt;
-			nm_log_dbg(LOGD_WIMAX, "#%u index #%u device %s", cnt, dev->deviceIndex, dev->deviceName);
-			iwmx_sdk_dev_add(cnt, dev->deviceIndex, dev->deviceName);
-		}
-	}
-	return 0;
-
-error_close:
-	WiMaxAPIClose(&g_api);
-error_wimaxapiopen:
-	return result;
-}
-
-void iwmx_sdk_api_exit(void)
-{
-	WIMAX_API_RET r;
-
-	char errstr[512];
-	UINT32 errstr_size = sizeof(errstr);
-
-	r = WiMaxAPIClose(&g_api);
-	if (r != WIMAX_API_RET_SUCCESS) {
-		GetErrorString(&g_api, r, errstr, &errstr_size);
-		nm_log_err(LOGD_WIMAX, "wmxsdk: WiMaxAPIClose failed with %d (%s)", r, errstr);
-	}
-	return;
-}
diff --git a/src/devices/wimax/iwmxsdk.h b/src/devices/wimax/iwmxsdk.h
deleted file mode 100644
index c8e76696..00000000
--- a/src/devices/wimax/iwmxsdk.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- *
- *  Copyright (C) 2011 Red Hat, Inc. All rights reserved.
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifndef __IWMXSDK_H__
-#define __IWMXSDK_H__
-
-#include "nm-wimax-util.h"
-
-#include <wimax/WiMaxTypesEx.h>
-#include <wimax/WiMaxAPIEx.h>
-
-struct wmxsdk;
-
-typedef void (*WimaxNewWmxsdkFunc) (struct wmxsdk *wmxsdk, void *user_data);
-
-typedef void (*WimaxStateChangeFunc) (struct wmxsdk *wmxsdk,
-                                      WIMAX_API_DEVICE_STATUS new_status,
-                                      WIMAX_API_DEVICE_STATUS old_status,
-                                      WIMAX_API_STATUS_REASON reason,
-                                      WIMAX_API_CONNECTION_PROGRESS_INFO info,
-                                      void *user_data);
-
-typedef void (*WimaxMediaStatusFunc) (struct wmxsdk *wmxsdk,
-                                      WIMAX_API_MEDIA_STATUS media_status,
-                                      void *user_data);
-
-typedef void (*WimaxConnectResultFunc) (struct wmxsdk *wmxsdk,
-                                        WIMAX_API_NETWORK_CONNECTION_RESP resp,
-                                        void *user_data);
-
-typedef void (*WimaxScanResultFunc) (struct wmxsdk *wmxsdk,
-                                     WIMAX_API_NSP_INFO_EX *nsps,
-                                     guint num_nsps,
-                                     void *user_data);
-
-typedef void (*WimaxRemovedFunc) (struct wmxsdk *wmxsdk, void *user_data);
-
-struct wmxsdk {
-	gint refcount;
-
-	WIMAX_API_DEVICE_ID device_id;
-
-	WimaxStateChangeFunc state_change_cb;
-	WimaxMediaStatusFunc media_status_cb;
-	WimaxConnectResultFunc connect_result_cb;
-	WimaxScanResultFunc scan_result_cb;
-	WimaxRemovedFunc removed_cb;
-	void *callback_data;
-
-	GMutex network_mutex;
-
-	WIMAX_API_DEVICE_STATUS status;
-	WIMAX_API_MEDIA_STATUS media_status;
-	GMutex status_mutex;
-
-	GMutex connect_mutex;
-
-	char name[100];
-	char ifname[16];
-};
-
-struct wmxsdk *iwmx_sdk_get_wmxsdk_for_iface(const char *iface);
-
-struct wmxsdk *wmxsdk_ref(struct wmxsdk *wmxsdk);
-void wmxsdk_unref(struct wmxsdk *wmxsdk);
-
-/* Register/unregister callbacks when a new wmxsdk is set up */
-void iwmx_sdk_new_callback_register(WimaxNewWmxsdkFunc callback, void *user_data);
-void iwmx_sdk_new_callback_unregister(WimaxNewWmxsdkFunc callback, void *user_data);
-
-void iwmx_sdk_set_callbacks(struct wmxsdk *wmxsdk,
-                            WimaxStateChangeFunc state_change_cb,
-                            WimaxMediaStatusFunc media_status_func,
-                            WimaxConnectResultFunc connect_result_cb,
-                            WimaxScanResultFunc scan_result_cb,
-                            WimaxRemovedFunc removed_cb,
-                            void *user_data);
-
-WIMAX_API_DEVICE_STATUS iwmxsdk_status_get(struct wmxsdk *wmxsdk);
-int iwmx_sdk_connect(struct wmxsdk *wmxsdk, const char *nsp_name);
-int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk);
-int iwmx_sdk_set_fast_reconnect_enabled(struct wmxsdk *wmxsdk, int enabled);
-WIMAX_API_CONNECTED_NSP_INFO_EX *iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk);
-WIMAX_API_LINK_STATUS_INFO_EX *iwmx_sdk_get_link_status_info(struct wmxsdk *wmxsdk);
-const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status);
-const char *iwmx_sdk_reason_to_str(WIMAX_API_STATUS_REASON reason);
-const char *iwmx_sdk_media_status_to_str(WIMAX_API_MEDIA_STATUS status);
-const char *iwmx_sdk_con_progress_to_str(WIMAX_API_CONNECTION_PROGRESS_INFO progress);
-int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state);
-int iwmx_sdk_get_networks(struct wmxsdk *wmxsdk);
-int iwmx_sdk_api_init(void);
-void iwmx_sdk_api_exit(void);
-
-#endif  /* __IWMXSDK_H__ */
diff --git a/src/devices/wimax/nm-device-wimax.c b/src/devices/wimax/nm-device-wimax.c
deleted file mode 100644
index 0c8f1cbb..00000000
--- a/src/devices/wimax/nm-device-wimax.c
+++ /dev/null
@@ -1,1416 +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) 2010 - 2011 Red Hat, Inc.
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <glib/gi18n.h>
-
-#include "nm-wimax-util.h"
-
-#include <WiMaxAPI.h>
-#include <WiMaxAPIEx.h>
-
-#include "nm-device-wimax.h"
-#include "nm-wimax-util.h"
-#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"
-#include "nm-rfkill-manager.h"
-#include "iwmxsdk.h"
-#include "nm-enum-types.h"
-#include "nm-dbus-glib-types.h"
-
-static gboolean impl_device_get_nsp_list (NMDeviceWimax *device, GPtrArray **list, GError **error);
-
-#include "nm-device-wimax-glue.h"
-
-G_DEFINE_TYPE (NMDeviceWimax, nm_device_wimax, NM_TYPE_DEVICE)
-
-enum {
-	PROP_0,
-	PROP_NSPS,
-	PROP_ACTIVE_NSP,
-	PROP_CENTER_FREQ,
-	PROP_RSSI,
-	PROP_CINR,
-	PROP_TX_POWER,
-	PROP_BSID,
-
-	LAST_PROP
-};
-
-enum {
-	NSP_ADDED,
-	NSP_REMOVED,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-#define NM_DEVICE_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
-                                        NM_TYPE_DEVICE_WIMAX, \
-                                        NMDeviceWimaxPrivate))
-
-typedef struct {
-	gboolean disposed;
-
-	struct wmxsdk *sdk;
-	WIMAX_API_DEVICE_STATUS status;
-	gboolean connect_failed;
-
-	gboolean enabled;
-	gboolean wimaxd_enabled;
-	guint activation_timeout_id;
-
-	/* Track whether stage1 (Prepare) is completed yet or not */
-	gboolean prepare_done;
-
-	guint sdk_action_defer_id;
-
-	guint link_timeout_id;
-	guint poll_id;
-
-	GSList *nsp_list;
-	NMWimaxNsp *current_nsp;
-
-	/* interesting stuff when connected */
-	guint center_freq;
-	gint rssi;
-	gint cinr;
-	gint tx_power;
-	char *bsid;
-} NMDeviceWimaxPrivate;
-
-/***********************************************************/
-
-static gboolean
-impl_device_get_nsp_list (NMDeviceWimax *self, GPtrArray **nsps, GError **error)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	GSList *iter;
-
-	*nsps = g_ptr_array_sized_new (4);
-	for (iter = priv->nsp_list; iter; iter = iter->next)
-		g_ptr_array_add (*nsps, g_strdup (nm_wimax_nsp_get_dbus_path (NM_WIMAX_NSP (iter->data))));
-
-	return TRUE;
-}
-
-static void
-set_current_nsp (NMDeviceWimax *self, NMWimaxNsp *new_nsp)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	NMWimaxNsp *old_nsp;
-	gboolean path_changed = FALSE;
-
-	old_nsp = priv->current_nsp;
-	priv->current_nsp = NULL;
-
-	if (new_nsp)
-		priv->current_nsp = g_object_ref (new_nsp);
-
-	if (old_nsp && new_nsp) {
-		path_changed = (g_strcmp0 (nm_wimax_nsp_get_dbus_path (old_nsp),
-		                           nm_wimax_nsp_get_dbus_path (new_nsp)) != 0);
-	}
-
-	/* Only notify if it's really changed */
-	if (old_nsp != new_nsp || path_changed)
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_ACTIVE_NSP);
-
-	if (old_nsp)
-		g_object_unref (old_nsp);
-}
-
-static gboolean
-activation_timed_out (gpointer data)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (data);
-
-	priv->activation_timeout_id = 0;
-	nm_device_state_changed (NM_DEVICE (data), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-
-	return FALSE;
-}
-
-static void
-emit_nsp_added_removed (NMDeviceWimax *self,
-                        guint signum,
-                        NMWimaxNsp *nsp,
-                        gboolean recheck_available_connections)
-{
-	g_signal_emit (self, signals[signum], 0, nsp);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_NSPS);
-	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
-	if (recheck_available_connections)
-		nm_device_recheck_available_connections (NM_DEVICE (self));
-}
-
-static void
-remove_all_nsps (NMDeviceWimax *self)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	set_current_nsp (self, NULL);
-
-	while (priv->nsp_list) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (priv->nsp_list->data);
-
-		priv->nsp_list = g_slist_remove (priv->nsp_list, nsp);
-		emit_nsp_added_removed (self, NSP_REMOVED, nsp, FALSE);
-		g_object_unref (nsp);
-	}
-
-	nm_device_recheck_available_connections (NM_DEVICE (self));
-}
-
-static NMWimaxNsp *
-get_nsp_by_name (NMDeviceWimax *self, const char *name)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	GSList *iter;
-
-	g_return_val_if_fail (name, NULL);
-
-	for (iter = priv->nsp_list; iter; iter = iter->next) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data);
-
-		if (!g_strcmp0 (nm_wimax_nsp_get_name (nsp), name))
-			return nsp;
-	}
-
-	return NULL;
-}
-
-static NMWimaxNsp *
-get_nsp_by_path (NMDeviceWimax *self, const char *path)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	GSList *iter;
-
-	g_return_val_if_fail (path, NULL);
-
-	for (iter = priv->nsp_list; iter; iter = iter->next) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data);
-
-		if (!strcmp (nm_wimax_nsp_get_dbus_path (nsp), path))
-			return nsp;
-	}
-
-	return NULL;
-}
-
-static gboolean
-update_availability (NMDeviceWimax *self, gboolean old_available)
-{
-	NMDevice *device = NM_DEVICE (self);
-	NMDeviceState state;
-	gboolean new_available, changed = FALSE;
-
-	new_available = nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-	if (new_available == old_available)
-		return FALSE;
-
-	state = nm_device_get_state (device);
-	if (state == NM_DEVICE_STATE_UNAVAILABLE) {
-		if (new_available == TRUE) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_DISCONNECTED,
-			                         NM_DEVICE_STATE_REASON_NONE);
-			changed = TRUE;
-		}
-	} else if (state >= NM_DEVICE_STATE_DISCONNECTED) {
-		if (new_available == FALSE) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_UNAVAILABLE,
-			                         NM_DEVICE_STATE_REASON_NONE);
-			changed = TRUE;
-		}
-	}
-
-	return changed;
-}
-
-/* NMDeviceInterface interface */
-
-static void
-set_enabled (NMDevice *device, gboolean enabled)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	gboolean old_available;
-	int ret;
-	const char *iface;
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): setting radio enabled %d -> %d",
-	            iface, priv->enabled, enabled);
-	if (priv->enabled == enabled)
-		return;
-
-	old_available = nm_device_is_available (NM_DEVICE (device), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-	priv->enabled = enabled;
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): radio now %s",
-	            iface, priv->enabled ? "enabled" : "disabled");
-
-	/* Set the WiMAX device RF state to the current user-specified enabled state */
-	if (priv->sdk) {
-		ret = iwmx_sdk_rf_state_set (priv->sdk,
-		                             enabled ? WIMAX_API_RF_ON : WIMAX_API_RF_OFF);
-		if (ret < 0 && ret != -EINPROGRESS) {
-			nm_log_warn (LOGD_WIMAX, "(%s): failed to %s radio",
-			             iface, priv->enabled ? "enable" : "disable");
-		}
-	}
-
-	update_availability (self, old_available);
-}
-
-/* NMDevice methods */
-
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingConnection *s_con;
-	NMSettingWimax *s_wimax;
-	const char *connection_type;
-	const char *mac;
-
-	if (!NM_DEVICE_CLASS (nm_device_wimax_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	connection_type = nm_setting_connection_get_connection_type (s_con);
-	if (strcmp (connection_type, NM_SETTING_WIMAX_SETTING_NAME))
-		return FALSE;
-
-	s_wimax = nm_connection_get_setting_wimax (connection);
-	if (!s_wimax)
-		return FALSE;
-
-	mac = nm_setting_wimax_get_mac_address (s_wimax);
-	if (mac && !nm_utils_hwaddr_matches (mac, -1, nm_device_get_hw_address (device), -1))
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
-check_connection_available (NMDevice *device,
-                            NMConnection *connection,
-                            NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-	const GSList *ns_iter = NULL;
-	NMWimaxNsp *nsp;
-
-	/* a connection that is available for a certain @specific_object, MUST
-	 * also be available in general (without @specific_object). */
-
-	if (specific_object) {
-		nsp = get_nsp_by_path (NM_DEVICE_WIMAX (device), specific_object);
-		return nsp ? nm_wimax_nsp_check_compatible (nsp, connection) : FALSE;
-	}
-
-	/* Ensure the connection applies to an NSP in the scan list */
-	for (ns_iter = priv->nsp_list; ns_iter; ns_iter = ns_iter->next) {
-		if (nm_wimax_nsp_check_compatible (NM_WIMAX_NSP (ns_iter->data), connection))
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean
-complete_connection (NMDevice *device,
-                     NMConnection *connection,
-                     const char *specific_object,
-                     const GSList *existing_connections,
-                     GError **error)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	NMSettingWimax *s_wimax;
-	const char *setting_mac;
-	const char *hw_address;
-	const char *nsp_name = NULL;
-	NMWimaxNsp *nsp = NULL;
-	GSList *iter;
-
-	s_wimax = nm_connection_get_setting_wimax (connection);
-
-	if (!specific_object) {
-		/* If not given a specific object, we need at minimum an NSP name */
-		if (!s_wimax) {
-			g_set_error_literal (error,
-			                     NM_DEVICE_ERROR,
-			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-			                     "A 'wimax' setting is required if no NSP path was given.");
-			return FALSE;
-		}
-
-		nsp_name = nm_setting_wimax_get_network_name (s_wimax);
-		if (!nsp_name || !strlen (nsp_name)) {
-			g_set_error_literal (error,
-			                     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;
-		}
-
-		/* Find a compatible NSP in the list */
-		nsp = get_nsp_by_name (self, nsp_name);
-
-		/* If we still don't have an NSP, then the WiMAX settings needs to be
-		 * fully specified by the client.  Might not be able to find the NSP
-		 * if the scan didn't find the NSP yet.
-		 */
-		if (!nsp) {
-			if (!nm_setting_verify (NM_SETTING (s_wimax), NULL, error))
-				return FALSE;
-		}
-	} else {
-		/* Find a compatible NSP in the list */
-		for (iter = priv->nsp_list; iter; iter = g_slist_next (iter)) {
-			if (!strcmp (specific_object, nm_wimax_nsp_get_dbus_path (NM_WIMAX_NSP (iter->data)))) {
-				nsp = NM_WIMAX_NSP (iter->data);
-				break;
-			}
-		}
-
-		if (!nsp) {
-			g_set_error (error,
-			             NM_DEVICE_ERROR,
-			             NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
-			             "The NSP %s was not in the scan list.",
-			             specific_object);
-			return FALSE;
-		}
-
-		nsp_name = nm_wimax_nsp_get_name (nsp);
-	}
-
-	/* Add a WiMAX setting if one doesn't exist */
-	if (!s_wimax) {
-		s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
-		nm_connection_add_setting (connection, NM_SETTING (s_wimax));
-	}
-
-	g_assert (nsp_name);
-	nm_utils_complete_generic (connection,
-	                           NM_SETTING_WIMAX_SETTING_NAME,
-	                           existing_connections,
-	                           nsp_name,
-	                           nsp_name,
-	                           NULL,
-	                           TRUE);
-	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);
-	if (setting_mac) {
-		/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-		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 {
-		/* Lock the connection to this device by default */
-		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;
-}
-
-static gboolean
-can_auto_connect (NMDevice *device,
-                  NMConnection *connection,
-                  char **specific_object)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-	GSList *iter;
-
-	if (!NM_DEVICE_CLASS (nm_device_wimax_parent_class)->can_auto_connect (device, connection, specific_object))
-		return FALSE;
-
-	for (iter = priv->nsp_list; iter; iter = iter->next) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data);
-
-		if (nm_wimax_nsp_check_compatible (nsp, connection)) {
-			*specific_object = (char *) nm_wimax_nsp_get_dbus_path (nsp);
-			return TRUE;
-		}
-	}
-
-	return FALSE;
-}
-
-static gboolean
-is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-
-	if (!priv->enabled)
-		return FALSE;
-
-	if (!priv->wimaxd_enabled)
-		return FALSE;
-
-	if (!nm_wimax_util_sdk_is_initialized ())
-		return FALSE;
-
-	if (!priv->sdk)
-		return FALSE;
-
-	return iwmxsdk_status_get (priv->sdk) >= WIMAX_API_DEVICE_STATUS_Ready;
-}
-
-static void
-clear_activation_timeout (NMDeviceWimax *self)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (priv->activation_timeout_id) {
-		g_source_remove (priv->activation_timeout_id);
-		priv->activation_timeout_id = 0;
-	}
-
-	priv->connect_failed = FALSE;
-}
-
-static void
-clear_link_timeout (NMDeviceWimax *self)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (priv->link_timeout_id) {
-		g_source_remove (priv->link_timeout_id);
-		priv->link_timeout_id = 0;
-	}
-}
-
-static void
-clear_connected_poll (NMDeviceWimax *self)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (priv->poll_id) {
-		g_source_remove (priv->poll_id);
-		priv->poll_id = 0;
-	}
-}
-
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-	NMActRequest *req;
-	GSList *iter;
-	const char *path;
-	NMWimaxNsp *nsp = NULL;
-
-	clear_link_timeout (NM_DEVICE_WIMAX (device));
-
-	*reason = NM_DEVICE_STATE_REASON_NONE;
-
-	req = nm_device_get_act_request (device);
-	if (!req)
-		return NM_ACT_STAGE_RETURN_FAILURE;
-
-	path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
-	if (!path)
-		return NM_ACT_STAGE_RETURN_FAILURE;
-
-	/* Find the NSP in the scan list */
-	for (iter = priv->nsp_list; iter; iter = iter->next) {
-		NMWimaxNsp *candidate = NM_WIMAX_NSP (iter->data);
-
-		if (!strcmp (path, nm_wimax_nsp_get_dbus_path (candidate))) {
-			nsp = candidate;
-			break;
-		}
-	}
-
-	/* Couldn't find the NSP for some reason */
-	if (nsp == NULL)
-		return NM_ACT_STAGE_RETURN_FAILURE;
-
-	set_current_nsp (NM_DEVICE_WIMAX (device), nsp);
-
-	priv->prepare_done = TRUE;
-
-	/* If the device is scanning, it won't connect, so we have to wait until
-	 * it's not scanning to proceed to stage 2.
-	 */
-	if (priv->status == WIMAX_API_DEVICE_STATUS_Scanning)
-		return NM_ACT_STAGE_RETURN_POSTPONE;
-
-	return NM_ACT_STAGE_RETURN_SUCCESS;
-}
-
-static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device);
-	NMConnection *connection;
-	NMSettingWimax *s_wimax;
-	const char *nsp_name, *iface;
-	int ret;
-
-	iface = nm_device_get_iface (device);
-	g_assert (iface);
-
-	connection = nm_device_get_connection (device);
-	g_assert (connection);
-
-	s_wimax = nm_connection_get_setting_wimax (connection);
-	g_assert (s_wimax);
-
-	nsp_name = nm_setting_wimax_get_network_name (s_wimax);
-	g_assert (nsp_name);
-
-	nm_log_info (LOGD_WIMAX, "(%s): connecting to NSP '%s'",
-	             iface, nsp_name);
-
-	priv->connect_failed = FALSE;
-	ret = iwmx_sdk_connect (priv->sdk, nsp_name);
-	if (ret < 0 && ret != -EINPROGRESS) {
-		nm_log_err (LOGD_WIMAX, "(%s): failed to connect to NSP '%s'",
-		            iface, nsp_name);
-		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
-	/* FIXME: Is 40 seconds good estimation? I have no idea */
-	priv->activation_timeout_id = g_timeout_add_seconds (40, activation_timed_out, device);
-
-	return NM_ACT_STAGE_RETURN_POSTPONE;
-}
-
-static void
-force_disconnect (NMDeviceWimax *self, struct wmxsdk *sdk)
-{
-	WIMAX_API_DEVICE_STATUS status;
-	int ret;
-	const char *iface;
-
-	g_return_if_fail (sdk != NULL);
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
-
-	status = iwmxsdk_status_get (sdk);
-	if ((int) status < 0) {
-		nm_log_err (LOGD_WIMAX, "(%s): failed to read WiMAX device status: %d",
-		            iface, status);
-		return;
-	}
-
-	if (   status == WIMAX_API_DEVICE_STATUS_Connecting
-	    || status == WIMAX_API_DEVICE_STATUS_Data_Connected) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): requesting disconnect", iface);
-		ret = iwmx_sdk_disconnect (sdk);
-		if (ret < 0 && ret != -EINPROGRESS) {
-			nm_log_err (LOGD_WIMAX, "(%s): failed to disconnect WiMAX device: %d",
-			            iface, ret);
-		}
-	}
-}
-
-static void
-deactivate (NMDevice *device)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	clear_activation_timeout (self);
-	clear_link_timeout (self);
-	clear_connected_poll (self);
-
-	set_current_nsp (self, NULL);
-
-	if (priv->sdk) {
-		/* Read explicit status here just to make sure we have the most
-		 * up-to-date status and to ensure we disconnect if needed.
-		 */
-		force_disconnect (self, priv->sdk);
-	}
-}
-
-/*************************************************************************/
-
-static void
-wmx_state_change_cb (struct wmxsdk *wmxsdk,
-                     WIMAX_API_DEVICE_STATUS new_status,
-                     WIMAX_API_DEVICE_STATUS old_status,
-                     WIMAX_API_STATUS_REASON reason,
-                     WIMAX_API_CONNECTION_PROGRESS_INFO progress,
-                     void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	NMDeviceState state;
-	const char *iface;
-	gboolean old_available = FALSE;
-	const char *nsp_name = NULL;
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
-	nm_log_info (LOGD_WIMAX, "(%s): wimax state change %s -> %s (%s (%d))",
-	             iface,
-	             iwmx_sdk_dev_status_to_str (old_status),
-	             iwmx_sdk_dev_status_to_str (new_status),
-	             iwmx_sdk_con_progress_to_str (progress),
-	             progress);
-
-	if (new_status == old_status)
-		return;
-
-	state = nm_device_get_state (NM_DEVICE (self));
-	old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-
-	priv->status = new_status;
-	if (priv->current_nsp)
-		nsp_name = nm_wimax_nsp_get_name (priv->current_nsp);
-
-	switch (new_status) {
-	case WIMAX_API_DEVICE_STATUS_UnInitialized:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
-	case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
-		if (priv->wimaxd_enabled) {
-			priv->wimaxd_enabled = FALSE;
-			if (update_availability (self, old_available))
-				return;
-		}
-		break;
-	case WIMAX_API_DEVICE_STATUS_Connecting:
-	case WIMAX_API_DEVICE_STATUS_Data_Connected:
-		/* If for some reason we're initially connected, force a disconnect here */
-		if (state < NM_DEVICE_STATE_DISCONNECTED)
-			force_disconnect (self, wmxsdk);
-		/* Fall through */
-	case WIMAX_API_DEVICE_STATUS_Ready:
-	case WIMAX_API_DEVICE_STATUS_Scanning:
-		if (priv->wimaxd_enabled == FALSE) {
-			priv->wimaxd_enabled = TRUE;
-			if (update_availability (self, old_available))
-				return;
-		}
-		break;
-	default:
-		nm_log_warn (LOGD_WIMAX, "(%s): unhandled WiMAX device state %d",
-		             iface, new_status);
-		break;
-	}
-
-	/* Handle activation success and failure */
-	if (nm_device_is_activating (NM_DEVICE (self))) {
-	    if (new_status == WIMAX_API_DEVICE_STATUS_Data_Connected) {
-			/* Success */
-			clear_activation_timeout (self);
-
-			nm_log_info (LOGD_WIMAX, "(%s): connected to '%s'",
-			             iface, nsp_name);
-			nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self));
-			return;
-		}
-
-		if (priv->connect_failed) {
-			/* Connection attempt failed */
-			nm_log_info (LOGD_WIMAX, "(%s): connection to '%s' failed: (%d) %s",
-			             iface, nsp_name, reason, iwmx_sdk_reason_to_str (reason));
-			nm_device_state_changed (NM_DEVICE (self),
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-			return;
-		}
-
-		/* If stage2 was postponed because the device was scanning or something,
-		 * then check if we need to move to stage2 now that the device might be
-		 * ready.
-		 */
-		if (state == NM_DEVICE_STATE_PREPARE && priv->prepare_done) {
-			if (   new_status == WIMAX_API_DEVICE_STATUS_Ready
-			    || new_status == WIMAX_API_DEVICE_STATUS_Connecting) {
-				nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
-				return;
-			}
-		}
-	}
-
-	/* Handle disconnection */
-	if (state == NM_DEVICE_STATE_ACTIVATED) {
-		if (   old_status == WIMAX_API_DEVICE_STATUS_Data_Connected
-			&& new_status < WIMAX_API_DEVICE_STATUS_Connecting) {
-
-			nm_log_info (LOGD_WIMAX, "(%s): disconnected from '%s': (%d) %s",
-				         iface, nsp_name, reason, iwmx_sdk_reason_to_str (reason));
-
-			nm_device_state_changed (NM_DEVICE (self),
-				                     NM_DEVICE_STATE_FAILED,
-				                     NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-		}
-	}
-}
-
-static gboolean
-link_timeout_cb (gpointer user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	priv->link_timeout_id = 0;
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): link timed out", nm_device_get_iface (NM_DEVICE (self)));
-	nm_device_state_changed (NM_DEVICE (self),
-	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_CARRIER);
-
-	return FALSE;
-}
-
-static void
-wmx_media_status_cb (struct wmxsdk *wmxsdk,
-                     WIMAX_API_MEDIA_STATUS new_status,
-                     void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	NMDeviceState state;
-	const char *iface;
-
-	iface = nm_device_get_iface (NM_DEVICE (self));
-	state = nm_device_get_state (NM_DEVICE (self));
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): media status change to %s",
-	            iface, iwmx_sdk_media_status_to_str (new_status));
-
-	/* We only care about media events while activated */
-	if (state != NM_DEVICE_STATE_ACTIVATED)
-		return;
-
-	clear_link_timeout (self);
-
-	switch (new_status) {
-	case WIMAX_API_MEDIA_STATUS_LINK_UP:
-		break;
-	case WIMAX_API_MEDIA_STATUS_LINK_DOWN:
-		nm_log_dbg (LOGD_WIMAX, "(%s): starting link timeout", iface);
-		priv->link_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, self);
-		break;
-	case WIMAX_API_MEDIA_STATUS_LINK_RENEW:
-		nm_log_dbg (LOGD_WIMAX, "(%s): renewing DHCP lease", iface);
-		if (!nm_device_dhcp4_renew (NM_DEVICE (self), TRUE)) {
-			nm_device_state_changed (NM_DEVICE (self),
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_DHCP_FAILED);
-		}
-		break;
-	default:
-		nm_log_err (LOGD_WIMAX, "(%s): unhandled media status %d", iface, new_status);
-		break;
-	}
-}
-
-static void
-wmx_connect_result_cb (struct wmxsdk *wmxsdk,
-                       WIMAX_API_NETWORK_CONNECTION_RESP result,
-                       void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (nm_device_is_activating (NM_DEVICE (self))) {
-		priv->connect_failed = (result == WIMAX_API_CONNECTION_SUCCESS);
-		/* Wait for the state change so we can get the reason code; we
-		 * cache the connect failure so we don't have to wait for the
-		 * activation timeout.
-		 */
-	}
-}
-
-static void
-remove_outdated_nsps (NMDeviceWimax *self,
-					  WIMAX_API_NSP_INFO_EX *nsp_list,
-					  guint32 list_size)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	GSList *iter;
-	GSList *to_remove = NULL;
-
-	for (iter = priv->nsp_list; iter; iter = iter->next) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data);
-		gboolean found = FALSE;
-		int i;
-
-		for (i = 0; i < list_size; i++) {
-			WIMAX_API_NSP_INFO_EX *info = &nsp_list[i];
-
-			if (!g_strcmp0 (nm_wimax_nsp_get_name (nsp), (char *) info->NSPName)) {
-				found = TRUE;
-				break;
-			}
-		}
-
-		if (!found)
-			to_remove = g_slist_prepend (to_remove, nsp);
-	}
-
-	for (iter = to_remove; iter; iter = iter->next) {
-		NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data);
-
-		emit_nsp_added_removed (self, NSP_REMOVED, nsp, FALSE);
-		priv->nsp_list = g_slist_remove (priv->nsp_list, nsp);
-		g_object_unref (nsp);
-	}
-
-	if (g_slist_length(to_remove) > 0)
-	    nm_device_recheck_available_connections (NM_DEVICE (self));
-
-	g_slist_free (to_remove);
-}
-
-static void
-wmx_scan_result_cb (struct wmxsdk *wmxsdk,
-                    WIMAX_API_NSP_INFO_EX *nsps,
-                    guint num_nsps,
-                    void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	const char *iface = nm_device_get_iface (NM_DEVICE (self));
-	int i;
-
-	remove_outdated_nsps (self, nsps, num_nsps);
-
-	/* Add new NSPs and update existing ones */
-	for (i = 0; i < num_nsps; i++) {
-		WIMAX_API_NSP_INFO_EX *sdk_nsp = &nsps[i];
-		const char *nsp_name = (const char *) sdk_nsp->NSPName;
-		NMWimaxNspNetworkType net_type;
-		guint signalq;
-		NMWimaxNsp *nsp;
-		gboolean new_nsp;
-
-		nsp = get_nsp_by_name (self, nsp_name);
-		new_nsp = (nsp == NULL);
-		if (new_nsp) {
-			nsp = nm_wimax_nsp_new (nsp_name);
-			nm_log_dbg (LOGD_WIMAX, "(%s): new WiMAX NSP '%s'", iface, nsp_name);
-		}
-
-		net_type = nm_wimax_util_convert_network_type (sdk_nsp->networkType);
-		if (net_type != nm_wimax_nsp_get_network_type (nsp))
-			g_object_set (nsp, NM_WIMAX_NSP_NETWORK_TYPE, net_type, NULL);
-
-		signalq = CLAMP (sdk_nsp->linkQuality, 0, 100);
-		if (signalq != nm_wimax_nsp_get_signal_quality (nsp))
-			g_object_set (nsp, NM_WIMAX_NSP_SIGNAL_QUALITY, signalq, NULL);
-
-		nm_log_dbg (LOGD_WIMAX, "(%s): WiMAX NSP '%s' quality %d%% type %d",
-			        iface, nsp_name, sdk_nsp->linkQuality, net_type);
-
-		if (new_nsp) {
-			priv->nsp_list = g_slist_append (priv->nsp_list, nsp);
-			nm_wimax_nsp_export_to_dbus (nsp);
-			emit_nsp_added_removed (self, NSP_ADDED, nsp, TRUE);
-		}
-	}
-}
-
-static void
-wmx_removed_cb (struct wmxsdk *wmxsdk, void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (!priv->sdk) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): removed unhandled WiMAX interface", wmxsdk->ifname);
-		return;
-	}
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): removed WiMAX interface", wmxsdk->ifname);
-
-	/* Clear callbacks just in case we don't hold the last reference */
-	iwmx_sdk_set_callbacks (priv->sdk, NULL, NULL, NULL, NULL, NULL, NULL);
-	wmxsdk_unref (priv->sdk);
-	priv->sdk = NULL;
-
-	priv->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
-	nm_device_state_changed (NM_DEVICE (self),
-							 NM_DEVICE_STATE_UNAVAILABLE,
-							 NM_DEVICE_STATE_REASON_NONE);
-}
-
-/*************************************************************************/
-
-static inline gint
-sdk_rssi_to_dbm (guint raw_rssi)
-{
-	/* Values range from 0x00 to 0x53, where -123dBm is encoded as 0x00 and
-	 * -40dBm encoded as 0x53 in 1dB increments.
-	 */
-	return raw_rssi - 123;
-}
-
-static inline gint
-sdk_cinr_to_db (guint raw_cinr)
-{
-	/* Values range from 0x00 to 0x3F, where -10dB is encoded as 0x00 and
-	 * 53dB encoded as 0x3F in 1dB increments.
-	 */
-	return raw_cinr - 10;
-}
-
-static inline gint
-sdk_tx_pow_to_dbm (guint raw_tx_pow)
-{
-	/* Values range from 0x00 to 0xFF, where -84dBm is encoded as 0x00 and
-	 * 43.5dBm is encoded as 0xFF in 0.5dB increments.  Normalize so that
-	 * 0 dBm == 0.
-	 */
-	return (int) (((double) raw_tx_pow / 2.0) - 84) * 2;
-}
-
-static void
-set_link_status (NMDeviceWimax *self, WIMAX_API_LINK_STATUS_INFO_EX *link_status)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	guint center_freq = 0;
-	gint conv_rssi = 0, conv_cinr = 0, conv_tx_pow = 0;
-	char *new_bsid = NULL;
-
-	if (link_status) {
-		center_freq = link_status->centerFrequency;
-		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 (link_status->bsId, 6);
-	}
-
-	if (priv->center_freq != center_freq) {
-		priv->center_freq = center_freq;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_CENTER_FREQUENCY);
-	}
-
-	if (priv->rssi != conv_rssi) {
-		priv->rssi = conv_rssi;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_RSSI);
-	}
-
-	if (priv->cinr != conv_cinr) {
-		priv->cinr = conv_cinr;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_CINR);
-	}
-
-	if (priv->tx_power != conv_tx_pow) {
-		priv->tx_power = conv_tx_pow;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_TX_POWER);
-	}
-
-	if (g_strcmp0 (priv->bsid, new_bsid) != 0) {
-		g_free (priv->bsid);
-		priv->bsid = new_bsid;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIMAX_BSID);
-	} else
-		g_free (new_bsid);
-}
-
-static gboolean
-connected_poll_cb (gpointer user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	WIMAX_API_CONNECTED_NSP_INFO_EX *sdk_nsp;
-	WIMAX_API_LINK_STATUS_INFO_EX *link_status;
-
-	g_return_val_if_fail (priv->sdk != NULL, FALSE);
-
-	/* Get details of the connected NSP */
-	sdk_nsp = iwmx_sdk_get_connected_network (priv->sdk);
-	if (sdk_nsp) {
-		const char *nsp_name = (const char *) sdk_nsp->NSPName;
-		NMWimaxNsp *nsp;
-
-		nsp = get_nsp_by_name (self, nsp_name);
-		if (nsp) {
-			NMWimaxNspNetworkType net_type;
-			guint signalq;
-
-			net_type = nm_wimax_util_convert_network_type (sdk_nsp->networkType);
-			if (net_type != nm_wimax_nsp_get_network_type (nsp))
-				g_object_set (nsp, NM_WIMAX_NSP_NETWORK_TYPE, net_type, NULL);
-
-			signalq = sdk_nsp->linkQuality;
-			if (signalq != nm_wimax_nsp_get_signal_quality (nsp))
-				g_object_set (nsp, NM_WIMAX_NSP_SIGNAL_QUALITY, signalq, NULL);
-
-			nm_log_dbg (LOGD_WIMAX, "(%s): WiMAX NSP '%s' quality %d%% type %d",
-					    nm_device_get_iface (NM_DEVICE (self)),
-					    nsp_name, sdk_nsp->linkQuality, net_type);
-		}
-		free (sdk_nsp);
-	}
-
-	/* Get details of the current radio link */
-	link_status = iwmx_sdk_get_link_status_info (priv->sdk);
-	if (link_status) {
-		set_link_status (self, link_status);
-		free (link_status);
-	}
-
-	return TRUE; /* reschedule */
-}
-
-static void
-device_state_changed (NMDevice *device,
-                      NMDeviceState new_state,
-                      NMDeviceState old_state,
-                      NMDeviceStateReason reason)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	/* Reset our stage1 (Prepare) done marker since it's only valid while in stage1 */
-	priv->prepare_done = FALSE;
-
-	if (new_state < NM_DEVICE_STATE_DISCONNECTED)
-		remove_all_nsps (self);
-
-	/* Request initial NSP list when device is first started */
-	if (   new_state == NM_DEVICE_STATE_DISCONNECTED
-	    && old_state < NM_DEVICE_STATE_DISCONNECTED) {
-		if (priv->sdk)
-			iwmx_sdk_get_networks (priv->sdk);
-	}
-
-	if (new_state == NM_DEVICE_STATE_FAILED || new_state <= NM_DEVICE_STATE_DISCONNECTED) {
-		set_current_nsp (self, NULL);
-		clear_activation_timeout (self);
-	}
-
-	if (new_state == NM_DEVICE_STATE_ACTIVATED) {
-		/* poll link quality and BSID */
-		clear_connected_poll (self);
-		priv->poll_id = g_timeout_add_seconds (10, connected_poll_cb, self);
-		connected_poll_cb (self);
-	} else {
-		clear_link_timeout (self);
-		clear_connected_poll (self);
-		set_link_status (self, NULL);
-	}
-}
-
-/*************************************************************************/
-
-static gboolean
-sdk_action_defer_cb (gpointer user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	gboolean old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
-
-	NM_DEVICE_WIMAX_GET_PRIVATE (self)->sdk_action_defer_id = 0;
-	update_availability (self, old_available);
-	return FALSE;
-}
-
-static void
-wmx_new_sdk_cb (struct wmxsdk *sdk, void *user_data)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (user_data);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	/* We only track one wmxsdk at a time because the WiMAX SDK is pretty stupid */
-	if (priv->sdk) {
-		nm_log_dbg (LOGD_WIMAX, "(%s): WiMAX interface already known", sdk->ifname);
-		return;
-	}
-
-	nm_log_dbg (LOGD_WIMAX, "(%s): new WiMAX interface (%s)", sdk->ifname, sdk->name);
-
-	/* Now that we have an SDK, schedule an idle handler to start the device up */
-	priv->sdk = wmxsdk_ref (sdk);
-	iwmx_sdk_set_callbacks(priv->sdk,
-	                       wmx_state_change_cb,
-	                       wmx_media_status_cb,
-	                       wmx_connect_result_cb,
-	                       wmx_scan_result_cb,
-	                       wmx_removed_cb,
-	                       self);
-	iwmx_sdk_set_fast_reconnect_enabled (priv->sdk, 0);
-
-	if (!priv->sdk_action_defer_id)
-		priv->sdk_action_defer_id = g_idle_add (sdk_action_defer_cb, self);
-}
-
-/*************************************************************************/
-
-NMDevice *
-nm_device_wimax_new (NMPlatformLink *platform_device)
-{
-	NMDevice *device;
-
-	g_return_val_if_fail (platform_device != NULL, NULL);
-
-	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIMAX,
-	                                    NM_DEVICE_PLATFORM_DEVICE, platform_device,
-	                                    NM_DEVICE_TYPE_DESC, "WiMAX",
-	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIMAX,
-	                                    NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WIMAX,
-	                                    NULL);
-	if (device) {
-		struct wmxsdk *sdk;
-
-		nm_wimax_util_sdk_ref ();
-
-		/* See if the SDK already knows about this interface */
-		sdk = iwmx_sdk_get_wmxsdk_for_iface (platform_device->name);
-		if (sdk)
-			wmx_new_sdk_cb (sdk, device);
-
-		/* If it doesn't, we want to be notified when it does */
-		iwmx_sdk_new_callback_register (wmx_new_sdk_cb, device);
-	}
-
-	return device;
-}
-
-static void
-nm_device_wimax_init (NMDeviceWimax *self)
-{
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	priv->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-			  const GValue *value, GParamSpec *pspec)
-{
-	switch (prop_id) {
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (object);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-	GPtrArray *array;
-	GSList *iter;
-
-	switch (prop_id) {
-	case PROP_NSPS:
-		array = g_ptr_array_sized_new (4);
-		for (iter = priv->nsp_list; iter; iter = g_slist_next (iter))
-			g_ptr_array_add (array, g_strdup (nm_wimax_nsp_get_dbus_path (NM_WIMAX_NSP (iter->data))));
-		g_value_take_boxed (value, array);
-		break;
-	case PROP_ACTIVE_NSP:
-		if (priv->current_nsp)
-			g_value_set_boxed (value, nm_wimax_nsp_get_dbus_path (priv->current_nsp));
-		else
-			g_value_set_boxed (value, "/");
-		break;
-	case PROP_CENTER_FREQ:
-		g_value_set_uint (value, priv->center_freq);
-		break;
-	case PROP_RSSI:
-		g_value_set_int (value, priv->rssi);
-		break;
-	case PROP_CINR:
-		g_value_set_int (value, priv->cinr);
-		break;
-	case PROP_TX_POWER:
-		g_value_set_int (value, priv->tx_power);
-		break;
-	case PROP_BSID:
-		g_value_set_string (value, priv->bsid);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-dispose (GObject *object)
-{
-	NMDeviceWimax *self = NM_DEVICE_WIMAX (object);
-	NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
-
-	if (priv->disposed)
-		goto done;
-
-	priv->disposed = TRUE;
-
-	clear_activation_timeout (self);
-	clear_link_timeout (self);
-	clear_connected_poll (self);
-
-	if (priv->sdk_action_defer_id)
-		g_source_remove (priv->sdk_action_defer_id);
-
-	if (priv->sdk) {
-		iwmx_sdk_set_callbacks (priv->sdk, NULL, NULL, NULL, NULL, NULL, NULL);
-		wmxsdk_unref (priv->sdk);
-	}
-
-	g_free (priv->bsid);
-
-	set_current_nsp (self, NULL);
-
-	g_slist_free_full (priv->nsp_list, g_object_unref);
-
-	iwmx_sdk_new_callback_unregister (wmx_new_sdk_cb, self);
-	nm_wimax_util_sdk_unref ();
-
-done:
-	G_OBJECT_CLASS (nm_device_wimax_parent_class)->dispose (object);
-}
-
-static void
-nm_device_wimax_class_init (NMDeviceWimaxClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
-
-	g_type_class_add_private (object_class, sizeof (NMDeviceWimaxPrivate));
-
-	/* Virtual methods */
-	object_class->set_property = set_property;
-	object_class->get_property = get_property;
-	object_class->dispose = dispose;
-
-	device_class->check_connection_compatible = check_connection_compatible;
-	device_class->check_connection_available = check_connection_available;
-	device_class->complete_connection = complete_connection;
-	device_class->can_auto_connect = can_auto_connect;
-	device_class->is_available = is_available;
-	device_class->act_stage1_prepare = act_stage1_prepare;
-	device_class->act_stage2_config = act_stage2_config;
-	device_class->deactivate = deactivate;
-	device_class->set_enabled = set_enabled;
-
-	device_class->state_changed = device_state_changed;
-
-	/* Properties */
-	g_object_class_install_property
-		(object_class, PROP_NSPS,
-		 g_param_spec_boxed (NM_DEVICE_WIMAX_NSPS, "", "",
-		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
-		                     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, "", "",
-		                     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, "", "",
-		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_RSSI,
-		 g_param_spec_int (NM_DEVICE_WIMAX_RSSI, "", "",
-		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READABLE |
-		                   G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_CINR,
-		 g_param_spec_int (NM_DEVICE_WIMAX_CINR, "", "",
-		                   G_MININT, G_MAXINT, 0,
-		                   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, "", "",
-		                   G_MININT, G_MAXINT, 0,
-		                   G_PARAM_READABLE |
-		                   G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_BSID,
-		 g_param_spec_string (NM_DEVICE_WIMAX_BSID, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	/* Signals */
-	signals[NSP_ADDED] =
-		g_signal_new ("nsp-added",
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_added),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__OBJECT,
-					  G_TYPE_NONE, 1,
-					  G_TYPE_OBJECT);
-
-	signals[NSP_REMOVED] =
-		g_signal_new ("nsp-removed",
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_removed),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__OBJECT,
-					  G_TYPE_NONE, 1,
-					  G_TYPE_OBJECT);
-
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_device_wimax_object_info);
-}
diff --git a/src/devices/wimax/nm-device-wimax.h b/src/devices/wimax/nm-device-wimax.h
deleted file mode 100644
index 30b2481f..00000000
--- a/src/devices/wimax/nm-device-wimax.h
+++ /dev/null
@@ -1,64 +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) 2010 - 2011 Red Hat, Inc.
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#ifndef __NETWORKMANAGER_DEVICE_WIMAX_H__
-#define __NETWORKMANAGER_DEVICE_WIMAX_H__
-
-#include "nm-device.h"
-#include "nm-wimax-nsp.h"
-
-G_BEGIN_DECLS
-
-#define NM_TYPE_DEVICE_WIMAX            (nm_device_wimax_get_type ())
-#define NM_DEVICE_WIMAX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIMAX, NMDeviceWimax))
-#define NM_DEVICE_WIMAX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass))
-#define NM_IS_DEVICE_WIMAX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIMAX))
-#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))
-
-#define NM_DEVICE_WIMAX_NSPS             "nsps"
-#define NM_DEVICE_WIMAX_ACTIVE_NSP       "active-nsp"
-#define NM_DEVICE_WIMAX_CENTER_FREQUENCY "center-frequency"
-#define NM_DEVICE_WIMAX_RSSI             "rssi"
-#define NM_DEVICE_WIMAX_CINR             "cinr"
-#define NM_DEVICE_WIMAX_TX_POWER         "tx-power"
-#define NM_DEVICE_WIMAX_BSID             "bsid"
-
-typedef struct {
-	NMDevice parent;
-} NMDeviceWimax;
-
-typedef struct {
-	NMDeviceClass parent;
-
-	/* Signals */
-	void (*nsp_added)   (NMDeviceWimax *wimax, NMWimaxNsp *nsp);
-	void (*nsp_removed) (NMDeviceWimax *wimax, NMWimaxNsp *nsp);
-	void (*properties_changed) (NMDeviceWimax *wimax, GHashTable *properties);
-} NMDeviceWimaxClass;
-
-GType nm_device_wimax_get_type (void);
-
-NMDevice   *nm_device_wimax_new            (NMPlatformLink *platform_device);
-
-G_END_DECLS
-
-#endif	/* NM_DEVICE_WIMAX_H */
diff --git a/src/devices/wimax/nm-wimax-factory.c b/src/devices/wimax/nm-wimax-factory.c
deleted file mode 100644
index 7dc580fc..00000000
--- a/src/devices/wimax/nm-wimax-factory.c
+++ /dev/null
@@ -1,91 +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) 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"
-#include "nm-setting-wimax.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))
-
-typedef struct {
-	GObject parent;
-} NMWimaxFactory;
-
-typedef struct {
-	GObjectClass parent;
-} NMWimaxFactoryClass;
-
-static GType nm_wimax_factory_get_type (void);
-
-static void device_factory_interface_init (NMDeviceFactory *factory_iface);
-
-G_DEFINE_TYPE_EXTENDED (NMWimaxFactory, nm_wimax_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_WIMAX_FACTORY, NULL);
-}
-
-/**************************************************************************/
-
-static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
-{
-	/* FIXME: check udev 'DEVTYPE' instead; but since we only support Intel
-	 * WiMAX devices for now this is appropriate.
-	 */
-	if (g_strcmp0 (plink->driver, "i2400m_usb") != 0)
-		return NULL;  /* unsupported */
-
-	return (NMDevice *) nm_device_wimax_new (plink);
-}
-
-NM_DEVICE_FACTORY_DECLARE_TYPES (
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WIMAX)
-	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIMAX_SETTING_NAME)
-)
-
-static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
-{
-	factory_iface->new_link = new_link;
-	factory_iface->get_supported_types = get_supported_types;
-}
-
-static void
-nm_wimax_factory_init (NMWimaxFactory *factory)
-{
-}
-
-static void
-nm_wimax_factory_class_init (NMWimaxFactoryClass *wf_class)
-{
-}
-
diff --git a/src/devices/wimax/nm-wimax-nsp.c b/src/devices/wimax/nm-wimax-nsp.c
deleted file mode 100644
index f4db63f0..00000000
--- a/src/devices/wimax/nm-wimax-nsp.c
+++ /dev/null
@@ -1,241 +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) 2010 - 2012 Red Hat, Inc.
- * Copyright (C) 2009 Novell, Inc.
- */
-
-#include "config.h"
-
-#include "nm-wimax-nsp.h"
-#include "nm-dbus-interface.h"
-#include "nm-dbus-manager.h"
-#include "nm-setting-wimax.h"
-#include "nm-wimax-nsp-glue.h"
-#include "nm-utils.h"
-
-G_DEFINE_TYPE (NMWimaxNsp, nm_wimax_nsp, G_TYPE_OBJECT)
-
-enum {
-	PROP_0,
-
-	PROP_NAME,
-	PROP_SIGNAL_QUALITY,
-	PROP_NETWORK_TYPE,
-
-	LAST_PROP
-};
-
-#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_WIMAX_NSP, NMWimaxNspPrivate))
-
-typedef struct {
-	char *dbus_path;
-
-	char *name;
-	guint32 signal_quality;
-	NMWimaxNspNetworkType network_type;
-} NMWimaxNspPrivate;
-
-NMWimaxNsp *
-nm_wimax_nsp_new (const char *name)
-{
-	g_return_val_if_fail (name != NULL, NULL);
-
-	return NM_WIMAX_NSP (g_object_new (NM_TYPE_WIMAX_NSP,
-									   NM_WIMAX_NSP_NAME, name,
-									   NULL));
-}
-
-const char *
-nm_wimax_nsp_get_name (NMWimaxNsp *self)
-{
-	g_return_val_if_fail (NM_IS_WIMAX_NSP (self), NULL);
-
-	return GET_PRIVATE (self)->name;
-}
-
-guint32
-nm_wimax_nsp_get_signal_quality (NMWimaxNsp *self)
-{
-	g_return_val_if_fail (NM_IS_WIMAX_NSP (self), 0);
-
-	return GET_PRIVATE (self)->signal_quality;
-}
-
-NMWimaxNspNetworkType
-nm_wimax_nsp_get_network_type (NMWimaxNsp *self)
-{
-	g_return_val_if_fail (NM_IS_WIMAX_NSP (self), 0);
-
-	return GET_PRIVATE (self)->network_type;
-}
-
-void
-nm_wimax_nsp_export_to_dbus (NMWimaxNsp *self)
-{
-	NMWimaxNspPrivate *priv;
-	static guint32 counter = 0;
-
-	g_return_if_fail (NM_IS_WIMAX_NSP (self));
-
-	priv = GET_PRIVATE (self);
-
-	g_return_if_fail (priv->dbus_path == NULL);
-
-	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_WIMAX_NSP "/%d", counter++);
-	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, self);
-}
-
-const char *
-nm_wimax_nsp_get_dbus_path (NMWimaxNsp *self)
-{
-	g_return_val_if_fail (NM_IS_WIMAX_NSP (self), NULL);
-
-	return GET_PRIVATE (self)->dbus_path;
-}
-
-gboolean
-nm_wimax_nsp_check_compatible (NMWimaxNsp *self,
-							   NMConnection *connection)
-{
-	NMWimaxNspPrivate *priv;
-	NMSettingWimax *s_wimax;
-
-	g_return_val_if_fail (NM_IS_WIMAX_NSP (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
-
-	priv = GET_PRIVATE (self);
-
-	s_wimax = nm_connection_get_setting_wimax (connection);
-	if (!s_wimax)
-		return FALSE;
-
-	return g_strcmp0 (nm_wimax_nsp_get_name (self), nm_setting_wimax_get_network_name (s_wimax)) == 0;
-}
-
-static void
-nm_wimax_nsp_init (NMWimaxNsp *self)
-{
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMWimaxNspPrivate *priv = GET_PRIVATE (object);
-	guint32 quality;
-	guint network_type;
-
-	switch (prop_id) {
-	case PROP_NAME:
-		/* Construct only */
-		priv->name = g_value_dup_string (value);
-		break;
-	case PROP_SIGNAL_QUALITY:
-		quality = g_value_get_uint (value);
-		if (quality != priv->signal_quality) {
-			priv->signal_quality = CLAMP (quality, 0, 100);
-			g_object_notify (object, NM_WIMAX_NSP_SIGNAL_QUALITY);
-		}
-		break;
-	case PROP_NETWORK_TYPE:
-		network_type = g_value_get_uint (value);
-		if (network_type != priv->network_type) {
-			priv->network_type = network_type;
-			g_object_notify (object, NM_WIMAX_NSP_NETWORK_TYPE);
-		}
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMWimaxNsp *self = NM_WIMAX_NSP (object);
-
-	switch (prop_id) {
-	case PROP_NAME:
-		g_value_set_string (value, nm_wimax_nsp_get_name (self));
-		break;
-	case PROP_SIGNAL_QUALITY:
-		g_value_set_uint (value, nm_wimax_nsp_get_signal_quality (self));
-		break;
-	case PROP_NETWORK_TYPE:
-		g_value_set_uint (value, nm_wimax_nsp_get_network_type (self));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-finalize (GObject *object)
-{
-	NMWimaxNspPrivate *priv = GET_PRIVATE (object);
-
-	g_free (priv->name);
-	g_free (priv->dbus_path);
-
-	G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->finalize (object);
-}
-
-static void
-nm_wimax_nsp_class_init (NMWimaxNspClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	g_type_class_add_private (object_class, sizeof (NMWimaxNspPrivate));
-
-	/* Virtual methods */
-	object_class->set_property = set_property;
-	object_class->get_property = get_property;
-	object_class->finalize = finalize;
-
-	g_object_class_install_property
-		(object_class, PROP_NAME,
-		 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, "", "",
-		                    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, "", "",
-		                    NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
-		                    NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER,
-		                    NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (klass),
-	                                        &dbus_glib_nm_wimax_nsp_object_info);
-}
diff --git a/src/devices/wimax/nm-wimax-nsp.h b/src/devices/wimax/nm-wimax-nsp.h
deleted file mode 100644
index 8d0a6a14..00000000
--- a/src/devices/wimax/nm-wimax-nsp.h
+++ /dev/null
@@ -1,63 +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 __NETWORKMANAGER_WIMAX_NSP_H__
-#define __NETWORKMANAGER_WIMAX_NSP_H__
-
-#include <glib-object.h>
-#include "nm-wimax-types.h"
-#include "nm-connection.h"
-
-#define NM_TYPE_WIMAX_NSP			(nm_wimax_nsp_get_type ())
-#define NM_WIMAX_NSP(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIMAX_NSP, NMWimaxNsp))
-#define NM_WIMAX_NSP_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_WIMAX_NSP, NMWimaxNspClass))
-#define NM_IS_WIMAX_NSP(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIMAX_NSP))
-#define NM_IS_WIMAX_NSP_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_WIMAX_NSP))
-#define NM_WIMAX_NSP_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_WIMAX_NSP, NMWimaxNspClass))
-
-#define NM_WIMAX_NSP_NAME           "name"
-#define NM_WIMAX_NSP_SIGNAL_QUALITY "signal-quality"
-#define NM_WIMAX_NSP_NETWORK_TYPE   "network-type"
-
-typedef struct {
-	GObject parent;
-} NMWimaxNsp;
-
-typedef struct {
-	GObjectClass parent;
-
-	/* Signals */
-	void (*properties_changed) (NMWimaxNsp *nsp, GHashTable *properties);
-} NMWimaxNspClass;
-
-GType nm_wimax_nsp_get_type (void);
-
-NMWimaxNsp            *nm_wimax_nsp_new                (const char *name);
-const char            *nm_wimax_nsp_get_name           (NMWimaxNsp *self);
-guint32                nm_wimax_nsp_get_signal_quality (NMWimaxNsp *self);
-NMWimaxNspNetworkType  nm_wimax_nsp_get_network_type   (NMWimaxNsp *self);
-
-void                   nm_wimax_nsp_export_to_dbus     (NMWimaxNsp *self);
-const char            *nm_wimax_nsp_get_dbus_path      (NMWimaxNsp *self);
-
-gboolean               nm_wimax_nsp_check_compatible   (NMWimaxNsp *self,
-														NMConnection *connection);
-
-#endif	/* NM_WIMAX_NSP_H */
diff --git a/src/devices/wimax/nm-wimax-types.h b/src/devices/wimax/nm-wimax-types.h
deleted file mode 100644
index 207d2477..00000000
--- a/src/devices/wimax/nm-wimax-types.h
+++ /dev/null
@@ -1,31 +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 __NETWORKMANAGER_WIMAX_TYPES_H__
-#define __NETWORKMANAGER_WIMAX_TYPES_H__
-
-typedef enum {
-	NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN,
-	NM_WIMAX_NSP_NETWORK_TYPE_HOME,
-	NM_WIMAX_NSP_NETWORK_TYPE_PARTNER,
-	NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER
-} NMWimaxNspNetworkType;
-
-#endif	/* NM_WIMAX_TYPES_H */
diff --git a/src/devices/wimax/nm-wimax-util.c b/src/devices/wimax/nm-wimax-util.c
deleted file mode 100644
index 0ae285ae..00000000
--- a/src/devices/wimax/nm-wimax-util.c
+++ /dev/null
@@ -1,83 +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.
- */
-
-#include "config.h"
-
-#include "nm-wimax-util.h"
-#include "nm-utils.h"
-#include "iwmxsdk.h"
-#include "nm-logging.h"
-
-static guint sdk_refcount = 0;
-
-void
-nm_wimax_util_sdk_ref (void)
-{
-	int ret = 0;
-
-	if (sdk_refcount == 0) {
-		ret = iwmx_sdk_api_init ();
-		if (ret != 0) {
-			nm_log_warn (LOGD_WIMAX, "Failed to initialize WiMAX: %d", ret);
-			return;
-		}
-	}
-	sdk_refcount++;
-}
-
-gboolean
-nm_wimax_util_sdk_is_initialized (void)
-{
-	return sdk_refcount > 0;
-}
-
-void
-nm_wimax_util_sdk_unref (void)
-{
-	g_return_if_fail (sdk_refcount > 0);
-
-	sdk_refcount--;
-	if (sdk_refcount == 0)
-		iwmx_sdk_api_exit ();
-}
-
-NMWimaxNspNetworkType
-nm_wimax_util_convert_network_type (WIMAX_API_NETWORK_TYPE wimax_network_type)
-{
-	NMWimaxNspNetworkType type;
-
-	switch (wimax_network_type) {
-	case WIMAX_API_HOME:
-		type = NM_WIMAX_NSP_NETWORK_TYPE_HOME;
-		break;
-	case WIMAX_API_PARTNER:
-		type = NM_WIMAX_NSP_NETWORK_TYPE_PARTNER;
-		break;
-	case WIMAX_API_ROAMING_PARTNER:
-		type = NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER;
-		break;
-	default:
-		type = NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN;
-		break;
-	}
-
-	return type;
-}
-
diff --git a/src/devices/wimax/nm-wimax-util.h b/src/devices/wimax/nm-wimax-util.h
deleted file mode 100644
index 5fedc6e8..00000000
--- a/src/devices/wimax/nm-wimax-util.h
+++ /dev/null
@@ -1,43 +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 __NETWORKMANAGER_WIMAX_UTIL_H__
-#define __NETWORKMANAGER_WIMAX_UTIL_H__
-
-#include <glib.h>
-
-#include "nm-macros-internal.h"
-
-NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes")
-#include <WiMaxType.h>
-NM_PRAGMA_WARNING_REENABLE
-
-#include <WiMaxError.h>
-#include "nm-wimax-types.h"
-
-void nm_wimax_util_sdk_ref (void);
-
-gboolean nm_wimax_util_sdk_is_initialized (void);
-
-void nm_wimax_util_sdk_unref (void);
-
-NMWimaxNspNetworkType nm_wimax_util_convert_network_type (WIMAX_API_NETWORK_TYPE wimax_network_type);
-
-#endif	/* NM_WIMAX_UTIL_H */
diff --git a/src/devices/wwan/Makefile.am b/src/devices/wwan/Makefile.am
index 055b07b9..107f630b 100644
--- a/src/devices/wwan/Makefile.am
+++ b/src/devices/wwan/Makefile.am
@@ -8,14 +8,14 @@ AM_CPPFLAGS = \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_srcdir}/libnm-core \
 	-I${top_builddir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
@@ -47,31 +47,29 @@ WWAN_SYMBOL_VIS_FILE=$(srcdir)/wwan-exports.ver
 libnm_wwan_la_LDFLAGS = \
 	-avoid-version \
 	-Wl,--version-script=$(WWAN_SYMBOL_VIS_FILE)
-libnm_wwan_la_LIBADD = $(DBUS_LIBS) $(MM_GLIB_LIBS)
+libnm_wwan_la_LIBADD = \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(GLIB_LIBS) \
+	$(MM_GLIB_LIBS)
 
 ###########################################################
 
-nm-device-modem-glue.h: $(top_srcdir)/introspection/nm-device-modem.xml
-	dbus-binding-tool --prefix=nm_device_modem --mode=glib-server --output=$@ $<
-
-BUILT_SOURCES += nm-device-modem-glue.h
-
 SYMBOL_VIS_FILE=$(srcdir)/exports.ver
 
 libnm_device_plugin_wwan_la_SOURCES = \
 	nm-wwan-factory.c \
 	nm-wwan-factory.h \
 	nm-device-modem.c \
-	nm-device-modem.h \
-	nm-device-modem-glue.h
+	nm-device-modem.h
 
 libnm_device_plugin_wwan_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_wwan_la_LIBADD = \
+	$(top_builddir)/introspection/libnmdbus.la \
 	libnm-wwan.la \
-	$(DBUS_LIBS)
+	$(GLIB_LIBS)
 
 ###########################################################
 
diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in
index 571b1b7d..ac13135e 100644
--- a/src/devices/wwan/Makefile.in
+++ b/src/devices/wwan/Makefile.in
@@ -140,7 +140,8 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(pkglibdir)"
 LTLIBRARIES = $(pkglib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-libnm_device_plugin_wwan_la_DEPENDENCIES = libnm-wwan.la \
+libnm_device_plugin_wwan_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la libnm-wwan.la \
 	$(am__DEPENDENCIES_1)
 am_libnm_device_plugin_wwan_la_OBJECTS = nm-wwan-factory.lo \
 	nm-device-modem.lo
@@ -154,8 +155,9 @@ libnm_device_plugin_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
 	$(AM_CFLAGS) $(CFLAGS) $(libnm_device_plugin_wwan_la_LDFLAGS) \
 	$(LDFLAGS) -o $@
-libnm_wwan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1)
+libnm_wwan_la_DEPENDENCIES =  \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__objects_1 = nm-modem-enum-types.lo
 am_libnm_wwan_la_OBJECTS = nm-modem-broadband.lo nm-modem-manager.lo \
 	nm-modem.lo $(am__objects_1)
@@ -243,7 +245,6 @@ 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@
@@ -255,8 +256,6 @@ CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 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@
@@ -266,6 +265,7 @@ DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
+DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
 ECHO_C = @ECHO_C@
@@ -320,16 +320,13 @@ 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@
+LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
+LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
 LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -366,6 +363,8 @@ NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@
+NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
@@ -394,7 +393,6 @@ 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@
@@ -409,6 +407,8 @@ 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_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
 SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
 SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
@@ -469,6 +469,7 @@ mkdir_p = @mkdir_p@
 nmbinary = @nmbinary@
 nmconfdir = @nmconfdir@
 nmdatadir = @nmdatadir@
+nmlibdir = @nmlibdir@
 nmrundir = @nmrundir@
 nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
@@ -476,6 +477,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -497,18 +499,18 @@ AM_CPPFLAGS = \
 	-I${top_srcdir}/src/devices \
 	-I${top_srcdir}/src/settings \
 	-I${top_srcdir}/src/platform \
-	-I${top_srcdir}/include \
-	-I$(top_builddir)/include \
+	-I${top_builddir}/introspection \
+	-I${top_srcdir}/shared \
+	-I$(top_builddir)/shared \
 	-I${top_srcdir}/libnm-core \
 	-I${top_builddir}/libnm-core \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
-	-DNETWORKMANAGER_COMPILATION \
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
-	$(DBUS_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
-BUILT_SOURCES = $(null) $(GLIB_GENERATED) nm-device-modem-glue.h
+BUILT_SOURCES = $(null) $(GLIB_GENERATED)
 pkglib_LTLIBRARIES = libnm-wwan.la libnm-device-plugin-wwan.la
 
 ###########################################################
@@ -531,22 +533,28 @@ libnm_wwan_la_LDFLAGS = \
 	-avoid-version \
 	-Wl,--version-script=$(WWAN_SYMBOL_VIS_FILE)
 
-libnm_wwan_la_LIBADD = $(DBUS_LIBS) $(MM_GLIB_LIBS)
+libnm_wwan_la_LIBADD = \
+	$(top_builddir)/introspection/libnmdbus.la \
+	$(GLIB_LIBS) \
+	$(MM_GLIB_LIBS)
+
+
+###########################################################
 SYMBOL_VIS_FILE = $(srcdir)/exports.ver
 libnm_device_plugin_wwan_la_SOURCES = \
 	nm-wwan-factory.c \
 	nm-wwan-factory.h \
 	nm-device-modem.c \
-	nm-device-modem.h \
-	nm-device-modem-glue.h
+	nm-device-modem.h
 
 libnm_device_plugin_wwan_la_LDFLAGS = \
 	-module -avoid-version \
 	-Wl,--version-script=$(SYMBOL_VIS_FILE)
 
 libnm_device_plugin_wwan_la_LIBADD = \
+	$(top_builddir)/introspection/libnmdbus.la \
 	libnm-wwan.la \
-	$(DBUS_LIBS)
+	$(GLIB_LIBS)
 
 
 ###########################################################
@@ -887,11 +895,6 @@ include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
 
-###########################################################
-
-nm-device-modem-glue.h: $(top_srcdir)/introspection/nm-device-modem.xml
-	dbus-binding-tool --prefix=nm_device_modem --mode=glib-server --output=$@ $<
-
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wwan.so $(SYMBOL_VIS_FILE)
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-wwan.so $(WWAN_SYMBOL_VIS_FILE)
diff --git a/src/devices/wwan/nm-device-modem-glue.h b/src/devices/wwan/nm-device-modem-glue.h
deleted file mode 100644
index 21271491..00000000
--- a/src/devices/wwan/nm-device-modem-glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-
-#ifndef __dbus_glib_marshal_nm_device_modem_MARSHAL_H__
-#define __dbus_glib_marshal_nm_device_modem_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_modem_MARSHAL_H__ */
-
-#include <dbus/dbus-glib.h>
-static const DBusGMethodInfo dbus_glib_nm_device_modem_methods[] = {
-};
-
-const DBusGObjectInfo dbus_glib_nm_device_modem_object_info = {  1,
-  dbus_glib_nm_device_modem_methods,
-  0,
-"\0",
-"org.freedesktop.NetworkManager.Device.Modem\0PropertiesChanged\0\0",
-"org.freedesktop.NetworkManager.Device.Modem\0ModemCapabilities\0modem_capabilities\0read\0org.freedesktop.NetworkManager.Device.Modem\0CurrentCapabilities\0current_capabilities\0read\0\0"
-};
-
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index ca724c04..edecd24d 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -21,14 +21,12 @@
 #include "config.h"
 
 #include <string.h>
-#include <glib.h>
 
+#include "nm-default.h"
 #include "nm-device-modem.h"
 #include "nm-modem.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"
 #include "nm-modem-broadband.h"
 #include "NetworkManagerUtils.h"
@@ -37,12 +35,12 @@
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceModem);
 
+#include "nmdbus-device-modem.h"
+
 G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate))
 
-#include "nm-device-modem-glue.h"
-
 typedef struct {
 	NMModem *modem;
 	NMDeviceModemCapabilities caps;
@@ -108,7 +106,7 @@ modem_prepare_result (NMModem *modem,
 			 * the device to be auto-activated anymore, which would risk locking
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
-			g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, FALSE, NULL);
+			nm_device_set_autoconnect (device, FALSE);
 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
 		}
 
@@ -256,6 +254,12 @@ data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 }
 
 static void
+ids_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+	nm_device_recheck_available_connections (NM_DEVICE (user_data));
+}
+
+static void
 modem_state_cb (NMModem *modem,
                 NMModemState new_state,
                 NMModemState old_state,
@@ -332,7 +336,7 @@ device_state_changed (NMDevice *device,
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (device);
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
-	NMConnection *connection = nm_device_get_connection (device);
+	NMConnection *connection = nm_device_get_applied_connection (device);
 
 	g_assert (priv->modem);
 
@@ -411,10 +415,7 @@ check_connection_available (NMDevice *device,
 		return FALSE;
 
 	if (state == NM_MODEM_STATE_LOCKED) {
-		NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (connection);
-
-		/* Can't use a connection without a PIN if the modem is locked */
-		if (!s_gsm || !nm_setting_gsm_get_pin (s_gsm))
+		if (!nm_connection_get_setting_gsm (connection))
 			return FALSE;
 	}
 
@@ -679,6 +680,10 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	 * while in the new ModemManager the data port is set afterwards when the bearer gets
 	 * created */
 	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
+
+	g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self);
 }
 
 static void
@@ -795,7 +800,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 		                    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),
-	                                        &dbus_glib_nm_device_modem_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (mclass),
+	                                        NMDBUS_TYPE_DEVICE_MODEM_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h
index 996195c4..1cf399e1 100644
--- a/src/devices/wwan/nm-device-modem.h
+++ b/src/devices/wwan/nm-device-modem.h
@@ -21,9 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_MODEM_H__
 #define __NETWORKMANAGER_DEVICE_MODEM_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
 #include "nm-device.h"
 #include "nm-modem.h"
 
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index ec4f4a2f..427f3ed6 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -20,28 +20,51 @@
 
 #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-core-internal.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-platform.h"
 
 G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
 
+typedef enum {
+    CONNECT_STEP_FIRST,
+    CONNECT_STEP_WAIT_FOR_SIM,
+    CONNECT_STEP_UNLOCK,
+    CONNECT_STEP_WAIT_FOR_READY,
+    CONNECT_STEP_CONNECT,
+    CONNECT_STEP_LAST
+} ConnectStep;
+
+typedef struct {
+	NMModemBroadband *self;
+	ConnectStep step;
+
+	MMModemCapability caps;
+	NMConnection *connection;
+	GCancellable *cancellable;
+	MMSimpleConnectProperties *connect_properties;
+	GArray *ip_types;
+	guint ip_types_i;
+	GError *first_error;
+} ConnectContext;
+
 struct _NMModemBroadbandPrivate {
 	/* The modem object from dbus */
 	MMObject *modem_object;
 	/* Per-interface objects */
 	MMModem *modem_iface;
 	MMModemSimple *simple_iface;
+	MMSim *sim_iface;
 
 	/* Connection setup */
+	ConnectContext *ctx;
 
 	MMBearer *bearer;
 	MMBearerIpConfig *ipv4_config;
@@ -257,45 +280,45 @@ create_gsm_connect_properties (NMConnection *connection)
 	return properties;
 }
 
-typedef struct {
-	NMModemBroadband *self;
-	MMModemCapability caps;
-	MMSimpleConnectProperties *connect_properties;
-	GArray *ip_types;
-	guint ip_types_i;
-	GError *first_error;
-} ActStageContext;
-
 static void
-act_stage_context_free (ActStageContext *ctx)
+connect_context_clear (NMModemBroadband *self)
 {
-	g_clear_error (&ctx->first_error);
-	g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
-	g_clear_object (&ctx->connect_properties);
-	g_object_unref (ctx->self);
-	g_slice_free (ActStageContext, ctx);
+	if (self->priv->ctx) {
+		ConnectContext *ctx = self->priv->ctx;
+
+		g_clear_error (&ctx->first_error);
+		g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
+		g_clear_object (&ctx->cancellable);
+		g_clear_object (&ctx->connection);
+		g_clear_object (&ctx->connect_properties);
+		g_clear_object (&ctx->self);
+		g_slice_free (ConnectContext, ctx);
+		self->priv->ctx = NULL;
+	}
 }
 
-static void act_stage_context_step (ActStageContext *ctx);
+static void connect_context_step (NMModemBroadband *self);
 
 static void
 connect_ready (MMModemSimple *simple_iface,
                GAsyncResult *res,
-               ActStageContext *ctx)
+               NMModemBroadband *self)
 {
+	ConnectContext *ctx = self->priv->ctx;
 	GError *error = NULL;
 	NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
 	NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
 
-	ctx->self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
-	if (!ctx->self->priv->bearer) {
+	self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+	if (!self->priv->bearer) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
 		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
-		     mm_modem_get_unlock_required (ctx->self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
-			/* Request PIN */
-			ask_for_pin (ctx->self);
+		     mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
 			g_error_free (error);
-			act_stage_context_free (ctx);
+
+			/* Request PIN */
+			ask_for_pin (self);
+			connect_context_clear (self);
 			return;
 		}
 
@@ -312,79 +335,190 @@ connect_ready (MMModemSimple *simple_iface,
 		 * retry with the next one, if any.
 		 */
 		ctx->ip_types_i++;
-		act_stage_context_step (ctx);
+		connect_context_clear (self);
 		return;
 	}
 
 	/* Grab IP configurations */
-	ctx->self->priv->ipv4_config = mm_bearer_get_ipv4_config (ctx->self->priv->bearer);
-	if (ctx->self->priv->ipv4_config)
-		ip4_method = get_bearer_ip_method (ctx->self->priv->ipv4_config);
+	self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer);
+	if (self->priv->ipv4_config)
+		ip4_method = get_bearer_ip_method (self->priv->ipv4_config);
 
-	ctx->self->priv->ipv6_config = mm_bearer_get_ipv6_config (ctx->self->priv->bearer);
-	if (ctx->self->priv->ipv6_config)
-		ip6_method = get_bearer_ip_method (ctx->self->priv->ipv6_config);
+	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 (ctx->self)));
-		g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-		act_stage_context_free (ctx);
+		             nm_modem_get_uid (NM_MODEM (self)));
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		connect_context_clear (self);
 		return;
 	}
 
-	g_object_set (ctx->self,
-	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (ctx->self->priv->bearer),
+	g_object_set (self,
+	              NM_MODEM_DATA_PORT,  mm_bearer_get_interface (self->priv->bearer),
 	              NM_MODEM_IP4_METHOD, ip4_method,
 	              NM_MODEM_IP6_METHOD, ip6_method,
-	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (ctx->self->priv->bearer),
+	              NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer),
 	              NULL);
 
-	g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
-	act_stage_context_free (ctx);
+	ctx->step++;
+	connect_context_step (self);
 }
 
 static void
-act_stage_context_step (ActStageContext *ctx)
+send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 {
-	if (ctx->ip_types_i < ctx->ip_types->len) {
-		NMModemIPType current;
-
-		current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);
-
-		if (current == NM_MODEM_IP_TYPE_IPV4)
-			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
-		else if (current == NM_MODEM_IP_TYPE_IPV6)
-			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
-		else if (current == NM_MODEM_IP_TYPE_IPV4V6)
-			mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
-		else
-			g_assert_not_reached ();
-
-		nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'",
-		            nm_modem_get_uid (NM_MODEM (ctx->self)),
-		            nm_modem_ip_type_to_string (current));
-
-		mm_modem_simple_connect (ctx->self->priv->simple_iface,
-		                         ctx->connect_properties,
-		                         NULL,
-		                         (GAsyncReadyCallback)connect_ready,
-		                         ctx);
+    GError *error = NULL;
+
+    if (!mm_sim_send_pin_finish (sim, result, &error)) {
+		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
+		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
+		     mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
+			ask_for_pin (self);
+		} else {
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
+		}
+		g_error_free (error);
 		return;
+    }
+
+	self->priv->ctx->step++;
+	connect_context_step (self);
+}
+
+static void
+connect_context_step (NMModemBroadband *self)
+{
+	ConnectContext *ctx = self->priv->ctx;
+
+	switch (ctx->step) {
+	case CONNECT_STEP_FIRST:
+		ctx->step++;
+		/* fall through */
+
+	case CONNECT_STEP_WAIT_FOR_SIM:
+		if (MODEM_CAPS_3GPP (ctx->caps) && !self->priv->sim_iface) {
+			/* Have to wait for the SIM to show up */
+			break;
+		}
+		ctx->step++;
+		/* fall through */
+
+	case CONNECT_STEP_UNLOCK:
+		if (   MODEM_CAPS_3GPP (ctx->caps)
+		    && mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN) {
+			NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection);
+			const char *pin = nm_setting_gsm_get_pin (s_gsm);
+
+			/* If we have a PIN already, send it.  If we don't, get it. */
+			if (pin) {
+				mm_sim_send_pin (self->priv->sim_iface,
+				                 pin,
+				                 ctx->cancellable,
+				                 (GAsyncReadyCallback) send_pin_ready,
+				                 self);
+			} else {
+				ask_for_pin (self);
+			}
+			break;
+		}
+		ctx->step++;
+		/* fall through */
+
+	case CONNECT_STEP_WAIT_FOR_READY: {
+		GError *error = NULL;
+
+		if (mm_modem_get_state (self->priv->modem_iface) <= MM_MODEM_STATE_LOCKED)
+			break;
+
+		/* Create core connect properties based on the modem capabilities */
+		g_assert (!ctx->connect_properties);
+
+		if (MODEM_CAPS_3GPP (ctx->caps))
+			ctx->connect_properties = create_gsm_connect_properties (ctx->connection);
+		else if (MODEM_CAPS_3GPP2 (ctx->caps))
+			ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
+		else {
+			nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
+			             nm_modem_get_uid (NM_MODEM (self)),
+			             nm_connection_get_id (ctx->connection));
+
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+			connect_context_clear (self);
+			break;
+		}
+		g_assert (ctx->connect_properties);
+
+		/* Build up list of IP types that we need to use in the retries */
+		ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error);
+		if (!ctx->ip_types) {
+			nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
+			             nm_modem_get_uid (NM_MODEM (self)),
+			             nm_connection_get_id (ctx->connection),
+			             error ? error->message : "unknown error");
+			g_clear_error (&error);
+
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+			connect_context_clear (self);
+			break;
+		}
+
+		ctx->step++;
+		/* fall through */
 	}
 
-	/* If we have a saved error from a previous attempt, use it */
-	if (!ctx->first_error)
-		ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
-		                                        NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                                        "invalid bearer IP configuration");
-
-	nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
-	             nm_modem_get_uid (NM_MODEM (ctx->self)),
-	             ctx->first_error->message);
-	g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
-	act_stage_context_free (ctx);
+	case CONNECT_STEP_CONNECT:
+		if (ctx->ip_types_i < ctx->ip_types->len) {
+			NMModemIPType current;
+
+			current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);
+
+			if (current == NM_MODEM_IP_TYPE_IPV4)
+				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
+			else if (current == NM_MODEM_IP_TYPE_IPV6)
+				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
+			else if (current == NM_MODEM_IP_TYPE_IPV4V6)
+				mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
+			else
+				g_assert_not_reached ();
+
+			nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'",
+			            nm_modem_get_uid (NM_MODEM (self)),
+			            nm_modem_ip_type_to_string (current));
+
+			mm_modem_simple_connect (self->priv->simple_iface,
+			                         ctx->connect_properties,
+			                         NULL,
+			                         (GAsyncReadyCallback) connect_ready,
+			                         self);
+			break;
+		}
+
+		ctx->step++;
+		/* fall through */
+
+	case CONNECT_STEP_LAST:
+		if (self->priv->ipv4_config || self->priv->ipv6_config) {
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
+		} else {
+			/* If we have a saved error from a previous attempt, use it */
+			if (!ctx->first_error)
+				ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
+				                                        NM_DEVICE_ERROR_INVALID_CONNECTION,
+				                                        "invalid bearer IP configuration");
+
+			nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
+					     nm_modem_get_uid (NM_MODEM (self)),
+					     ctx->first_error->message);
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
+		}
+
+		connect_context_clear (self);
+		break;
+	}
 }
 
 static NMActStageReturn
@@ -393,8 +527,6 @@ act_stage1_prepare (NMModem *_self,
                     NMDeviceStateReason *reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
-	ActStageContext *ctx;
-	GError *error = NULL;
 
 	/* Make sure we can get the Simple interface from the modem */
 	if (!self->priv->simple_iface) {
@@ -407,41 +539,16 @@ act_stage1_prepare (NMModem *_self,
 		}
 	}
 
-	/* Allocate new context for this activation stage attempt */
-	ctx = g_slice_new0 (ActStageContext);
-	ctx->self = NM_MODEM_BROADBAND (g_object_ref (self));
-	ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
+	connect_context_clear (self);
 
-	/* Create core connect properties based on the modem capabilities */
-	if (MODEM_CAPS_3GPP (ctx->caps))
-		ctx->connect_properties = create_gsm_connect_properties (connection);
-	else if (MODEM_CAPS_3GPP2 (ctx->caps))
-		ctx->connect_properties = create_cdma_connect_properties (connection);
-	else {
-		nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             nm_connection_get_id (connection));
-		act_stage_context_free (ctx);
-		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-	g_assert (ctx->connect_properties);
-
-	/* Checkout list of IP types that we need to use in the retries */
-	ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), connection, &error);
-	if (!ctx->ip_types) {
-		nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             nm_connection_get_id (connection),
-		             error ? error->message : "unknown error");
-		g_clear_error (&error);
-		act_stage_context_free (ctx);
-		*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
+	/* Allocate new context for this connect stage attempt */
+	self->priv->ctx = g_slice_new0 (ConnectContext);
+	self->priv->ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
+	self->priv->ctx->cancellable = g_cancellable_new ();
+	self->priv->ctx->connection = g_object_ref (connection);
 
 	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->priv->simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000);
-	act_stage_context_step (ctx);
+	connect_context_step (self);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -715,6 +822,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 {
 	GError *error = NULL;
 	NMIP4Config *config = NULL;
+	const char *data_port;
 	const gchar *address_string;
 	const gchar *gw_string;
 	guint32 address_network;
@@ -724,6 +832,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	guint i;
 
 	g_assert (self->priv->ipv4_config);
+	g_assert (self->priv->bearer);
 
 	nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:",
 	             nm_modem_get_uid (NM_MODEM (self)));
@@ -743,10 +852,13 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	gw_string = mm_bearer_ip_config_get_gateway (self->priv->ipv4_config);
 	ip4_string_to_num (gw_string, &gw);
 
-	config = nm_ip4_config_new ();
+	data_port = mm_bearer_get_interface (self->priv->bearer);
+	g_assert (data_port);
+	config = nm_ip4_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
 	memset (&address, 0, sizeof (address));
 	address.address = address_network;
+	address.peer_address = address_network;
 	address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
 	address.source = NM_IP_CONFIG_SOURCE_WWAN;
 	nm_ip4_config_add_address (config, &address);
@@ -796,6 +908,7 @@ stage3_ip6_done (NMModemBroadband *self)
 {
 	GError *error = NULL;
 	NMIP6Config *config = NULL;
+	const char *data_port;
 	const gchar *address_string;
 	NMPlatformIP6Address address;
 	NMModemIPMethod ip_method;
@@ -833,7 +946,9 @@ stage3_ip6_done (NMModemBroadband *self)
 	nm_log_info (LOGD_MB, "(%s): IPv6 base configuration:",
 	             nm_modem_get_uid (NM_MODEM (self)));
 
-	config = nm_ip6_config_new ();
+	data_port = mm_bearer_get_interface (self->priv->bearer);
+	g_assert (data_port);
+	config = nm_ip6_config_new (nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port));
 
 	address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv6_config);
 	nm_ip6_config_add_address (config, &address);
@@ -941,10 +1056,11 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
 	GError *error = NULL;
 
 	if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) {
-		if (ctx->warn)
+		if (ctx->warn && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
 			nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: %s",
 			             nm_modem_get_uid (NM_MODEM (ctx->self)),
 			             error->message);
+		}
 		g_simple_async_result_take_error (ctx->result, error);
 	}
 
@@ -1042,7 +1158,6 @@ modem_state_changed (MMModem *modem,
                      MMModemStateChangeReason reason,
                      NMModemBroadband *self)
 {
-
 	/* After the SIM is unlocked MM1 will move the device to INITIALIZING which
 	 * is an unavailable state.  That makes state handling confusing here, so
 	 * suppress this state change and let the modem move from LOCKED to DISABLED.
@@ -1053,6 +1168,9 @@ modem_state_changed (MMModem *modem,
 	nm_modem_set_state (NM_MODEM (self),
 	                    mm_state_to_nm (new_state),
 	                    mm_modem_state_change_reason_get_string (reason));
+
+	if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_READY)
+		connect_context_step (self);
 }
 
 /*****************************************************************************/
@@ -1114,12 +1232,23 @@ get_sim_ready (MMModem *modem,
 	GError *error = NULL;
 	MMSim *new_sim;
 
+
 	new_sim = mm_modem_get_sim_finish (modem, res, &error);
-	if (new_sim) {
+	if (new_sim != self->priv->sim_iface) {
+		g_clear_object (&self->priv->sim_iface);
+		self->priv->sim_iface = new_sim;
+	} else
+		g_clear_object (&new_sim);
+
+	if (self->priv->sim_iface) {
 		g_object_set (G_OBJECT (self),
-		              NM_MODEM_SIM_ID, mm_sim_get_identifier (new_sim),
+		              NM_MODEM_SIM_ID, mm_sim_get_identifier (self->priv->sim_iface),
+		              NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (self->priv->sim_iface),
 		              NULL);
-		g_object_unref (new_sim);
+
+		/* If we're waiting for the SIM during a connect, proceed with the connect */
+		if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_SIM)
+			connect_context_step (self);
 	} else {
 		nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
@@ -1142,7 +1271,10 @@ sim_changed (MMModem *modem, GParamSpec *pspec, gpointer user_data)
 		                  (GAsyncReadyCallback) get_sim_ready,
 		                  g_object_ref (self));
 	} else
-		g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, NULL, NULL);
+		g_object_set (G_OBJECT (self),
+		              NM_MODEM_SIM_ID, NULL,
+		              NM_MODEM_SIM_OPERATOR_ID, NULL,
+		              NULL);
 }
 
 static void
@@ -1226,11 +1358,13 @@ dispose (GObject *object)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (object);
 
+	connect_context_clear (self);
 	g_clear_object (&self->priv->ipv4_config);
 	g_clear_object (&self->priv->ipv6_config);
 	g_clear_object (&self->priv->bearer);
 	g_clear_object (&self->priv->modem_iface);
 	g_clear_object (&self->priv->simple_iface);
+	g_clear_object (&self->priv->sim_iface);
 	g_clear_object (&self->priv->modem_object);
 
 	G_OBJECT_CLASS (nm_modem_broadband_parent_class)->dispose (object);
diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h
index 2268bac4..a54e63a6 100644
--- a/src/devices/wwan/nm-modem-broadband.h
+++ b/src/devices/wwan/nm-modem-broadband.h
@@ -21,7 +21,6 @@
 #ifndef __NETWORKMANAGER_MODEM_BROADBAND_H__
 #define __NETWORKMANAGER_MODEM_BROADBAND_H__
 
-#include <glib-object.h>
 #include "nm-modem.h"
 
 G_BEGIN_DECLS
diff --git a/src/devices/wwan/nm-modem-enum-types.c b/src/devices/wwan/nm-modem-enum-types.c
index 6fbb3b6d..2d02ede9 100644
--- a/src/devices/wwan/nm-modem-enum-types.c
+++ b/src/devices/wwan/nm-modem-enum-types.c
@@ -3,6 +3,8 @@
 
 /* Generated by glib-mkenums. Do not edit */
 
+#include "config.h"
+
 #include "nm-modem-enum-types.h"
 
 #include "nm-modem.h"
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index f0e56926..c28d86ad 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -27,7 +27,8 @@
 #include <libmm-glib.h>
 
 #include "nm-modem-manager.h"
-#include "nm-logging.h"
+#include "nm-default.h"
+#include "nm-dbus-compat.h"
 #include "nm-modem.h"
 #include "nm-modem-broadband.h"
 
@@ -193,10 +194,7 @@ modem_manager_name_owner_changed (MMManager *modem_manager,
 	gchar *name_owner;
 
 	/* Quit poking, if any */
-	if (self->priv->mm_launch_id) {
-		g_source_remove (self->priv->mm_launch_id);
-		self->priv->mm_launch_id = 0;
-	}
+	nm_clear_g_source (&self->priv->mm_launch_id);
 
 	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (modem_manager));
 	if (!name_owner) {
@@ -265,7 +263,7 @@ modem_manager_poke (NMModemManager *self)
 	g_dbus_connection_call (self->priv->dbus_connection,
 	                        "org.freedesktop.ModemManager1",
 	                        "/org/freedesktop/ModemManager1",
-	                        "org.freedesktop.DBus.Peer",
+	                        DBUS_INTERFACE_PEER,
 	                        "Ping",
 	                        NULL, /* inputs */
 	                        NULL, /* outputs */
@@ -434,10 +432,7 @@ dispose (GObject *object)
 {
 	NMModemManager *self = NM_MODEM_MANAGER (object);
 
-	if (self->priv->mm_launch_id) {
-		g_source_remove (self->priv->mm_launch_id);
-		self->priv->mm_launch_id = 0;
-	}
+	nm_clear_g_source (&self->priv->mm_launch_id);
 
 	modem_manager_clear_signals (self);
 	g_clear_object (&self->priv->modem_manager);
diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h
index 9e0db41e..b0014c6a 100644
--- a/src/devices/wwan/nm-modem-manager.h
+++ b/src/devices/wwan/nm-modem-manager.h
@@ -23,7 +23,6 @@
 #ifndef __NETWORKMANAGER_MODEM_MANAGER_H__
 #define __NETWORKMANAGER_MODEM_MANAGER_H__
 
-#include <glib-object.h>
 #include "nm-modem.h"
 
 #define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type ())
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 3385a6fd..15104307 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -22,17 +22,17 @@
 #include "config.h"
 
 #include <string.h>
+
 #include "nm-modem.h"
+#include "nm-core-internal.h"
 #include "nm-platform.h"
-#include "nm-dbus-manager.h"
 #include "nm-setting-connection.h"
-#include "nm-properties-changed-signal.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
-#include "nm-dbus-glib-types.h"
 #include "nm-modem-enum-types.h"
 #include "nm-route-manager.h"
+#include "gsystem-local-alloc.h"
 
 G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
 
@@ -52,6 +52,7 @@ enum {
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
 	PROP_IP_TYPES,
+	PROP_SIM_OPERATOR_ID,
 
 	LAST_PROP
 };
@@ -71,12 +72,13 @@ typedef struct {
 	char *device_id;
 	char *sim_id;
 	NMModemIPType ip_types;
+	char *sim_operator_id;
 
 	NMPPPManager *ppp_manager;
 
 	NMActRequest *act_request;
 	guint32 secrets_tries;
-	guint32 secrets_id;
+	NMActRequestGetSecretsCallId secrets_id;
 
 	guint32 mm_ip_timeout;
 
@@ -287,7 +289,7 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv4 but IPv4 is "
-			                     "unsuported by the modem.");
+			                     "unsupported by the modem.");
 			return NULL;
 		}
 		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV4);
@@ -299,7 +301,7 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv6 but IPv6 is "
-			                     "unsuported by the modem.");
+			                     "unsupported by the modem.");
 			return NULL;
 		}
 		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV6);
@@ -350,6 +352,24 @@ nm_modem_get_connection_ip_type (NMModem *self,
 	return NULL;
 }
 
+const char *
+nm_modem_get_device_id (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->device_id;
+}
+
+const char *
+nm_modem_get_sim_id (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->sim_id;
+}
+
+const char *
+nm_modem_get_sim_operator_id (NMModem *self)
+{
+	return NM_MODEM_GET_PRIVATE (self)->sim_operator_id;
+}
+
 /*****************************************************************************/
 /* IP method PPP */
 
@@ -493,7 +513,7 @@ ppp_stage3_ip_config_start (NMModem *self,
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 
 	if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
-		NMConnection *connection = nm_act_request_get_connection (req);
+		NMConnection *connection = nm_act_request_get_applied_connection (req);
 
 		g_assert (connection);
 		if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL))
@@ -511,7 +531,7 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 	priv->ppp_manager = nm_ppp_manager_new (priv->data_port);
 	if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, ip_timeout, &error)) {
-		g_signal_connect (priv->ppp_manager, "state-changed",
+		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_STATE_CHANGED,
 		                  G_CALLBACK (ppp_state_changed),
 		                  self);
 		g_signal_connect (priv->ppp_manager, "ip4-config",
@@ -532,8 +552,7 @@ ppp_stage3_ip_config_start (NMModem *self,
 		            error && error->message ? error->message : "(unknown)");
 		g_error_free (error);
 
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
+		nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 
 		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
 		ret = NM_ACT_STAGE_RETURN_FAILURE;
@@ -563,7 +582,7 @@ 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);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 
@@ -680,7 +699,7 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 	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);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 
@@ -725,16 +744,13 @@ cancel_get_secrets (NMModem *self)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
-	if (priv->secrets_id) {
-		nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id);
-		priv->secrets_id = 0;
-	}
+	nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id);
 }
 
 static void
 modem_secrets_cb (NMActRequest *req,
-                  guint32 call_id,
-                  NMConnection *connection,
+                  NMActRequestGetSecretsCallId call_id,
+                  NMSettingsConnection *connection,
                   GError *error,
                   gpointer user_data)
 {
@@ -743,7 +759,10 @@ modem_secrets_cb (NMActRequest *req,
 
 	g_return_if_fail (call_id == priv->secrets_id);
 
-	priv->secrets_id = 0;
+	priv->secrets_id = NULL;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
 	if (error)
 		nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message);
@@ -751,7 +770,7 @@ modem_secrets_cb (NMActRequest *req,
 	g_signal_emit (self, signals[AUTH_RESULT], 0, error);
 }
 
-gboolean
+void
 nm_modem_get_secrets (NMModem *self,
                       const char *setting_name,
                       gboolean request_new,
@@ -770,10 +789,8 @@ nm_modem_get_secrets (NMModem *self,
 	                                               hint,
 	                                               modem_secrets_cb,
 	                                               self);
-	if (priv->secrets_id)
-		g_signal_emit (self, signals[AUTH_REQUESTED], 0);
-
-	return !!(priv->secrets_id);
+	g_return_if_fail (priv->secrets_id);
+	g_signal_emit (self, signals[AUTH_REQUESTED], 0);
 }
 
 /*****************************************************************************/
@@ -793,8 +810,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
                              NMDeviceStateReason *reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-	NMActStageReturn ret;
-	GPtrArray *hints = NULL;
+	gs_unref_ptrarray GPtrArray *hints = NULL;
 	const char *setting_name = NULL;
 	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 	NMConnection *connection;
@@ -803,7 +819,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 		g_object_unref (priv->act_request);
 	priv->act_request = g_object_ref (req);
 
-	connection = nm_act_request_get_connection (req);
+	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
 
 	setting_name = nm_connection_need_secrets (connection, &hints);
@@ -823,18 +839,9 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	                                               hints ? g_ptr_array_index (hints, 0) : NULL,
 	                                               modem_secrets_cb,
 	                                               self);
-	if (priv->secrets_id) {
-		g_signal_emit (self, signals[AUTH_REQUESTED], 0);
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else {
-		*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
-	if (hints)
-		g_ptr_array_free (hints, TRUE);
-
-	return ret;
+	g_return_val_if_fail (priv->secrets_id, NM_ACT_STAGE_RETURN_FAILURE);
+	g_signal_emit (self, signals[AUTH_REQUESTED], 0);
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 /*****************************************************************************/
@@ -859,6 +866,67 @@ nm_modem_act_stage2_config (NMModem *self,
 gboolean
 nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 {
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	if (g_str_equal (nm_setting_connection_get_connection_type (s_con),
+	                 NM_SETTING_GSM_SETTING_NAME)) {
+		NMSettingGsm *s_gsm;
+		const char *str;
+
+		s_gsm = nm_connection_get_setting_gsm (connection);
+		if (!s_gsm)
+			return FALSE;
+
+		str = nm_setting_gsm_get_device_id (s_gsm);
+		if (str) {
+			if (!priv->device_id) {
+				nm_log_dbg (LOGD_MB, "(%s): %s/%s has device-id, device does not",
+				            priv->uid,
+				            nm_connection_get_uuid (connection),
+				            nm_connection_get_id (connection));
+				return FALSE;
+			}
+			if (strcmp (str, priv->device_id)) {
+				nm_log_dbg (LOGD_MB, "(%s): %s/%s device-id mismatch",
+				            priv->uid,
+				            nm_connection_get_uuid (connection),
+				            nm_connection_get_id (connection));
+				return FALSE;
+			}
+		}
+
+		/* SIM properties may not be available before the SIM is unlocked, so
+		 * to ensure that autoconnect works, the connection's SIM properties
+		 * are only compared if present on the device.
+		 */
+
+		str = nm_setting_gsm_get_sim_id (s_gsm);
+		if (str && priv->sim_id) {
+			if (strcmp (str, priv->sim_id)) {
+				nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-id mismatch",
+				            priv->uid,
+				            nm_connection_get_uuid (connection),
+				            nm_connection_get_id (connection));
+				return FALSE;
+			}
+		}
+
+		str = nm_setting_gsm_get_sim_operator_id (s_gsm);
+		if (str && priv->sim_operator_id) {
+			if (strcmp (str, priv->sim_operator_id)) {
+				nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-operator-id mismatch",
+				            priv->uid,
+				            nm_connection_get_uuid (connection),
+				            nm_connection_get_id (connection));
+				return FALSE;
+			}
+		}
+	}
+
 	if (NM_MODEM_GET_CLASS (self)->check_connection_compatible)
 		return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection);
 	return FALSE;
@@ -899,10 +967,7 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 
 	priv->in_bytes = priv->out_bytes = 0;
 
-	if (priv->ppp_manager) {
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
-	}
+	nm_exported_object_clear_and_unexport (&priv->ppp_manager);
 
 	if (device) {
 		g_return_if_fail (NM_IS_DEVICE (device));
@@ -1315,6 +1380,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_IP_TYPES:
 		g_value_set_uint (value, priv->ip_types);
 		break;
+	case PROP_SIM_OPERATOR_ID:
+		g_value_set_string (value, priv->sim_operator_id);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1326,6 +1394,7 @@ set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
+	const char *s;
 
 	switch (prop_id) {
 	case PROP_PATH:
@@ -1369,6 +1438,12 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IP_TYPES:
 		priv->ip_types = g_value_get_uint (value);
 		break;
+	case PROP_SIM_OPERATOR_ID:
+		g_clear_pointer (&priv->sim_operator_id, g_free);
+		s = g_value_get_string (value);
+		if (s && s[0])
+			priv->sim_operator_id = g_strdup (s);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1400,6 +1475,7 @@ finalize (GObject *object)
 	g_free (priv->data_port);
 	g_free (priv->device_id);
 	g_free (priv->sim_id);
+	g_free (priv->sim_operator_id);
 
 	G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object);
 }
@@ -1514,6 +1590,13 @@ nm_modem_class_init (NMModemClass *klass)
 		                    0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4,
 		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
+	g_object_class_install_property
+		(object_class, PROP_SIM_OPERATOR_ID,
+		 g_param_spec_string (NM_MODEM_SIM_OPERATOR_ID, "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+		                      G_PARAM_STATIC_STRINGS));
+
 	/* Signals */
 
 	signals[PPP_STATS] =
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 2cace890..60e5589b 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -22,7 +22,7 @@
 #ifndef __NETWORKMANAGER_MODEM_H__
 #define __NETWORKMANAGER_MODEM_H__
 
-#include <glib-object.h>
+#include "nm-default.h"
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-device.h"
 
@@ -36,18 +36,19 @@ G_BEGIN_DECLS
 #define NM_MODEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_MODEM, NMModemClass))
 
 /* Properties */
-#define NM_MODEM_UID          "uid"
-#define NM_MODEM_PATH         "path"
-#define NM_MODEM_DRIVER       "driver"
-#define NM_MODEM_CONTROL_PORT "control-port"
-#define NM_MODEM_DATA_PORT    "data-port"
-#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 */
+#define NM_MODEM_UID             "uid"
+#define NM_MODEM_PATH            "path"
+#define NM_MODEM_DRIVER          "driver"
+#define NM_MODEM_CONTROL_PORT    "control-port"
+#define NM_MODEM_DATA_PORT       "data-port"
+#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 */
+#define NM_MODEM_SIM_OPERATOR_ID "sim-operator-id"
 
 /* Signals */
 #define NM_MODEM_PPP_STATS         "ppp-stats"
@@ -179,12 +180,15 @@ typedef struct {
 
 GType nm_modem_get_type (void);
 
-const char *nm_modem_get_path         (NMModem *modem);
-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);
+const char *nm_modem_get_path            (NMModem *modem);
+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);
+const char *nm_modem_get_device_id       (NMModem *modem);
+const char *nm_modem_get_sim_id          (NMModem *modem);
+const char *nm_modem_get_sim_operator_id (NMModem *modem);
+gboolean    nm_modem_get_iid             (NMModem *modem, NMUtilsIPv6IfaceId *out_iid);
 
 gboolean    nm_modem_owns_port        (NMModem *modem, const char *iface);
 
@@ -218,10 +222,10 @@ NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem,
 
 void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config);
 
-gboolean nm_modem_get_secrets (NMModem *modem,
-                               const char *setting_name,
-                               gboolean request_new,
-                               const char *hint);
+void nm_modem_get_secrets (NMModem *modem,
+                           const char *setting_name,
+                           gboolean request_new,
+                           const char *hint);
 
 void nm_modem_deactivate (NMModem *modem, NMDevice *device);
 
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index 49b0b0af..2fd9e0bd 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -29,12 +29,12 @@
 #include "nm-setting-cdma.h"
 #include "nm-modem-manager.h"
 #include "nm-device-modem.h"
-#include "nm-logging.h"
+#include "nm-default.h"
 #include "nm-platform.h"
 
 static GType nm_wwan_factory_get_type (void);
 
-static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+static void device_factory_interface_init (NMDeviceFactoryInterface *factory_iface);
 
 G_DEFINE_TYPE_EXTENDED (NMWwanFactory, nm_wwan_factory, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
@@ -96,9 +96,14 @@ NM_DEVICE_FACTORY_DECLARE_TYPES (
 )
 
 static NMDevice *
-new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error)
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
 {
-	g_warn_if_fail (plink->type == NM_LINK_TYPE_WWAN_ETHERNET);
+	g_return_val_if_fail (plink, NULL);
+	g_return_val_if_fail (plink->type == NM_LINK_TYPE_WWAN_ETHERNET, NULL);
 	*out_ignore = TRUE;
 	return NULL;
 }
@@ -123,10 +128,10 @@ nm_wwan_factory_init (NMWwanFactory *self)
 }
 
 static void
-device_factory_interface_init (NMDeviceFactory *factory_iface)
+device_factory_interface_init (NMDeviceFactoryInterface *factory_iface)
 {
 	factory_iface->get_supported_types = get_supported_types;
-	factory_iface->new_link = new_link;
+	factory_iface->create_device = create_device;
 	factory_iface->start = start;
 }
 
diff --git a/src/devices/wwan/nm-wwan-factory.h b/src/devices/wwan/nm-wwan-factory.h
index c6c03f1f..78031e60 100644
--- a/src/devices/wwan/nm-wwan-factory.h
+++ b/src/devices/wwan/nm-wwan-factory.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_WWAN_FACTORY_H__
 #define __NETWORKMANAGER_WWAN_FACTORY_H__
 
-#include <glib-object.h>
+#include "nm-default.h"
 
 #define NM_TYPE_WWAN_FACTORY (nm_wwan_factory_get_type ())
 #define NM_WWAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WWAN_FACTORY, NMWwanFactory))