about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-05-11 22:08:45 +0200
committerMichael Biebl <biebl@debian.org>2018-05-11 22:08:45 +0200
commitee9c73a923909e23a649407be77e25235d769e25 (patch)
treee21c923621fa278e737da693df9eb60ea31a6067 /src
parentf60117b41d5433be1b4a96d82cd11d0c3dce9b63 (diff)
New upstream version 1.10.8 upstream/1.10.8
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c185
-rw-r--r--src/NetworkManagerUtils.h21
-rw-r--r--src/devices/adsl/meson.build34
-rw-r--r--src/devices/adsl/nm-device-adsl.c46
-rw-r--r--src/devices/bluetooth/meson.build45
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c4
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c1
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c2
-rw-r--r--src/devices/bluetooth/nm-device-bt.c77
-rw-r--r--src/devices/meson.build22
-rw-r--r--src/devices/nm-acd-manager.c489
-rw-r--r--src/devices/nm-acd-manager.h43
-rw-r--r--src/devices/nm-arping-manager.c474
-rw-r--r--src/devices/nm-arping-manager.h43
-rw-r--r--src/devices/nm-device-bond.c26
-rw-r--r--src/devices/nm-device-bridge.c26
-rw-r--r--src/devices/nm-device-dummy.c24
-rw-r--r--src/devices/nm-device-ethernet.c90
-rw-r--r--src/devices/nm-device-ethernet.h18
-rw-r--r--src/devices/nm-device-factory.c69
-rw-r--r--src/devices/nm-device-generic.c23
-rw-r--r--src/devices/nm-device-infiniband.c27
-rw-r--r--src/devices/nm-device-ip-tunnel.c78
-rw-r--r--src/devices/nm-device-ip-tunnel.h4
-rw-r--r--src/devices/nm-device-macsec.c59
-rw-r--r--src/devices/nm-device-macsec.h3
-rw-r--r--src/devices/nm-device-macvlan.c42
-rw-r--r--src/devices/nm-device-macvlan.h3
-rw-r--r--src/devices/nm-device-ppp.c93
-rw-r--r--src/devices/nm-device-private.h6
-rw-r--r--src/devices/nm-device-tun.c355
-rw-r--r--src/devices/nm-device-veth.c41
-rw-r--r--src/devices/nm-device-vlan.c29
-rw-r--r--src/devices/nm-device-vlan.h4
-rw-r--r--src/devices/nm-device-vxlan.c110
-rw-r--r--src/devices/nm-device-vxlan.h3
-rw-r--r--src/devices/nm-device.c3716
-rw-r--r--src/devices/nm-device.h42
-rw-r--r--src/devices/nm-lldp-listener.c45
-rw-r--r--src/devices/ovs/meson.build38
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.c21
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c19
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c21
-rw-r--r--src/devices/ovs/nm-ovsdb.c31
-rw-r--r--src/devices/team/meson.build35
-rw-r--r--src/devices/team/nm-device-team.c29
-rw-r--r--src/devices/tests/meson.build18
-rw-r--r--src/devices/tests/test-arping.c (renamed from src/devices/tests/test-acd.c)63
-rw-r--r--src/devices/tests/test-lldp.c52
-rw-r--r--src/devices/wifi/meson.build51
-rw-r--r--src/devices/wifi/nm-device-iwd.c1921
-rw-r--r--src/devices/wifi/nm-device-iwd.h62
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c38
-rw-r--r--src/devices/wifi/nm-device-wifi.c381
-rw-r--r--src/devices/wifi/nm-device-wifi.h12
-rw-r--r--src/devices/wifi/nm-iwd-manager.c694
-rw-r--r--src/devices/wifi/nm-iwd-manager.h65
-rw-r--r--src/devices/wifi/nm-wifi-ap.c205
-rw-r--r--src/devices/wifi/nm-wifi-ap.h21
-rw-r--r--src/devices/wifi/nm-wifi-common.c205
-rw-r--r--src/devices/wifi/nm-wifi-common.h37
-rw-r--r--src/devices/wifi/nm-wifi-factory.c29
-rw-r--r--src/devices/wifi/nm-wifi-utils.c32
-rw-r--r--src/devices/wifi/nm-wifi-utils.h2
-rw-r--r--src/devices/wifi/tests/meson.build13
-rw-r--r--src/devices/wwan/libnm-wwan.ver2
-rw-r--r--src/devices/wwan/meson.build75
-rw-r--r--src/devices/wwan/nm-device-modem.c96
-rw-r--r--src/devices/wwan/nm-modem-broadband.c65
-rw-r--r--src/devices/wwan/nm-modem-ofono.c26
-rw-r--r--src/devices/wwan/nm-modem.c294
-rw-r--r--src/devices/wwan/nm-modem.h19
-rw-r--r--src/devices/wwan/nm-wwan-factory.c8
-rw-r--r--src/dhcp/meson.build22
-rw-r--r--src/dhcp/nm-dhcp-client.c119
-rw-r--r--src/dhcp/nm-dhcp-client.h49
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c330
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h15
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c59
-rw-r--r--src/dhcp/nm-dhcp-dhcpcanon.c14
-rw-r--r--src/dhcp/nm-dhcp-dhcpcd.c17
-rw-r--r--src/dhcp/nm-dhcp-helper.c2
-rw-r--r--src/dhcp/nm-dhcp-listener.c84
-rw-r--r--src/dhcp/nm-dhcp-manager.c118
-rw-r--r--src/dhcp/nm-dhcp-manager.h15
-rw-r--r--src/dhcp/nm-dhcp-systemd.c115
-rw-r--r--src/dhcp/nm-dhcp-utils.c8
-rw-r--r--src/dhcp/nm-dhcp-utils.h2
-rw-r--r--src/dhcp/tests/leases/basic.leases31
-rw-r--r--src/dhcp/tests/leases/malformed1.leases15
-rw-r--r--src/dhcp/tests/leases/malformed2.leases15
-rw-r--r--src/dhcp/tests/leases/malformed3.leases15
-rw-r--r--src/dhcp/tests/meson.build19
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c318
-rw-r--r--src/dhcp/tests/test-dhcp-utils.c21
-rw-r--r--src/dns/nm-dns-dnsmasq.c344
-rw-r--r--src/dns/nm-dns-manager.c819
-rw-r--r--src/dns/nm-dns-manager.h33
-rw-r--r--src/dns/nm-dns-plugin.c4
-rw-r--r--src/dns/nm-dns-plugin.h4
-rw-r--r--src/dns/nm-dns-systemd-resolved.c289
-rw-r--r--src/dns/nm-dns-unbound.c2
-rw-r--r--src/dnsmasq/nm-dnsmasq-manager.c2
-rw-r--r--src/dnsmasq/tests/meson.build14
-rw-r--r--src/main.c47
-rw-r--r--src/meson.build319
-rw-r--r--src/ndisc/nm-lndp-ndisc.c52
-rw-r--r--src/ndisc/nm-ndisc.c55
-rw-r--r--src/ndisc/nm-ndisc.h33
-rw-r--r--src/ndisc/tests/meson.build23
-rw-r--r--src/nm-act-request.c8
-rw-r--r--src/nm-active-connection.c384
-rw-r--r--src/nm-active-connection.h9
-rw-r--r--src/nm-audit-manager.c2
-rw-r--r--src/nm-audit-manager.h1
-rw-r--r--src/nm-auth-manager.c623
-rw-r--r--src/nm-auth-manager.h62
-rw-r--r--src/nm-auth-subject.c39
-rw-r--r--src/nm-auth-subject.h4
-rw-r--r--src/nm-auth-utils.c489
-rw-r--r--src/nm-auth-utils.h25
-rw-r--r--src/nm-bus-manager.c1024
-rw-r--r--src/nm-bus-manager.h93
-rw-r--r--src/nm-checkpoint-manager.c326
-rw-r--r--src/nm-checkpoint-manager.h24
-rw-r--r--src/nm-checkpoint.c288
-rw-r--r--src/nm-checkpoint.h29
-rw-r--r--src/nm-config-data.c307
-rw-r--r--src/nm-config-data.h24
-rw-r--r--src/nm-config.c20
-rw-r--r--src/nm-config.h3
-rw-r--r--src/nm-connectivity.c589
-rw-r--r--src/nm-connectivity.h29
-rw-r--r--src/nm-core-utils.c278
-rw-r--r--src/nm-core-utils.h36
-rw-r--r--src/nm-dbus-manager.c1626
-rw-r--r--src/nm-dbus-manager.h107
-rw-r--r--src/nm-dbus-object.c320
-rw-r--r--src/nm-dbus-object.h202
-rw-r--r--src/nm-dbus-utils.c312
-rw-r--r--src/nm-dbus-utils.h212
-rw-r--r--src/nm-dhcp4-config.c34
-rw-r--r--src/nm-dhcp6-config.c34
-rw-r--r--src/nm-dispatcher.c13
-rw-r--r--src/nm-exported-object.c1055
-rw-r--r--src/nm-exported-object.h85
-rw-r--r--src/nm-firewall-manager.c2
-rw-r--r--src/nm-iface-helper.c115
-rw-r--r--src/nm-ip4-config.c212
-rw-r--r--src/nm-ip4-config.h200
-rw-r--r--src/nm-ip6-config.c216
-rw-r--r--src/nm-ip6-config.h10
-rw-r--r--src/nm-logging.c7
-rw-r--r--src/nm-logging.h12
-rw-r--r--src/nm-manager.c2442
-rw-r--r--src/nm-manager.h36
-rw-r--r--src/nm-netns.c4
-rw-r--r--src/nm-pacrunner-manager.c15
-rw-r--r--src/nm-policy.c175
-rw-r--r--src/nm-session-monitor.c2
-rw-r--r--src/nm-test-utils-core.h32
-rw-r--r--src/nm-types.h16
-rw-r--r--src/org.freedesktop.NetworkManager.conf2
-rw-r--r--src/platform/nm-fake-platform.c1
-rw-r--r--src/platform/nm-linux-platform.c779
-rw-r--r--src/platform/nm-netlink.c1491
-rw-r--r--src/platform/nm-netlink.h511
-rw-r--r--src/platform/nm-platform-utils.c3
-rw-r--r--src/platform/nm-platform.c644
-rw-r--r--src/platform/nm-platform.h117
-rw-r--r--src/platform/nmp-netns.c4
-rw-r--r--src/platform/nmp-object.c94
-rw-r--r--src/platform/nmp-object.h31
-rw-r--r--src/platform/tests/meson.build37
-rw-r--r--src/platform/tests/monitor.c3
-rw-r--r--src/platform/tests/test-address.c5
-rw-r--r--src/platform/tests/test-cleanup.c3
-rw-r--r--src/platform/tests/test-common.c116
-rw-r--r--src/platform/tests/test-common.h30
-rw-r--r--src/platform/tests/test-link.c135
-rw-r--r--src/platform/tests/test-nmp-object.c27
-rw-r--r--src/platform/tests/test-route.c3
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.c372
-rw-r--r--src/platform/wifi/wifi-utils-private.h14
-rw-r--r--src/platform/wifi/wifi-utils-wext.c42
-rw-r--r--src/platform/wifi/wifi-utils.c61
-rw-r--r--src/platform/wifi/wifi-utils.h2
-rw-r--r--src/ppp/meson.build41
-rw-r--r--src/ppp/nm-ppp-manager-call.c2
-rw-r--r--src/ppp/nm-ppp-manager.c302
-rw-r--r--src/ppp/nm-ppp-manager.h1
-rw-r--r--src/ppp/nm-pppd-plugin.c32
-rw-r--r--src/settings/nm-agent-manager.c160
-rw-r--r--src/settings/nm-agent-manager.h2
-rw-r--r--src/settings/nm-secret-agent.c140
-rw-r--r--src/settings/nm-settings-connection.c664
-rw-r--r--src/settings/nm-settings-connection.h61
-rw-r--r--src/settings/nm-settings.c769
-rw-r--r--src/settings/nm-settings.h10
-rw-r--r--src/settings/plugins/ibft/meson.build48
-rw-r--r--src/settings/plugins/ibft/tests/meson.build22
-rw-r--r--src/settings/plugins/ibft/tests/test-ibft.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build77
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-inotify-helper.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c134
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c162
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c66
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/meson.build22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-32
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-42
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c195
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.c2912
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.h55
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection.c233
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection.h46
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-parser.c734
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-parser.h42
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-utils.c830
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-utils.h71
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-plugin.c524
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-plugin.h38
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c584
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h41
-rw-r--r--src/settings/plugins/ifnet/tests/net158
-rw-r--r--src/settings/plugins/ifnet/tests/net.all864
-rw-r--r--src/settings/plugins/ifnet/tests/nm-system-settings.conf5
-rw-r--r--src/settings/plugins/ifnet/tests/test-ifnet.c396
-rw-r--r--src/settings/plugins/ifnet/tests/test_ca_cert.pem27
-rw-r--r--src/settings/plugins/ifnet/tests/wpa_supplicant.conf70
-rw-r--r--src/settings/plugins/ifupdown/meson.build53
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c6
-rw-r--r--src/settings/plugins/ifupdown/tests/meson.build17
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c2
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection10
-rw-r--r--src/settings/plugins/keyfile/tests/meson.build21
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c88
-rw-r--r--src/settings/plugins/meson.build15
-rw-r--r--src/supplicant/nm-supplicant-config.c71
-rw-r--r--src/supplicant/nm-supplicant-config.h3
-rw-r--r--src/supplicant/nm-supplicant-interface.c42
-rw-r--r--src/supplicant/nm-supplicant-interface.h8
-rw-r--r--src/supplicant/nm-supplicant-manager.c15
-rw-r--r--src/supplicant/nm-supplicant-settings-verify.c9
-rw-r--r--src/supplicant/nm-supplicant-types.h6
-rw-r--r--src/supplicant/tests/meson.build14
-rw-r--r--src/supplicant/tests/test-supplicant-config.c196
-rw-r--r--src/systemd/meson.build70
-rw-r--r--src/systemd/sd-adapt/device-nodes.h3
-rw-r--r--src/systemd/sd-adapt/errno-list.h3
-rw-r--r--src/systemd/sd-adapt/locale-util.h3
-rw-r--r--src/systemd/sd-adapt/memfd-util.h3
-rw-r--r--src/systemd/sd-adapt/nm-sd-adapt.h23
-rw-r--r--src/systemd/sd-adapt/procfs-util.h3
-rw-r--r--src/systemd/sd-adapt/terminal-util.h3
-rw-r--r--src/systemd/src/basic/alloc-util.c3
-rw-r--r--src/systemd/src/basic/alloc-util.h16
-rw-r--r--src/systemd/src/basic/async.h3
-rw-r--r--src/systemd/src/basic/escape.c1
-rw-r--r--src/systemd/src/basic/escape.h1
-rw-r--r--src/systemd/src/basic/ether-addr-util.c6
-rw-r--r--src/systemd/src/basic/ether-addr-util.h1
-rw-r--r--src/systemd/src/basic/extract-word.c4
-rw-r--r--src/systemd/src/basic/extract-word.h1
-rw-r--r--src/systemd/src/basic/fd-util.c403
-rw-r--r--src/systemd/src/basic/fd-util.h39
-rw-r--r--src/systemd/src/basic/fileio.c156
-rw-r--r--src/systemd/src/basic/fileio.h15
-rw-r--r--src/systemd/src/basic/fs-util.c471
-rw-r--r--src/systemd/src/basic/fs-util.h28
-rw-r--r--src/systemd/src/basic/hash-funcs.c57
-rw-r--r--src/systemd/src/basic/hash-funcs.h24
-rw-r--r--src/systemd/src/basic/hashmap.c163
-rw-r--r--src/systemd/src/basic/hashmap.h41
-rw-r--r--src/systemd/src/basic/hexdecoct.c214
-rw-r--r--src/systemd/src/basic/hexdecoct.h1
-rw-r--r--src/systemd/src/basic/hostname-util.c103
-rw-r--r--src/systemd/src/basic/hostname-util.h7
-rw-r--r--src/systemd/src/basic/in-addr-util.c3
-rw-r--r--src/systemd/src/basic/in-addr-util.h1
-rw-r--r--src/systemd/src/basic/io-util.c13
-rw-r--r--src/systemd/src/basic/io-util.h1
-rw-r--r--src/systemd/src/basic/list.h4
-rw-r--r--src/systemd/src/basic/log.h83
-rw-r--r--src/systemd/src/basic/macro.h57
-rw-r--r--src/systemd/src/basic/mempool.c1
-rw-r--r--src/systemd/src/basic/mempool.h1
-rw-r--r--src/systemd/src/basic/parse-util.c113
-rw-r--r--src/systemd/src/basic/parse-util.h21
-rw-r--r--src/systemd/src/basic/path-util.c85
-rw-r--r--src/systemd/src/basic/path-util.h15
-rw-r--r--src/systemd/src/basic/prioq.c3
-rw-r--r--src/systemd/src/basic/prioq.h1
-rw-r--r--src/systemd/src/basic/process-util.c504
-rw-r--r--src/systemd/src/basic/process-util.h77
-rw-r--r--src/systemd/src/basic/random-util.c6
-rw-r--r--src/systemd/src/basic/random-util.h1
-rw-r--r--src/systemd/src/basic/refcnt.h1
-rw-r--r--src/systemd/src/basic/set.h13
-rw-r--r--src/systemd/src/basic/signal-util.h18
-rw-r--r--src/systemd/src/basic/socket-util.c163
-rw-r--r--src/systemd/src/basic/socket-util.h19
-rw-r--r--src/systemd/src/basic/sparse-endian.h4
-rw-r--r--src/systemd/src/basic/stdio-util.h7
-rw-r--r--src/systemd/src/basic/string-table.c1
-rw-r--r--src/systemd/src/basic/string-table.h10
-rw-r--r--src/systemd/src/basic/string-util.c152
-rw-r--r--src/systemd/src/basic/string-util.h25
-rw-r--r--src/systemd/src/basic/strv.c62
-rw-r--r--src/systemd/src/basic/strv.h18
-rw-r--r--src/systemd/src/basic/time-util.c31
-rw-r--r--src/systemd/src/basic/time-util.h7
-rw-r--r--src/systemd/src/basic/umask-util.h1
-rw-r--r--src/systemd/src/basic/utf8.c20
-rw-r--r--src/systemd/src/basic/utf8.h3
-rw-r--r--src/systemd/src/basic/util.c382
-rw-r--r--src/systemd/src/basic/util.h25
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c11
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.h1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.h1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h3
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-network.c16
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-option.c11
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-packet.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-protocol.h1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h67
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-lease-internal.h3
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-network.c5
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c347
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-protocol.h8
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.h1
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c1
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.h1
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c6
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.h1
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c30
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h4
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c60
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c32
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c292
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c48
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c4
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c2
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c1
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c56
-rw-r--r--src/systemd/src/libsystemd/sd-id128/id128-util.c13
-rw-r--r--src/systemd/src/libsystemd/sd-id128/id128-util.h1
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c1
-rw-r--r--src/systemd/src/shared/dns-domain.c32
-rw-r--r--src/systemd/src/shared/dns-domain.h2
-rw-r--r--src/systemd/src/systemd/_sd-common.h1
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h6
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h1
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h11
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h6
-rw-r--r--src/systemd/src/systemd/sd-event.h6
-rw-r--r--src/systemd/src/systemd/sd-id128.h1
-rw-r--r--src/systemd/src/systemd/sd-ipv4acd.h1
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h1
-rw-r--r--src/systemd/src/systemd/sd-lldp.h1
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h1
-rw-r--r--src/tests/config/meson.build26
-rw-r--r--src/tests/config/nm-test-device.c2
-rw-r--r--src/tests/config/test-config.c39
-rw-r--r--src/tests/meson.build47
-rw-r--r--src/tests/test-general-with-expect.c126
-rw-r--r--src/tests/test-general.c25
-rw-r--r--src/tests/test-ip6-config.c18
-rw-r--r--src/tests/test-resolvconf-capture.c298
-rw-r--r--src/vpn/nm-vpn-connection.c95
-rw-r--r--src/vpn/nm-vpn-connection.h1
380 files changed, 21884 insertions, 26168 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index b208ffc6..89bd357f 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -31,6 +31,7 @@
 #include "nm-core-internal.h"
 
 #include "platform/nm-platform.h"
+#include "nm-exported-object.h"
 #include "nm-auth-utils.h"
 
 /*****************************************************************************/
@@ -67,93 +68,98 @@ nm_utils_get_shared_wifi_permission (NMConnection *connection)
 /*****************************************************************************/
 
 static char *
-get_new_connection_name (NMConnection *const*existing_connections,
+get_new_connection_name (const GSList *existing,
                          const char *preferred,
                          const char *fallback_prefix)
 {
-	gs_free const char **existing_names = NULL;
-	guint i, existing_len = 0;
+	GSList *names = NULL;
+	const GSList *iter;
+	char *cname = NULL;
+	int i = 0;
+	gboolean preferred_found = FALSE;
 
 	g_assert (fallback_prefix);
 
-	if (existing_connections) {
-		existing_len = NM_PTRARRAY_LEN (existing_connections);
-		existing_names = g_new (const char *, existing_len);
-		for (i = 0; i < existing_len; i++) {
-			NMConnection *candidate;
-			const char *id;
+	for (iter = existing; iter; iter = g_slist_next (iter)) {
+		NMConnection *candidate = NM_CONNECTION (iter->data);
+		const char *id;
 
-			candidate = existing_connections[i];
-			nm_assert (NM_IS_CONNECTION (candidate));
+		id = nm_connection_get_id (candidate);
+		g_assert (id);
+		names = g_slist_append (names, (gpointer) id);
 
-			id = nm_connection_get_id (candidate);
-			nm_assert (id);
-
-			existing_names[i] = id;
-
-			if (   preferred
-				&& nm_streq (preferred, id)) {
-				/* the preferred name is already taken. Forget about it. */
-				preferred = NULL;
-			}
-		}
-		nm_assert (!existing_connections[i]);
+		if (preferred && !preferred_found && (strcmp (preferred, id) == 0))
+			preferred_found = TRUE;
 	}
 
 	/* Return the preferred name if it was unique */
-	if (preferred)
+	if (preferred && !preferred_found) {
+		g_slist_free (names);
 		return g_strdup (preferred);
+	}
 
 	/* Otherwise find the next available unique connection name using the given
 	 * connection name template.
 	 */
-	for (i = 1; TRUE; i++) {
+	while (!cname && (i++ < 10000)) {
 		char *temp;
+		gboolean found = FALSE;
 
-		/* TRANSLATORS: the first %s is a prefix for the connection id, such
+		/* Translators: the first %s is a prefix for the connection id, such
 		 * as "Wired Connection" or "VPN Connection". The %d is a number
 		 * that is combined with the first argument to create a unique
 		 * connection id. */
-		temp = g_strdup_printf (C_("connection id fallback", "%s %u"),
+		temp = g_strdup_printf (C_("connection id fallback", "%s %d"),
 		                        fallback_prefix, i);
-
-		if (nm_utils_strv_find_first ((char **) existing_names,
-		                              existing_len,
-		                              temp) < 0)
-			return temp;
-
-		g_free (temp);
+		for (iter = names; iter; iter = g_slist_next (iter)) {
+			if (!strcmp (iter->data, temp)) {
+				found = TRUE;
+				break;
+			}
+		}
+		if (!found)
+			cname = temp;
+		else
+			g_free (temp);
 	}
+
+	g_slist_free (names);
+	return cname;
 }
 
 static char *
 get_new_connection_ifname (NMPlatform *platform,
-                           NMConnection *const*existing_connections,
+                           const GSList *existing,
                            const char *prefix)
 {
-	guint i, j;
-
-	for (i = 0; TRUE; i++) {
-		char *name;
+	int i;
+	char *name;
+	const GSList *iter;
+	gboolean found;
 
+	for (i = 0; i < 500; i++) {
 		name = g_strdup_printf ("%s%d", prefix, i);
 
 		if (nm_platform_link_get_by_ifname (platform, name))
 			goto next;
 
-		if (existing_connections) {
-			for (j = 0; existing_connections[j]; j++) {
-				if (nm_streq0 (nm_connection_get_interface_name (existing_connections[j]),
-				               name))
-					goto next;
+		for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
+			NMConnection *candidate = iter->data;
+
+			if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) {
+				found = TRUE;
+				break;
 			}
 		}
 
-		return name;
+		if (!found)
+			return name;
 
-next:
+	next:
 		g_free (name);
 	}
+
+	return NULL;
 }
 
 const char *
@@ -245,7 +251,7 @@ void
 nm_utils_complete_generic (NMPlatform *platform,
                            NMConnection *connection,
                            const char *ctype,
-                           NMConnection *const*existing_connections,
+                           const GSList *existing,
                            const char *preferred_id,
                            const char *fallback_id_prefix,
                            const char *ifname_prefix,
@@ -272,14 +278,14 @@ nm_utils_complete_generic (NMPlatform *platform,
 
 	/* Add a connection ID if absent */
 	if (!nm_setting_connection_get_id (s_con)) {
-		id = get_new_connection_name (existing_connections, preferred_id, fallback_id_prefix);
+		id = get_new_connection_name (existing, preferred_id, fallback_id_prefix);
 		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
 		g_free (id);
 	}
 
 	/* Add an interface name, if requested */
 	if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
-		ifname = get_new_connection_ifname (platform, existing_connections, ifname_prefix);
+		ifname = get_new_connection_ifname (platform, existing, ifname_prefix);
 		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
 		g_free (ifname);
 	}
@@ -874,37 +880,58 @@ nm_utils_match_connection (NMConnection *const*connections,
 
 /*****************************************************************************/
 
-int
-nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
-                                const char *match_device_type,
-                                const GSList *specs,
-                                int no_match_value)
+/**
+ * nm_utils_g_value_set_object_path:
+ * @value: a #GValue, initialized to store an object path
+ * @object: (allow-none): an #NMExportedObject
+ *
+ * Sets @value to @object's object path. If @object is %NULL, or not
+ * exported, @value is set to "/".
+ */
+void
+nm_utils_g_value_set_object_path (GValue *value, gpointer object)
+{
+	g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object));
+
+	if (object && nm_exported_object_is_exported (object))
+		g_value_set_string (value, nm_exported_object_get_path (object));
+	else
+		g_value_set_string (value, "/");
+}
+
+/**
+ * nm_utils_g_value_set_object_path_array:
+ * @value: a #GValue, initialized to store an object path
+ * @objects: a #GSList of #NMExportedObjects
+ * @filter_func: (allow-none): function to call on each object in @objects
+ * @user_data: data to pass to @filter_func
+ *
+ * Sets @value to an array of object paths of the objects in @objects.
+ */
+void
+nm_utils_g_value_set_object_path_array (GValue *value,
+                                        GSList *objects,
+                                        NMUtilsObjectFunc filter_func,
+                                        gpointer user_data)
 {
-	NMMatchSpecMatchType m;
-
-	/* we can only match by certain properties that are available on the
-	 * platform link (and even @pllink might be missing.
-	 *
-	 * It's still useful because of specs like "*" and "except:interface-name:eth0",
-	 * which match even in that case. */
-	m = nm_match_spec_device (specs,
-	                          pllink ? pllink->name : NULL,
-	                          match_device_type,
-	                          pllink ? pllink->driver : NULL,
-	                          NULL,
-	                          NULL,
-	                          NULL);
-
-	switch (m) {
-	case NM_MATCH_SPEC_MATCH:
-		return TRUE;
-	case NM_MATCH_SPEC_NEG_MATCH:
-		return FALSE;
-	case NM_MATCH_SPEC_NO_MATCH:
-		return no_match_value;
+	char **paths;
+	guint i;
+	GSList *iter;
+
+	paths = g_new (char *, g_slist_length (objects) + 1);
+	for (i = 0, iter = objects; iter; iter = iter->next) {
+		NMExportedObject *object = iter->data;
+		const char *path;
+
+		path = nm_exported_object_get_path (object);
+		if (!path)
+			continue;
+		if (filter_func && !filter_func ((GObject *) object, user_data))
+			continue;
+		paths[i++] = g_strdup (path);
 	}
-	nm_assert_not_reached ();
-	return no_match_value;
+	paths[i] = NULL;
+	g_value_take_boxed (value, paths);
 }
 
-
+/*****************************************************************************/
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 13bdb67e..e5f28b27 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -31,7 +31,7 @@ const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);
 void nm_utils_complete_generic (NMPlatform *platform,
                                 NMConnection *connection,
                                 const char *ctype,
-                                NMConnection *const*existing_connections,
+                                const GSList *existing,
                                 const char *preferred_id,
                                 const char *fallback_id_prefix,
                                 const char *ifname_prefix,
@@ -48,10 +48,21 @@ NMConnection *nm_utils_match_connection (NMConnection *const*connections,
                                          NMUtilsMatchFilterFunc match_filter_func,
                                          gpointer match_filter_data);
 
-int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
-                                    const char *match_device_type,
-                                    const GSList *specs,
-                                    int no_match_value);
+void nm_utils_g_value_set_object_path (GValue *value, gpointer object);
+
+/**
+ * NMUtilsObjectFunc:
+ * @object: the object to filter on
+ * @user_data: data passed to the function from the caller
+ *
+ * Returns: %TRUE if the object should be used, %FALSE if not
+ */
+typedef gboolean (*NMUtilsObjectFunc) (GObject *object, gpointer user_data);
+
+void nm_utils_g_value_set_object_path_array (GValue *value,
+                                             GSList *objects,
+                                             NMUtilsObjectFunc filter_func,
+                                             gpointer user_data);
 
 /*****************************************************************************/
 
diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build
deleted file mode 100644
index 4b0fade0..00000000
--- a/src/devices/adsl/meson.build
+++ /dev/null
@@ -1,34 +0,0 @@
-sources = files(
-  'nm-atm-manager.c',
-  'nm-device-adsl.c'
-)
-
-deps = [
-  libudev_dep,
-  nm_dep
-]
-
-libnm_device_plugin_adsl = shared_module(
-  'nm-device-plugin-adsl',
-  sources: sources,
-  dependencies: deps,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_adsl
-
-run_target(
-  'check-local-devices-adsl',
-  command: [check_exports, libnm_device_plugin_adsl.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_adsl
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-adsl: src/devices/adsl/libnm-device-plugin-adsl.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/adsl/.libs/libnm-device-plugin-adsl.so)
-'''
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 91331376..e9bd41ae 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -39,6 +39,8 @@
 #include "nm-setting-adsl.h"
 #include "nm-utils.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Adsl.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDeviceAdsl);
 
@@ -114,7 +116,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingAdsl *s_adsl;
@@ -135,6 +137,8 @@ complete_connection (NMDevice *device,
 	                           _("ADSL connection"),
 	                           NULL,
 	                           FALSE); /* No IPv6 yet by default */
+
+
 	return TRUE;
 }
 
@@ -427,22 +431,8 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
-ppp_ifindex_set (NMPPPManager *ppp_manager,
-                 int ifindex,
-                 const char *iface,
-                 gpointer user_data)
-{
-	NMDevice *device = NM_DEVICE (user_data);
-
-	if (!nm_device_set_ip_ifindex (device, ifindex)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	}
-}
-
-static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
@@ -450,6 +440,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 
 	/* Ignore PPP IP4 events that come in after initial configuration */
 	if (nm_device_activate_ip4_state_in_conf (device)) {
+		nm_device_set_ip_iface (device, iface);
 		nm_device_activate_schedule_ip4_config_result (device, config);
 	}
 }
@@ -508,9 +499,6 @@ act_stage3_ip4_config_start (NMDevice *device,
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
 	                  G_CALLBACK (ppp_state_changed),
 	                  self);
-	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_CALLBACK (ppp_ifindex_set),
-	                  self);
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_CALLBACK (ppp_ip4_config),
 	                  self);
@@ -651,24 +639,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_adsl = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_ADSL,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier", "b", NM_DEVICE_CARRIER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed  = constructed;
@@ -676,8 +650,6 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_adsl);
-
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 
 	parent_class->check_connection_compatible = check_connection_compatible;
@@ -694,4 +666,8 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 	                       G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_ADSL_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build
deleted file mode 100644
index eb200679..00000000
--- a/src/devices/bluetooth/meson.build
+++ /dev/null
@@ -1,45 +0,0 @@
-sources = files(
-  'nm-bluez-device.c',
-  'nm-bluez-manager.c',
-  'nm-bluez4-adapter.c',
-  'nm-bluez4-manager.c',
-  'nm-bluez5-manager.c',
-  'nm-bt-error.c',
-  'nm-device-bt.c'
-)
-
-deps = [
-  libnm_wwan_dep,
-  nm_dep
-]
-
-if enable_bluez5_dun
-  sources += files('nm-bluez5-dun.c')
-
-  deps += bluez5_dep
-endif
-
-libnm_device_plugin_bluetooth = shared_module(
-  'nm-device-plugin-bluetooth',
-  sources: sources,
-  dependencies: deps,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_bluetooth
-
-run_target(
-  'check-local-devices-bluetooth',
-  command: [check_exports, libnm_device_plugin_bluetooth.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_bluetooth
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-bluetooth: src/devices/bluetooth/libnm-device-plugin-bluetooth.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/bluetooth/.libs/libnm-device-plugin-bluetooth.so)
-'''
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index cc9e38c8..bd3cf18a 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -254,7 +254,7 @@ pan_connection_check_create (NMBluezDevice *self)
 		g_assert (connection_compatible (self, added));
 		g_assert (nm_connection_compare (added, connection, NM_SETTING_COMPARE_FLAG_EXACT));
 
-		nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE);
+		nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED, TRUE);
 
 		priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
 		priv->pan_connection = added;
@@ -1186,7 +1186,7 @@ dispose (GObject *object)
 		/* Check whether we want to remove the created connection. If so, we take a reference
 		 * and delete it at the end of dispose(). */
 		if (NM_FLAGS_HAS (nm_settings_connection_get_flags (NM_SETTINGS_CONNECTION (priv->pan_connection)),
-		                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
+		                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED))
 			to_delete = g_object_ref (priv->pan_connection);
 
 		priv->pan_connection = NULL;
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index c8ef7a27..0f19f998 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include "nm-dbus-interface.h"
+#include "nm-utils/nm-hash-utils.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 #include "nm-core-internal.h"
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 5d3bd23a..de3f4072 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -30,7 +30,7 @@
 
 #include "nm-core-internal.h"
 
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 #include "devices/nm-device-bridge.h"
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 1d237d92..977c1e1a 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -43,6 +43,8 @@
 #include "devices/wwan/nm-modem-manager.h"
 #include "devices/wwan/nm-modem.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Bluetooth.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBt);
 
@@ -215,7 +217,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
@@ -538,16 +540,11 @@ modem_ip4_config_result (NMModem *modem,
 }
 
 static void
-ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
+	NMDevice *self = NM_DEVICE (user_data);
 
-	if (!nm_device_set_ip_ifindex (device,
-	                               nm_modem_get_ip_ifindex (modem))) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	}
+	nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
 }
 
 static gboolean
@@ -643,24 +640,29 @@ component_added (NMDevice *device, GObject *component)
 	NMDeviceBt *self = NM_DEVICE_BT (device);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 	NMModem *modem;
+	const gchar *modem_data_port;
+	const gchar *modem_control_port;
+	char *base;
 	NMDeviceState state;
 	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (   !component
-	    || !NM_IS_MODEM (component))
+	if (!component || !NM_IS_MODEM (component))
 		return FALSE;
-
 	modem = NM_MODEM (component);
+
+	modem_data_port = nm_modem_get_data_port (modem);
+	modem_control_port = nm_modem_get_control_port (modem);
+	g_return_val_if_fail (modem_data_port != NULL || modem_control_port != NULL, FALSE);
+
 	if (!priv->rfcomm_iface)
 		return FALSE;
 
-	{
-		gs_free char *base = NULL;
-
-		base = g_path_get_basename (priv->rfcomm_iface);
-		if (!nm_streq (base, nm_modem_get_control_port (modem)))
-			return FALSE;
+	base = g_path_get_basename (priv->rfcomm_iface);
+	if (g_strcmp0 (base, modem_data_port) && g_strcmp0 (base, modem_control_port)) {
+		g_free (base);
+		return FALSE;
 	}
+	g_free (base);
 
 	/* Got the modem */
 	nm_clear_g_source (&priv->timeout_id);
@@ -694,7 +696,7 @@ component_added (NMDevice *device, GObject *component)
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
 	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
 
-	g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
+	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
 
 	/* Kick off the modem connection */
 	if (!modem_stage1 (self, modem, &failure_reason))
@@ -751,7 +753,7 @@ bluez_connect_cb (GObject *object,
                   GAsyncResult *res,
                   void *user_data)
 {
-	gs_unref_object NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 	GError *error = NULL;
 	const char *device;
@@ -759,9 +761,6 @@ bluez_connect_cb (GObject *object,
 	device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object),
 	                                         res, &error);
 
-	if (!nm_device_is_activating (NM_DEVICE (self)))
-		return;
-
 	if (!device) {
 		_LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message);
 		g_clear_error (&error);
@@ -769,6 +768,7 @@ bluez_connect_cb (GObject *object,
 		nm_device_state_changed (NM_DEVICE (self),
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_BT_FAILED);
+		g_object_unref (self);
 		return;
 	}
 
@@ -776,13 +776,7 @@ bluez_connect_cb (GObject *object,
 		g_free (priv->rfcomm_iface);
 		priv->rfcomm_iface = g_strdup (device);
 	} else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
-		if (!nm_device_set_ip_iface (NM_DEVICE (self), device)) {
-			_LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device);
-			nm_device_state_changed (NM_DEVICE (self),
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_BT_FAILED);
-			return;
-		}
+		nm_device_set_ip_iface (NM_DEVICE (self), device);
 	}
 
 	_LOGD (LOGD_BT, "connect request successful");
@@ -790,6 +784,7 @@ bluez_connect_cb (GObject *object,
 	/* Stage 3 gets scheduled when Bluez says we're connected */
 	priv->have_iface = TRUE;
 	check_connect_continue (self);
+	g_object_unref (self);
 }
 
 static void
@@ -1150,26 +1145,10 @@ finalize (GObject *object)
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_bluetooth = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_BLUETOOTH,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",      "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Name",           "s",  NM_DEVICE_BT_NAME),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("BtCapabilities", "u",  NM_DEVICE_BT_CAPABILITIES),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_bt_class_init (NMDeviceBtClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
@@ -1178,8 +1157,6 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bluetooth);
-
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->can_auto_connect = can_auto_connect;
 	device_class->deactivate = deactivate;
@@ -1223,4 +1200,8 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	                  G_TYPE_NONE, 2,
 	                  G_TYPE_UINT /*guint32 in_bytes*/,
 	                  G_TYPE_UINT /*guint32 out_bytes*/);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BLUETOOTH_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/meson.build b/src/devices/meson.build
deleted file mode 100644
index 2d874659..00000000
--- a/src/devices/meson.build
+++ /dev/null
@@ -1,22 +0,0 @@
-subdir('adsl')
-
-if enable_modem_manager
-  subdir('wwan')
-  subdir('bluetooth')
-endif
-
-if enable_wifi
-  subdir('wifi')
-endif
-
-if enable_teamdctl
-  subdir('team')
-endif
-
-if enable_ovs
-  subdir('ovs')
-endif
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c
deleted file mode 100644
index 1bade4ff..00000000
--- a/src/devices/nm-acd-manager.c
+++ /dev/null
@@ -1,489 +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.
- *
- * Copyright (C) 2015-2018 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-acd-manager.h"
-
-#include <netinet/in.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "platform/nm-platform.h"
-#include "nm-utils.h"
-#include "NetworkManagerUtils.h"
-#include "n-acd/src/n-acd.h"
-
-/*****************************************************************************/
-
-typedef enum {
-	STATE_INIT,
-	STATE_PROBING,
-	STATE_PROBE_DONE,
-	STATE_ANNOUNCING,
-} State;
-
-typedef struct {
-	in_addr_t address;
-	gboolean duplicate;
-	NMAcdManager *manager;
-	NAcd *acd;
-	GIOChannel *channel;
-	guint event_id;
-} AddressInfo;
-
-enum {
-	PROBE_TERMINATED,
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	int            ifindex;
-	guint8         hwaddr[ETH_ALEN];
-	State          state;
-	GHashTable    *addresses;
-	guint          completed;
-} NMAcdManagerPrivate;
-
-struct _NMAcdManager {
-	GObject parent;
-	NMAcdManagerPrivate _priv;
-};
-
-struct _NMAcdManagerClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT)
-
-#define NM_ACD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAcdManager, NM_IS_ACD_MANAGER)
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN         LOGD_IP4
-#define _NMLOG_PREFIX_NAME    "acd"
-#define _NMLOG(level, ...) \
-    G_STMT_START { \
-        char _sbuf[64]; \
-        int _ifindex = (self) ? NM_ACD_MANAGER_GET_PRIVATE (self)->ifindex : 0; \
-        \
-        nm_log ((level), _NMLOG_DOMAIN, \
-                nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \
-                NULL, \
-                "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                _NMLOG_PREFIX_NAME, \
-                self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \
-                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
-    } G_STMT_END
-
-/*****************************************************************************/
-
-static const char *
-_acd_event_to_string (unsigned int event)
-{
-	switch (event) {
-	case N_ACD_EVENT_READY:
-		return "ready";
-	case N_ACD_EVENT_USED:
-		return "used";
-	case N_ACD_EVENT_DEFENDED:
-		return "defended";
-	case N_ACD_EVENT_CONFLICT:
-		return "conflict";
-	case N_ACD_EVENT_DOWN:
-		return "down";
-	}
-	return NULL;
-}
-
-#define acd_event_to_string(event) NM_UTILS_LOOKUP_STR (_acd_event_to_string, event)
-
-static const char *
-_acd_error_to_string (int error)
-{
-	if (error < 0)
-		return strerror(-error);
-
-	switch (error) {
-	case _N_ACD_E_SUCCESS:
-		return "success";
-	case N_ACD_E_DONE:
-		return "no more events (engine running)";
-	case N_ACD_E_STOPPED:
-		return "no more events (engine stopped)";
-	case N_ACD_E_PREEMPTED:
-		return "preempted";
-	case N_ACD_E_INVALID_ARGUMENT:
-		return "invalid argument";
-	case N_ACD_E_BUSY:
-		return "busy";
-	}
-	return NULL;
-}
-
-#define acd_error_to_string(error) NM_UTILS_LOOKUP_STR (_acd_error_to_string, error)
-
-/*****************************************************************************/
-
-/**
- * nm_acd_manager_add_address:
- * @self: a #NMAcdManager
- * @address: an IP address
- *
- * Add @address to the list of IP addresses to probe.
-
- * Returns: %TRUE on success, %FALSE if the address was already in the list
- */
-gboolean
-nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address)
-{
-	NMAcdManagerPrivate *priv;
-	AddressInfo *info;
-
-	g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE);
-	priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	g_return_val_if_fail (priv->state == STATE_INIT, FALSE);
-
-	if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address)))
-		return FALSE;
-
-	info = g_slice_new0 (AddressInfo);
-	info->address = address;
-	info->manager = self;
-
-	g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info);
-
-	return TRUE;
-}
-
-static gboolean
-acd_event (GIOChannel *source, GIOCondition condition, gpointer data)
-{
-	AddressInfo *info = data;
-	NMAcdManager *self = info->manager;
-	NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	NAcdEvent *event;
-	char address_str[INET_ADDRSTRLEN];
-	gs_free char *hwaddr_str = NULL;
-	int r;
-
-	if (   n_acd_dispatch (info->acd)
-	    || n_acd_pop_event (info->acd, &event))
-		return G_SOURCE_CONTINUE;
-
-	switch (event->event) {
-	case N_ACD_EVENT_READY:
-		info->duplicate = FALSE;
-		if (priv->state == STATE_ANNOUNCING) {
-			r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE);
-			if (r) {
-				_LOGW ("couldn't announce address %s on interface '%s': %s",
-				       nm_utils_inet4_ntop (info->address, address_str),
-				       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex),
-				       acd_error_to_string (r));
-			} else {
-				_LOGD ("announcing address %s",
-				       nm_utils_inet4_ntop (info->address, address_str));
-			}
-		}
-		break;
-	case N_ACD_EVENT_USED:
-		info->duplicate = TRUE;
-		break;
-	case N_ACD_EVENT_DEFENDED:
-		_LOGD ("defended address %s from host %s",
-		       nm_utils_inet4_ntop (info->address, address_str),
-		       (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender,
-		                                           event->defended.n_sender)));
-		break;
-	case N_ACD_EVENT_CONFLICT:
-		_LOGW ("conflict for address %s detected with host %s on interface '%s'",
-		       nm_utils_inet4_ntop (info->address, address_str),
-		       (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender,
-		                                           event->defended.n_sender)),
-		       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex));
-		break;
-	default:
-		_LOGD ("event '%s' for address %s",
-		       acd_event_to_string (event->event),
-		       nm_utils_inet4_ntop (info->address, address_str));
-		return G_SOURCE_CONTINUE;
-	}
-
-	if (   priv->state == STATE_PROBING
-	    && ++priv->completed == g_hash_table_size (priv->addresses)) {
-		priv->state = STATE_PROBE_DONE;
-		g_signal_emit (self, signals[PROBE_TERMINATED], 0);
-	}
-
-	return G_SOURCE_CONTINUE;
-}
-
-static gboolean
-acd_probe_start (NMAcdManager *self,
-                 AddressInfo *info,
-                 guint64 timeout)
-{
-	NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	NAcdConfig *config;
-	int r, fd;
-
-	r = n_acd_new (&info->acd);
-	if (r) {
-		_LOGW ("could not create ACD for %s on interface '%s': %s",
-		       nm_utils_inet4_ntop (info->address, NULL),
-		       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex),
-		       acd_error_to_string (r));
-		return FALSE;
-	}
-
-	n_acd_get_fd (info->acd, &fd);
-	info->channel = g_io_channel_unix_new (fd);
-	info->event_id = g_io_add_watch (info->channel, G_IO_IN, acd_event, info);
-
-	config = &(NAcdConfig) {
-		.ifindex = priv->ifindex,
-		.mac = priv->hwaddr,
-		.n_mac = ETH_ALEN,
-		.ip = info->address,
-		.timeout_msec = timeout,
-		.transport = N_ACD_TRANSPORT_ETHERNET,
-	};
-
-	r = n_acd_start (info->acd, config);
-	if (r) {
-		_LOGW ("could not start probe for %s on interface '%s': %s",
-		       nm_utils_inet4_ntop (info->address, NULL),
-		       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex),
-		       acd_error_to_string (r));
-		return FALSE;
-	}
-
-	_LOGD ("start probe for %s", nm_utils_inet4_ntop (info->address, NULL));
-
-	return TRUE;
-}
-
-/**
- * nm_acd_manager_start_probe:
- * @self: a #NMAcdManager
- * @timeout: maximum probe duration in milliseconds
- * @error: location to store error, or %NULL
- *
- * Start probing IP addresses for duplicates; when the probe terminates a
- * PROBE_TERMINATED signal is emitted.
- *
- * Returns: %TRUE if at least one probe could be started, %FALSE otherwise
- */
-gboolean
-nm_acd_manager_start_probe (NMAcdManager *self, guint timeout)
-{
-	NMAcdManagerPrivate *priv;
-	GHashTableIter iter;
-	AddressInfo *info;
-	gboolean success = FALSE;
-
-	g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE);
-	priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	g_return_val_if_fail (priv->state == STATE_INIT, FALSE);
-
-	priv->completed = 0;
-
-	g_hash_table_iter_init (&iter, priv->addresses);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
-		success |= acd_probe_start (self, info, timeout);
-
-	if (success)
-		priv->state = STATE_PROBING;
-
-	return success;
-}
-
-/**
- * nm_acd_manager_reset:
- * @self: a #NMAcdManager
- *
- * Stop any operation in progress and reset @self to the initial state.
- */
-void
-nm_acd_manager_reset (NMAcdManager *self)
-{
-	NMAcdManagerPrivate *priv;
-
-	g_return_if_fail (NM_IS_ACD_MANAGER (self));
-	priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-
-	g_hash_table_remove_all (priv->addresses);
-
-	priv->state = STATE_INIT;
-}
-
-/**
- * nm_acd_manager_destroy:
- * @self: the #NMAcdManager
- *
- * Calls nm_acd_manager_reset() and unrefs @self.
- */
-void
-nm_acd_manager_destroy (NMAcdManager *self)
-{
-	g_return_if_fail (NM_IS_ACD_MANAGER (self));
-
-	nm_acd_manager_reset (self);
-	g_object_unref (self);
-}
-
-/**
- * nm_acd_manager_check_address:
- * @self: a #NMAcdManager
- * @address: an IP address
- *
- * Check if an IP address is duplicate. @address must have been added with
- * nm_acd_manager_add_address().
- *
- * Returns: %TRUE if the address is not duplicate, %FALSE otherwise
- */
-gboolean
-nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address)
-{
-	NMAcdManagerPrivate *priv;
-	AddressInfo *info;
-
-	g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE);
-	priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	g_return_val_if_fail (   priv->state == STATE_INIT
-	                      || priv->state == STATE_PROBE_DONE, FALSE);
-
-	info = g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address));
-	g_return_val_if_fail (info, FALSE);
-
-	return !info->duplicate;
-}
-
-/**
- * nm_acd_manager_announce_addresses:
- * @self: a #NMAcdManager
- *
- * Start announcing addresses.
- */
-void
-nm_acd_manager_announce_addresses (NMAcdManager *self)
-{
-	NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	AddressInfo *info;
-	int r;
-
-	if (priv->state == STATE_INIT) {
-		/* n-acd can't announce without probing, therefore let's
-		 * start a fake probe with zero timeout and then perform
-		 * the announce. */
-		priv->state = STATE_ANNOUNCING;
-		g_hash_table_iter_init (&iter, priv->addresses);
-		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
-			if (!acd_probe_start (self, info, 0)) {
-				_LOGW ("couldn't announce address %s on interface '%s'",
-				       nm_utils_inet4_ntop (info->address, NULL),
-				       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex));
-			}
-		}
-	} else if (priv->state == STATE_PROBE_DONE) {
-		priv->state = STATE_ANNOUNCING;
-		g_hash_table_iter_init (&iter, priv->addresses);
-		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
-			if (info->duplicate)
-				continue;
-			r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE);
-			if (r) {
-				_LOGW ("couldn't announce address %s on interface '%s': %s",
-				       nm_utils_inet4_ntop (info->address, NULL),
-				       nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex),
-				       acd_error_to_string (r));
-			} else
-				_LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, NULL));
-		}
-	} else
-		nm_assert_not_reached ();
-}
-
-static void
-destroy_address_info (gpointer data)
-{
-	AddressInfo *info = (AddressInfo *) data;
-
-	g_clear_pointer (&info->channel, g_io_channel_unref);
-	g_clear_pointer (&info->acd, n_acd_free);
-	nm_clear_g_source (&info->event_id);
-
-	g_slice_free (AddressInfo, info);
-}
-
-/*****************************************************************************/
-
-static void
-nm_acd_manager_init (NMAcdManager *self)
-{
-	NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-
-	priv->addresses = g_hash_table_new_full (nm_direct_hash, NULL,
-	                                         NULL, destroy_address_info);
-	priv->state = STATE_INIT;
-}
-
-NMAcdManager *
-nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len)
-{
-	NMAcdManager *self;
-	NMAcdManagerPrivate *priv;
-
-	g_return_val_if_fail (hwaddr, NULL);
-	g_return_val_if_fail (hwaddr_len == ETH_ALEN, NULL);
-
-	self = g_object_new (NM_TYPE_ACD_MANAGER, NULL);
-	priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-	priv->ifindex = ifindex;
-	memcpy (priv->hwaddr, hwaddr, ETH_ALEN);
-
-	return self;
-}
-
-static void
-dispose (GObject *object)
-{
-	NMAcdManager *self = NM_ACD_MANAGER (object);
-	NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self);
-
-	g_clear_pointer (&priv->addresses, g_hash_table_destroy);
-
-	G_OBJECT_CLASS (nm_acd_manager_parent_class)->dispose (object);
-}
-
-static void
-nm_acd_manager_class_init (NMAcdManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = dispose;
-
-	signals[PROBE_TERMINATED] =
-	    g_signal_new (NM_ACD_MANAGER_PROBE_TERMINATED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 0);
-}
diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h
deleted file mode 100644
index eeede5da..00000000
--- a/src/devices/nm-acd-manager.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.
- *
- * Copyright (C) 2015-2018 Red Hat, Inc.
- */
-
-#ifndef __NM_ACD_MANAGER__
-#define __NM_ACD_MANAGER__
-
-#include <netinet/in.h>
-
-#define NM_TYPE_ACD_MANAGER            (nm_acd_manager_get_type ())
-#define NM_ACD_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACD_MANAGER, NMAcdManager))
-#define NM_ACD_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_ACD_MANAGER, NMAcdManagerClass))
-#define NM_IS_ACD_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACD_MANAGER))
-#define NM_IS_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_ACD_MANAGER))
-#define NM_ACD_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_ACD_MANAGER, NMAcdManagerClass))
-
-#define NM_ACD_MANAGER_PROBE_TERMINATED  "probe-terminated"
-
-typedef struct _NMAcdManagerClass NMAcdManagerClass;
-
-GType nm_acd_manager_get_type (void);
-
-NMAcdManager *nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len);
-void nm_acd_manager_destroy (NMAcdManager *self);
-gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address);
-gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout);
-gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address);
-void nm_acd_manager_announce_addresses (NMAcdManager *self);
-void nm_acd_manager_reset (NMAcdManager *self);
-
-#endif /* __NM_ACD_MANAGER__ */
diff --git a/src/devices/nm-arping-manager.c b/src/devices/nm-arping-manager.c
new file mode 100644
index 00000000..51f80e08
--- /dev/null
+++ b/src/devices/nm-arping-manager.c
@@ -0,0 +1,474 @@
+/* -*- 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.
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-arping-manager.h"
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "platform/nm-platform.h"
+#include "nm-utils.h"
+#include "NetworkManagerUtils.h"
+
+/*****************************************************************************/
+
+typedef enum {
+	STATE_INIT,
+	STATE_PROBING,
+	STATE_PROBE_DONE,
+	STATE_ANNOUNCING,
+} State;
+
+typedef struct {
+	in_addr_t address;
+	GPid pid;
+	guint watch;
+	gboolean duplicate;
+	NMArpingManager *manager;
+} AddressInfo;
+
+/*****************************************************************************/
+
+enum {
+	PROBE_TERMINATED,
+	LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+	int            ifindex;
+	State          state;
+	GHashTable    *addresses;
+	guint          completed;
+	guint          timer;
+	guint          round2_id;
+} NMArpingManagerPrivate;
+
+struct _NMArpingManager {
+	GObject parent;
+	NMArpingManagerPrivate _priv;
+};
+
+struct _NMArpingManagerClass {
+	GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMArpingManager, nm_arping_manager, G_TYPE_OBJECT)
+
+#define NM_ARPING_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMArpingManager, NM_IS_ARPING_MANAGER)
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN         LOGD_IP4
+#define _NMLOG_PREFIX_NAME    "arping"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        char _sbuf[64]; \
+        int _ifindex = (self) ? NM_ARPING_MANAGER_GET_PRIVATE (self)->ifindex : 0; \
+        \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \
+                NULL, \
+                "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \
+                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+    } G_STMT_END
+
+/*****************************************************************************/
+
+/**
+ * nm_arping_manager_add_address:
+ * @self: a #NMArpingManager
+ * @address: an IP address
+ *
+ * Add @address to the list of IP addresses to probe.
+
+ * Returns: %TRUE on success, %FALSE if the address was already in the list
+ */
+gboolean
+nm_arping_manager_add_address (NMArpingManager *self, in_addr_t address)
+{
+	NMArpingManagerPrivate *priv;
+	AddressInfo *info;
+
+	g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE);
+	priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->state == STATE_INIT, FALSE);
+
+	if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address))) {
+		_LOGD ("address already exists");
+		return FALSE;
+	}
+
+	info = g_slice_new0 (AddressInfo);
+	info->address = address;
+	info->manager = self;
+
+	g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info);
+
+	return TRUE;
+}
+
+static void
+arping_watch_cb (GPid pid, gint status, gpointer user_data)
+{
+	AddressInfo *info = user_data;
+	NMArpingManager *self = info->manager;
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	const char *addr;
+
+	info->pid = 0;
+	info->watch = 0;
+	addr = nm_utils_inet4_ntop (info->address, NULL);
+
+	if (WIFEXITED (status)) {
+		if (WEXITSTATUS (status) != 0) {
+			_LOGD ("%s already used in the %s network",
+			       addr, nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex));
+			info->duplicate = TRUE;
+		} else
+			_LOGD ("DAD succeeded for %s", addr);
+	} else {
+		_LOGD ("stopped unexpectedly with status %d for %s", status, addr);
+	}
+
+	if (++priv->completed == g_hash_table_size (priv->addresses)) {
+		priv->state = STATE_PROBE_DONE;
+		nm_clear_g_source (&priv->timer);
+		g_signal_emit (self, signals[PROBE_TERMINATED], 0);
+	}
+}
+
+static gboolean
+arping_timeout_cb (gpointer user_data)
+{
+	NMArpingManager *self = user_data;
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	GHashTableIter iter;
+	AddressInfo *info;
+
+	priv->timer = 0;
+
+	g_hash_table_iter_init (&iter, priv->addresses);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
+		nm_clear_g_source (&info->watch);
+		if (info->pid) {
+			_LOGD ("DAD timed out for %s",
+			       nm_utils_inet4_ntop (info->address, NULL));
+			nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4,
+			                           "arping", 1000, NULL, NULL);
+			info->pid = 0;
+		}
+	}
+
+	priv->state = STATE_PROBE_DONE;
+	g_signal_emit (self, signals[PROBE_TERMINATED], 0);
+
+	return G_SOURCE_REMOVE;
+}
+
+/**
+ * nm_arping_manager_start_probe:
+ * @self: a #NMArpingManager
+ * @timeout: maximum probe duration in milliseconds
+ * @error: location to store error, or %NULL
+ *
+ * Start probing IP addresses for duplicates; when the probe terminates a
+ * PROBE_TERMINATED signal is emitted.
+ *
+ * Returns: %TRUE if at least one probe could be started, %FALSE otherwise
+ */
+gboolean
+nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error)
+{
+	const char *argv[] = { NULL, "-D", "-q", "-I", NULL, "-c", NULL, "-w", NULL, NULL, NULL };
+	NMArpingManagerPrivate *priv;
+	GHashTableIter iter;
+	AddressInfo *info;
+	gs_free char *timeout_str = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+	g_return_val_if_fail (timeout, FALSE);
+
+	priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	g_return_val_if_fail (priv->state == STATE_INIT, FALSE);
+
+	argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex);
+	if (!argv[4]) {
+		/* The device was probably just removed. */
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "can't find a name for ifindex %d", priv->ifindex);
+		return FALSE;
+	}
+
+	priv->completed = 0;
+
+	argv[0] = nm_utils_find_helper ("arping", NULL, NULL);
+	if (!argv[0]) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "arping could not be found");
+		return FALSE;
+	}
+
+	timeout_str = g_strdup_printf ("%u", timeout / 1000 + 2);
+	argv[6] = timeout_str;
+	argv[8] = timeout_str;
+
+	g_hash_table_iter_init (&iter, priv->addresses);
+
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
+		gs_free char *tmp_str = NULL;
+
+		argv[9] = nm_utils_inet4_ntop (info->address, NULL);
+		_LOGD ("run %s", (tmp_str = g_strjoinv (" ", (char **) argv)));
+
+		if (g_spawn_async (NULL, (char **) argv, NULL,
+		                   G_SPAWN_STDOUT_TO_DEV_NULL |
+		                   G_SPAWN_STDERR_TO_DEV_NULL |
+		                   G_SPAWN_DO_NOT_REAP_CHILD,
+		                   NULL, NULL, &info->pid, NULL)) {
+			info->watch = g_child_watch_add (info->pid, arping_watch_cb, info);
+			success = TRUE;
+		}
+	}
+
+	if (success) {
+		priv->timer = g_timeout_add (timeout, arping_timeout_cb, self);
+		priv->state = STATE_PROBING;
+	} else {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "could not spawn arping process");
+	}
+
+	return success;
+}
+
+/**
+ * nm_arping_manager_reset:
+ * @self: a #NMArpingManager
+ *
+ * Stop any operation in progress and reset @self to the initial state.
+ */
+void
+nm_arping_manager_reset (NMArpingManager *self)
+{
+	NMArpingManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_ARPING_MANAGER (self));
+	priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+
+	nm_clear_g_source (&priv->timer);
+	nm_clear_g_source (&priv->round2_id);
+	g_hash_table_remove_all (priv->addresses);
+
+	priv->state = STATE_INIT;
+}
+
+/**
+ * nm_arping_manager_destroy:
+ * @self: the #NMArpingManager
+ *
+ * Calls nm_arping_manager_reset() and unrefs @self.
+ */
+void
+nm_arping_manager_destroy (NMArpingManager *self)
+{
+	g_return_if_fail (NM_IS_ARPING_MANAGER (self));
+
+	nm_arping_manager_reset (self);
+	g_object_unref (self);
+}
+
+/**
+ * nm_arping_manager_check_address:
+ * @self: a #NMArpingManager
+ * @address: an IP address
+ *
+ * Check if an IP address is duplicate. @address must have been added with
+ * nm_arping_manager_add_address().
+ *
+ * Returns: %TRUE if the address is not duplicate, %FALSE otherwise
+ */
+gboolean
+nm_arping_manager_check_address (NMArpingManager *self, in_addr_t address)
+{
+	NMArpingManagerPrivate *priv;
+	AddressInfo *info;
+
+	g_return_val_if_fail (NM_IS_ARPING_MANAGER (self), FALSE);
+	priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	g_return_val_if_fail (   priv->state == STATE_INIT
+	                      || priv->state == STATE_PROBE_DONE, FALSE);
+
+	info = g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address));
+	g_return_val_if_fail (info, FALSE);
+
+	return !info->duplicate;
+}
+
+static void
+send_announcements (NMArpingManager *self, const char *mode_arg)
+{
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	const char *argv[] = { NULL, mode_arg, "-q", "-I", NULL, "-c", "1", NULL, NULL };
+	int ip_arg = G_N_ELEMENTS (argv) - 2;
+	GError *error = NULL;
+	GHashTableIter iter;
+	AddressInfo *info;
+
+	argv[4] = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex);
+	if (!argv[4]) {
+		/* The device was probably just removed. */
+		_LOGW ("can't find a name for ifindex %d", priv->ifindex);
+		return;
+	}
+
+	argv[0] = nm_utils_find_helper ("arping", NULL, NULL);
+	if (!argv[0]) {
+		_LOGW ("arping could not be found; no ARPs will be sent");
+		return;
+	}
+
+	g_hash_table_iter_init (&iter, priv->addresses);
+
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
+		gs_free char *tmp_str = NULL;
+		gboolean success;
+
+		if (info->duplicate)
+			continue;
+
+		argv[ip_arg] = nm_utils_inet4_ntop (info->address, NULL);
+		_LOGD ("run %s", (tmp_str = g_strjoinv (" ", (char **) argv)));
+
+		success = g_spawn_async (NULL, (char **) argv, NULL,
+		                         G_SPAWN_STDOUT_TO_DEV_NULL |
+		                         G_SPAWN_STDERR_TO_DEV_NULL,
+		                         NULL, NULL, NULL, &error);
+		if (!success) {
+			_LOGW ("could not send ARP for address %s: %s", argv[ip_arg],
+			       error->message);
+			g_clear_error (&error);
+		}
+	}
+}
+
+static gboolean
+arp_announce_round2 (gpointer self)
+{
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE ((NMArpingManager *) self);
+
+	priv->round2_id = 0;
+	send_announcements (self, "-U");
+	priv->state = STATE_INIT;
+	g_hash_table_remove_all (priv->addresses);
+
+	return G_SOURCE_REMOVE;
+}
+
+/**
+ * nm_arping_manager_announce_addresses:
+ * @self: a #NMArpingManager
+ *
+ * Start announcing addresses.
+ */
+void
+nm_arping_manager_announce_addresses (NMArpingManager *self)
+{
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (   priv->state == STATE_INIT
+	                  || priv->state == STATE_PROBE_DONE);
+
+	send_announcements (self, "-A");
+	nm_clear_g_source (&priv->round2_id);
+	priv->round2_id = g_timeout_add_seconds (2, arp_announce_round2, self);
+	priv->state = STATE_ANNOUNCING;
+}
+
+static void
+destroy_address_info (gpointer data)
+{
+	AddressInfo *info = (AddressInfo *) data;
+
+	nm_clear_g_source (&info->watch);
+
+	if (info->pid) {
+		nm_utils_kill_child_async (info->pid, SIGTERM, LOGD_IP4, "arping",
+		                           1000, NULL, NULL);
+	}
+
+	g_slice_free (AddressInfo, info);
+}
+
+/*****************************************************************************/
+
+static void
+nm_arping_manager_init (NMArpingManager *self)
+{
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+
+	priv->addresses = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                         NULL, destroy_address_info);
+	priv->state = STATE_INIT;
+}
+
+NMArpingManager *
+nm_arping_manager_new (int ifindex)
+{
+	NMArpingManager *self;
+	NMArpingManagerPrivate *priv;
+
+	self = g_object_new (NM_TYPE_ARPING_MANAGER, NULL);
+	priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+	priv->ifindex = ifindex;
+	return self;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMArpingManager *self = NM_ARPING_MANAGER (object);
+	NMArpingManagerPrivate *priv = NM_ARPING_MANAGER_GET_PRIVATE (self);
+
+	nm_clear_g_source (&priv->timer);
+	nm_clear_g_source (&priv->round2_id);
+	g_clear_pointer (&priv->addresses, g_hash_table_destroy);
+
+	G_OBJECT_CLASS (nm_arping_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_arping_manager_class_init (NMArpingManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = dispose;
+
+	signals[PROBE_TERMINATED] =
+	    g_signal_new (NM_ARPING_MANAGER_PROBE_TERMINATED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
+}
diff --git a/src/devices/nm-arping-manager.h b/src/devices/nm-arping-manager.h
new file mode 100644
index 00000000..c8a86af0
--- /dev/null
+++ b/src/devices/nm-arping-manager.h
@@ -0,0 +1,43 @@
+/* -*- 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.
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_ARPING_MANAGER__
+#define __NM_ARPING_MANAGER__
+
+#include <netinet/in.h>
+
+#define NM_TYPE_ARPING_MANAGER            (nm_arping_manager_get_type ())
+#define NM_ARPING_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ARPING_MANAGER, NMArpingManager))
+#define NM_ARPING_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_ARPING_MANAGER, NMArpingManagerClass))
+#define NM_IS_ARPING_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ARPING_MANAGER))
+#define NM_IS_ARPING_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_ARPING_MANAGER))
+#define NM_ARPING_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_ARPING_MANAGER, NMArpingManagerClass))
+
+#define NM_ARPING_MANAGER_PROBE_TERMINATED  "probe-terminated"
+
+typedef struct _NMArpingManagerClass NMArpingManagerClass;
+
+GType nm_arping_manager_get_type (void);
+
+NMArpingManager *nm_arping_manager_new (int ifindex);
+void nm_arping_manager_destroy (NMArpingManager *self);
+gboolean nm_arping_manager_add_address (NMArpingManager *self, in_addr_t address);
+gboolean nm_arping_manager_start_probe (NMArpingManager *self, guint timeout, GError **error);
+gboolean nm_arping_manager_check_address (NMArpingManager *self, in_addr_t address);
+void nm_arping_manager_announce_addresses (NMArpingManager *self);
+void nm_arping_manager_reset (NMArpingManager *self);
+
+#endif /* __NM_ARPING_MANAGER__ */
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 2dd9494a..910dd0bf 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -32,6 +32,8 @@
 #include "nm-core-internal.h"
 #include "nm-ip4-config.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Bond.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBond);
 
@@ -76,7 +78,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingBond *s_bond;
@@ -605,31 +607,13 @@ nm_device_bond_init (NMDeviceBond * self)
 	nm_assert (nm_device_is_master (NM_DEVICE (self)));
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_bond = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_BOND,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",   "b",  NM_DEVICE_CARRIER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves",    "ao", NM_DEVICE_SLAVES),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_bond_class_init (NMDeviceBondClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND)
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bond);
-
 	parent_class->is_master = TRUE;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->check_connection_compatible = check_connection_compatible;
@@ -645,6 +629,10 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->release_slave = release_slave;
 	parent_class->can_reapply_change = can_reapply_change;
 	parent_class->reapply_connection = reapply_connection;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BOND_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index c81a0253..74689aef 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -30,6 +30,8 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Bridge.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBridge);
 
@@ -115,7 +117,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingBridge *s_bridge;
@@ -486,31 +488,13 @@ nm_device_bridge_init (NMDeviceBridge * self)
 	nm_assert (nm_device_is_master (NM_DEVICE (self)));
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_bridge = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_BRIDGE,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",   "b",  NM_DEVICE_CARRIER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves",    "ao", NM_DEVICE_SLAVES),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE)
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bridge);
-
 	parent_class->is_master = TRUE;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->check_connection_compatible = check_connection_compatible;
@@ -527,6 +511,10 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_BRIDGE_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c
index f8bc8e75..085c44e6 100644
--- a/src/devices/nm-device-dummy.c
+++ b/src/devices/nm-device-dummy.c
@@ -28,6 +28,8 @@
 #include "nm-setting-dummy.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Dummy.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceDummy);
 
@@ -55,7 +57,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingDummy *s_dummy;
@@ -154,29 +156,13 @@ nm_device_dummy_init (NMDeviceDummy *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_dummy = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_DUMMY,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s",  NM_DEVICE_HW_ADDRESS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_dummy_class_init (NMDeviceDummyClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_DUMMY)
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_dummy);
-
 	device_class->connection_type = NM_SETTING_DUMMY_SETTING_NAME;
 	device_class->complete_connection = complete_connection;
 	device_class->check_connection_compatible = check_connection_compatible;
@@ -185,6 +171,10 @@ nm_device_dummy_class_init (NMDeviceDummyClass *klass)
 	device_class->update_connection = update_connection;
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_DUMMY_SKELETON,
+	                                        NULL);
 }
 
 
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 9b46545b..3ca86e06 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
+
 #include <libudev.h>
 
 #include "nm-device-private.h"
@@ -50,7 +51,8 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "nm-utils/nm-udev-utils.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.Wired.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceEthernet);
@@ -548,7 +550,7 @@ build_supplicant_config (NMDeviceEthernet *self,
 	mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 	                                nm_device_get_ifindex (NM_DEVICE (self)));
 
-	config = nm_supplicant_config_new (FALSE, FALSE);
+	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, error)) {
@@ -656,9 +658,12 @@ handle_auth_or_fail (NMDeviceEthernet *self,
                      NMActRequest *req,
                      gboolean new_secrets)
 {
+	NMDeviceEthernetPrivate *priv;
 	const char *setting_name;
 	NMConnection *applied_connection;
 
+	priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
 	if (!nm_device_auth_retries_try_next (NM_DEVICE (self)))
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
@@ -668,14 +673,13 @@ handle_auth_or_fail (NMDeviceEthernet *self,
 
 	applied_connection = nm_act_request_get_applied_connection (req);
 	setting_name = nm_connection_need_secrets (applied_connection, NULL);
-	if (!setting_name) {
+	if (setting_name) {
+		wired_secrets_get_secrets (self, setting_name,
+		                           NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+		                             | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
+	} else
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
 
-	wired_secrets_get_secrets (self, setting_name,
-	                             NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
-	                           | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
@@ -738,7 +742,7 @@ supplicant_interface_init (NMDeviceEthernet *self)
 		return FALSE;
 	}
 
-	/* Listen for its state signals */
+	/* Listen for it's state signals */
 	priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface,
 	                                                    NM_SUPPLICANT_INTERFACE_STATE,
 	                                                    G_CALLBACK (supplicant_iface_state_cb),
@@ -952,22 +956,8 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
-ppp_ifindex_set (NMPPPManager *ppp_manager,
-                 int ifindex,
-                 const char *iface,
-                 gpointer user_data)
-{
-	NMDevice *device = NM_DEVICE (user_data);
-
-	if (!nm_device_set_ip_ifindex (device, ifindex)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-	}
-}
-
-static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
@@ -975,6 +965,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 
 	/* Ignore PPP IP4 events that come in after initial configuration */
 	if (nm_device_activate_ip4_state_in_conf (device)) {
+		nm_device_set_ip_iface (device, iface);
 		nm_device_activate_schedule_ip4_config_result (device, config);
 	}
 }
@@ -1021,9 +1012,6 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
 	                  G_CALLBACK (ppp_state_changed),
 	                  self);
-	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_CALLBACK (ppp_ifindex_set),
-	                  self);
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_CALLBACK (ppp_ip4_config),
 	                  self);
@@ -1373,7 +1361,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingWired *s_wired;
@@ -1437,9 +1425,7 @@ new_default_connection (NMDevice *self)
 	NMConnection *connection;
 	NMSettingsConnection *const*connections;
 	NMSetting *setting;
-	struct udev_device *dev;
 	const char *perm_hw_addr;
-	const char *uprop = "0";
 	gs_free char *defname = NULL;
 	gs_free char *uuid = NULL;
 	gs_free char *machine_id = NULL;
@@ -1484,26 +1470,6 @@ new_default_connection (NMDevice *self)
 	g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL);
 	nm_connection_add_setting (connection, setting);
 
-	/* Check if we should create a Link-Local only connection */
-	dev = nm_platform_link_get_udev_device (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ip_ifindex (self));
-	if (dev)
-		uprop = udev_device_get_property_value (dev, "NM_AUTO_DEFAULT_LINK_LOCAL_ONLY");
-
-	if (nm_udev_utils_property_as_boolean (uprop)) {
-		setting = nm_setting_ip4_config_new ();
-		g_object_set (setting,
-		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
-		              NULL);
-		nm_connection_add_setting (connection, setting);
-
-		setting = nm_setting_ip6_config_new ();
-		g_object_set (setting,
-		              NM_SETTING_IP_CONFIG_METHOD,  NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
-		              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
-		              NULL);
-		nm_connection_add_setting (connection, setting);
-	}
-
 	return connection;
 }
 
@@ -1735,28 +1701,10 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_wired = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_WIRED,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",       "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PermHwAddress",   "s",  NM_DEVICE_PERM_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Speed",           "u",  NM_DEVICE_ETHERNET_SPEED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("S390Subchannels", "as", NM_DEVICE_ETHERNET_S390_SUBCHANNELS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",         "b",  NM_DEVICE_CARRIER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate));
@@ -1768,8 +1716,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wired);
-
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
@@ -1803,6 +1749,10 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	                        G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_ETHERNET_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h
index 22fae293..a50a7d85 100644
--- a/src/devices/nm-device-ethernet.h
+++ b/src/devices/nm-device-ethernet.h
@@ -24,12 +24,12 @@
 
 #include "nm-device.h"
 
-#define NM_TYPE_DEVICE_ETHERNET                 (nm_device_ethernet_get_type ())
-#define NM_DEVICE_ETHERNET(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet))
-#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
-#define NM_IS_DEVICE_ETHERNET(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET))
-#define NM_IS_DEVICE_ETHERNET_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ETHERNET))
-#define NM_DEVICE_ETHERNET_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+#define NM_TYPE_DEVICE_ETHERNET			(nm_device_ethernet_get_type ())
+#define NM_DEVICE_ETHERNET(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet))
+#define NM_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+#define NM_IS_DEVICE_ETHERNET(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET))
+#define NM_IS_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ETHERNET))
+#define NM_DEVICE_ETHERNET_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
 
 #define NM_DEVICE_ETHERNET_SPEED "speed"
 #define NM_DEVICE_ETHERNET_S390_SUBCHANNELS "s390-subchannels"
@@ -37,12 +37,12 @@
 struct _NMDeviceEthernetPrivate;
 
 typedef struct {
-        NMDevice parent;
-        struct _NMDeviceEthernetPrivate *_priv;
+	NMDevice parent;
+	struct _NMDeviceEthernetPrivate *_priv;
 } NMDeviceEthernet;
 
 typedef struct {
-        NMDeviceClass parent;
+	NMDeviceClass parent;
 } NMDeviceEthernetClass;
 
 GType nm_device_ethernet_get_type (void);
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index d8e30346..97f011c5 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -340,16 +340,42 @@ factories_list_unref (GSList *list)
 	g_slist_free_full (list, g_object_unref);
 }
 
-static void
-load_factories_from_dir (const char *dirname,
-                         NMDeviceFactoryManagerFactoryFunc callback,
-                         gpointer user_data)
+void
+nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback,
+                                          gpointer user_data)
 {
 	NMDeviceFactory *factory;
 	GError *error = NULL;
 	char **path, **paths;
 
-	paths = nm_utils_read_plugin_paths (dirname, PLUGIN_PREFIX);
+	g_return_if_fail (factories_by_link == NULL);
+	g_return_if_fail (factories_by_setting == NULL);
+
+	factories_by_link = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+	factories_by_setting = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, (GDestroyNotify) factories_list_unref);
+
+#define _ADD_INTERNAL(get_type_fcn) \
+	G_STMT_START { \
+		GType get_type_fcn (void); \
+		_load_internal_factory (get_type_fcn (), \
+		                        callback, user_data); \
+	} G_STMT_END
+
+	_ADD_INTERNAL (nm_bond_device_factory_get_type);
+	_ADD_INTERNAL (nm_bridge_device_factory_get_type);
+	_ADD_INTERNAL (nm_dummy_device_factory_get_type);
+	_ADD_INTERNAL (nm_ethernet_device_factory_get_type);
+	_ADD_INTERNAL (nm_infiniband_device_factory_get_type);
+	_ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type);
+	_ADD_INTERNAL (nm_macsec_device_factory_get_type);
+	_ADD_INTERNAL (nm_macvlan_device_factory_get_type);
+	_ADD_INTERNAL (nm_ppp_device_factory_get_type);
+	_ADD_INTERNAL (nm_tun_device_factory_get_type);
+	_ADD_INTERNAL (nm_veth_device_factory_get_type);
+	_ADD_INTERNAL (nm_vlan_device_factory_get_type);
+	_ADD_INTERNAL (nm_vxlan_device_factory_get_type);
+
+	paths = nm_utils_read_plugin_paths (NMPLUGINDIR, PLUGIN_PREFIX);
 	if (!paths)
 		return;
 
@@ -394,36 +420,3 @@ load_factories_from_dir (const char *dirname,
 	g_strfreev (paths);
 }
 
-void
-nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback,
-                                          gpointer user_data)
-{
-	g_return_if_fail (factories_by_link == NULL);
-	g_return_if_fail (factories_by_setting == NULL);
-
-	factories_by_link = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_object_unref);
-	factories_by_setting = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, (GDestroyNotify) factories_list_unref);
-
-#define _ADD_INTERNAL(get_type_fcn) \
-	G_STMT_START { \
-		GType get_type_fcn (void); \
-		_load_internal_factory (get_type_fcn (), \
-		                        callback, user_data); \
-	} G_STMT_END
-
-	_ADD_INTERNAL (nm_bond_device_factory_get_type);
-	_ADD_INTERNAL (nm_bridge_device_factory_get_type);
-	_ADD_INTERNAL (nm_dummy_device_factory_get_type);
-	_ADD_INTERNAL (nm_ethernet_device_factory_get_type);
-	_ADD_INTERNAL (nm_infiniband_device_factory_get_type);
-	_ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type);
-	_ADD_INTERNAL (nm_macsec_device_factory_get_type);
-	_ADD_INTERNAL (nm_macvlan_device_factory_get_type);
-	_ADD_INTERNAL (nm_ppp_device_factory_get_type);
-	_ADD_INTERNAL (nm_tun_device_factory_get_type);
-	_ADD_INTERNAL (nm_veth_device_factory_get_type);
-	_ADD_INTERNAL (nm_vlan_device_factory_get_type);
-	_ADD_INTERNAL (nm_vxlan_device_factory_get_type);
-
-	load_factories_from_dir (NMPLUGINDIR, callback, user_data);
-}
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 27eaf8d6..f6a670b2 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -26,6 +26,8 @@
 #include "platform/nm-platform.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Generic.h"
+
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
@@ -201,25 +203,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_generic_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_generic = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_GENERIC,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",       "s", NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("TypeDescription", "s", NM_DEVICE_GENERIC_TYPE_DESCRIPTION),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_generic_class_init (NMDeviceGenericClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_GENERIC_SETTING_NAME, NM_LINK_TYPE_ANY)
@@ -229,8 +216,6 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_generic);
-
 	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->get_type_description = get_type_description;
@@ -244,4 +229,8 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_GENERIC_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 781bbd69..09ad2855 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -32,6 +32,8 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Infiniband.h"
+
 #define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition"
 
 /*****************************************************************************/
@@ -175,7 +177,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingInfiniband *s_infiniband;
@@ -366,25 +368,10 @@ nm_device_infiniband_init (NMDeviceInfiniband * self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_infiniband = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_INFINIBAND,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",       "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",         "b",  NM_DEVICE_CARRIER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_INFINIBAND_SETTING_NAME, NM_LINK_TYPE_INFINIBAND)
@@ -392,8 +379,6 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_infiniband);
-
 	parent_class->create_and_realize = create_and_realize;
 	parent_class->unrealize = unrealize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
@@ -411,6 +396,10 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	                           G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_INFINIBAND_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
@@ -444,7 +433,7 @@ create_device (NMDeviceFactory *factory,
 	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
 	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_INFINIBAND,
-	                                  /* NOTE: Partition should probably be a different link type! */
+	                                  /* XXX: Partition should probably be a different link type! */
 	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, is_partition,
 	                                  NULL);
 }
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 59ca9ed5..af3cfe4c 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -37,6 +37,8 @@
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.IPTunnel.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceIPTunnel);
 
@@ -53,7 +55,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIPTunnel,
 	PROP_OUTPUT_KEY,
 	PROP_ENCAPSULATION_LIMIT,
 	PROP_FLOW_LABEL,
-	PROP_FLAGS,
 );
 
 typedef struct {
@@ -68,7 +69,6 @@ typedef struct {
 	char *output_key;
 	guint8 encap_limit;
 	guint32 flow_label;
-	NMIPTunnelFlags flags;
 } NMDeviceIPTunnelPrivate;
 
 struct _NMDeviceIPTunnel {
@@ -86,30 +86,6 @@ G_DEFINE_TYPE (NMDeviceIPTunnel, nm_device_ip_tunnel, NM_TYPE_DEVICE)
 
 /*****************************************************************************/
 
-static guint32
-ip6tnl_flags_setting_to_plat (NMIPTunnelFlags flags)
-{
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT    == IP6_TNL_F_IGN_ENCAP_LIMIT);
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS    == IP6_TNL_F_USE_ORIG_TCLASS);
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL == IP6_TNL_F_USE_ORIG_FLOWLABEL);
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV           == IP6_TNL_F_MIP6_DEV);
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY      == IP6_TNL_F_RCV_DSCP_COPY);
-	G_STATIC_ASSERT (NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK    == IP6_TNL_F_USE_ORIG_FWMARK);
-
-	/* NOTE: "accidentally", the numeric values correspond.
-	 *       For flags added in the future, that might no longer
-	 *       be the case. */
-	return flags & _NM_IP_TUNNEL_FLAG_ALL_IP6TNL;
-}
-
-static NMIPTunnelFlags
-ip6tnl_flags_plat_to_setting (guint32 flags)
-{
-	return flags & ((guint32) _NM_IP_TUNNEL_FLAG_ALL_IP6TNL);
-}
-
-/*****************************************************************************/
-
 static gboolean
 address_equal_pp (int family, const char *a, const char *b)
 {
@@ -153,7 +129,6 @@ update_properties_from_ifindex (NMDevice *device, int ifindex)
 	guint8 ttl = 0, tos = 0, encap_limit = 0;
 	gboolean pmtud = FALSE;
 	guint32 flow_label = 0;
-	NMIPTunnelFlags flags = NM_IP_TUNNEL_FLAG_NONE;
 	char *key;
 
 	if (ifindex <= 0) {
@@ -271,7 +246,6 @@ clear:
 		tos = lnk->tclass;
 		encap_limit = lnk->encap_limit;
 		flow_label = lnk->flow_label;
-		flags = ip6tnl_flags_plat_to_setting (lnk->flags);
 	} else
 		g_return_if_reached ();
 
@@ -333,11 +307,6 @@ out:
 		priv->flow_label = flow_label;
 		_notify (self, PROP_FLOW_LABEL);
 	}
-
-	if (priv->flags != flags) {
-		priv->flags = flags;
-		_notify (self, PROP_FLAGS);
-	}
 }
 
 static void
@@ -358,7 +327,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingIPTunnel *s_ip_tunnel;
@@ -716,7 +685,6 @@ create_and_realize (NMDevice *device,
 		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;
-		lnk_ip6tnl.flags = ip6tnl_flags_setting_to_plat (nm_setting_ip_tunnel_get_flags (s_ip_tunnel));
 
 		plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), iface, &lnk_ip6tnl, out_plink);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
@@ -846,9 +814,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_FLOW_LABEL:
 		g_value_set_uint (value, priv->flow_label);
 		break;
-	case PROP_FLAGS:
-		g_value_set_uint (value, priv->flags);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -905,35 +870,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_ip_tunnel_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_ip_tunnel = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",               "u",  NM_DEVICE_IP_TUNNEL_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent",             "o",  NM_DEVICE_PARENT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Local",              "s",  NM_DEVICE_IP_TUNNEL_LOCAL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Remote",             "s",  NM_DEVICE_IP_TUNNEL_REMOTE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ttl",                "y",  NM_DEVICE_IP_TUNNEL_TTL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tos",                "y",  NM_DEVICE_IP_TUNNEL_TOS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PathMtuDiscovery",   "b",  NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("InputKey",           "s",  NM_DEVICE_IP_TUNNEL_INPUT_KEY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("OutputKey",          "s",  NM_DEVICE_IP_TUNNEL_OUTPUT_KEY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("EncapsulationLimit", "y",  NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("FlowLabel",          "u",  NM_DEVICE_IP_TUNNEL_FLOW_LABEL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Flags",              "u",  NM_DEVICE_IP_TUNNEL_FLAGS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
@@ -941,8 +881,6 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ip_tunnel);
-
 	device_class->link_changed = link_changed;
 	device_class->can_reapply_change = can_reapply_change;
 	device_class->complete_connection = complete_connection;
@@ -1021,13 +959,11 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_FLAGS] =
-	     g_param_spec_uint (NM_DEVICE_IP_TUNNEL_FLAGS, "", "",
-	                        0, G_MAXUINT32, 0,
-	                        G_PARAM_READWRITE |
-	                        G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_IPTUNNEL_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h
index 1109ace4..4bff6e33 100644
--- a/src/devices/nm-device-ip-tunnel.h
+++ b/src/devices/nm-device-ip-tunnel.h
@@ -41,7 +41,9 @@
 #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"
-#define NM_DEVICE_IP_TUNNEL_FLAGS               "flags"
+
+/* defined in the parent class, but exposed on D-Bus by the subclass. */
+#define NM_DEVICE_IP_TUNNEL_PARENT              NM_DEVICE_PARENT
 
 typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel;
 typedef struct _NMDeviceIPTunnelClass NMDeviceIPTunnelClass;
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index 895ea34f..5ef8f938 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -33,6 +33,8 @@
 #include "supplicant/nm-supplicant-interface.h"
 #include "supplicant/nm-supplicant-config.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Macsec.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceMacsec);
 
@@ -184,12 +186,8 @@ update_properties (NMDevice *device)
 		nm_device_parent_set_ifindex (device, props->parent_ifindex);
 
 #define CHECK_PROPERTY_CHANGED(field, prop) \
-	G_STMT_START { \
-		if (priv->props.field != props->field) { \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
+	if (props->field != priv->props.field) \
+		_notify (self, prop)
 
 	CHECK_PROPERTY_CHANGED (sci, PROP_SCI);
 	CHECK_PROPERTY_CHANGED (cipher_suite, PROP_CIPHER_SUITE);
@@ -204,6 +202,7 @@ update_properties (NMDevice *device)
 	CHECK_PROPERTY_CHANGED (scb, PROP_SCB);
 	CHECK_PROPERTY_CHANGED (replay_protect, PROP_REPLAY_PROTECT);
 
+	priv->props = *props;
 	g_object_thaw_notify ((GObject *) device);
 }
 
@@ -223,7 +222,7 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error)
 	mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 	                                nm_device_get_ifindex (NM_DEVICE (self)));
 
-	config = nm_supplicant_config_new (FALSE, FALSE);
+	config = nm_supplicant_config_new ();
 
 	s_macsec = (NMSettingMacsec *)
 		nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC);
@@ -478,9 +477,12 @@ handle_auth_or_fail (NMDeviceMacsec *self,
                      NMActRequest *req,
                      gboolean new_secrets)
 {
+	NMDeviceMacsecPrivate *priv;
 	const char *setting_name;
 	NMConnection *applied_connection;
 
+	priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
+
 	if (!nm_device_auth_retries_try_next (NM_DEVICE (self)))
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
@@ -490,14 +492,13 @@ handle_auth_or_fail (NMDeviceMacsec *self,
 
 	applied_connection = nm_act_request_get_applied_connection (req);
 	setting_name = nm_connection_need_secrets (applied_connection, NULL);
-	if (!setting_name) {
+	if (setting_name) {
+		macsec_secrets_get_secrets (self, setting_name,
+		                            NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+		                             | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
+	} else
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
 
-	macsec_secrets_get_secrets (self, setting_name,
-	                              NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
-	                            | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
@@ -811,36 +812,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_macsec_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_macsec = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_MACSEC,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent",        "o",  NM_DEVICE_PARENT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Sci",           "t",  NM_DEVICE_MACSEC_SCI),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("IcvLength",     "y",  NM_DEVICE_MACSEC_ICV_LENGTH),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CipherSuite",   "t",  NM_DEVICE_MACSEC_CIPHER_SUITE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Window",        "u",  NM_DEVICE_MACSEC_WINDOW),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("EncodingSa",    "y",  NM_DEVICE_MACSEC_ENCODING_SA),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Validation",    "s",  NM_DEVICE_MACSEC_VALIDATION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Encrypt",       "b",  NM_DEVICE_MACSEC_ENCRYPT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Protect",       "b",  NM_DEVICE_MACSEC_PROTECT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("IncludeSci",    "b",  NM_DEVICE_MACSEC_INCLUDE_SCI),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Es",            "b",  NM_DEVICE_MACSEC_ES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Scb",           "b",  NM_DEVICE_MACSEC_SCB),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ReplayProtect", "b",  NM_DEVICE_MACSEC_REPLAY_PROTECT),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACSEC)
@@ -848,8 +823,6 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macsec);
-
 	parent_class->act_stage2_config = act_stage2_config;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->create_and_realize = create_and_realize;
@@ -913,6 +886,10 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_MACSEC_SKELETON,
+	                                        NULL);
 }
 
 /*************************************************************/
diff --git a/src/devices/nm-device-macsec.h b/src/devices/nm-device-macsec.h
index 23e9d2c9..17b33bf5 100644
--- a/src/devices/nm-device-macsec.h
+++ b/src/devices/nm-device-macsec.h
@@ -43,6 +43,9 @@
 #define NM_DEVICE_MACSEC_SCB             "scb"
 #define NM_DEVICE_MACSEC_REPLAY_PROTECT  "replay-protect"
 
+/* defined in the parent class, but exposed on D-Bus by the subclass. */
+#define NM_DEVICE_MACSEC_PARENT          NM_DEVICE_PARENT
+
 typedef struct _NMDeviceMacsec NMDeviceMacsec;
 typedef struct _NMDeviceMacsecClass NMDeviceMacsecClass;
 
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index b8e748d6..2a461543 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -36,6 +36,8 @@
 #include "nm-ip4-config.h"
 #include "nm-utils.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Macvlan.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceMacvlan);
 
@@ -195,17 +197,12 @@ update_properties (NMDevice *device)
 	g_object_freeze_notify (object);
 
 	nm_device_parent_set_ifindex (device, plink->parent);
+	if (priv->props.mode != props->mode)
+		_notify (self, PROP_MODE);
+	if (priv->props.no_promisc != props->no_promisc)
+		_notify (self, PROP_NO_PROMISC);
 
-#define CHECK_PROPERTY_CHANGED(field, prop) \
-	G_STMT_START { \
-		if (priv->props.field != props->field) { \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
-
-	CHECK_PROPERTY_CHANGED (mode, PROP_MODE);
-	CHECK_PROPERTY_CHANGED (no_promisc, PROP_NO_PROMISC);
+	priv->props = *props;
 
 	g_object_thaw_notify (object);
 }
@@ -334,7 +331,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingMacvlan *s_macvlan;
@@ -473,27 +470,10 @@ nm_device_macvlan_init (NMDeviceMacvlan *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_macvlan = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_MACVLAN,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent",    "o", NM_DEVICE_PARENT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",      "s", NM_DEVICE_MACVLAN_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NoPromisc", "b", NM_DEVICE_MACVLAN_NO_PROMISC),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tab",       "b", NM_DEVICE_MACVLAN_TAP),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
@@ -501,8 +481,6 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macvlan);
-
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->complete_connection = complete_connection;
@@ -535,6 +513,10 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	                           G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_MACVLAN_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index c7d4be7c..e6d3a333 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -34,6 +34,9 @@
 #define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc"
 #define NM_DEVICE_MACVLAN_TAP        "tap"
 
+/* defined in the parent class, but exposed on D-Bus by the subclass. */
+#define NM_DEVICE_MACVLAN_PARENT     NM_DEVICE_PARENT
+
 typedef struct _NMDeviceMacvlan NMDeviceMacvlan;
 typedef struct _NMDeviceMacvlanClass NMDeviceMacvlanClass;
 
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index 94df0cae..639ec44a 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -26,6 +26,8 @@
 #include "ppp/nm-ppp-manager-call.h"
 #include "ppp/nm-ppp-status.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Ppp.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDevicePpp);
 
@@ -33,7 +35,8 @@ _LOG_DECLARE_SELF(NMDevicePpp);
 
 typedef struct _NMDevicePppPrivate {
 	NMPPPManager *ppp_manager;
-	NMIP4Config  *ip4_config;
+	NMIP4Config  *pending_ip4_config;
+	char         *pending_ifname;
 } NMDevicePppPrivate;
 
 struct _NMDevicePpp {
@@ -85,52 +88,46 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 	case NM_PPP_STATUS_DEAD:
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
 		break;
+	case NM_PPP_STATUS_RUNNING:
+		nm_device_activate_schedule_stage3_ip_config_start (device);
+		break;
 	default:
 		break;
 	}
 }
 
 static void
-ppp_ifindex_set (NMPPPManager *ppp_manager,
-                 int ifindex,
-                 const char *iface,
-                 gpointer user_data)
-{
-	NMDevice *device = NM_DEVICE (user_data);
-	gs_free char *old_name = NULL;
-
-	if (!nm_device_take_over_link (device, ifindex, &old_name)) {
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-		return;
-	}
-
-	if (old_name)
-		nm_manager_remove_device (nm_manager_get (), old_name, NM_DEVICE_TYPE_PPP);
-
-	nm_device_activate_schedule_stage3_ip_config_start (device);
-}
-
-static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDevicePpp *self = NM_DEVICE_PPP (device);
 	NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+	gboolean renamed;
 
 	_LOGT (LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd");
 
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
 		if (nm_device_activate_ip4_state_in_conf (device)) {
+			if (!nm_device_take_over_link (device, iface, &renamed)) {
+				nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
+				                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+				return;
+			}
+			if (renamed)
+				nm_manager_remove_device (nm_manager_get (), iface, NM_DEVICE_TYPE_PPP);
+
 			nm_device_activate_schedule_ip4_config_result (device, config);
 			return;
 		}
 	} else {
-		if (priv->ip4_config)
-			g_object_unref (priv->ip4_config);
-		priv->ip4_config = g_object_ref (config);
+		if (priv->pending_ip4_config)
+			g_object_unref (priv->pending_ip4_config);
+		priv->pending_ip4_config = g_object_ref (config);
+		g_free (priv->pending_ifname);
+		priv->pending_ifname = g_strdup (iface);
 	}
 }
 
@@ -149,7 +146,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE);
 	g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE);
 
-	g_clear_object (&priv->ip4_config);
+	g_clear_object (&priv->pending_ip4_config);
+	nm_clear_g_free (&priv->pending_ifname);
 
 	priv->ppp_manager = nm_ppp_manager_create (nm_setting_pppoe_get_parent (s_pppoe), &error);
 
@@ -177,9 +175,6 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
 	                  G_CALLBACK (ppp_state_changed),
 	                  self);
-	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_CALLBACK (ppp_ifindex_set),
-	                  self);
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_CALLBACK (ppp_ip4_config),
 	                  self);
@@ -194,12 +189,17 @@ act_stage3_ip4_config_start (NMDevice *device,
 {
 	NMDevicePpp *self = NM_DEVICE_PPP (device);
 	NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
+	gboolean renamed;
 
-	if (priv->ip4_config) {
+	if (priv->pending_ip4_config) {
+		if  (!nm_device_take_over_link (device, priv->pending_ifname, &renamed))
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		if (renamed)
+			nm_manager_remove_device (nm_manager_get (), priv->pending_ifname, NM_DEVICE_TYPE_PPP);
 		if (out_config)
-			*out_config = g_steal_pointer (&priv->ip4_config);
+			*out_config = g_steal_pointer (&priv->pending_ip4_config);
 		else
-			g_clear_object (&priv->ip4_config);
+			g_clear_object (&priv->pending_ip4_config);
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 	}
 
@@ -207,6 +207,14 @@ act_stage3_ip4_config_start (NMDevice *device,
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *self,
+                             NMIP6Config **out_config,
+                             NMDeviceStateReason *out_failure_reason)
+{
+	return NM_ACT_STAGE_RETURN_IP_FAIL;
+}
+
 static gboolean
 create_and_realize (NMDevice *device,
                     NMConnection *connection,
@@ -255,40 +263,33 @@ dispose (GObject *object)
 	NMDevicePpp *self = NM_DEVICE_PPP (object);
 	NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self);
 
-	g_clear_object (&priv->ip4_config);
+	g_clear_object (&priv->pending_ip4_config);
+	nm_clear_g_free (&priv->pending_ifname);
 
 	G_OBJECT_CLASS (nm_device_ppp_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_ppp = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_PPP,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ppp_class_init (NMDevicePppClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_PPPOE_SETTING_NAME, NM_LINK_TYPE_PPP)
 
 	object_class->dispose = dispose;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ppp);
-
 	parent_class->act_stage2_config = act_stage2_config;
 	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->create_and_realize = create_and_realize;
 	parent_class->deactivate = deactivate;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_PPP_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index b0d3ffa4..f1486c54 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -45,8 +45,6 @@ enum NMActStageReturn {
 
 NMSettings *nm_device_get_settings (NMDevice *self);
 
-gboolean nm_device_set_ip_ifindex (NMDevice *self, int ifindex);
-
 gboolean nm_device_set_ip_iface (NMDevice *self, const char *iface);
 
 void nm_device_activate_schedule_stage3_ip_config_start (NMDevice *device);
@@ -59,7 +57,7 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar
 
 void nm_device_take_down (NMDevice *self, gboolean block);
 
-gboolean nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name);
+gboolean nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed);
 
 gboolean nm_device_hw_addr_set (NMDevice *device,
                                 const char *addr,
@@ -141,4 +139,4 @@ gboolean nm_device_match_hwaddr (NMDevice *device,
                                  NMConnection *connection,
                                  gboolean fail_if_no_hwaddr);
 
-#endif /* NM_DEVICE_PRIVATE_H */
+#endif	/* NM_DEVICE_PRIVATE_H */
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index c3ce4b73..a7d7c0bf 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -25,7 +25,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
-#include <linux/if_tun.h>
 
 #include "nm-act-request.h"
 #include "nm-device-private.h"
@@ -35,6 +34,8 @@
 #include "nm-setting-tun.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Tun.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceTun);
 
@@ -50,7 +51,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceTun,
 );
 
 typedef struct {
-	NMPlatformLnkTun props;
+	NMPlatformTunProperties props;
+	const char *mode;
 } NMDeviceTunPrivate;
 
 struct _NMDeviceTun {
@@ -69,62 +71,48 @@ G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE)
 /*****************************************************************************/
 
 static void
-update_properties_from_struct (NMDeviceTun *self,
-                               const NMPlatformLnkTun *props)
-{
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
-	const NMPlatformLnkTun props0 = { };
-
-	if (!props) {
-		/* allow passing %NULL to reset all properties. */
-		props = &props0;
-	}
-
-	g_object_freeze_notify (G_OBJECT (self));
-
-#define CHECK_PROPERTY_CHANGED_VALID(field, prop) \
-	G_STMT_START { \
-		if (   priv->props.field != props->field \
-		    || priv->props.field##_valid != props->field##_valid) { \
-			priv->props.field##_valid = props->field##_valid; \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
-
-#define CHECK_PROPERTY_CHANGED(field, prop) \
-	G_STMT_START { \
-		if (priv->props.field != props->field) { \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
-
-	CHECK_PROPERTY_CHANGED_VALID (owner, PROP_OWNER);
-	CHECK_PROPERTY_CHANGED_VALID (group, PROP_GROUP);
-	CHECK_PROPERTY_CHANGED (type, PROP_MODE);
-	CHECK_PROPERTY_CHANGED (pi, PROP_NO_PI);
-	CHECK_PROPERTY_CHANGED (vnet_hdr, PROP_VNET_HDR);
-	CHECK_PROPERTY_CHANGED (multi_queue, PROP_MULTI_QUEUE);
-
-	g_object_thaw_notify (G_OBJECT (self));
-}
-
-static void
 update_properties (NMDeviceTun *self)
 {
-	NMPlatformLnkTun props_storage;
-	const NMPlatformLnkTun *props = NULL;
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	GObject *object = G_OBJECT (self);
+	NMPlatformTunProperties props;
 	int ifindex;
 
 	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
-	if (   ifindex > 0
-	    && nm_platform_link_tun_get_properties (nm_device_get_platform (NM_DEVICE (self)),
-	                                            ifindex,
-	                                            &props_storage))
-		props = &props_storage;
-
-	update_properties_from_struct (self, props);
+	if (ifindex > 0) {
+		if (!nm_platform_link_tun_get_properties (nm_device_get_platform (NM_DEVICE (self)), 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);
+
+	if (priv->props.owner != props.owner)
+		_notify (self, PROP_OWNER);
+	if (priv->props.group != props.group)
+		_notify (self, PROP_GROUP);
+	if (priv->props.no_pi != props.no_pi)
+		_notify (self, PROP_NO_PI);
+	if (priv->props.vnet_hdr != props.vnet_hdr)
+		_notify (self, PROP_VNET_HDR);
+	if (priv->props.multi_queue != props.multi_queue)
+		_notify (self, PROP_MULTI_QUEUE);
+
+	memcpy (&priv->props, &props, sizeof (NMPlatformTunProperties));
+
+	g_object_thaw_notify (object);
 }
 
 static NMDeviceCapabilities
@@ -145,7 +133,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingTun *s_tun;
@@ -169,57 +157,61 @@ complete_connection (NMDevice *device,
 	return TRUE;
 }
 
+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;
+}
+
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (device);
-	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
-	NMSettingTun *s_tun;
+	NMSettingTun *s_tun = nm_connection_get_setting_tun (connection);
+	NMPlatformTunProperties props;
 	NMSettingTunMode mode;
-	char s_buf[100];
-	const char *str;
-
-	/* Note: since we read tun properties from sysctl for older kernels,
-	 *       we don't get proper change notifications. Make sure that all our
-	 *       tun properties are up to date at this point. We should not do this,
-	 *       if we would entirely rely on netlink events. */
-	update_properties (NM_DEVICE_TUN (device));
+	gint64 user, group;
+	char *str;
 
-	switch (priv->props.type) {
-	case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; break;
-	case IFF_TAP: mode = NM_SETTING_TUN_MODE_TAP; break;
-	default:
-		/* Huh? */
-		return;
-	}
-
-	s_tun = nm_connection_get_setting_tun (connection);
 	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_device_get_platform (device), nm_device_get_ifindex (device), &props)) {
+		_LOGW (LOGD_PLATFORM, "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, (guint) mode, NULL);
+		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);
 
-	str = priv->props.owner_valid
-	      ? nm_sprintf_buf (s_buf, "%" G_GINT32_FORMAT, priv->props.owner)
-	      : NULL;
-	if (!nm_streq0 (str, nm_setting_tun_get_owner (s_tun)))
+	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);
+	}
 
-	str = priv->props.group_valid
-	      ? nm_sprintf_buf (s_buf, "%" G_GINT32_FORMAT, priv->props.group)
-	      : NULL;
-	if (!nm_streq0 (str, nm_setting_tun_get_group (s_tun)))
+	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 (priv->props.pi != nm_setting_tun_get_pi (s_tun))
-		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, (gboolean) priv->props.pi, NULL);
-	if (priv->props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun))
-		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, (gboolean) priv->props.vnet_hdr, NULL);
-	if (priv->props.multi_queue != nm_setting_tun_get_multi_queue (s_tun))
-		g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, (gboolean) priv->props.multi_queue, NULL);
+	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
@@ -230,42 +222,23 @@ create_and_realize (NMDevice *device,
                     GError **error)
 {
 	const char *iface = nm_device_get_iface (device);
-	NMPlatformLnkTun props = { };
 	NMPlatformError plerr;
 	NMSettingTun *s_tun;
-	gint64 owner, group;
+	gint64 user, group;
 
 	s_tun = nm_connection_get_setting_tun (connection);
-	g_return_val_if_fail (s_tun, FALSE);
-
-	switch (nm_setting_tun_get_mode (s_tun)) {
-	case NM_SETTING_TUN_MODE_TAP: props.type = IFF_TAP; break;
-	case NM_SETTING_TUN_MODE_TUN: props.type = IFF_TUN; break;
-	default:
-		g_return_val_if_reached (FALSE);
-	}
+	g_assert (s_tun);
 
-	owner = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
-	if (owner != -1) {
-		props.owner_valid = TRUE;
-		props.owner = owner;
-	}
+	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 (group != -1) {
-		props.group_valid = TRUE;
-		props.group = group;
-	}
 
-	props.pi = nm_setting_tun_get_pi (s_tun);
-	props.vnet_hdr = nm_setting_tun_get_vnet_hdr (s_tun);
-	props.multi_queue = nm_setting_tun_get_multi_queue (s_tun);
-	props.persist = TRUE;
-
-	plerr = nm_platform_link_tun_add (nm_device_get_platform (device),
-	                                  iface,
-	                                  &props,
-	                                  out_plink,
-	                                  NULL);
+	plerr = nm_platform_link_tun_add (nm_device_get_platform (device), 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) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create TUN/TAP interface '%s' for '%s': %s",
@@ -279,22 +252,13 @@ create_and_realize (NMDevice *device,
 }
 
 static gboolean
-_same_og (const char *str, gboolean og_valid, guint32 og_num)
-{
-	gint64 v;
-
-	v = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXINT32, -1);
-	return    (!og_valid && (           v  == (gint64) -1))
-	       || ( og_valid && (((guint32) v) == og_num     ));
-}
-
-static gboolean
 check_connection_compatible (NMDevice *device, NMConnection *connection)
 {
 	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;
@@ -304,21 +268,18 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	if (nm_device_is_real (device)) {
-		switch (priv->props.type) {
-		case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; break;
-		case IFF_TAP: mode = NM_SETTING_TUN_MODE_TAP; break;
-		default:
-			/* Huh? */
-			return FALSE;
-		}
-
+		mode = tun_mode_from_string (priv->mode);
 		if (mode != nm_setting_tun_get_mode (s_tun))
 			return FALSE;
-		if (!_same_og (nm_setting_tun_get_owner (s_tun), priv->props.owner_valid, priv->props.owner))
+
+		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 (!_same_og (nm_setting_tun_get_group (s_tun), priv->props.group_valid, priv->props.group))
+		if (group != priv->props.group)
 			return FALSE;
-		if (nm_setting_tun_get_pi (s_tun) != priv->props.pi)
+		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;
@@ -341,7 +302,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		return ret;
 
 	/* Nothing to do for TUN devices */
-	if (priv->props.type == IFF_TUN)
+	if (g_strcmp0 (priv->mode, "tap"))
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 
 	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
@@ -353,8 +314,16 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 static void
 unrealize_notify (NMDevice *device)
 {
+	NMDeviceTun *self = NM_DEVICE_TUN (device);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	guint i;
+
 	NM_DEVICE_CLASS (nm_device_tun_parent_class)->unrealize_notify (device);
-	update_properties_from_struct (NM_DEVICE_TUN (device), NULL);
+
+	memset (&priv->props, 0, sizeof (NMPlatformTunProperties));
+
+	for (i = 1; i < _PROPERTY_ENUMS_LAST; i++)
+		g_object_notify_by_pspec ((GObject *) self, obj_properties[i]);
 }
 
 /*****************************************************************************/
@@ -365,25 +334,19 @@ get_property (GObject *object, guint prop_id,
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (object);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
-	const char *s;
 
 	switch (prop_id) {
 	case PROP_OWNER:
-		g_value_set_int64 (value, priv->props.owner_valid ? (gint64) priv->props.owner : (gint64) -1);
+		g_value_set_int64 (value, priv->props.owner);
 		break;
 	case PROP_GROUP:
-		g_value_set_int64 (value, priv->props.group_valid ? (gint64) priv->props.group : (gint64) -1);
+		g_value_set_int64 (value, priv->props.group);
 		break;
 	case PROP_MODE:
-		switch (priv->props.type) {
-		case IFF_TUN: s = "tun"; break;
-		case IFF_TAP: s = "tap"; break;
-		default:      s = NULL;  break;
-		}
-		g_value_set_static_string (value, s);
+		g_value_set_string (value, priv->mode);
 		break;
 	case PROP_NO_PI:
-		g_value_set_boolean (value, !priv->props.pi);
+		g_value_set_boolean (value, priv->props.no_pi);
 		break;
 	case PROP_VNET_HDR:
 		g_value_set_boolean (value, priv->props.vnet_hdr);
@@ -397,6 +360,33 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDeviceTun *self = NM_DEVICE_TUN (object);
+	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
+	const char *str;
+
+	switch (prop_id) {
+	case PROP_MODE:
+		/* construct-only */
+		str = g_value_get_string (value);
+
+		/* mode is G_PARAM_STATIC_STRINGS */
+		if (g_strcmp0 (str, "tun") == 0)
+			priv->mode = "tun";
+		else if (g_strcmp0 (str, "tap") == 0)
+			priv->mode = "tap";
+		else
+			g_return_if_fail (FALSE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
 /*****************************************************************************/
 
 static void
@@ -404,37 +394,16 @@ nm_device_tun_init (NMDeviceTun *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_tun = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_TUN,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Owner",      "x",  NM_DEVICE_TUN_OWNER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Group",      "x",  NM_DEVICE_TUN_GROUP),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",       "s",  NM_DEVICE_TUN_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("NoPi",       "b",  NM_DEVICE_TUN_NO_PI),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VnetHdr",    "b",  NM_DEVICE_TUN_VNET_HDR),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("MultiQueue", "b",  NM_DEVICE_TUN_MULTI_QUEUE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",  "s",  NM_DEVICE_HW_ADDRESS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_tun_class_init (NMDeviceTunClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN)
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP)
 
 	object_class->get_property = get_property;
-
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_tun);
+	object_class->set_property = set_property;
 
 	device_class->connection_type = NM_SETTING_TUN_SETTING_NAME;
 	device_class->link_changed = link_changed;
@@ -459,8 +428,9 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 
 	obj_properties[PROP_MODE] =
 	     g_param_spec_string (NM_DEVICE_TUN_MODE, "", "",
-	                          NULL,
-	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+	                          "tun",
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_NO_PI] =
 	     g_param_spec_boolean (NM_DEVICE_TUN_NO_PI, "", "",
@@ -478,6 +448,10 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 	                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_TUN_SKELETON,
+	                                        NULL);
 }
 
 
@@ -493,19 +467,42 @@ create_device (NMDeviceFactory *factory,
                NMConnection *connection,
                gboolean *out_ignore)
 {
-	g_return_val_if_fail (!plink || plink->type == NM_LINK_TYPE_TUN, NULL);
-	g_return_val_if_fail (!connection || nm_streq0 (nm_connection_get_connection_type (connection), NM_SETTING_TUN_SETTING_NAME), NULL);
+	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_IFACE, iface,
 	                                  NM_DEVICE_TYPE_DESC, "Tun",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TUN,
-	                                  NM_DEVICE_LINK_TYPE, (guint) NM_LINK_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_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_class->create_device = create_device;
 );
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index 186173eb..a8c4bcc8 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -30,6 +30,8 @@
 #include "platform/nm-platform.h"
 #include "nm-device-factory.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Veth.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVeth);
 
@@ -99,18 +101,11 @@ nm_device_veth_init (NMDeviceVeth *self)
 }
 
 static void
-parent_changed_notify (NMDevice *device,
-                       int old_ifindex,
-                       NMDevice *old_parent,
-                       int new_ifindex,
-                       NMDevice *new_parent)
+notify (GObject *object, GParamSpec *pspec)
 {
-	NM_DEVICE_CLASS (nm_device_veth_parent_class)->parent_changed_notify (device,
-	                                                                      old_ifindex,
-	                                                                      old_parent,
-	                                                                      new_ifindex,
-	                                                                      new_parent);
-	_notify (NM_DEVICE_VETH (device), PROP_PEER);
+	if (nm_streq (pspec->name, NM_DEVICE_PARENT))
+		_notify (NM_DEVICE_VETH (object), PROP_PEER);
+	G_OBJECT_CLASS (nm_device_veth_parent_class)->notify (object, pspec);
 }
 
 static void
@@ -125,7 +120,7 @@ get_property (GObject *object, guint prop_id,
 		peer = nm_device_parent_get_device (NM_DEVICE (self));
 		if (peer && !NM_IS_DEVICE_VETH (peer))
 			peer = NULL;
-		nm_dbus_utils_g_value_set_object_path (value, peer);
+		nm_utils_g_value_set_object_path (value, peer);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -133,35 +128,19 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_veth = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_VETH,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Peer", "o", NM_DEVICE_VETH_PEER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_veth_class_init (NMDeviceVethClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH)
 
 	object_class->get_property = get_property;
-
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_veth);
+	object_class->notify = notify;
 
 	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
 	device_class->link_changed = link_changed;
-	device_class->parent_changed_notify = parent_changed_notify;
 
 	obj_properties[PROP_PEER] =
 	    g_param_spec_string (NM_DEVICE_VETH_PEER, "", "",
@@ -170,6 +149,10 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 	                         G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VETH_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index ae6a0f36..81512335 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -37,6 +37,8 @@
 #include "nm-core-internal.h"
 #include "platform/nmp-object.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Vlan.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVlan);
 
@@ -132,7 +134,7 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
 		 */
 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
 		if (s_ip6)
-			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6);
+			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6, FALSE);
 	}
 }
 
@@ -379,7 +381,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingVlan *s_vlan;
@@ -575,35 +577,16 @@ nm_device_vlan_init (NMDeviceVlan * self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_vlan = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_VLAN,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",   "b", NM_DEVICE_CARRIER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent",    "o", NM_DEVICE_PARENT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VlanId",    "u", NM_DEVICE_VLAN_ID),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_VLAN_SETTING_NAME, NM_LINK_TYPE_VLAN)
 
 	object_class->get_property = get_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vlan);
-
 	parent_class->create_and_realize = create_and_realize;
 	parent_class->link_changed = link_changed;
 	parent_class->unrealize_notify = unrealize_notify;
@@ -625,6 +608,10 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	                        | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VLAN_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h
index 375e8fa4..0d788940 100644
--- a/src/devices/nm-device-vlan.h
+++ b/src/devices/nm-device-vlan.h
@@ -36,8 +36,12 @@ typedef enum {
 	NM_VLAN_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
 } NMVlanError;
 
+/* D-Bus exported properties */
 #define NM_DEVICE_VLAN_ID         "vlan-id"
 
+/* defined in the parent class, but exposed on D-Bus by the subclass. */
+#define NM_DEVICE_VLAN_PARENT     NM_DEVICE_PARENT
+
 typedef struct _NMDeviceVlan NMDeviceVlan;
 typedef struct _NMDeviceVlanClass NMDeviceVlanClass;
 
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index e1252223..d9efe840 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -35,6 +35,8 @@
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Vxlan.h"
+
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVxlan);
 
@@ -95,40 +97,42 @@ update_properties (NMDevice *device)
 
 	if (priv->props.parent_ifindex != props->parent_ifindex)
 		nm_device_parent_set_ifindex (device, props->parent_ifindex);
-
-#define CHECK_PROPERTY_CHANGED(field, prop) \
-	G_STMT_START { \
-		if (priv->props.field != props->field) { \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
-
-#define CHECK_PROPERTY_CHANGED_IN6ADDR(field, prop) \
-	G_STMT_START { \
-		if (memcmp (&priv->props.field, &props->field, sizeof (props->field)) != 0) { \
-			priv->props.field = props->field; \
-			_notify (self, prop); \
-		} \
-	} G_STMT_END
-
-	CHECK_PROPERTY_CHANGED (id, PROP_ID);
-	CHECK_PROPERTY_CHANGED (local, PROP_LOCAL);
-	CHECK_PROPERTY_CHANGED_IN6ADDR (local6, PROP_LOCAL);
-	CHECK_PROPERTY_CHANGED (group, PROP_GROUP);
-	CHECK_PROPERTY_CHANGED_IN6ADDR (group6, PROP_GROUP);
-	CHECK_PROPERTY_CHANGED (tos, PROP_TOS);
-	CHECK_PROPERTY_CHANGED (ttl, PROP_TTL);
-	CHECK_PROPERTY_CHANGED (learning, PROP_LEARNING);
-	CHECK_PROPERTY_CHANGED (ageing, PROP_AGEING);
-	CHECK_PROPERTY_CHANGED (limit, PROP_LIMIT);
-	CHECK_PROPERTY_CHANGED (src_port_min, PROP_SRC_PORT_MIN);
-	CHECK_PROPERTY_CHANGED (src_port_max, PROP_SRC_PORT_MAX);
-	CHECK_PROPERTY_CHANGED (dst_port, PROP_DST_PORT);
-	CHECK_PROPERTY_CHANGED (proxy, PROP_PROXY);
-	CHECK_PROPERTY_CHANGED (rsc, PROP_RSC);
-	CHECK_PROPERTY_CHANGED (l2miss, PROP_L2MISS);
-	CHECK_PROPERTY_CHANGED (l3miss, PROP_L3MISS);
+	if (priv->props.id != props->id)
+		_notify (self, PROP_ID);
+	if (priv->props.local != props->local)
+		_notify (self, PROP_LOCAL);
+	if (memcmp (&priv->props.local6, &props->local6, sizeof (props->local6)) != 0)
+		_notify (self, PROP_LOCAL);
+	if (priv->props.group != props->group)
+		_notify (self, PROP_GROUP);
+	if (memcmp (&priv->props.group6, &props->group6, sizeof (props->group6)) != 0)
+		_notify (self, PROP_GROUP);
+	if (priv->props.tos != props->tos)
+		_notify (self, PROP_TOS);
+	if (priv->props.ttl != props->ttl)
+		_notify (self, PROP_TTL);
+	if (priv->props.learning != props->learning)
+		_notify (self, PROP_LEARNING);
+	if (priv->props.ageing != props->ageing)
+		_notify (self, PROP_AGEING);
+	if (priv->props.limit != props->limit)
+		_notify (self, PROP_LIMIT);
+	if (priv->props.src_port_min != props->src_port_min)
+		_notify (self, PROP_SRC_PORT_MIN);
+	if (priv->props.src_port_max != props->src_port_max)
+		_notify (self, PROP_SRC_PORT_MAX);
+	if (priv->props.dst_port != props->dst_port)
+		_notify (self, PROP_DST_PORT);
+	if (priv->props.proxy != props->proxy)
+		_notify (self, PROP_PROXY);
+	if (priv->props.rsc != props->rsc)
+		_notify (self, PROP_RSC);
+	if (priv->props.l2miss != props->l2miss)
+		_notify (self, PROP_L2MISS);
+	if (priv->props.l3miss != props->l3miss)
+		_notify (self, PROP_L3MISS);
+
+	priv->props = *props;
 
 	g_object_thaw_notify (object);
 }
@@ -314,7 +318,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingVxlan *s_vxlan;
@@ -541,48 +545,16 @@ nm_device_vxlan_init (NMDeviceVxlan *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_vxlan = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_VXLAN,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Parent",     "o", NM_DEVICE_PARENT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",  "s", NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Id",         "u", NM_DEVICE_VXLAN_ID),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Group",      "s", NM_DEVICE_VXLAN_GROUP),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Local",      "s", NM_DEVICE_VXLAN_LOCAL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Tos",        "y", NM_DEVICE_VXLAN_TOS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ttl",        "y", NM_DEVICE_VXLAN_TTL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Learning",   "b", NM_DEVICE_VXLAN_LEARNING),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ageing",     "u", NM_DEVICE_VXLAN_AGEING),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Limit",      "u", NM_DEVICE_VXLAN_LIMIT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DstPort",    "q", NM_DEVICE_VXLAN_DST_PORT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SrcPortMin", "q", NM_DEVICE_VXLAN_SRC_PORT_MIN),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SrcPortMax", "q", NM_DEVICE_VXLAN_SRC_PORT_MAX),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Proxy",      "b", NM_DEVICE_VXLAN_PROXY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Rsc",        "b", NM_DEVICE_VXLAN_RSC),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("L2miss",     "b", NM_DEVICE_VXLAN_L2MISS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("L3miss",     "b", NM_DEVICE_VXLAN_L3MISS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VXLAN)
 
 	object_class->get_property = get_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vxlan);
-
 	device_class->link_changed = link_changed;
 	device_class->unrealize_notify = unrealize_notify;
 	device_class->connection_type = NM_SETTING_VXLAN_SETTING_NAME;
@@ -685,6 +657,10 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 	                           G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_VXLAN_SKELETON,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h
index 511b7156..6f0102dc 100644
--- a/src/devices/nm-device-vxlan.h
+++ b/src/devices/nm-device-vxlan.h
@@ -46,6 +46,9 @@
 #define NM_DEVICE_VXLAN_L2MISS       "l2miss"
 #define NM_DEVICE_VXLAN_L3MISS       "l3miss"
 
+/* defined in the parent class, but exposed on D-Bus by the subclass. */
+#define NM_DEVICE_VXLAN_PARENT       NM_DEVICE_PARENT
+
 typedef struct _NMDeviceVxlan NMDeviceVxlan;
 typedef struct _NMDeviceVxlanClass NMDeviceVxlanClass;
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 815de29a..e79bc541 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -34,7 +34,6 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <linux/if_addr.h>
-#include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
 
@@ -49,7 +48,6 @@
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 #include "dhcp/nm-dhcp-manager.h"
-#include "dhcp/nm-dhcp-utils.h"
 #include "nm-act-request.h"
 #include "nm-proxy-config.h"
 #include "nm-ip4-config.h"
@@ -66,13 +64,13 @@
 #include "nm-netns.h"
 #include "nm-dispatcher.h"
 #include "nm-config.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 #include "dns/nm-dns-manager.h"
-#include "nm-acd-manager.h"
 #include "nm-core-internal.h"
 #include "systemd/nm-sd.h"
 #include "nm-lldp-listener.h"
 #include "nm-audit-manager.h"
+#include "nm-arping-manager.h"
 #include "nm-connectivity.h"
 #include "nm-dbus-interface.h"
 #include "nm-device-vlan.h"
@@ -80,6 +78,9 @@
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
 
+#include "introspection/org.freedesktop.NetworkManager.Device.h"
+#include "introspection/org.freedesktop.NetworkManager.Device.Statistics.h"
+
 /*****************************************************************************/
 
 #define DEFAULT_AUTOCONNECT    TRUE
@@ -129,13 +130,13 @@ typedef struct {
 	int ifindex;
 } DeleteOnDeactivateData;
 
-typedef void (*AcdCallback) (NMDevice *, NMIP4Config **, gboolean);
+typedef void (*ArpingCallback) (NMDevice *, NMIP4Config **, gboolean);
 
 typedef struct {
-	AcdCallback callback;
+	ArpingCallback callback;
 	NMDevice *device;
 	NMIP4Config **configs;
-} AcdData;
+} ArpingData;
 
 typedef enum {
 	HW_ADDR_TYPE_UNSET = 0,
@@ -151,24 +152,6 @@ typedef enum {
 	FIREWALL_STATE_WAIT_IP_CONFIG,
 } FirewallState;
 
-typedef struct {
-	NMIPConfig *orig;      /* the original configuration applied to the device */
-	NMIPConfig *current;   /* configuration after external changes.  NULL means
-	                          that the original configuration didn't change. */
-} AppliedConfig;
-
-struct _NMDeviceConnectivityHandle {
-	CList concheck_lst;
-	NMDevice *self;
-	NMDeviceConnectivityCallback callback;
-	gpointer user_data;
-	NMConnectivityCheckHandle *c_handle;
-	guint64 seq;
-	bool is_periodic:1;
-	bool is_periodic_bump:1;
-	bool is_periodic_bump_on_complete:1;
-};
-
 /*****************************************************************************/
 
 enum {
@@ -182,7 +165,6 @@ enum {
 	REMOVED,
 	RECHECK_AUTO_ACTIVATE,
 	RECHECK_ASSUME,
-	CONNECTIVITY_CHANGED,
 	LAST_SIGNAL,
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -247,18 +229,12 @@ typedef struct _NMDevicePrivate {
 		NMDeviceStateReason reason;
 	} queued_state;
 
-	union {
-		struct {
-			guint queued_ip_config_id_6;
-			guint queued_ip_config_id_4;
-		};
-		guint queued_ip_config_id_x[2];
-	};
-
+	guint queued_ip4_config_id;
+	guint queued_ip6_config_id;
 	GSList *pending_actions;
 	GSList *dad6_failed_addrs;
 
-	NMDBusTrackObjPath parent_device;
+	NMDevice *parent_device;
 
 	char *        udi;
 	char *        iface;   /* may change, could be renamed by user */
@@ -282,13 +258,11 @@ typedef struct _NMDevicePrivate {
 	bool queued_ip4_config_pending:1;
 	bool queued_ip6_config_pending:1;
 
-	bool update_ip_config_completed_v4:1;
-	bool update_ip_config_completed_v6:1;
-
 	char *        ip_iface;
 	int           ip_ifindex;
 	NMDeviceType  type;
 	char *        type_desc;
+	char *        type_description;
 	NMLinkType    link_type;
 	NMDeviceCapabilities capabilities;
 	char *        driver;
@@ -323,7 +297,9 @@ typedef struct _NMDevicePrivate {
 
 	NMActRequest *  queued_act_request;
 	bool            queued_act_request_is_waiting_for_carrier:1;
-	NMDBusTrackObjPath act_request;
+	bool            act_request_public:1;
+	NMActRequest   *act_request;
+	gulong          act_request_id;
 	ActivationHandleData act_handle4; /* for layer2 and IPv4. */
 	ActivationHandleData act_handle6;
 	guint           recheck_assume_id;
@@ -380,12 +356,6 @@ typedef struct _NMDevicePrivate {
 
 	NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:4;
 
-	bool            is_enslaved:1;
-	bool            master_ready_handled:1;
-
-	bool            ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */
-	bool            ipv6ll_has:1;
-
 	/* Generic DHCP stuff */
 	char *          dhcp_anycast_address;
 
@@ -396,56 +366,17 @@ typedef struct _NMDevicePrivate {
 	NMPacrunnerManager *pacrunner_manager;
 	NMPacrunnerCallId *pacrunner_call_id;
 
-	/* IP configuration info. Combined config from VPN, settings, and device */
-	union {
-		struct {
-			NMIP6Config *ip_config_6;
-			NMIP4Config *ip_config_4;
-		};
-		NMIPConfig *ip_config_x[2];
-	};
-
+	/* IP4 configuration info */
+	NMIP4Config *   ip4_config;     /* Combined config from VPN, settings, and device */
 	union {
 		const IpState   ip4_state;
 		IpState         ip4_state_;
 	};
-	AppliedConfig   dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
-
-	/* config from the setting */
-	union {
-		struct {
-			NMIP6Config *con_ip_config_6;
-			NMIP4Config *con_ip_config_4;
-		};
-		NMIPConfig *con_ip_config_x[2];
-	};
-
-	/* Stuff added outside NM */
-	union {
-		struct {
-			NMIP6Config *ext_ip_config_6;
-			NMIP4Config *ext_ip_config_4;
-		};
-		NMIPConfig *ext_ip_config_x[2];
-	};
-
-	/* VPNs which use this device */
-	union {
-		struct {
-			GSList *vpn_configs_6;
-			GSList *vpn_configs_4;
-		};
-		GSList *vpn_configs_x[2];
-	};
-
-	/* WWAN configuration */
-	union {
-		struct {
-			AppliedConfig  wwan_ip_config_6;
-			AppliedConfig  wwan_ip_config_4;
-		};
-		AppliedConfig wwan_ip_config_x[2];
-	};
+	NMIP4Config *   con_ip4_config; /* config from the setting */
+	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 */
 
 	bool v4_has_shadowed_routes;
 	const char *ip4_rp_filter;
@@ -465,8 +396,8 @@ typedef struct _NMDevicePrivate {
 		guint timeout;
 		guint watch;
 		GPid pid;
-		char *binary;
-		char *address;
+		const char *binary;
+		const char *address;
 		guint deadline;
 	} gw_ping;
 
@@ -487,17 +418,22 @@ typedef struct _NMDevicePrivate {
 	/* IPv4 DAD stuff */
 	struct {
 		GSList *          dad_list;
-		NMAcdManager * announcing;
-	} acd;
+		NMArpingManager * announcing;
+	} arping;
 
+	/* IP6 configuration info */
+	NMIP6Config *  ip6_config;
 	union {
 		const IpState   ip6_state;
 		IpState         ip6_state_;
 	};
-	AppliedConfig  ac_ip6_config;  /* config from IPv6 autoconfiguration */
+	NMIP6Config *  con_ip6_config; /* config from the setting */
+	NMIP6Config *  wwan_ip6_config;
+	NMIP6Config *  ext_ip6_config; /* Stuff added outside NM */
 	NMIP6Config *  ext_ip6_config_captured; /* Configuration captured from platform. */
+	GSList *       vpn6_configs;   /* VPNs which use this device */
+	bool           nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
 	NMIP6Config *  dad6_ip6_config;
-	struct in6_addr ipv6ll_addr;
 
 	GHashTable *   rt6_temporary_not_available;
 
@@ -505,6 +441,8 @@ typedef struct _NMDevicePrivate {
 	gulong         ndisc_changed_id;
 	gulong         ndisc_timeout_id;
 	NMSettingIP6ConfigPrivacy ndisc_use_tempaddr;
+	/* IP6 config from autoconf */
+	NMIP6Config *  ac_ip6_config;
 
 	guint          linklocal6_timeout_id;
 	guint8         linklocal6_dad_counter;
@@ -518,7 +456,7 @@ typedef struct _NMDevicePrivate {
 		gulong           prefix_sigid;
 		NMDhcp6Config *  config;
 		/* IP6 config from DHCP */
-		AppliedConfig    ip6_config;
+		NMIP6Config *    ip6_config;
 		/* Event ID of the current IP6 config from DHCP */
 		char *           event_id;
 		guint            needed_prefixes;
@@ -530,6 +468,8 @@ typedef struct _NMDevicePrivate {
 
 	/* master interface for bridge/bond/team slave */
 	NMDevice *      master;
+	bool            is_enslaved;
+	bool            master_ready_handled;
 	gulong          master_ready_id;
 
 	/* slave management */
@@ -542,26 +482,9 @@ typedef struct _NMDevicePrivate {
 	NMNetns *netns;
 
 	NMLldpListener *lldp_listener;
-
-	NMConnectivity *concheck_mgr;
-
-	/* if periodic checks are enabled, this is the source id for the next check. */
-	guint concheck_p_cur_id;
-
-	/* the currently configured max periodic interval. */
-	guint concheck_p_max_interval;
-
-	/* the current interval. If we are probing, the interval might be lower
-	 * then the configured max interval. */
-	guint concheck_p_cur_interval;
-
-	/* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval
-	 * concheck_p_cur_interval. */
-	gint64 concheck_p_cur_basetime_ns;
-
 	NMConnectivityState connectivity_state;
-
-	CList concheck_lst_head;
+	gulong concheck_periodic_id;
+	guint64 concheck_seq;
 
 	guint check_delete_unrealized_id;
 
@@ -574,35 +497,35 @@ typedef struct _NMDevicePrivate {
 
 } NMDevicePrivate;
 
-G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDevice, NM_IS_DEVICE)
 
 /*****************************************************************************/
 
-static const NMDBusInterfaceInfoExtended interface_info_device;
-static const GDBusSignalInfo signal_info_state_changed;
-
 static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url);
 
-static gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_configs);
+static gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs);
 
-static gboolean nm_device_set_ip_config (NMDevice *self,
-                                         int addr_family,
-                                         NMIPConfig *config,
-                                         gboolean commit,
-                                         GPtrArray *ip4_dev_route_blacklist);
+static gboolean nm_device_set_ip4_config (NMDevice *self,
+                                          NMIP4Config *config,
+                                          gboolean commit,
+                                          GPtrArray *ip4_dev_route_blacklist);
+static gboolean ip4_config_merge_and_apply (NMDevice *self,
+                                            gboolean commit);
 
-static gboolean ip_config_merge_and_apply (NMDevice *self,
-                                           int addr_family,
-                                           gboolean commit);
+static gboolean nm_device_set_ip6_config (NMDevice *self,
+                                          NMIP6Config *config,
+                                          gboolean commit);
+static gboolean ip6_config_merge_and_apply (NMDevice *self,
+                                            gboolean commit);
 
 static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
 static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success);
 static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason);
 
-static void addrconf6_start_with_link_ready (NMDevice *self);
-static gboolean linklocal6_start (NMDevice *self);
+static gboolean addrconf6_start_with_link_ready (NMDevice *self);
+static NMActStageReturn linklocal6_start (NMDevice *self);
 
 static void _carrier_wait_check_queued_act_request (NMDevice *self);
 static gint64 _get_carrier_wait_ms (NMDevice *self);
@@ -632,8 +555,6 @@ static void _set_mtu (NMDevice *self, guint32 mtu);
 static void _commit_mtu (NMDevice *self, const NMIP4Config *config);
 static void _cancel_activation (NMDevice *self);
 
-static void concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic);
-
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (queued_state_to_string, NMDeviceState,
@@ -758,16 +679,6 @@ nm_device_get_platform (NMDevice *self)
 	return nm_netns_get_platform (nm_device_get_netns (self));
 }
 
-static NMConnectivity *
-concheck_get_mgr (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (G_UNLIKELY (!priv->concheck_mgr))
-		priv->concheck_mgr = g_object_ref (nm_connectivity_get ());
-	return priv->concheck_mgr;
-}
-
 static NMIP4Config *
 _ip4_config_new (NMDevice *self)
 {
@@ -782,105 +693,6 @@ _ip6_config_new (NMDevice *self)
 	                          nm_device_get_ip_ifindex (self));
 }
 
-static NMIPConfig *
-_ip_config_new (NMDevice *self, int addr_family)
-{
-	nm_assert_addr_family (addr_family);
-
-	return addr_family == AF_INET
-	       ? (gpointer) _ip4_config_new (self)
-	       : (gpointer) _ip6_config_new (self);
-}
-
-static void
-applied_config_clear (AppliedConfig *config)
-{
-	g_clear_object (&config->current);
-	g_clear_object (&config->orig);
-}
-
-static void
-applied_config_init (AppliedConfig *config, gpointer ip_config)
-{
-	nm_g_object_ref (ip_config);
-	applied_config_clear (config);
-	config->orig = ip_config;
-}
-
-static void
-applied_config_init_new (AppliedConfig *config, NMDevice *self, int addr_family)
-{
-	gs_unref_object NMIPConfig *c = _ip_config_new (self, addr_family);
-
-	applied_config_init (config, c);
-}
-
-static NMIPConfig *
-applied_config_get_current (AppliedConfig *config)
-{
-	return config->current ?: config->orig;
-}
-
-static void
-applied_config_add_address (AppliedConfig *config, const NMPlatformIPAddress *address)
-{
-	if (config->orig)
-		nm_ip_config_add_address (config->orig, address);
-	else
-		nm_assert (!config->current);
-
-	if (config->current)
-		nm_ip_config_add_address (config->current, address);
-}
-
-static void
-applied_config_add_nameserver (AppliedConfig *config, const NMIPAddr *ns)
-{
-	if (config->orig)
-		nm_ip_config_add_nameserver (config->orig, ns);
-	else
-		nm_assert (!config->current);
-
-	if (config->current)
-		nm_ip_config_add_nameserver (config->current, ns);
-}
-
-static void
-applied_config_add_search (AppliedConfig *config, const char *new)
-{
-	if (config->orig)
-		nm_ip_config_add_search (config->orig, new);
-	else
-		nm_assert (!config->current);
-
-	if (config->current)
-		nm_ip_config_add_search (config->current, new);
-}
-
-static void
-applied_config_reset_searches (AppliedConfig *config)
-{
-	if (config->orig)
-		nm_ip_config_reset_searches (config->orig);
-	else
-		nm_assert (!config->current);
-
-	if (config->current)
-		nm_ip_config_reset_searches (config->current);
-}
-
-static void
-applied_config_reset_nameservers (AppliedConfig *config)
-{
-	if (config->orig)
-		nm_ip_config_reset_nameservers (config->orig);
-	else
-		nm_assert (!config->current);
-
-	if (config->current)
-		nm_ip_config_reset_nameservers (config->current);
-}
-
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState,
@@ -1010,18 +822,29 @@ nm_device_assume_state_reset (NMDevice *self)
 /*****************************************************************************/
 
 static void
-init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config)
+init_ip4_config_dns_priority (NMDevice *self, NMIP4Config *config)
 {
 	gs_free char *value = NULL;
 	gint priority;
 
 	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               (nm_ip_config_get_addr_family (config) == AF_INET)
-	                                                 ? "ipv4.dns-priority"
-	                                                 : "ipv6.dns-priority",
+	                                               "ipv4.dns-priority",
 	                                               self);
 	priority = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT, G_MAXINT, 0);
-	nm_ip_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL);
+	nm_ip4_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL);
+}
+
+static void
+init_ip6_config_dns_priority (NMDevice *self, NMIP6Config *config)
+{
+	gs_free char *value = NULL;
+	gint priority;
+
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               "ipv6.dns-priority",
+	                                               self);
+	priority = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT, G_MAXINT, 0);
+	nm_ip6_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL);
 }
 
 /*****************************************************************************/
@@ -1060,11 +883,6 @@ nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property
 	if (!nm_device_get_ip_ifindex (self))
 		return fallback;
 
-	/* for this kind of sysctl (e.g. "rp_filter"), kernel effectively uses the
-	 * MAX of the per-device value and the "all" value.
-	 *
-	 * Also do that, by reading both sysctls and return the maximum. */
-
 	v = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self),
 	                                        NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET,
 	                                                                                                  buf,
@@ -1230,7 +1048,7 @@ _set_ip_state (NMDevice *self, int addr_family, IpState new_state)
 
 	p =   (addr_family == AF_INET)
 	    ? &priv->ip4_state_
-	    : &priv->ip6_state_;
+        : &priv->ip6_state_;
 
 	if (*p != new_state) {
 		_LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)",
@@ -1271,26 +1089,28 @@ nm_device_get_iface (NMDevice *self)
 }
 
 gboolean
-nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name)
+nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const NMPlatformLink *plink;
 	NMPlatform *platform;
 	gboolean up, success = TRUE;
-	gs_free char *name = NULL;
+	int ifindex;
 
 	g_return_val_if_fail (priv->ifindex <= 0, FALSE);
+	g_return_val_if_fail (ifname, FALSE);
 
-	NM_SET_OUT (old_name, NULL);
+	NM_SET_OUT (renamed, FALSE);
 
 	platform = nm_device_get_platform (self);
-	plink = nm_platform_link_get (platform, ifindex);
+	plink = nm_platform_link_get_by_ifname (platform, ifname);
 	if (!plink)
 		return FALSE;
 
-	if (!nm_streq (plink->name, nm_device_get_iface (self))) {
+	ifindex = plink->ifindex;
+
+	if (!nm_streq (ifname, nm_device_get_iface (self))) {
 		up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP);
-		name = g_strdup (plink->name);
 
 		/* Rename the link to the device ifname */
 		if (up)
@@ -1299,8 +1119,7 @@ nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name)
 		if (up)
 			nm_platform_link_set_up (platform, ifindex, NULL);
 
-		if (success)
-			NM_SET_OUT (old_name, g_steal_pointer (&name));
+		NM_SET_OUT (renamed, success);
 	}
 
 	if (success) {
@@ -1372,102 +1191,71 @@ nm_device_get_ip_ifindex (const NMDevice *self)
 	return priv->ip_iface ? priv->ip_ifindex : priv->ifindex;
 }
 
-static void
-_set_ip_ifindex (NMDevice *self,
-                 int ifindex,
-                 const char *ifname)
+/**
+ * nm_device_set_ip_iface:
+ * @self: the #NMDevice
+ * @iface: the new IP interface name
+ *
+ * Updates the IP interface name and possibly the ifindex.
+ *
+ * Returns: %TRUE if the anything (name or ifindex) changed, %FALSE if nothing
+ * changed.
+ */
+gboolean
+nm_device_set_ip_iface (NMDevice *self, const char *iface)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMPlatform *platform;
-	gboolean eq_name;
-
-	/* normalize arguments */
-	if (ifindex <= 0) {
-		ifindex = 0;
-		ifname = NULL;
-	}
-
-	eq_name = nm_streq0 (priv->ip_iface, ifname);
+	NMDevicePrivate *priv;
+	int ifindex;
 
-	if (   eq_name
-	    && priv->ip_ifindex == ifindex)
-		return;
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	_LOGD (LOGD_DEVICE, "ip-ifindex: update ip-interface to %s%s%s, ifindex %d",
-	       NM_PRINT_FMT_QUOTE_STRING (ifname),
-	       ifindex);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (nm_streq0 (iface, priv->ip_iface)) {
+		if (!iface)
+			return FALSE;
+		ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface);
+		if (   ifindex <= 0
+		    || priv->ip_ifindex == ifindex)
+			return FALSE;
 
-	priv->ip_ifindex = ifindex;
-	if (!eq_name) {
+		priv->ip_ifindex = ifindex;
+		_LOGD (LOGD_DEVICE, "ip-ifname: update ifindex for ifname '%s': %d", iface, priv->ip_ifindex);
+	} else {
 		g_free (priv->ip_iface);
-		priv->ip_iface = g_strdup (ifname);
-		_notify (self, PROP_IP_IFACE);
+		priv->ip_iface = g_strdup (iface);
+
+		if (iface) {
+			/* The @iface name is not in sync with the platform cache.
+			 * So, there is no point asking the platform cache to resolve
+			 * the ifindex. Instead, we can only hope that the interface
+			 * with this name still exists and we resolve the ifindex
+			 * anew.
+			 */
+			priv->ip_ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface);
+			if (priv->ip_ifindex > 0)
+				_LOGD (LOGD_DEVICE, "ip-ifname: set ifname '%s', ifindex %d", iface, priv->ip_ifindex);
+			else
+				_LOGW (LOGD_DEVICE, "ip-ifname: set ifname '%s', unknown ifindex", iface);
+		} else {
+			priv->ip_ifindex = 0;
+			_LOGD (LOGD_DEVICE, "ip-ifname: clear ifname");
+		}
 	}
 
 	if (priv->ip_ifindex > 0) {
-		platform = nm_device_get_platform (self);
-
-		nm_platform_process_events_ensure_link (platform,
-		                                        priv->ip_ifindex,
-		                                        priv->ip_iface);
-
-		if (nm_platform_check_kernel_support (platform,
+		if (nm_platform_check_kernel_support (nm_device_get_platform (self),
 		                                      NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
-			nm_platform_link_set_user_ipv6ll_enabled (platform, priv->ip_ifindex, TRUE);
+			nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ip_ifindex, TRUE);
 
-		if (!nm_platform_link_is_up (platform, priv->ip_ifindex))
-			nm_platform_link_set_up (platform, priv->ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (nm_device_get_platform (self), priv->ip_ifindex))
+			nm_platform_link_set_up (nm_device_get_platform (self), priv->ip_ifindex, NULL);
 	}
 
 	/* We don't care about any saved values from the old iface */
 	g_hash_table_remove_all (priv->ip6_saved_properties);
-}
 
-gboolean
-nm_device_set_ip_ifindex (NMDevice *self, int ifindex)
-{
-	char ifname_buf[IFNAMSIZ];
-	const char *ifname = NULL;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-	g_return_val_if_fail (nm_device_is_activating (self), FALSE);
-
-	if (ifindex > 0) {
-		ifname = nm_platform_if_indextoname (nm_device_get_platform (self), ifindex, ifname_buf);
-		if (!ifname)
-			_LOGW (LOGD_DEVICE, "ip-ifindex: ifindex %d not found", ifindex);
-	}
-
-	_set_ip_ifindex (self, ifindex, ifname);
-	return ifindex > 0;
-}
-
-/**
- * nm_device_set_ip_iface:
- * @self: the #NMDevice
- * @ifname: the new IP interface name
- *
- * Updates the IP interface name and possibly the ifindex.
- *
- * Returns: %TRUE if an interface with name @ifname exists,
- *   and %FALSE, if @ifname is %NULL or no such interface exists.
- */
-gboolean
-nm_device_set_ip_iface (NMDevice *self, const char *ifname)
-{
-	int ifindex = 0;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-	g_return_val_if_fail (nm_device_is_activating (self), FALSE);
-
-	if (ifname) {
-		ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), ifname);
-		if (ifindex <= 0)
-			_LOGW (LOGD_DEVICE, "ip-ifindex: ifname %s not found", ifname);
-	}
-
-	_set_ip_ifindex (self, ifindex, ifname);
-	return ifindex > 0;
+	_notify (self, PROP_IP_IFACE);
+	return TRUE;
 }
 
 static gboolean
@@ -1514,9 +1302,12 @@ nm_device_parent_get_ifindex (NMDevice *self)
 NMDevice *
 nm_device_parent_get_device (NMDevice *self)
 {
+	NMDevicePrivate *priv;
+
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->parent_device.obj;
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	return priv->parent_device;
 }
 
 static void
@@ -1538,7 +1329,7 @@ _parent_set_ifindex (NMDevice *self,
 	NMDevice *parent_device;
 	gboolean changed = FALSE;
 	int old_ifindex;
-	gs_unref_object NMDevice *old_device = NULL;
+	NMDevice *old_device;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
@@ -1548,15 +1339,16 @@ _parent_set_ifindex (NMDevice *self,
 		parent_ifindex = 0;
 
 	old_ifindex = priv->parent_ifindex;
+	old_device = priv->parent_device;
 
 	if (priv->parent_ifindex == parent_ifindex) {
 		if (parent_ifindex > 0) {
 			if (   !force_check
-			    && priv->parent_device.obj
-			    && nm_device_get_ifindex (priv->parent_device.obj) == parent_ifindex)
+			    && priv->parent_device
+			    && nm_device_get_ifindex (priv->parent_device) == parent_ifindex)
 				return FALSE;
 		} else {
-			if (!priv->parent_device.obj)
+			if (!priv->parent_device)
 				return FALSE;
 		}
 	} else {
@@ -1571,23 +1363,24 @@ _parent_set_ifindex (NMDevice *self,
 	} else
 		parent_device = NULL;
 
-	if (parent_device != priv->parent_device.obj) {
-		old_device = nm_g_object_ref (priv->parent_device.obj);
-		nm_dbus_track_obj_path_set (&priv->parent_device, parent_device, TRUE);
+	if (parent_device != priv->parent_device) {
+		priv->parent_device = parent_device;
 		changed = TRUE;
 	}
 
 	if (changed) {
 		if (priv->parent_ifindex <= 0)
 			_LOGD (LOGD_DEVICE, "parent: clear");
-		else if (!priv->parent_device.obj)
+		else if (!priv->parent_device)
 			_LOGD (LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex);
 		else {
 			_LOGD (LOGD_DEVICE, "parent: ifindex %d, device %p, %s", priv->parent_ifindex,
-			       priv->parent_device.obj, nm_device_get_iface (priv->parent_device.obj));
+			       priv->parent_device, nm_device_get_iface (priv->parent_device));
 		}
 
-		NM_DEVICE_GET_CLASS (self)->parent_changed_notify (self, old_ifindex, old_device, priv->parent_ifindex, priv->parent_device.obj);
+		NM_DEVICE_GET_CLASS (self)->parent_changed_notify (self, old_ifindex, old_device, priv->parent_ifindex, priv->parent_device);
+
+		_notify (self, PROP_PARENT);
 	}
 	return changed;
 }
@@ -1612,7 +1405,7 @@ nm_device_parent_notify_changed (NMDevice *self,
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->parent_ifindex > 0) {
-		if (   priv->parent_device.obj == change_candidate
+		if (   priv->parent_device == change_candidate
 		    || priv->parent_ifindex == nm_device_get_ifindex (change_candidate))
 			return _parent_set_ifindex (self, priv->parent_ifindex, device_removed);
 	}
@@ -1918,13 +1711,16 @@ nm_device_get_route_metric_default (NMDeviceType device_type)
 static gboolean
 default_route_metric_penalty_detect (NMDevice *self)
 {
+#if WITH_CONCHECK
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* currently we don't differentiate between IPv4 and IPv6 when detecting
 	 * connectivity. */
 	if (   priv->connectivity_state != NM_CONNECTIVITY_FULL
-	    && nm_connectivity_check_enabled (concheck_get_mgr (self)))
+		&& nm_connectivity_check_enabled (nm_connectivity_get ())) {
 		return TRUE;
+	}
+#endif
 
 	return FALSE;
 }
@@ -1992,34 +1788,6 @@ out:
 	return nm_utils_ip_route_metric_normalize (addr_family, route_metric);
 }
 
-static NMSettingConnectionMdns
-_get_mdns (NMDevice *self)
-{
-	NMConnection *connection;
-	NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), NM_SETTING_CONNECTION_MDNS_DEFAULT);
-
-	connection = nm_device_get_applied_connection (self);
-	if (connection)
-		mdns = nm_setting_connection_get_mdns (nm_connection_get_setting_connection (connection));
-
-	if (mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT) {
-		gs_free char *value = NULL;
-
-		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                               "connection.mdns",
-		                                               self);
-		mdns = _nm_utils_ascii_str_to_int64 (value,
-		                                     10,
-		                                     NM_SETTING_CONNECTION_MDNS_NO,
-		                                     NM_SETTING_CONNECTION_MDNS_YES,
-		                                     NM_SETTING_CONNECTION_MDNS_DEFAULT);
-	}
-
-	return mdns;
-}
-
 guint32
 nm_device_get_route_table (NMDevice *self,
                            int addr_family,
@@ -2096,12 +1864,12 @@ nm_device_get_best_default_route (NMDevice *self,
 
 	switch (addr_family) {
 	case AF_INET:
-		return priv->ip_config_4 ? nm_ip4_config_best_default_route_get (priv->ip_config_4) : NULL;
+		return priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL;
 	case AF_INET6:
-		return priv->ip_config_6 ? nm_ip6_config_best_default_route_get (priv->ip_config_6) : NULL;
+		return priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL;
 	case AF_UNSPEC:
-		return    (priv->ip_config_4 ? nm_ip4_config_best_default_route_get (priv->ip_config_4) : NULL)
-		       ?: (priv->ip_config_6 ? nm_ip6_config_best_default_route_get (priv->ip_config_6) : NULL);
+		return    (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL)
+		       ?: (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL);
 	default:
 		g_return_val_if_reached (NULL);
 	}
@@ -2129,23 +1897,18 @@ nm_device_get_type_description (NMDevice *self)
 static const char *
 get_type_description (NMDevice *self)
 {
-	NMDeviceClass *klass;
-
-	nm_assert (NM_IS_DEVICE (self));
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	klass = NM_DEVICE_GET_CLASS (self);
-	if (G_UNLIKELY (!klass->default_type_description)) {
+	if (!priv->type_description) {
 		const char *typename;
-		gs_free char *s = NULL;
 
 		typename = G_OBJECT_TYPE_NAME (self);
 		if (g_str_has_prefix (typename, "NMDevice"))
 			typename += 8;
-		s = g_ascii_strdown (typename, -1);
-		klass->default_type_description = g_intern_string (s);
+		priv->type_description = g_ascii_strdown (typename, -1);
 	}
 
-	return klass->default_type_description;
+	return priv->type_description;
 }
 
 gboolean
@@ -2159,7 +1922,7 @@ nm_device_get_act_request (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->act_request.obj;
+	return NM_DEVICE_GET_PRIVATE (self)->act_request;
 }
 
 NMSettingsConnection *
@@ -2167,7 +1930,7 @@ nm_device_get_settings_connection (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL;
+	return priv->act_request ? nm_act_request_get_settings_connection (priv->act_request) : NULL;
 }
 
 NMConnection *
@@ -2179,7 +1942,7 @@ nm_device_get_applied_connection (NMDevice *self)
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return priv->act_request.obj ? nm_act_request_get_applied_connection (priv->act_request.obj) : NULL;
+	return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL;
 }
 
 gboolean
@@ -2187,10 +1950,10 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!priv->act_request.obj)
+	if (!priv->act_request)
 		return FALSE;
 
-	return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request.obj, compare_flags);
+	return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request, compare_flags);
 }
 
 NMSetting *
@@ -2218,549 +1981,127 @@ nm_device_get_physical_port_id (NMDevice *self)
 
 /*****************************************************************************/
 
-typedef enum {
-	CONCHECK_SCHEDULE_UPDATE_INTERVAL,
-	CONCHECK_SCHEDULE_CHECK_EXTERNAL,
-	CONCHECK_SCHEDULE_CHECK_PERIODIC,
-	CONCHECK_SCHEDULE_RETURNED_MIN,
-	CONCHECK_SCHEDULE_RETURNED_BUMP,
-	CONCHECK_SCHEDULE_RETURNED_MAX,
-} ConcheckScheduleMode;
-
-static NMDeviceConnectivityHandle *concheck_start (NMDevice *self,
-                                                   NMDeviceConnectivityCallback callback,
-                                                   gpointer user_data,
-                                                   gboolean is_periodic);
-
-static void concheck_periodic_schedule_set (NMDevice *self,
-                                            ConcheckScheduleMode mode);
-
-static gboolean
-concheck_periodic_timeout_cb (gpointer user_data)
-{
-	NMDevice *self = user_data;
-
-	_LOGt (LOGD_CONCHECK, "connectivity: periodic timeout");
-	concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC);
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-concheck_is_possible (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (   !nm_device_is_real (self)
-	    || NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_LOOPBACK))
-		return FALSE;
-
-	/* we enable periodic checks for every device state (except UNKNOWN). Especially with
-	 * unmanaged devices, it is interesting to know whether we have connectivity on that device. */
-	if (priv->state == NM_DEVICE_STATE_UNKNOWN)
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
-concheck_periodic_schedule_do (NMDevice *self, gint64 interval_ns)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean periodic_check_disabled = FALSE;
-
-	/* we always cancel whatever was pending. */
-	if (nm_clear_g_source (&priv->concheck_p_cur_id))
-		periodic_check_disabled = TRUE;
-
-	if (priv->concheck_p_max_interval == 0) {
-		/* periodic checks are disabled */
-		goto out;
-	}
-
-	nm_assert (interval_ns >= 0);
-
-	if (!concheck_is_possible (self))
-		goto out;
-
-	_LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %u milliseconds (%u seconds interval)",
-	       periodic_check_disabled ? "re-" : "",
-	       (guint) (interval_ns / NM_UTILS_NS_PER_MSEC),
-	       priv->concheck_p_cur_interval);
-
-	nm_assert (priv->concheck_p_cur_interval > 0);
-	priv->concheck_p_cur_id = g_timeout_add (interval_ns / NM_UTILS_NS_PER_MSEC,
-	                                         concheck_periodic_timeout_cb,
-	                                         self);
-	return TRUE;
-out:
-	if (periodic_check_disabled)
-		_LOGT (LOGD_CONCHECK, "connectivity: periodic-check: unscheduled");
-	return FALSE;
-}
-
-#define CONCHECK_P_PROBE_INTERVAL 1
-
-static void
-concheck_periodic_schedule_set (NMDevice *self,
-                                ConcheckScheduleMode mode)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gint64 new_expiry, exp_expiry, cur_expiry, tdiff;
-	gint64 now_ns = 0;
-
-	if (priv->concheck_p_max_interval == 0) {
-		/* periodic check is disabled. Nothing to do. */
-		return;
-	}
-
-	if (!priv->concheck_p_cur_id) {
-		/* we currently don't have a timeout scheduled. No need to reschedule
-		 * another one... */
-		if (mode == CONCHECK_SCHEDULE_UPDATE_INTERVAL) {
-			/* ... unless, we are initalizing. In this case, setup the current current
-			 * interval and schedule a perform a check right away.  */
-			priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL);
-			priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
-			if (concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND))
-				concheck_start (self, NULL, NULL, TRUE);
-		}
-		return;
-	}
-
-	switch (mode) {
-	case CONCHECK_SCHEDULE_UPDATE_INTERVAL:
-		/* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */
-
-		nm_assert (priv->concheck_p_max_interval > 0);
-		nm_assert (priv->concheck_p_cur_interval > 0);
-
-		if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) {
-			/* we currently have a shorter interval set, than what we now have. Either,
-			 * because we are probing, or because the previous max interval was shorter.
-			 *
-			 * Either way, the current timer is set just fine. Nothing to do, we will
-			 * probe our way up. */
-			return;
-		}
-
-		cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND);
-		nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
-
-		priv->concheck_p_cur_interval = priv->concheck_p_max_interval;
-		if (cur_expiry <= now_ns) {
-			/* Since the last time we scheduled a periodic check, already more than the
-			 * new max_interval passed. We need to start a check right away (and
-			 * schedule a timeout in cur-interval in the future). */
-			priv->concheck_p_cur_basetime_ns = now_ns;
-			if (concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND))
-				concheck_start (self, NULL, NULL, TRUE);
-		} else {
-			/* we are reducing the max-interval to a shorter interval that we have currently
-			 * scheduled (with cur_interval).
-			 *
-			 * However, since the last time we scheduled the check, not even the new max-interval
-			 * expired. All we need to do, is reschedule the timer to expire sooner. The cur_basetime
-			 * is unchanged. */
-			concheck_periodic_schedule_do (self, cur_expiry - now_ns);
-		}
-		return;
-
-	case CONCHECK_SCHEDULE_CHECK_EXTERNAL:
-		/* a external connectivity check delays our periodic check. We reset the counter. */
-		priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
-		concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
-		return;
-
-	case CONCHECK_SCHEDULE_CHECK_PERIODIC:
-	{
-		gboolean any_periodic_pending;
-		NMDeviceConnectivityHandle *handle;
-		guint old_interval = priv->concheck_p_cur_interval;
-
-		any_periodic_pending = FALSE;
-		c_list_for_each_entry (handle, &priv->concheck_lst_head, concheck_lst) {
-			if (handle->is_periodic_bump) {
-				handle->is_periodic_bump = FALSE;
-				handle->is_periodic_bump_on_complete = FALSE;
-				any_periodic_pending = TRUE;
-			}
-		}
-		if (any_periodic_pending) {
-			/* we reached a timeout to schedule a new periodic request, however we still
-			 * have period requests pending that didn't complete yet. We need to bump the
-			 * interval already. */
-			priv->concheck_p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_p_max_interval);
-		}
-
-		/* we just reached a timeout. The expected expiry (exp_expiry) should be
-		 * pretty close to now_ns.
-		 *
-		 * We want to reschedule the timeout at exp_expiry (aka now) + cur_interval. */
-		nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
-		exp_expiry = priv->concheck_p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND);
-		new_expiry = exp_expiry + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
-		tdiff = NM_MAX (new_expiry - now_ns, 0);
-		priv->concheck_p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
-		concheck_periodic_schedule_do (self, tdiff);
-		handle = concheck_start (self, NULL, NULL, TRUE);
-		if (old_interval != priv->concheck_p_cur_interval) {
-			/* we just bumped the interval already when scheduling this check.
-			 * When the handle returns, don't bump a second time.
-			 *
-			 * But if we reach the timeout again before the handle returns (this
-			 * code here) we will still bump the interval. */
-			handle->is_periodic_bump_on_complete = FALSE;
-		}
-		return;
-	}
-
-	/* we just got an event that we lost connectivity (that is, concheck returned). We reset
-	 * the interval to min/max or increase the probe interval (bump). */
-	case CONCHECK_SCHEDULE_RETURNED_MIN:
-		priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL);
-		break;
-	case CONCHECK_SCHEDULE_RETURNED_MAX:
-		priv->concheck_p_cur_interval = priv->concheck_p_max_interval;
-		break;
-	case CONCHECK_SCHEDULE_RETURNED_BUMP:
-		priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval);
-		break;
-	}
-
-	/* we are here, because we returned from a connectivity check and adjust the current interval.
-	 *
-	 * But note that we calculate the new timeout based on the time when we scheduled the
-	 * last check, instead of counting from now. The reaons is, that we want that the times
-	 * when we schedule checks be at precise intervals, without including the time it took for
-	 * the connectivity check. */
-	new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
-	tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0);
-	priv->concheck_p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
-	concheck_periodic_schedule_do (self, tdiff);
-}
-
-void
-nm_device_check_connectivity_update_interval (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	guint new_interval;
-
-	new_interval = nm_connectivity_get_interval (concheck_get_mgr (self));
-
-	new_interval = NM_MIN (new_interval, 7 *24 * 3600);
-
-	if (new_interval != priv->concheck_p_max_interval) {
-		_LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval);
-		priv->concheck_p_max_interval = new_interval;
-	}
-
-	if (!new_interval) {
-		/* this will cancel any potentially pending timeout. */
-		concheck_periodic_schedule_do (self, 0);
-
-		/* also update the fake connectivity state. */
-		concheck_update_state (self, NM_CONNECTIVITY_FAKE, TRUE);
-		return;
-	}
-
-	concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_UPDATE_INTERVAL);
-}
-
 static void
-concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump)
+update_connectivity_state (NMDevice *self, NMConnectivityState state)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	/* @state is a result of the connectivity check. We only expect a precise
-	 * number of possible values. */
-	nm_assert (NM_IN_SET (state, NM_CONNECTIVITY_LIMITED,
-	                             NM_CONNECTIVITY_PORTAL,
-	                             NM_CONNECTIVITY_FULL,
-	                             NM_CONNECTIVITY_FAKE,
-	                             NM_CONNECTIVITY_ERROR));
-
-	if (state == NM_CONNECTIVITY_ERROR) {
-		/* on error, we don't change the current connectivity state,
-		 * except making UNKNOWN to NONE. */
-		state = priv->connectivity_state;
-		if (state == NM_CONNECTIVITY_UNKNOWN)
-			state = NM_CONNECTIVITY_NONE;
-	} else if (state == NM_CONNECTIVITY_FAKE) {
-		/* If the connectivity check is disabled and we obtain a fake
-		 * result, make an optimistic guess. */
+	/* If the connectivity check is disabled, make an optimistic guess. */
+	if (state == NM_CONNECTIVITY_UNKNOWN) {
 		if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
-			/* FIXME: the fake connectivity state depends on the availablility of
-			 * a default route. However, we have no mechanism that rechecks the
-			 * value if a device route appears/disappears after the device
-			 * was activated. */
 			if (nm_device_get_best_default_route (self, AF_UNSPEC))
 				state = NM_CONNECTIVITY_FULL;
 			else
 				state = NM_CONNECTIVITY_LIMITED;
-		} else
+		} else {
 			state = NM_CONNECTIVITY_NONE;
+		}
 	}
 
-	if (priv->connectivity_state == state) {
-		/* we got a connectivty update, but the state didn't change. If we were probing,
-		 * we bump the probe frequency. */
-		if (allow_periodic_bump)
-			concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP);
-		return;
-	}
-	/* we need to update the probe interval before emitting signals. Emitting
-	 * a signal might call back into NMDevice and change the probe settings.
-	 * So, do that first. */
-	if (state == NM_CONNECTIVITY_FULL) {
-		/* we reached full connectivity state. Stop probing by setting the
-		 * interval to the max. */
-		concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX);
-	} else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) {
-		/* we are about to loose connectivity. (re)start probing by setting
-		 * the timeout interval to the min. */
-		concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN);
-	} else {
-		if (allow_periodic_bump)
-			concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP);
-	}
-
-	_LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s",
-	       nm_connectivity_state_to_string (priv->connectivity_state),
-	       nm_connectivity_state_to_string (state));
-	priv->connectivity_state = state;
-
-	_notify (self, PROP_CONNECTIVITY);
-	g_signal_emit (self, signals[CONNECTIVITY_CHANGED], 0);
+	if (priv->connectivity_state != state) {
+#if WITH_CONCHECK
+		_LOGD (LOGD_CONCHECK, "state changed from %s to %s",
+		       nm_connectivity_state_to_string (priv->connectivity_state),
+		       nm_connectivity_state_to_string (state));
+#endif
+		priv->connectivity_state = state;
+		_notify (self, PROP_CONNECTIVITY);
 
-	if (   priv->state == NM_DEVICE_STATE_ACTIVATED
-	    && !nm_device_sys_iface_state_is_external (self)) {
-		if (   nm_device_get_best_default_route (self, AF_INET)
-		    && !ip_config_merge_and_apply (self, AF_INET, TRUE))
-			_LOGW (LOGD_IP4, "Failed to update IPv4 route metric");
-		if (   nm_device_get_best_default_route (self, AF_INET6)
-		    && !ip_config_merge_and_apply (self, AF_INET6, TRUE))
-			_LOGW (LOGD_IP6, "Failed to update IPv6 route metric");
+		if (   priv->state == NM_DEVICE_STATE_ACTIVATED
+		    && !nm_device_sys_iface_state_is_external (self)) {
+			if (   nm_device_get_best_default_route (self, AF_INET)
+			    && !ip4_config_merge_and_apply (self, TRUE))
+				_LOGW (LOGD_IP4, "Failed to update IPv4 route metric");
+			if (   nm_device_get_best_default_route (self, AF_INET6)
+			    && !ip6_config_merge_and_apply (self, TRUE))
+				_LOGW (LOGD_IP6, "Failed to update IPv6 route metric");
+		}
 	}
 }
 
+typedef struct {
+	NMDevice *self;
+	NMDeviceConnectivityCallback callback;
+	gpointer user_data;
+	guint64 seq;
+} ConnectivityCheckData;
+
 static void
-concheck_handle_complete (NMDeviceConnectivityHandle *handle,
-                          GError *error)
+concheck_done (ConnectivityCheckData *data)
 {
-	/* The moment we invoke the callback, we unlink it. It signals
-	 * that @handle is handled -- as far as the callee of callback
-	 * is concerned. */
-	c_list_unlink (&handle->concheck_lst);
-
-	if (handle->c_handle)
-		nm_connectivity_check_cancel (handle->c_handle);
-
-	if (handle->callback) {
-		handle->callback (handle->self,
-		                  handle,
-		                  NM_DEVICE_GET_PRIVATE (handle->self)->connectivity_state,
-		                  error,
-		                  handle->user_data);
-	}
+	NMDevice *self = data->self;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	g_slice_free (NMDeviceConnectivityHandle, handle);
+	/* The unsolicited connectivity checks don't hook a callback. */
+	if (data->callback)
+		data->callback (data->self, priv->connectivity_state, data->user_data);
+	g_object_unref (data->self);
+	g_slice_free (ConnectivityCheckData, data);
 }
 
+#if WITH_CONCHECK
 static void
-concheck_cb (NMConnectivity *connectivity,
-             NMConnectivityCheckHandle *c_handle,
-             NMConnectivityState state,
-             GError *error,
-             gpointer user_data)
+concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
 {
-	_nm_unused gs_unref_object NMDevice *self_keep_alive = NULL;
-	NMDevice *self;
-	NMDevicePrivate *priv;
-	NMDeviceConnectivityHandle *handle;
-	NMDeviceConnectivityHandle *other_handle;
-	gboolean handle_is_alive;
-	gboolean allow_periodic_bump;
-	gboolean any_periodic_before;
-	gboolean any_periodic_after;
-	guint64 seq;
-
-	handle = user_data;
-	nm_assert (handle->c_handle == c_handle);
-	nm_assert (NM_IS_DEVICE (handle->self));
-
-	handle->c_handle = NULL;
-	self = handle->self;
-
-	if (nm_utils_error_is_cancelled (error, FALSE)) {
-		/* the only place where we nm_connectivity_check_cancel(@c_handle), is
-		 * from inside concheck_handle_complete(). This is a recursive call,
-		 * nothing to do. */
-		_LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, cancelled)",
-		       (long long unsigned) handle->seq);
-		return;
-	}
-
-	self_keep_alive = g_object_ref (self);
-
-	_LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s%s%s%s)",
-	       (long long unsigned) handle->seq,
-	       nm_connectivity_state_to_string (state),
-	       NM_PRINT_FMT_QUOTED (error, ", error: ", error->message, "", ""));
-
-	/* we keep NMConnectivity instance alive. It cannot be disposing. */
-	nm_assert (!nm_utils_error_is_cancelled (error, TRUE));
-
-	/* keep @self alive, while we invoke callbacks. */
-	priv = NM_DEVICE_GET_PRIVATE (self);
-
-	nm_assert (!handle || c_list_contains (&priv->concheck_lst_head, &handle->concheck_lst));
-
-	seq = handle->seq;
-
-	/* find out, if there are any periodic checks pending (either whether they
-	 * were scheduled before or after @handle. */
-	any_periodic_before = FALSE;
-	any_periodic_after = FALSE;
-	c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) {
-		if (other_handle->is_periodic_bump_on_complete) {
-			if (other_handle->seq < seq)
-				any_periodic_before = TRUE;
-			else if (other_handle->seq > seq)
-				any_periodic_after = TRUE;
-		}
-	}
-	if (NM_IN_SET (state, NM_CONNECTIVITY_ERROR)) {
-		/* the request failed. We consider this periodic check only as completed if
-		 * this was a periodic check, and there are not checks pending (either
-		 * before or after this one).
-		 *
-		 * We allow_periodic_bump, if the request failed and there are
-		 * still other requests periodic pending. */
-		allow_periodic_bump =    handle->is_periodic_bump_on_complete
-		                      && !any_periodic_before
-		                      && !any_periodic_after;
-	} else {
-		/* the request succeeded. This marks the completion of a periodic check,
-		 * if this handle was periodic, or any previously scheduled one (that
-		 * we are going to complete below). */
-		allow_periodic_bump =    handle->is_periodic_bump_on_complete
-		                      || any_periodic_before;
-	}
-
-	/* first update the new state, and emit signals. */
-	concheck_update_state (self, state, allow_periodic_bump);
-
-	handle_is_alive = FALSE;
-
-	/* we might have invoked callbacks during concheck_update_state(). The caller might have
-	 * cancelled and thus destroyed @handle. We have to check whether handle is still alive,
-	 * by searching it in the list of alive handles.
-	 *
-	 * Also, we might want to complete all pending callbacks that were started before
-	 * @handle, as they are automatically obsoleted. */
-check_handles:
-	c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) {
-		if (other_handle->seq >= seq) {
-			/* it's not guaranteed that @handle is still in the list. It might already
-			 * be canceled while invoking callbacks for a previous other_handle.
-			 * If it is already cancelled, @handle is a dangling pointer.
-			 *
-			 * Since @seq is assigned uniquely and increasing, either @other_handle is
-			 * @handle (and thus, handle is alive), or it isn't. */
-			if (other_handle == handle)
-				handle_is_alive = TRUE;
-			break;
-		}
-
-		nm_assert (other_handle != handle);
-
-		if (!NM_IN_SET (state, NM_CONNECTIVITY_ERROR)) {
-			/* we also want to complete handles that were started before the current
-			 * @handle. Their response is out-dated. */
-			concheck_handle_complete (other_handle, NULL);
-
-			/* we invoked callbacks, other handles might be cancelled and removed from the list.
-			 * Need to iterate the list from the start. */
-			goto check_handles;
-		}
-	}
+	ConnectivityCheckData *data = user_data;
+	NMDevice *self = data->self;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnectivity *connectivity = NM_CONNECTIVITY (source_object);
+	NMConnectivityState state;
+	GError *error = NULL;
 
-	if (!handle_is_alive) {
-		/* We didn't find @handle in the list of alive handles. Thus, the handles
-		 * was cancelled while we were invoking events. Nothing to do, and don't
-		 * touch the dangling pointer. */
-		return;
+	state = nm_connectivity_check_finish (connectivity, result, &error);
+	if (error) {
+		_LOGW (LOGD_DEVICE, "connectivity checking on '%s' failed: %s",
+		       nm_device_get_iface (self), error->message);
+		g_error_free (error);
 	}
 
-	concheck_handle_complete (handle, NULL);
+	if (data->seq == priv->concheck_seq)
+		update_connectivity_state (data->self, state);
+	concheck_done (data);
 }
+#endif /* WITH_CONCHECK */
 
-static NMDeviceConnectivityHandle *
-concheck_start (NMDevice *self,
-                NMDeviceConnectivityCallback callback,
-                gpointer user_data,
-                gboolean is_periodic)
+static gboolean
+no_concheck (gpointer user_data)
 {
-	static guint64 seq_counter = 0;
-	NMDevicePrivate *priv;
-	NMDeviceConnectivityHandle *handle;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
-
-	priv = NM_DEVICE_GET_PRIVATE (self);
-
-	handle = g_slice_new0 (NMDeviceConnectivityHandle);
-	handle->seq = ++seq_counter;
-	handle->self = self;
-	handle->callback = callback;
-	handle->user_data = user_data;
-	handle->is_periodic = is_periodic;
-	handle->is_periodic_bump = is_periodic;
-	handle->is_periodic_bump_on_complete = is_periodic;
+	ConnectivityCheckData *data = user_data;
 
-	c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst);
-
-	_LOGT (LOGD_CONCHECK, "connectivity: start check (seq:%llu%s)",
-	       (long long unsigned) handle->seq,
-	       is_periodic ? ", periodic-check" : "");
-
-	handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self),
-	                                                nm_device_get_ip_iface (self),
-	                                                concheck_cb,
-	                                                handle);
-	return handle;
+	concheck_done (data);
+	return G_SOURCE_REMOVE;
 }
 
-NMDeviceConnectivityHandle *
+void
 nm_device_check_connectivity (NMDevice *self,
                               NMDeviceConnectivityCallback callback,
                               gpointer user_data)
 {
-	NMDeviceConnectivityHandle *handle;
-
-	if (!concheck_is_possible (self))
-		return NULL;
-
-	concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL);
-	handle = concheck_start (self, callback, user_data, FALSE);
-	return handle;
-}
+	ConnectivityCheckData *data;
+#if WITH_CONCHECK
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+#endif
 
-void
-nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle)
-{
-	gs_free_error GError *cancelled_error = NULL;
+	data = g_slice_new0 (ConnectivityCheckData);
+	data->self = g_object_ref (self);
+	data->callback = callback;
+	data->user_data = user_data;
 
-	g_return_if_fail (handle);
-	g_return_if_fail (NM_IS_DEVICE (handle->self));
-	g_return_if_fail (!c_list_is_empty (&handle->concheck_lst));
+#if WITH_CONCHECK
+	if (priv->concheck_periodic_id) {
+		data->seq = ++priv->concheck_seq;
 
-	/* nobody has access to periodic handles, and cannot cancel
-	 * them externally. */
-	nm_assert (!handle->is_periodic);
+		/* Kick off a real connectivity check. */
+		nm_connectivity_check_async (nm_connectivity_get (),
+		                             nm_device_get_ip_iface (self),
+		                             concheck_cb,
+		                             data);
+		return;
+	}
+#endif
 
-	nm_utils_error_set_cancelled (&cancelled_error, FALSE, "NMDevice");
-	concheck_handle_complete (handle, cancelled_error);
+	/* Fake one. */
+	g_idle_add (no_concheck, data);
 }
 
 NMConnectivityState
@@ -2771,6 +2112,43 @@ nm_device_get_connectivity_state (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->connectivity_state;
 }
 
+#if WITH_CONCHECK
+static void
+concheck_periodic (NMConnectivity *connectivity, NMDevice *self)
+{
+	nm_device_check_connectivity (self, NULL, NULL);
+}
+#endif
+
+static void
+concheck_periodic_update (NMDevice *self)
+{
+#if WITH_CONCHECK
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean check_enable;
+
+	check_enable =    (priv->state == NM_DEVICE_STATE_ACTIVATED)
+	               && nm_device_get_best_default_route (self, AF_UNSPEC);
+
+	if (check_enable && !priv->concheck_periodic_id) {
+		/* We just gained a default route. Enable periodic checking. */
+		priv->concheck_periodic_id = g_signal_connect (nm_connectivity_get (),
+		                                               NM_CONNECTIVITY_PERIODIC_CHECK,
+		                                               G_CALLBACK (concheck_periodic), self);
+		/* Also kick off a check right away. */
+		nm_device_check_connectivity (self, NULL, NULL);
+	} else if (!check_enable && priv->concheck_periodic_id) {
+		/* The default route has gone off, and so has connectivity. */
+		nm_clear_g_signal_handler (nm_connectivity_get (), &priv->concheck_periodic_id);
+		update_connectivity_state (self, NM_CONNECTIVITY_NONE);
+	}
+#else
+	/* update_connectivity_state() figures out how to lie about
+	 * connectivity state if the actual state is not really known. */
+	update_connectivity_state (self, NM_CONNECTIVITY_UNKNOWN);
+#endif
+}
+
 /*****************************************************************************/
 
 static SlaveInfo *
@@ -2848,7 +2226,7 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c
 	/* Since slave devices don't have their own IP configuration,
 	 * set the MTU here.
 	 */
-	_commit_mtu (slave, NM_DEVICE_GET_PRIVATE (slave)->ip_config_4);
+	_commit_mtu (slave, NM_DEVICE_GET_PRIVATE (slave)->ip4_config);
 
 	return success;
 }
@@ -2986,6 +2364,8 @@ nm_device_update_dynamic_ip_setup (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 	GError *error = NULL;
+	gconstpointer addr;
+	size_t addr_length;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -3018,6 +2398,8 @@ nm_device_update_dynamic_ip_setup (NMDevice *self)
 
 	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_device_get_platform (self), priv->ifindex, &addr_length);
+
 		if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) {
 			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
 			       priv->lldp_listener, error->message);
@@ -3054,12 +2436,12 @@ carrier_changed (NMDevice *self, gboolean carrier)
 			* is restored. */
 			if (priv->state == NM_DEVICE_STATE_ACTIVATED)
 				nm_device_update_dynamic_ip_setup (self);
-			else {
-				if (nm_device_activate_ip4_state_in_wait (self))
-					nm_device_activate_stage3_ip4_start (self);
-				if (nm_device_activate_ip6_state_in_wait (self))
-					nm_device_activate_stage3_ip6_start (self);
-			}
+			/* If needed, also resume IP configuration that is
+			 * waiting for carrier. */
+			if (nm_device_activate_ip4_state_in_wait (self))
+				nm_device_activate_stage3_ip4_start (self);
+			if (nm_device_activate_ip6_state_in_wait (self))
+				nm_device_activate_stage3_ip6_start (self);
 			return;
 		}
 		/* fall-through and change state of device */
@@ -3254,14 +2636,12 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
 
 	now = nm_utils_get_monotonic_timestamp_s ();
 
-	head_entry = nm_ip6_config_lookup_addresses (priv->ip_config_6);
+	head_entry = nm_ip6_config_lookup_addresses (priv->ip6_config);
 	addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress),
 	                               head_entry ? head_entry->len : 0);
 	nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) {
 		const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj);
 		NMNDiscAddress *ndisc_addr;
-		guint32 lifetime, preferred;
-		gint32 base;
 
 		if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
 			continue;
@@ -3273,35 +2653,19 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
 		if (addr->plen != 64)
 			continue;
 
-		/* resolve the timestamps relative to a new base.
-		 *
-		 * Note that for convenience, platform @addr might have timestamp and/or
-		 * lifetime unset. We don't allow that flexibility for ndisc and require
-		 * well defined timestamps. */
-		if (addr->timestamp) {
-			nm_assert (addr->timestamp < G_MAXINT32);
-			base = addr->timestamp;
-		} else
-			base = now;
-
-		lifetime = nm_utils_lifetime_get (addr->timestamp, addr->lifetime, addr->preferred,
-		                                  base, &preferred);
-		if (!lifetime)
-			continue;
-
 		g_array_set_size (addresses, addresses->len+1);
 		ndisc_addr = &g_array_index (addresses, NMNDiscAddress, addresses->len-1);
 		ndisc_addr->address = addr->address;
-		ndisc_addr->timestamp = base;
-		ndisc_addr->lifetime = lifetime;
-		ndisc_addr->preferred = preferred;
+		ndisc_addr->timestamp = addr->timestamp;
+		ndisc_addr->lifetime = addr->lifetime;
+		ndisc_addr->preferred = addr->preferred;
 	}
 
-	len = nm_ip6_config_get_num_nameservers (priv->ip_config_6);
+	len = nm_ip6_config_get_num_nameservers (priv->ip6_config);
 	dns_servers = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscDNSServer), len);
 	g_array_set_size (dns_servers, len);
 	for (i = 0; i < len; i++) {
-		const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (priv->ip_config_6, i);
+		const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (priv->ip6_config, i);
 		NMNDiscDNSServer *ndisc_nameserver;
 
 		ndisc_nameserver = &g_array_index (dns_servers, NMNDiscDNSServer, i);
@@ -3310,11 +2674,11 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
 		ndisc_nameserver->lifetime = NM_NDISC_ROUTER_LIFETIME;
 	}
 
-	len = nm_ip6_config_get_num_searches (priv->ip_config_6);
+	len = nm_ip6_config_get_num_searches (priv->ip6_config);
 	dns_domains = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscDNSDomain), len);
 	g_array_set_size (dns_domains, len);
 	for (i = 0; i < len; i++) {
-		const char *search = nm_ip6_config_get_search (priv->ip_config_6, i);
+		const char *search = nm_ip6_config_get_search (priv->ip6_config, i);
 		NMNDiscDNSDomain *ndisc_search;
 
 		ndisc_search = &g_array_index (dns_domains, NMNDiscDNSDomain, i);
@@ -3440,11 +2804,11 @@ device_link_changed (NMDevice *self)
 		/* the link was down and just came up. That happens for example, while changing MTU.
 		 * We must restore IP configuration. */
 		if (priv->ip4_state == IP_DONE) {
-			if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+			if (!ip4_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again");
 		}
 		if (priv->ip6_state == IP_DONE) {
-			if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+			if (!ip6_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again");
 		}
 	}
@@ -3948,8 +3312,8 @@ realize_start_setup (NMDevice *self,
 	g_return_if_fail (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT));
 	g_return_if_fail (priv->ip_ifindex <= 0);
 	g_return_if_fail (priv->ip_iface == NULL);
-	g_return_if_fail (!priv->queued_ip_config_id_4);
-	g_return_if_fail (!priv->queued_ip_config_id_6);
+	g_return_if_fail (!priv->queued_ip4_config_id);
+	g_return_if_fail (!priv->queued_ip6_config_id);
 
 	_LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0);
 
@@ -3996,7 +3360,7 @@ realize_start_setup (NMDevice *self,
 
 		if (nm_platform_check_kernel_support (nm_device_get_platform (self),
 		                                      NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
-			priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex);
+			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex);
 
 		if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex))
 			capabilities |= NM_DEVICE_CAP_SRIOV;
@@ -4095,9 +3459,6 @@ nm_device_realize_finish (NMDevice *self, const NMPlatformLink *plink)
 	if (plink)
 		device_recheck_slave_status (self, plink);
 
-	priv->update_ip_config_completed_v4 = FALSE;
-	priv->update_ip_config_completed_v6 = FALSE;
-
 	priv->real = TRUE;
 	_notify (self, PROP_REAL);
 
@@ -4323,7 +3684,7 @@ nm_device_notify_component_added (NMDevice *self, GObject *component)
  * because that ethernet interface is controlled by the WWAN device and cannot
  * be used independently of the WWAN device.
  *
- * Returns: %TRUE if @self or its components own the interface name,
+ * Returns: %TRUE if @self or it's components owns the interface name,
  * %FALSE if not
  */
 gboolean
@@ -4613,7 +3974,7 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update)
 
 	/* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the
 	 * master to enslave us. */
-	if (   nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request.obj))
+	if (   nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))
 	    && !priv->is_enslaved)
 		return;
 
@@ -4801,8 +4162,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config)
 	if (!unconfigure_ip_config)
 		return;
 
-	nm_device_set_ip_config (self, AF_INET, NULL, FALSE, NULL);
-	nm_device_set_ip_config (self, AF_INET6, NULL, FALSE, NULL);
+	nm_device_set_ip4_config (self, NULL, FALSE, NULL);
+	nm_device_set_ip6_config (self, NULL, FALSE);
 }
 
 static gboolean
@@ -5054,9 +4415,9 @@ device_has_config (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	/* Check for IP configuration. */
-	if (priv->ip_config_4 && nm_ip4_config_get_num_addresses (priv->ip_config_4))
+	if (priv->ip4_config && nm_ip4_config_get_num_addresses (priv->ip4_config))
 		return TRUE;
-	if (priv->ip_config_6 && nm_ip6_config_get_num_addresses (priv->ip_config_6))
+	if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config))
 		return TRUE;
 
 	/* The existence of a software device is good enough. */
@@ -5181,10 +4542,10 @@ nm_device_generate_connection (NMDevice *self,
 		}
 	} else {
 		/* Only regular and master devices get IP configuration; slaves do not */
-		s_ip4 = nm_ip4_config_create_setting (priv->ip_config_4);
+		s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
 		nm_connection_add_setting (connection, s_ip4);
 
-		s_ip6 = nm_ip6_config_create_setting (priv->ip_config_6);
+		s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
 		nm_connection_add_setting (connection, s_ip6);
 
 		nm_connection_add_setting (connection, nm_setting_proxy_new ());
@@ -5241,45 +4602,34 @@ nm_device_generate_connection (NMDevice *self,
 	return g_steal_pointer (&connection);
 }
 
-/**
- * nm_device_complete_connection:
- *
- * Complete the connection. This is solely used for AddAndActivate where the user
- * may pass in an incomplete connection and a device, and the device tries to
- * make sense of it and complete it for activation. Otherwise, this is not
- * used.
- *
- * Returns: success or failure.
- */
 gboolean
 nm_device_complete_connection (NMDevice *self,
                                NMConnection *connection,
                                const char *specific_object,
-                               NMConnection *const*existing_connections,
+                               const GSList *existing_connections,
                                GError **error)
 {
-	NMDeviceClass *klass;
-
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	gboolean success = FALSE;
 
-	klass = NM_DEVICE_GET_CLASS (self);
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (connection != NULL, FALSE);
 
-	if (!klass->complete_connection) {
+	if (!NM_DEVICE_GET_CLASS (self)->complete_connection) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		             "Device class %s had no complete_connection method",
 		             G_OBJECT_TYPE_NAME (self));
 		return FALSE;
 	}
 
-	if (!klass->complete_connection (self,
-	                                 connection,
-	                                 specific_object,
-	                                 existing_connections,
-	                                 error))
-		return FALSE;
+	success = NM_DEVICE_GET_CLASS (self)->complete_connection (self,
+	                                                           connection,
+	                                                           specific_object,
+	                                                           existing_connections,
+	                                                           error);
+	if (success)
+		success = nm_connection_verify (connection, error);
 
-	return nm_connection_verify (connection, error);
+	return success;
 }
 
 gboolean
@@ -5388,12 +4738,15 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *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 (!nm_device_is_master (self))
 		return FALSE;
 
@@ -5806,9 +5159,8 @@ activate_stage1_device_prepare (NMDevice *self)
 	_set_ip_state (self, AF_INET6, IP_NONE);
 
 	/* Notify the new ActiveConnection along with the state change */
-	nm_dbus_track_obj_path_set (&priv->act_request,
-	                            priv->act_request.obj,
-	                            TRUE);
+	priv->act_request_public = TRUE;
+	_notify (self, PROP_ACTIVE_CONNECTION);
 
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
@@ -5844,7 +5196,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
 	activation_source_schedule (self, activate_stage1_device_prepare, AF_INET);
 }
@@ -5862,6 +5214,8 @@ lldp_init (NMDevice *self, gboolean restart)
 
 	if (priv->ifindex > 0 && lldp_rx_enabled (self)) {
 		gs_free_error GError *error = NULL;
+		gconstpointer addr;
+		size_t addr_length;
 
 		if (priv->lldp_listener) {
 			if (restart && nm_lldp_listener_is_running (priv->lldp_listener))
@@ -5875,6 +5229,8 @@ lldp_init (NMDevice *self, gboolean restart)
 		}
 
 		if (!nm_lldp_listener_is_running (priv->lldp_listener)) {
+			addr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &addr_length);
+
 			if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
 				_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
 			else {
@@ -6021,7 +5377,7 @@ activate_stage2_device_config (NMDevice *self)
 
 		if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
 			nm_device_master_enslave_slave (self, info->slave, nm_device_get_applied_connection (info->slave));
-		else if (   priv->act_request.obj
+		else if (   priv->act_request
 		         && nm_device_sys_iface_state_is_external (self)
 		         && slave_state <= NM_DEVICE_STATE_DISCONNECTED)
 			nm_device_queue_recheck_assume (info->slave);
@@ -6046,10 +5402,10 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
 	if (!priv->master_ready_handled) {
-		NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request.obj);
+		NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
 		NMActiveConnection *master;
 
 		master = nm_active_connection_get_master (active);
@@ -6087,9 +5443,13 @@ nm_device_ip_method_failed (NMDevice *self,
                             int addr_family,
                             NMDeviceStateReason reason)
 {
+	NMDevicePrivate *priv;
+
 	g_return_if_fail (NM_IS_DEVICE (self));
 	g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6));
 
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
 	_set_ip_state (self, addr_family, IP_FAIL);
 
 	if (get_ip_config_may_fail (self, addr_family))
@@ -6130,16 +5490,16 @@ get_ipv4_dad_timeout (NMDevice *self)
 }
 
 static void
-acd_data_destroy (gpointer ptr, GClosure *closure)
+arping_data_destroy (gpointer ptr, GClosure *closure)
 {
-	AcdData *data = ptr;
+	ArpingData *data = ptr;
 	int i;
 
 	if (data) {
 		for (i = 0; data->configs && data->configs[i]; i++)
 			g_object_unref (data->configs[i]);
 		g_free (data->configs);
-		g_slice_free (AcdData, data);
+		g_slice_free (ArpingData, data);
 	}
 }
 
@@ -6159,7 +5519,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes
 }
 
 static void
-acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data)
+arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *data)
 {
 	NMDevice *self;
 	NMDevicePrivate *priv;
@@ -6174,7 +5534,7 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data)
 
 	for (i = 0; data->configs && data->configs[i]; i++) {
 		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) {
-			result = nm_acd_manager_check_address (acd_manager, address->address);
+			result = nm_arping_manager_check_address (arping_manager, address->address);
 			success &= result;
 
 			_NMLOG (result ? LOGL_DEBUG : LOGL_WARN,
@@ -6187,8 +5547,8 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data)
 
 	data->callback (self, data->configs, success);
 
-	priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager);
-	nm_acd_manager_destroy (acd_manager);
+	priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
+	nm_arping_manager_destroy (arping_manager);
 }
 
 /**
@@ -6202,17 +5562,18 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data)
  * be started. @configs will be unreferenced after @cb has been called.
  */
 static void
-ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb)
+ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMAcdManager *acd_manager;
+	NMArpingManager *arping_manager;
 	const NMPlatformIP4Address *address;
 	NMDedupMultiIter ipconf_iter;
-	AcdData *data;
+	ArpingData *data;
 	guint timeout;
 	gboolean ret, addr_found;
-	const guint8 *hwaddr_arr;
-	size_t length;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
+	GError *error = NULL;
 	guint i;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
@@ -6227,14 +5588,14 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb)
 	}
 
 	timeout = get_ipv4_dad_timeout (self);
-	hwaddr_arr = nm_platform_link_get_address (nm_device_get_platform (self),
-	                                           nm_device_get_ip_ifindex (self),
-	                                           &length);
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self),
+	                                        nm_device_get_ip_ifindex (self),
+	                                        &hw_addr_len);
 
 	if (   !timeout
-	    || !hwaddr_arr
+	    || !hw_addr
+	    || !hw_addr_len
 	    || !addr_found
-	    || length != ETH_ALEN
 	    || nm_device_sys_iface_state_is_external_or_assume (self)) {
 
 		/* DAD not needed, signal success */
@@ -6247,36 +5608,36 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb)
 		return;
 	}
 
-	/* don't take additional references of @acd_manager that outlive @self.
+	/* don't take additional references of @arping_manager that outlive @self.
 	 * Otherwise, the callback can be invoked on a dangling pointer as we don't
 	 * disconnect the handler. */
-	acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hwaddr_arr, length);
-	priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager);
+	arping_manager = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
+	priv->arping.dad_list = g_slist_append (priv->arping.dad_list, arping_manager);
 
-	data = g_slice_new0 (AcdData);
+	data = g_slice_new0 (ArpingData);
 	data->configs = configs;
 	data->callback = cb;
 	data->device = self;
 
 	for (i = 0; configs[i]; i++) {
 		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address)
-			nm_acd_manager_add_address (acd_manager, address->address);
+			nm_arping_manager_add_address (arping_manager, address->address);
 	}
 
-	g_signal_connect_data (acd_manager, NM_ACD_MANAGER_PROBE_TERMINATED,
-	                       G_CALLBACK (acd_manager_probe_terminated), data,
-	                       acd_data_destroy, 0);
+	g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED,
+	                       G_CALLBACK (arping_manager_probe_terminated), data,
+	                       arping_data_destroy, 0);
 
-	ret = nm_acd_manager_start_probe (acd_manager, timeout);
+	ret = nm_arping_manager_start_probe (arping_manager, timeout, &error);
 
 	if (!ret) {
-		_LOGW (LOGD_DEVICE, "acd probe failed");
+		_LOGW (LOGD_DEVICE, "arping probe failed: %s", error->message);
 
 		/* DAD could not be started, signal success */
 		cb (self, configs, TRUE);
 
-		priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager);
-		nm_acd_manager_destroy (acd_manager);
+		priv->arping.dad_list = g_slist_remove (priv->arping.dad_list, arping_manager);
+		nm_arping_manager_destroy (arping_manager);
 	}
 }
 
@@ -6338,10 +5699,10 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 	NMIP4Config *config;
 	int r;
 
-	if (priv->act_request.obj == NULL)
+	if (priv->act_request == NULL)
 		return;
 
-	connection = nm_act_request_get_applied_connection (priv->act_request.obj);
+	connection = nm_act_request_get_applied_connection (priv->act_request);
 	g_assert (connection);
 
 	/* Ignore if the connection isn't an AutoIP connection */
@@ -6375,8 +5736,9 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 			nm_clear_g_source (&priv->ipv4ll_timeout);
 			nm_device_activate_schedule_ip4_config_result (self, config);
 		} else if (priv->ip4_state == IP_DONE) {
-			applied_config_init (&priv->dev_ip4_config, config);
-			if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) {
+			g_clear_object (&priv->dev_ip4_config);
+			priv->dev_ip4_config = g_object_ref (config);
+			if (!ip4_config_merge_and_apply (self, TRUE)) {
 				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
 				nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
 			}
@@ -6472,42 +5834,55 @@ ipv4ll_start (NMDevice *self)
 /*****************************************************************************/
 
 static void
-ensure_con_ip_config (NMDevice *self, int addr_family)
+ensure_con_ip4_config (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
-	const gboolean IS_IPv4 = (addr_family == AF_INET);
-	NMIPConfig *con_ip_config;
 
-	if (priv->con_ip_config_x[IS_IPv4])
+	if (priv->con_ip4_config)
 		return;
 
 	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		return;
 
-	con_ip_config = _ip_config_new (self, addr_family);
+	priv->con_ip4_config = _ip4_config_new (self);
+	nm_ip4_config_merge_setting (priv->con_ip4_config,
+	                             nm_connection_get_setting_ip4_config (connection),
+	                             nm_device_get_route_table (self, AF_INET, TRUE),
+	                             nm_device_get_route_metric (self, AF_INET));
 
-	if (IS_IPv4) {
-		nm_ip4_config_merge_setting (NM_IP4_CONFIG (con_ip_config),
-		                             nm_connection_get_setting_ip4_config (connection),
-		                             _get_mdns (self),
-		                             nm_device_get_route_table (self, addr_family, TRUE),
-		                             nm_device_get_route_metric (self, addr_family));
-	} else {
-		nm_ip6_config_merge_setting (NM_IP6_CONFIG (con_ip_config),
-		                             nm_connection_get_setting_ip6_config (connection),
-		                             nm_device_get_route_table (self, addr_family, TRUE),
-		                             nm_device_get_route_metric (self, addr_family));
+	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
+		/* For assumed connections ignore all addresses and routes. */
+		nm_ip4_config_reset_addresses (priv->con_ip4_config);
+		nm_ip4_config_reset_routes (priv->con_ip4_config);
 	}
+}
+
+static void
+ensure_con_ip6_config (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	if (priv->con_ip6_config)
+		return;
+
+	connection = nm_device_get_applied_connection (self);
+	if (!connection)
+		return;
+
+	priv->con_ip6_config = _ip6_config_new (self);
+	nm_ip6_config_merge_setting (priv->con_ip6_config,
+	                             nm_connection_get_setting_ip6_config (connection),
+	                             nm_device_get_route_table (self, AF_INET6, TRUE),
+	                             nm_device_get_route_metric (self, AF_INET6));
 
 	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 		/* For assumed connections ignore all addresses and routes. */
-		nm_ip_config_reset_addresses (con_ip_config);
-		nm_ip_config_reset_routes (con_ip_config);
+		nm_ip6_config_reset_addresses (priv->con_ip6_config);
+		nm_ip6_config_reset_routes (priv->con_ip6_config);
 	}
-
-	priv->con_ip_config_x[IS_IPv4] = con_ip_config;
 }
 
 /*****************************************************************************/
@@ -6535,222 +5910,105 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	}
 
 	if (priv->dhcp4.config) {
-		nm_dbus_object_clear_and_unexport (&priv->dhcp4.config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
 		_notify (self, PROP_DHCP4_CONFIG);
 	}
 }
 
 static gboolean
-ip_config_merge_and_apply (NMDevice *self,
-                           int addr_family,
-                           gboolean commit)
+ip4_config_merge_and_apply (NMDevice *self,
+                            gboolean commit)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean success;
-	gs_unref_object NMIPConfig *composite = NULL;
-	NMIPConfig *config;
-	gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
 	NMConnection *connection;
+	gboolean success;
+	NMIP4Config *composite;
 	gboolean ignore_auto_routes = FALSE;
 	gboolean ignore_auto_dns = FALSE;
 	gboolean ignore_default_routes = FALSE;
 	GSList *iter;
-	const char *ip6_addr_gen_token = NULL;
-	const gboolean IS_IPv4 = (addr_family == AF_INET);
+	gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
 
 	if (nm_device_sys_iface_state_is_external (self))
-		commit = FALSE;
-
-	connection = nm_device_get_applied_connection (self);
+		commit = 0;
 
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
+	connection = nm_device_get_applied_connection (self);
 	if (connection) {
-		NMSettingIPConfig *s_ip =   IS_IPv4
-		                          ? nm_connection_get_setting_ip4_config (connection)
-		                          : nm_connection_get_setting_ip6_config (connection);
+		NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
-		if (s_ip) {
-			ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip);
-			ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip);
+		if (s_ip4) {
+			ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4);
+			ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4);
 
 			/* if the connection has an explicit gateway, we also ignore
 			 * the default routes from other sources. */
-			ignore_default_routes =    nm_setting_ip_config_get_never_default (s_ip)
-			                        || nm_setting_ip_config_get_gateway (s_ip);
-
-			if (!IS_IPv4) {
-				NMSettingIP6Config *s_ip6 = NM_SETTING_IP6_CONFIG (s_ip);
-
-				if (nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64)
-					ip6_addr_gen_token = nm_setting_ip6_config_get_token (s_ip6);
-			}
+			ignore_default_routes =    nm_setting_ip_config_get_never_default (s_ip4)
+			                        || nm_setting_ip_config_get_gateway (s_ip4);
 		}
 	}
 
-	composite = _ip_config_new (self, addr_family);
-
-	if (!IS_IPv4) {
-		nm_ip6_config_set_privacy (NM_IP6_CONFIG (composite),
-		                           priv->ndisc
-		                             ? priv->ndisc_use_tempaddr
-		                             : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
-	}
-
-	init_ip_config_dns_priority (self, composite);
+	composite = _ip4_config_new (self);
+	init_ip4_config_dns_priority (self, composite);
 
 	if (commit) {
-		if (priv->queued_ip_config_id_x[IS_IPv4])
-			update_ext_ip_config (self, addr_family, FALSE);
-		ensure_con_ip_config (self, addr_family);
-	}
-
-	if (!IS_IPv4) {
-		if (   commit
-		    && priv->ipv6ll_has) {
-			const NMPlatformIP6Address ll_a = {
-				.address = priv->ipv6ll_addr,
-				.plen = 64,
-				.addr_source = NM_IP_CONFIG_SOURCE_IP6LL,
-			};
-			const NMPlatformIP6Route ll_r = {
-				.network.s6_addr16[0] = htons (0xfe80u),
-				.plen = 64,
-				.metric = nm_device_get_route_metric (self, addr_family),
-				.rt_source = NM_IP_CONFIG_SOURCE_IP6LL,
-			};
-
-			nm_assert (IN6_IS_ADDR_LINKLOCAL (&priv->ipv6ll_addr));
-
-			nm_ip6_config_add_address (NM_IP6_CONFIG (composite), &ll_a);
-			nm_ip6_config_add_route (NM_IP6_CONFIG (composite), &ll_r, NULL);
-		}
-	}
-
-	if (commit) {
-		gboolean v;
-
-		v = default_route_metric_penalty_detect (self);
-		if (IS_IPv4)
-			priv->default_route_metric_penalty_ip4_has = v;
-		else
-			priv->default_route_metric_penalty_ip6_has = v;
-	}
-
-	/* Merge all the IP configs into the composite config */
-
-	if (IS_IPv4) {
-		config = applied_config_get_current (&priv->dev_ip4_config);
-		if (config) {
-			nm_ip4_config_merge (NM_IP4_CONFIG (composite), NM_IP4_CONFIG (config),
-			                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
-			                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
-			                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
-			                     default_route_metric_penalty_get (self, addr_family));
-		}
+		if (priv->queued_ip4_config_id)
+			update_ext_ip_config (self, AF_INET, FALSE, FALSE);
+		ensure_con_ip4_config (self);
 	}
 
-	if (!IS_IPv4) {
-		config = applied_config_get_current (&priv->ac_ip6_config);
-		if (config) {
-			nm_ip6_config_merge (NM_IP6_CONFIG (composite), NM_IP6_CONFIG (config),
-			                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
-			                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
-			                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
-			                     default_route_metric_penalty_get (self, addr_family));
-		}
-	}
+	if (commit)
+		priv->default_route_metric_penalty_ip4_has = default_route_metric_penalty_detect (self);
 
-	if (!IS_IPv4) {
-		config = applied_config_get_current (&priv->dhcp6.ip6_config);
-		if (config) {
-			nm_ip6_config_merge (NM_IP6_CONFIG (composite), NM_IP6_CONFIG (config),
-			                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
-			                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
-			                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
-			                     default_route_metric_penalty_get (self, addr_family));
-		}
+	if (priv->dev_ip4_config) {
+		nm_ip4_config_merge (composite, priv->dev_ip4_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
+		                     default_route_metric_penalty_get (self, AF_INET));
 	}
 
-	for (iter = priv->vpn_configs_x[IS_IPv4]; iter; iter = iter->next)
-		nm_ip_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0);
+	for (iter = priv->vpn4_configs; iter; iter = iter->next)
+		nm_ip4_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0);
 
-	if (priv->ext_ip_config_x[IS_IPv4])
-		nm_ip_config_merge (composite, priv->ext_ip_config_x[IS_IPv4], NM_IP_CONFIG_MERGE_DEFAULT, 0);
+	if (priv->ext_ip4_config)
+		nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0);
 
 	/* Merge WWAN config *last* to ensure modem-given settings overwrite
 	 * any external stuff set by pppd or other scripts.
 	 */
-	config = applied_config_get_current (&priv->wwan_ip_config_x[IS_IPv4]);
-	if (config) {
-		nm_ip_config_merge (composite, config,
-		                      (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
-		                    | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
-		                    | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
-		                    default_route_metric_penalty_get (self, addr_family));
-	}
-
-	if (!IS_IPv4) {
-		if (priv->rt6_temporary_not_available) {
-			const NMPObject *o;
-			GHashTableIter hiter;
-
-			g_hash_table_iter_init (&hiter, priv->rt6_temporary_not_available);
-			while (g_hash_table_iter_next (&hiter, (gpointer *) &o, NULL)) {
-				nm_ip6_config_add_route (NM_IP6_CONFIG (composite),
-				                         NMP_OBJECT_CAST_IP6_ROUTE (o),
-				                         NULL);
-			}
-		}
+	if (priv->wwan_ip4_config) {
+		nm_ip4_config_merge (composite, priv->wwan_ip4_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
+		                     default_route_metric_penalty_get (self, AF_INET));
 	}
 
 	/* Merge user overrides into the composite config. For assumed connections,
-	 * con_ip_config_x is empty. */
-	if (priv->con_ip_config_x[IS_IPv4]) {
-		nm_ip_config_merge (composite, priv->con_ip_config_x[IS_IPv4], NM_IP_CONFIG_MERGE_DEFAULT,
-		                    default_route_metric_penalty_get (self, addr_family));
+	 * con_ip4_config is empty. */
+	if (priv->con_ip4_config) {
+		nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT,
+		                     default_route_metric_penalty_get (self, AF_INET));
 	}
 
 	if (commit) {
-		if (IS_IPv4) {
-			nm_ip4_config_add_dependent_routes (NM_IP4_CONFIG (composite),
-			                                    nm_device_get_route_table (self, addr_family, TRUE),
-			                                    nm_device_get_route_metric (self, addr_family),
-			                                    &ip4_dev_route_blacklist);
-		} else {
-			nm_ip6_config_add_dependent_routes (NM_IP6_CONFIG (composite),
-			                                    nm_device_get_route_table (self, addr_family, TRUE),
-			                                    nm_device_get_route_metric (self, addr_family));
-		}
+		nm_ip4_config_add_dependent_routes (composite,
+		                                    nm_device_get_route_table (self, AF_INET, TRUE),
+		                                    nm_device_get_route_metric (self, AF_INET),
+		                                    &ip4_dev_route_blacklist);
 	}
 
-	if (IS_IPv4) {
-		if (commit) {
-			if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit)
-				NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, NM_IP4_CONFIG (composite));
-		}
-	}
-
-	if (!IS_IPv4) {
-		if (commit) {
-			NMUtilsIPv6IfaceId iid;
-
-			if (   ip6_addr_gen_token
-			    && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) {
-				nm_platform_link_set_ipv6_token (nm_device_get_platform (self),
-				                                 nm_device_get_ip_ifindex (self),
-				                                 iid);
-			}
-		}
-	}
-
-	success = nm_device_set_ip_config (self, addr_family, composite, commit, ip4_dev_route_blacklist);
 	if (commit) {
-		if (IS_IPv4)
-			priv->v4_commit_first_time = FALSE;
-		else
-			priv->v6_commit_first_time = FALSE;
+		if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit)
+			NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite);
 	}
 
+	success = nm_device_set_ip4_config (self, composite, commit, ip4_dev_route_blacklist);
+	g_object_unref (composite);
+
+	if (commit)
+		priv->v4_commit_first_time = FALSE;
 	return success;
 }
 
@@ -6761,9 +6019,10 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 
 	g_return_val_if_fail (config, FALSE);
 
-	applied_config_init (&priv->dev_ip4_config, config);
+	g_clear_object (&priv->dev_ip4_config);
+	priv->dev_ip4_config = g_object_ref (config);
 
-	if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) {
+	if (!ip4_config_merge_and_apply (self, TRUE)) {
 		_LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change.");
 		return FALSE;
 	}
@@ -6803,8 +6062,8 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 	 * on the interface.
 	 */
 	if (   priv->ip4_state == IP_DONE
-	    && priv->con_ip_config_4
-	    && nm_ip4_config_get_num_addresses (priv->con_ip_config_4) > 0)
+	    && priv->con_ip4_config
+	    && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0)
 		goto clear_config;
 
 	/* Fail the method in case of timeout or failure during initial
@@ -6835,7 +6094,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 clear_config:
 	/* The previous configuration is no longer valid */
 	if (priv->dhcp4.config) {
-		nm_dbus_object_clear_and_unexport (&priv->dhcp4.config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
 		priv->dhcp4.config = nm_dhcp4_config_new ();
 		_notify (self, PROP_DHCP4_CONFIG);
 	}
@@ -6900,7 +6159,6 @@ dhcp4_state_changed (NMDhcpClient *client,
 			manual = _ip4_config_new (self);
 			nm_ip4_config_merge_setting (manual,
 			                             nm_connection_get_setting_ip4_config (connection),
-			                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
 			                             nm_device_get_route_table (self, AF_INET, TRUE),
 			                             nm_device_get_route_metric (self, AF_INET));
 
@@ -6976,100 +6234,14 @@ get_dhcp_timeout (NMDevice *self, int addr_family)
 	return timeout ?: NM_DHCP_TIMEOUT_DEFAULT;
 }
 
-static GBytes *
-dhcp4_get_client_id (NMDevice *self, NMConnection *connection)
-{
-	NMSettingIPConfig *s_ip4;
-	const char *client_id;
-	gs_free char *client_id_default = NULL;
-	guint8 *client_id_buf;
-	gboolean is_mac;
-
-	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-	client_id = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
-
-	if (!client_id) {
-		client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                                           "ipv4.dhcp-client-id", self);
-		if (client_id_default && client_id_default[0])
-			client_id = client_id_default;
-	}
-
-	if (!client_id)
-		return NULL;
-
-	if (   (is_mac = nm_streq (client_id, "mac"))
-	    || nm_streq (client_id, "perm-mac")) {
-		const char *hwaddr;
-		char addr_buf[NM_UTILS_HWADDR_LEN_MAX];
-		gsize addr_len;
-		guint8 addr_type;
-
-		hwaddr = is_mac
-		         ? nm_device_get_hw_address (self)
-		         : nm_device_get_permanent_hw_address (self);
-		if (!hwaddr)
-			return NULL;
-
-		if (!_nm_utils_hwaddr_aton (hwaddr, addr_buf, sizeof (addr_buf), &addr_len))
-			g_return_val_if_reached (NULL);
-
-		switch (addr_len) {
-		case ETH_ALEN:
-			addr_type = ARPHRD_ETHER;
-			break;
-		default:
-			/* unsupported type. */
-			return NULL;
-		}
-
-		client_id_buf = g_malloc (addr_len + 1);
-		client_id_buf[0] = addr_type;
-		memcpy (&client_id_buf[1], addr_buf, addr_len);
-		return g_bytes_new_take (client_id_buf, addr_len + 1);
-	}
-
-	if (nm_streq (client_id, "stable")) {
-		NMUtilsStableType stable_type;
-		const char *stable_id;
-		GChecksum *sum;
-		guint8 buf[20];
-		gsize buf_size;
-		guint32 salted_header;
-
-		stable_id = _get_stable_id (self, connection, &stable_type);
-		if (!stable_id)
-			g_return_val_if_reached (NULL);
-
-		salted_header = htonl (2011610591 + stable_type);
-
-		sum = g_checksum_new (G_CHECKSUM_SHA1);
-
-		g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header));
-		g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id));
-
-		buf_size = sizeof (buf);
-		g_checksum_get_digest (sum, buf, &buf_size);
-		nm_assert (buf_size == sizeof (buf));
-
-		g_checksum_free (sum);
-
-		client_id_buf = g_malloc (1 + 15);
-		client_id_buf[0] = 0;
-		memcpy (&client_id_buf[1], buf, 15);
-		return g_bytes_new_take (client_id_buf, 1 + 15);
-	}
-
-	return nm_dhcp_utils_client_id_string_to_bytes (client_id);
-}
-
 static NMActStageReturn
 dhcp4_start (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingIPConfig *s_ip4;
-	gs_unref_bytes GBytes *hwaddr = NULL;
-	gs_unref_bytes GBytes *client_id = NULL;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
+	GByteArray *tmp = NULL;
 	NMConnection *connection;
 
 	connection = nm_device_get_applied_connection (self);
@@ -7078,31 +6250,36 @@ dhcp4_start (NMDevice *self)
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
 	/* Clear old exported DHCP options */
-	nm_dbus_object_clear_and_unexport (&priv->dhcp4.config);
+	nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
 	priv->dhcp4.config = nm_dhcp4_config_new ();
 
-	hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self),
-	                                                nm_device_get_ip_ifindex (self));
-
-	client_id = dhcp4_get_client_id (self, connection);
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len);
+	if (hw_addr_len) {
+		tmp = g_byte_array_sized_new (hw_addr_len);
+		g_byte_array_append (tmp, hw_addr, hw_addr_len);
+	}
 
+	/* Begin DHCP on the interface */
 	g_warn_if_fail (priv->dhcp4.client == NULL);
 	priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
 	                                                nm_netns_get_multi_idx (nm_device_get_netns (self)),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
-	                                                hwaddr,
+	                                                tmp,
 	                                                nm_connection_get_uuid (connection),
 	                                                nm_device_get_route_table (self, AF_INET, TRUE),
 	                                                nm_device_get_route_metric (self, AF_INET),
 	                                                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)),
-	                                                client_id,
+	                                                nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)),
 	                                                get_dhcp_timeout (self, AF_INET),
 	                                                priv->dhcp_anycast_address,
 	                                                NULL);
 
+	if (tmp)
+		g_byte_array_free (tmp, TRUE);
+
 	if (!priv->dhcp4.client)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
@@ -7175,7 +6352,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection)
 		guint32 count = 0;
 
 		if (G_UNLIKELY (!shared_ips))
-			shared_ips = g_hash_table_new (nm_direct_hash, NULL);
+			shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal);
 		else {
 			while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) {
 				count += ntohl (0x100);
@@ -7360,7 +6537,6 @@ act_stage3_ip4_config_start (NMDevice *self,
 		config = _ip4_config_new (self);
 		nm_ip4_config_merge_setting (config,
 		                             nm_connection_get_setting_ip4_config (connection),
-		                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
 		                             nm_device_get_route_table (self, AF_INET, TRUE),
 		                             nm_device_get_route_metric (self, AF_INET));
 
@@ -7397,7 +6573,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE;
-	applied_config_clear (&priv->dhcp6.ip6_config);
+	g_clear_object (&priv->dhcp6.ip6_config);
 	g_clear_pointer (&priv->dhcp6.event_id, g_free);
 	nm_clear_g_source (&priv->dhcp6.grace_id);
 
@@ -7415,18 +6591,148 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE);
 
 	if (priv->dhcp6.config) {
-		nm_dbus_object_clear_and_unexport (&priv->dhcp6.config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp6.config);
 		_notify (self, PROP_DHCP6_CONFIG);
 	}
 }
 
 static gboolean
+ip6_config_merge_and_apply (NMDevice *self,
+                            gboolean commit)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	gboolean success;
+	NMIP6Config *composite;
+	gboolean ignore_auto_routes = FALSE;
+	gboolean ignore_auto_dns = FALSE;
+	gboolean ignore_default_routes = FALSE;
+	const char *token = NULL;
+	GSList *iter;
+
+	if (nm_device_sys_iface_state_is_external (self))
+		commit = 0;
+
+	/* Apply ignore-auto-routes and ignore-auto-dns settings */
+	connection = nm_device_get_applied_connection (self);
+	if (connection) {
+		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
+
+		if (s_ip6) {
+			NMSettingIP6Config *ip6 = NM_SETTING_IP6_CONFIG (s_ip6);
+
+			ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip6);
+			ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip6);
+
+			/* if the connection has an explicit gateway, we also ignore
+			 * the default routes from other sources. */
+			ignore_default_routes =    nm_setting_ip_config_get_never_default (s_ip6)
+			                        || nm_setting_ip_config_get_gateway (s_ip6);
+
+			if (nm_setting_ip6_config_get_addr_gen_mode (ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64)
+				token = nm_setting_ip6_config_get_token (ip6);
+		}
+	}
+
+	composite = _ip6_config_new (self);
+	nm_ip6_config_set_privacy (composite,
+	                           priv->ndisc ?
+	                           priv->ndisc_use_tempaddr :
+	                           NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	init_ip6_config_dns_priority (self, composite);
+
+	if (commit) {
+		if (priv->queued_ip6_config_id)
+			update_ext_ip_config (self, AF_INET6, FALSE, FALSE);
+		ensure_con_ip6_config (self);
+	}
+
+	if (commit)
+		priv->default_route_metric_penalty_ip6_has = default_route_metric_penalty_detect (self);
+
+	/* Merge all the IP configs into the composite config */
+	if (priv->ac_ip6_config) {
+		nm_ip6_config_merge (composite, priv->ac_ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
+		                     default_route_metric_penalty_get (self, AF_INET6));
+	}
+	if (priv->dhcp6.ip6_config) {
+		nm_ip6_config_merge (composite, priv->dhcp6.ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
+		                     default_route_metric_penalty_get (self, AF_INET6));
+	}
+
+	for (iter = priv->vpn6_configs; iter; iter = iter->next)
+		nm_ip6_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0);
+
+	if (priv->ext_ip6_config)
+		nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, 0);
+
+	/* Merge WWAN config *last* to ensure modem-given settings overwrite
+	 * any external stuff set by pppd or other scripts.
+	 */
+	if (priv->wwan_ip6_config) {
+		nm_ip6_config_merge (composite, priv->wwan_ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0),
+		                     default_route_metric_penalty_get (self, AF_INET6));
+	}
+
+	if (priv->rt6_temporary_not_available) {
+		const NMPObject *o;
+		GHashTableIter hiter;
+
+		g_hash_table_iter_init (&hiter, priv->rt6_temporary_not_available);
+		while (g_hash_table_iter_next (&hiter, (gpointer *) &o, NULL)) {
+			nm_ip6_config_add_route (composite,
+			                         NMP_OBJECT_CAST_IP6_ROUTE (o),
+			                         NULL);
+		}
+	}
+
+	/* Merge user overrides into the composite config. For assumed connections,
+	 * con_ip6_config is empty. */
+	if (priv->con_ip6_config) {
+		nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT,
+		                     default_route_metric_penalty_get (self, AF_INET6));
+	}
+
+	if (commit) {
+		nm_ip6_config_add_dependent_routes (composite,
+		                                    nm_device_get_route_table (self, AF_INET6, TRUE),
+		                                    nm_device_get_route_metric (self, AF_INET6));
+	}
+
+	/* Allow setting MTU etc */
+	if (commit) {
+		NMUtilsIPv6IfaceId iid;
+
+		if (token && nm_utils_ipv6_interface_identifier_get_from_token (&iid, token)) {
+			nm_platform_link_set_ipv6_token (nm_device_get_platform (self),
+			                                 nm_device_get_ip_ifindex (self),
+			                                 iid);
+		}
+	}
+
+	success = nm_device_set_ip6_config (self, composite, commit);
+	g_object_unref (composite);
+	if (commit)
+		priv->v6_commit_first_time = FALSE;
+	return success;
+}
+
+static gboolean
 dhcp6_lease_change (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection;
 
-	if (!applied_config_get_current (&priv->dhcp6.ip6_config)) {
+	if (priv->dhcp6.ip6_config == NULL) {
 		_LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind");
 		return FALSE;
 	}
@@ -7437,7 +6743,7 @@ dhcp6_lease_change (NMDevice *self)
 	g_assert (settings_connection);
 
 	/* Apply the updated config */
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) {
+	if (!ip6_config_merge_and_apply (self, TRUE)) {
 		_LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event");
 		return FALSE;
 	}
@@ -7481,8 +6787,8 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 		 * on the interface.
 		 */
 		if (   priv->ip6_state == IP_DONE
-		    && priv->con_ip_config_6
-		    && nm_ip6_config_get_num_addresses (priv->con_ip_config_6))
+		    && priv->con_ip6_config
+		    && nm_ip6_config_get_num_addresses (priv->con_ip6_config))
 			goto clear_config;
 
 		/* Fail the method in case of timeout or failure during initial
@@ -7519,7 +6825,7 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 clear_config:
 	/* The previous configuration is no longer valid */
 	if (priv->dhcp6.config) {
-		nm_dbus_object_clear_and_unexport (&priv->dhcp6.config);
+		nm_exported_object_clear_and_unexport (&priv->dhcp6.config);
 		priv->dhcp6.config = nm_dhcp6_config_new ();
 		_notify (self, PROP_DHCP6_CONFIG);
 	}
@@ -7571,16 +6877,16 @@ dhcp6_state_changed (NMDhcpClient *client,
 			const NMPlatformIP6Address *a;
 
 			nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a)
-				applied_config_add_address (&priv->dhcp6.ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (a));
+				nm_ip6_config_add_address (priv->dhcp6.ip6_config, a);
 		} else {
+			g_clear_object (&priv->dhcp6.ip6_config);
 			g_clear_pointer (&priv->dhcp6.event_id, g_free);
 			if (ip6_config) {
-				applied_config_init (&priv->dhcp6.ip6_config, ip6_config);
+				priv->dhcp6.ip6_config = g_object_ref (ip6_config);
 				priv->dhcp6.event_id = g_strdup (event_id);
 				nm_dhcp6_config_set_options (priv->dhcp6.config, options);
 				_notify (self, PROP_DHCP6_CONFIG);
-			} else
-				applied_config_clear (&priv->dhcp6.ip6_config);
+			}
 		}
 
 		/* After long time we have been able to renew the lease:
@@ -7590,7 +6896,7 @@ dhcp6_state_changed (NMDhcpClient *client,
 			_set_ip_state (self, AF_INET6, IP_CONF);
 
 		if (priv->ip6_state == IP_CONF) {
-			if (!applied_config_get_current (&priv->dhcp6.ip6_config)) {
+			if (priv->dhcp6.ip6_config == NULL) {
 				nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED);
 				break;
 			}
@@ -7641,32 +6947,34 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingIPConfig *s_ip6;
-	gs_unref_bytes GBytes *hwaddr = NULL;
+	GByteArray *tmp = NULL;
+	const guint8 *hw_addr;
+	size_t hw_addr_len = 0;
 	const NMPlatformIP6Address *ll_addr = NULL;
 
 	g_assert (connection);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 
-	if (priv->ext_ip6_config_captured) {
-		ll_addr = nm_ip6_config_find_first_address (priv->ext_ip6_config_captured,
-		                                              NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-		                                            | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL);
-	}
+	if (priv->ext_ip6_config_captured)
+		ll_addr = nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE);
 
 	if (!ll_addr) {
 		_LOGW (LOGD_DHCP6, "can't start DHCPv6: no link-local address");
 		return FALSE;
 	}
 
-	hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self),
-	                                                nm_device_get_ip_ifindex (self));
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len);
+	if (hw_addr_len) {
+		tmp = g_byte_array_sized_new (hw_addr_len);
+		g_byte_array_append (tmp, hw_addr, hw_addr_len);
+	}
 
 	priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_multi_index (self),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
-	                                                hwaddr,
+	                                                tmp,
 	                                                &ll_addr->address,
 	                                                nm_connection_get_uuid (connection),
 	                                                nm_device_get_route_table (self, AF_INET6, TRUE),
@@ -7678,6 +6986,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	                                                (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
 	                                                nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)),
 	                                                priv->dhcp6.needed_prefixes);
+	if (tmp)
+		g_byte_array_free (tmp, TRUE);
 
 	if (priv->dhcp6.client) {
 		priv->dhcp6.state_sigid = g_signal_connect (priv->dhcp6.client,
@@ -7703,11 +7013,11 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll)
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip6;
 
-	nm_dbus_object_clear_and_unexport (&priv->dhcp6.config);
+	nm_exported_object_clear_and_unexport (&priv->dhcp6.config);
 	priv->dhcp6.config = nm_dhcp6_config_new ();
 
-	nm_assert (!applied_config_get_current (&priv->dhcp6.ip6_config));
-	applied_config_clear (&priv->dhcp6.ip6_config);
+	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_applied_connection (self);
@@ -7718,12 +7028,17 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll)
 		nm_device_add_pending_action (self, NM_PENDING_ACTION_DHCP6, TRUE);
 
 	if (wait_for_ll) {
+		NMActStageReturn ret;
+
 		/* ensure link local is ready... */
-		if (!linklocal6_start (self)) {
-			/* wait for the LL address to show up */
+		ret = linklocal6_start (self);
+		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+			/* success; wait for the LL address to show up */
 			return TRUE;
 		}
-		/* already have the LL address; kick off DHCP */
+
+		/* success; already have the LL address; kick off DHCP */
+		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
 
 	if (!dhcp6_start_with_link_ready (self, connection))
@@ -7785,19 +7100,19 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatformIP6Address address = *subnet;
 
-	if (!applied_config_get_current (&priv->ac_ip6_config))
-		applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6);
+	if (!priv->ac_ip6_config)
+		priv->ac_ip6_config = _ip6_config_new (self);
 
 	/* Assign a ::1 address in the subnet for us. */
 	address.address.s6_addr32[3] |= htonl (1);
-	applied_config_add_address (&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (&address));
+	nm_ip6_config_add_address (priv->ac_ip6_config, &address);
 
 	_LOGD (LOGD_IP6, "ipv6-pd: using %s address (preferred for %u seconds)",
 	       nm_utils_inet6_ntop (&address.address, NULL),
 	       subnet->preferred);
 
 	/* This also updates the ndisc if there are actual changes. */
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing");
 }
 
@@ -7812,11 +7127,11 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device)
 	NMIP6Config *from_config = NULL;
 	guint i, len;
 
-	if (applied_config_get_current (&priv->ac_ip6_config)) {
-		applied_config_reset_nameservers (&priv->ac_ip6_config);
-		applied_config_reset_searches (&priv->ac_ip6_config);
+	if (priv->ac_ip6_config) {
+		nm_ip6_config_reset_nameservers (priv->ac_ip6_config);
+		nm_ip6_config_reset_searches (priv->ac_ip6_config);
 	} else
-		applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6);
+		priv->ac_ip6_config = _ip6_config_new (self);
 
 	if (from_device)
 		from_config = nm_device_get_ip6_config (from_device);
@@ -7825,28 +7140,34 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device)
 
 	len = nm_ip6_config_get_num_nameservers (from_config);
 	for (i = 0; i < len; i++) {
-		applied_config_add_nameserver (&priv->ac_ip6_config,
-		                               (const NMIPAddr *) nm_ip6_config_get_nameserver (from_config, i));
+		nm_ip6_config_add_nameserver (priv->ac_ip6_config,
+		                              nm_ip6_config_get_nameserver (from_config, i));
 	}
 
 	len = nm_ip6_config_get_num_searches (from_config);
 	for (i = 0; i < len; i++) {
-		applied_config_add_search (&priv->ac_ip6_config,
-		                           nm_ip6_config_get_search (from_config, i));
+		nm_ip6_config_add_search (priv->ac_ip6_config,
+		                          nm_ip6_config_get_search (from_config, i));
 	}
 
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "ipv6-pd: failed applying DNS config for connection sharing");
 }
 
 /*****************************************************************************/
 
 static void
-linklocal6_failed (NMDevice *self)
+linklocal6_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	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);
 }
 
@@ -7861,26 +7182,17 @@ linklocal6_timeout_cb (gpointer user_data)
 }
 
 static void
-linklocal6_check_complete (NMDevice *self)
+linklocal6_complete (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	const char *method;
 
-	if (!priv->linklocal6_timeout_id) {
-		/* we are not waiting for linklocal to complete. Nothing to do. */
-		return;
-	}
+	g_assert (priv->linklocal6_timeout_id);
+	g_assert (priv->ext_ip6_config_captured);
+	g_assert (nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE));
 
-	if (   !priv->ext_ip6_config_captured
-	    || !nm_ip6_config_find_first_address (priv->ext_ip6_config_captured,
-	                                            NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-	                                          | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) {
-		/* we don't have a non-tentative link local address yet. Wait longer. */
-		return;
-	}
-
-	nm_clear_g_source (&priv->linklocal6_timeout_id);
+	linklocal6_cleanup (self);
 
 	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
@@ -7890,9 +7202,12 @@ linklocal6_check_complete (NMDevice *self)
 	_LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method);
 
 	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
-	    || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0)
-		addrconf6_start_with_link_ready (self);
-	else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
+	    || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) {
+		if (!addrconf6_start_with_link_ready (self)) {
+			/* Time out IPv6 instead of failing the entire activation */
+			nm_device_activate_schedule_ip6_config_timeout (self);
+		}
+	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
 		if (!dhcp6_start_with_link_ready (self, connection)) {
 			/* Time out IPv6 instead of failing the entire activation */
 			nm_device_activate_schedule_ip6_config_timeout (self);
@@ -7907,26 +7222,27 @@ static void
 check_and_add_ipv6ll_addr (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ip_ifindex = nm_device_get_ip_ifindex (self);
 	struct in6_addr lladdr;
 	NMConnection *connection;
 	NMSettingIP6Config *s_ip6 = NULL;
 	GError *error = NULL;
-	const char *addr_type;
 
-	if (!priv->ipv6ll_handle)
+	if (priv->nm_ipv6ll == FALSE)
 		return;
 
-	if (   priv->ext_ip6_config_captured
-	    && nm_ip6_config_find_first_address (priv->ext_ip6_config_captured,
-	                                           NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-	                                         | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL
-	                                         | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) {
-		/* Already have an LL address, nothing to do */
-		return;
-	}
+	if (priv->ext_ip6_config_captured) {
+		NMDedupMultiIter ipconf_iter;
+		const NMPlatformIP6Address *addr;
 
-	priv->ipv6ll_has = FALSE;
-	memset (&priv->ipv6ll_addr, 0, sizeof (priv->ipv6ll_addr));
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ext_ip6_config_captured, &addr) {
+			if (   IN6_IS_ADDR_LINKLOCAL (&addr->address)
+			    && !(addr->n_ifa_flags & IFA_F_DADFAILED)) {
+				/* Already have an LL address, nothing to do */
+				return;
+			}
+		}
+	}
 
 	memset (&lladdr, 0, sizeof (lladdr));
 	lladdr.s6_addr16[0] = htons (0xfe80);
@@ -7952,7 +7268,7 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 			linklocal6_failed (self);
 			return;
 		}
-		addr_type = "stable-privacy";
+		_LOGD (LOGD_IP6, "linklocal6: using IPv6 stable-privacy addressing");
 	} else {
 		NMUtilsIPv6IfaceId iid;
 
@@ -7968,30 +7284,37 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 			_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_identifier (&lladdr, iid);
-		addr_type = "EUI-64";
 	}
 
-	_LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", addr_type, nm_utils_inet6_ntop (&lladdr, NULL));
-	priv->ipv6ll_has = TRUE;
-	priv->ipv6ll_addr = lladdr;
-	ip_config_merge_and_apply (self, AF_INET6, TRUE);
+	_LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
+	if (!nm_platform_ip6_address_add (nm_device_get_platform (self),
+	                                  ip_ifindex,
+	                                  lladdr,
+	                                  64,
+	                                  in6addr_any,
+	                                  NM_PLATFORM_LIFETIME_PERMANENT,
+	                                  NM_PLATFORM_LIFETIME_PERMANENT,
+	                                  0)) {
+		_LOGW (LOGD_IP6, "failed to add IPv6 link-local address %s",
+		       nm_utils_inet6_ntop (&lladdr, NULL));
+	}
 }
 
-static gboolean
+static NMActStageReturn
 linklocal6_start (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	const char *method;
 
-	nm_clear_g_source (&priv->linklocal6_timeout_id);
+	linklocal6_cleanup (self);
 
 	if (   priv->ext_ip6_config_captured
-	    && nm_ip6_config_find_first_address (priv->ext_ip6_config_captured,
-	                                           NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL
-	                                         | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL))
-		return TRUE;
+	    && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE))
+		return NM_ACT_STAGE_RETURN_SUCCESS;
 
 	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
@@ -8007,7 +7330,8 @@ linklocal6_start (NMDevice *self)
 	 * (rh #1101809)
 	 */
 	priv->linklocal6_timeout_id = g_timeout_add_seconds (15, linklocal6_timeout_cb, self);
-	return FALSE;
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 /*****************************************************************************/
@@ -8072,7 +7396,7 @@ _set_mtu (NMDevice *self, guint32 mtu)
 
 	if (priv->master) {
 		/* changing the MTU of a slave, might require the master to reset
-		 * its MTU. Note that the master usually cannot set a MTU larger
+		 * it's MTU. Note that the master usually cannot set a MTU larger
 		 * then the slave's. Hence, when the slave increases the MTU,
 		 * master might want to retry setting the MTU. */
 		nm_device_commit_mtu (priv->master);
@@ -8249,7 +7573,7 @@ nm_device_commit_mtu (NMDevice *self)
 	if (   state >= NM_DEVICE_STATE_CONFIG
 	    && state < NM_DEVICE_STATE_DEACTIVATING) {
 		_LOGT (LOGD_DEVICE, "mtu: commit-mtu...");
-		_commit_mtu (self, NM_DEVICE_GET_PRIVATE (self)->ip_config_4);
+		_commit_mtu (self, NM_DEVICE_GET_PRIVATE (self)->ip4_config);
 	} else
 		_LOGT (LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_str (state));
 }
@@ -8261,10 +7585,10 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	guint i;
 
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
-	if (!applied_config_get_current (&priv->ac_ip6_config))
-		applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6);
+	if (!priv->ac_ip6_config)
+		priv->ac_ip6_config = _ip6_config_new (self);
 
 	if (changed & NM_NDISC_CONFIG_ADDRESSES) {
 		guint8 plen;
@@ -8285,23 +7609,16 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 		} else
 			plen = 128;
 
-		nm_ip6_config_reset_addresses_ndisc ((NMIP6Config *) priv->ac_ip6_config.orig,
+		nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config,
 		                                     rdata->addresses,
 		                                     rdata->addresses_n,
 		                                     plen,
 		                                     ifa_flags);
-		if (priv->ac_ip6_config.current) {
-			nm_ip6_config_reset_addresses_ndisc ((NMIP6Config *) priv->ac_ip6_config.current,
-			                                     rdata->addresses,
-			                                     rdata->addresses_n,
-			                                     plen,
-			                                     ifa_flags);
-		}
 	}
 
 	if (NM_FLAGS_ANY (changed,   NM_NDISC_CONFIG_ROUTES
 	                           | NM_NDISC_CONFIG_GATEWAYS)) {
-		nm_ip6_config_reset_routes_ndisc ((NMIP6Config *) priv->ac_ip6_config.orig,
+		nm_ip6_config_reset_routes_ndisc (priv->ac_ip6_config,
 		                                  rdata->gateways,
 		                                  rdata->gateways_n,
 		                                  rdata->routes,
@@ -8310,34 +7627,22 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 		                                  nm_device_get_route_metric (self, AF_INET6),
 		                                  nm_platform_check_kernel_support (nm_device_get_platform (self),
 		                                                                    NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF));
-		if (priv->ac_ip6_config.current) {
-			nm_ip6_config_reset_routes_ndisc ((NMIP6Config *) priv->ac_ip6_config.current,
-			                                  rdata->gateways,
-			                                  rdata->gateways_n,
-			                                  rdata->routes,
-			                                  rdata->routes_n,
-			                                  nm_device_get_route_table (self, AF_INET6, TRUE),
-			                                  nm_device_get_route_metric (self, AF_INET6),
-			                                  nm_platform_check_kernel_support (nm_device_get_platform (self),
-			                                                                    NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF));
-		}
-
 	}
 
 	if (changed & NM_NDISC_CONFIG_DNS_SERVERS) {
 		/* Rebuild DNS server list from neighbor discovery cache. */
-		applied_config_reset_nameservers (&priv->ac_ip6_config);
+		nm_ip6_config_reset_nameservers (priv->ac_ip6_config);
 
 		for (i = 0; i < rdata->dns_servers_n; i++)
-			applied_config_add_nameserver (&priv->ac_ip6_config, (const NMIPAddr *) &rdata->dns_servers[i].address);
+			nm_ip6_config_add_nameserver (priv->ac_ip6_config, &rdata->dns_servers[i].address);
 	}
 
 	if (changed & NM_NDISC_CONFIG_DNS_DOMAINS) {
 		/* Rebuild domain list from neighbor discovery cache. */
-		applied_config_reset_searches (&priv->ac_ip6_config);
+		nm_ip6_config_reset_searches (priv->ac_ip6_config);
 
 		for (i = 0; i < rdata->dns_domains_n; i++)
-			applied_config_add_search (&priv->ac_ip6_config, rdata->dns_domains[i].domain);
+			nm_ip6_config_add_search (priv->ac_ip6_config, rdata->dns_domains[i].domain);
 	}
 
 	if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) {
@@ -8387,21 +7692,16 @@ ndisc_ra_timeout (NMNDisc *ndisc, NMDevice *self)
 		 * ever receive one, then time out IPv6.  But if there is other
 		 * IPv6 configuration, like manual IPv6 addresses or external IPv6
 		 * config, consider that sufficient for IPv6 success.
-		 *
-		 * FIXME: it doesn't seem correct to determine this based on which
-		 *        addresses we find inside priv->ip_config_6.
 		 */
-		if (   priv->ip_config_6
-		    && nm_ip6_config_find_first_address (priv->ip_config_6,
-		                                           NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
-		                                         | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))
+		if (   priv->ip6_config
+		    && nm_ip6_config_get_address_first_nontentative (priv->ip6_config, FALSE))
 			nm_device_activate_schedule_ip6_config_result (self);
 		else
 			nm_device_activate_schedule_ip6_config_timeout (self);
 	}
 }
 
-static void
+static gboolean
 addrconf6_start_with_link_ready (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -8419,10 +7719,12 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	}
 
 	/* Apply any manual configuration before starting RA */
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+	if (!ip6_config_merge_and_apply (self, TRUE)) {
 		_LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration");
+		g_clear_object (&priv->con_ip6_config);
+	}
 
-	/* FIXME: These sysctls would probably be better set by the lndp ndisc itself. */
+	/* XXX: These sysctls would probably be better set by the lndp ndisc itself. */
 	switch (nm_ndisc_get_node_type (priv->ndisc)) {
 	case NM_NDISC_NODE_TYPE_HOST:
 		/* Accepting prefixes from discovered routers. */
@@ -8453,7 +7755,7 @@ addrconf6_start_with_link_ready (NMDevice *self)
 
 	ndisc_set_router_config (priv->ndisc, self);
 	nm_ndisc_start (priv->ndisc);
-	return;
+	return TRUE;
 }
 
 static NMNDiscNodeType
@@ -8476,6 +7778,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
+	NMActStageReturn ret;
 	NMSettingIP6Config *s_ip6 = NULL;
 	GError *error = NULL;
 	NMUtilsStableType stable_type;
@@ -8484,8 +7787,11 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
 
-	nm_assert (!applied_config_get_current (&priv->ac_ip6_config));
-	applied_config_clear (&priv->ac_ip6_config);
+	g_warn_if_fail (priv->ac_ip6_config == NULL);
+	if (priv->ac_ip6_config) {
+		g_object_unref (priv->ac_ip6_config);
+		priv->ac_ip6_config = NULL;
+	}
 
 	g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref);
 	nm_clear_g_source (&priv->rt6_temporary_not_available_id);
@@ -8522,14 +7828,15 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 		nm_device_add_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, TRUE);
 
 	/* ensure link local is ready... */
-	if (!linklocal6_start (self)) {
-		/* wait for the LL address to show up */
+	ret = linklocal6_start (self);
+	if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+		/* success; wait for the LL address to show up */
 		return TRUE;
 	}
 
-	/* already have the LL address; kick off neighbor discovery */
-	addrconf6_start_with_link_ready (self);
-	return TRUE;
+	/* success; already have the LL address; kick off neighbor discovery */
+	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+	return addrconf6_start_with_link_ready (self);
 }
 
 static void
@@ -8542,7 +7849,7 @@ addrconf6_cleanup (NMDevice *self)
 
 	nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE);
 
-	applied_config_clear (&priv->ac_ip6_config);
+	g_clear_object (&priv->ac_ip6_config);
 	g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref);
 	nm_clear_g_source (&priv->rt6_temporary_not_available_id);
 	g_clear_object (&priv->ndisc);
@@ -8596,8 +7903,7 @@ restore_ip6_properties (NMDevice *self)
 	g_hash_table_iter_init (&iter, priv->ip6_saved_properties);
 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 		/* Don't touch "disable_ipv6" if we're doing userland IPv6LL */
-		if (   priv->ipv6ll_handle
-		    && nm_streq (key, "disable_ipv6"))
+		if (priv->nm_ipv6ll && strcmp (key, "disable_ipv6") == 0)
 			continue;
 		nm_device_ipv6_sysctl_set (self, key, value);
 	}
@@ -8607,7 +7913,7 @@ static inline void
 set_disable_ipv6 (NMDevice *self, const char *value)
 {
 	/* We only touch disable_ipv6 when NM is not managing the IPv6LL address */
-	if (!NM_DEVICE_GET_PRIVATE (self)->ipv6ll_handle)
+	if (NM_DEVICE_GET_PRIVATE (self)->nm_ipv6ll == FALSE)
 		nm_device_ipv6_sysctl_set (self, "disable_ipv6", value);
 }
 
@@ -8622,7 +7928,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 	                                       NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
 		return;
 
-	priv->ipv6ll_handle = enable;
+	priv->nm_ipv6ll = enable;
 	if (ifindex > 0) {
 		NMPlatformError plerr;
 		const char *detail = enable ? "enable" : "disable";
@@ -8771,14 +8077,14 @@ act_stage3_ip6_config_start (NMDevice *self,
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
 		if (   !priv->master
 		    && !nm_device_sys_iface_state_is_external (self)) {
-			gboolean ipv6ll_handle_old = priv->ipv6ll_handle;
+			gboolean old_nm_ipv6ll = priv->nm_ipv6ll;
 
 			/* When activating an IPv6 'ignore' connection we need to revert back
 			 * to kernel IPv6LL, but the kernel won't actually assign an address
 			 * to the interface until disable_ipv6 is bounced.
 			 */
 			set_nm_ipv6ll (self, FALSE);
-			if (ipv6ll_handle_old)
+			if (old_nm_ipv6ll == TRUE)
 				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 			restore_ip6_properties (self);
 		}
@@ -8789,7 +8095,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 	 * expose any ipv6 sysctls or allow presence of any addresses on the interface,
 	 * including LL, which * would make it impossible to autoconfigure MTU to a
 	 * correct value. */
-	_commit_mtu (self, priv->ip_config_4);
+	_commit_mtu (self, priv->ip4_config);
 
 	/* Any method past this point requires an IPv6LL address. Use NM-controlled
 	 * IPv6LL if this is not an assumed connection, since assumed connections
@@ -8810,6 +8116,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 	priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self),
 	                                                       nm_device_get_platform (self),
 	                                                       nm_device_get_ip_ifindex (self),
+	                                                       FALSE,
 	                                                       NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 
 	ip6_privacy = _ip6_privacy_get (self);
@@ -8822,9 +8129,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 		} else
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
-		ret = linklocal6_start (self)
-		      ? NM_ACT_STAGE_RETURN_SUCCESS
-		      : NM_ACT_STAGE_RETURN_POSTPONE;
+		ret = linklocal6_start (self);
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
 		priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED;
 		if (!dhcp6_start (self, TRUE)) {
@@ -8934,8 +8239,8 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 		/* Here we get a static IPv6 config, like for Shared where it's
 		 * autogenerated or from modems where it comes from ModemManager.
 		 */
-		nm_assert (!applied_config_get_current (&priv->ac_ip6_config));
-		applied_config_init (&priv->ac_ip6_config, ip6_config);
+		g_warn_if_fail (priv->ac_ip6_config == NULL);
+		priv->ac_ip6_config = ip6_config;
 		nm_device_activate_schedule_ip6_config_result (self);
 	} else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) {
 		_set_ip_state (self, AF_INET6, IP_DONE);
@@ -9073,7 +8378,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
 	/* Add the interface to the specified firewall zone */
 	if (priv->fw_state == FIREWALL_STATE_UNMANAGED) {
@@ -9145,7 +8450,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
 	activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET);
 }
@@ -9201,7 +8506,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request.obj);
+	g_return_if_fail (priv->act_request);
 
 	activation_source_schedule (self, activate_stage4_ip6_config_timeout, AF_INET6);
 }
@@ -9319,9 +8624,9 @@ arp_cleanup (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->acd.announcing) {
-		nm_acd_manager_destroy (priv->acd.announcing);
-		priv->acd.announcing = NULL;
+	if (priv->arping.announcing) {
+		nm_arping_manager_destroy (priv->arping.announcing);
+		priv->arping.announcing = NULL;
 	}
 }
 
@@ -9341,7 +8646,7 @@ arp_announce (NMDevice *self)
 	                                        nm_device_get_ip_ifindex (self),
 	                                        &hw_addr_len);
 
-	if (!hw_addr || hw_addr_len != ETH_ALEN)
+	if (!hw_addr_len || !hw_addr)
 		return;
 
 	/* We only care about manually-configured addresses; DHCP- and autoip-configured
@@ -9357,19 +8662,19 @@ arp_announce (NMDevice *self)
 	if (num == 0)
 		return;
 
-	priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hw_addr, hw_addr_len);
+	priv->arping.announcing = nm_arping_manager_new (nm_device_get_ip_ifindex (self));
 
 	for (i = 0; i < num; i++) {
 		NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i);
 		in_addr_t addr;
 
 		if (inet_pton (AF_INET, nm_ip_address_get_address (ip), &addr) == 1)
-			nm_acd_manager_add_address (priv->acd.announcing, addr);
+			nm_arping_manager_add_address (priv->arping.announcing, addr);
 		else
 			g_warn_if_reached ();
 	}
 
-	nm_acd_manager_announce_addresses (priv->acd.announcing);
+	nm_arping_manager_announce_addresses (priv->arping.announcing);
 }
 
 static void
@@ -9394,7 +8699,8 @@ activate_stage5_ip4_config_result (NMDevice *self)
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
-	if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) {
+	/* NULL to use the existing priv->dev_ip4_config */
+	if (!ip4_config_merge_and_apply (self, TRUE)) {
 		_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed");
 		nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		return;
@@ -9406,7 +8712,7 @@ activate_stage5_ip4_config_result (NMDevice *self)
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
 		gs_free_error GError *error = NULL;
 
-		if (!start_sharing (self, priv->ip_config_4, &error)) {
+		if (!start_sharing (self, priv->ip4_config, &error)) {
 			_LOGW (LOGD_SHARING, "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed: %s", error->message);
 			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
 			return;
@@ -9442,7 +8748,10 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf
 	g_return_if_fail (NM_IS_DEVICE (self));
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	applied_config_init (&priv->dev_ip4_config, config);
+	g_clear_object (&priv->dev_ip4_config);
+	if (config)
+		priv->dev_ip4_config = g_object_ref (config);
+
 	activation_source_schedule (self, activate_stage5_ip4_config_result, AF_INET);
 }
 
@@ -9467,32 +8776,6 @@ nm_device_activate_ip4_state_done (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE;
 }
 
-static void
-dad6_add_pending_address (NMDevice *self,
-                          NMPlatform *platform,
-                          int ifindex,
-                          const struct in6_addr *address,
-                          NMIP6Config **dad6_config)
-{
-	const NMPlatformIP6Address *pl_addr;
-
-	pl_addr = nm_platform_ip6_address_get (platform,
-	                                       ifindex,
-	                                       *address);
-	if (   pl_addr
-	    && NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_TENTATIVE)
-	    && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_DADFAILED)
-	    && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
-		_LOGt (LOGD_DEVICE, "IPv6 DAD: pending address %s",
-		       nm_platform_ip6_address_to_string (pl_addr, NULL, 0));
-
-		if (!*dad6_config)
-			*dad6_config = _ip6_config_new (self);
-
-		nm_ip6_config_add_address (*dad6_config, pl_addr);
-	}
-}
-
 /*
  * Returns a NMIP6Config containing NM-configured addresses which
  * have the tentative flag, or NULL if none is present.
@@ -9501,43 +8784,42 @@ static NMIP6Config *
 dad6_get_pending_addresses (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMIP6Config *confs[] = { (NMIP6Config *) applied_config_get_current (&priv->ac_ip6_config),
-	                         (NMIP6Config *) applied_config_get_current (&priv->dhcp6.ip6_config),
-	                         priv->con_ip_config_6,
-	                         (NMIP6Config *) applied_config_get_current (&priv->wwan_ip_config_6) };
-	const NMPlatformIP6Address *addr;
+	NMIP6Config *confs[] = { priv->ac_ip6_config,
+	                         priv->dhcp6.ip6_config,
+	                         priv->con_ip6_config,
+	                         priv->wwan_ip6_config };
+	const NMPlatformIP6Address *addr, *pl_addr;
 	NMIP6Config *dad6_config = NULL;
 	NMDedupMultiIter ipconf_iter;
 	guint i;
 	int ifindex;
-	NMPlatform *platform;
 
 	ifindex = nm_device_get_ip_ifindex (self);
 	g_return_val_if_fail (ifindex > 0, NULL);
 
-	platform = nm_device_get_platform (self);
-
-	if (priv->ipv6ll_has) {
-		dad6_add_pending_address (self,
-		                          platform,
-		                          ifindex,
-		                          &priv->ipv6ll_addr,
-		                          &dad6_config);
-	}
-
 	/* We are interested only in addresses that we have explicitly configured,
 	 * not in externally added ones.
 	 */
 	for (i = 0; i < G_N_ELEMENTS (confs); i++) {
-		if (!confs[i])
-			continue;
-
-		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) {
-			dad6_add_pending_address (self,
-			                          platform,
-			                          ifindex,
-			                          &addr->address,
-			                          &dad6_config);
+		if (confs[i]) {
+
+			nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) {
+				pl_addr = nm_platform_ip6_address_get (nm_device_get_platform (self),
+				                                       ifindex,
+				                                       addr->address);
+				if (   pl_addr
+				    && NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_TENTATIVE)
+				    && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_DADFAILED)
+				    && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
+					_LOGt (LOGD_DEVICE, "IPv6 DAD: pending address %s",
+					       nm_platform_ip6_address_to_string (pl_addr, NULL, 0));
+
+					if (!dad6_config)
+						dad6_config = _ip6_config_new (self);
+
+					nm_ip6_config_add_address (dad6_config, pl_addr);
+				}
+			}
 		}
 	}
 
@@ -9569,10 +8851,10 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
-	if (ip_config_merge_and_apply (self, AF_INET6, TRUE)) {
+	if (ip6_config_merge_and_apply (self, TRUE)) {
 		if (   priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE
 		    && priv->ip6_state == IP_CONF) {
-			if (applied_config_get_current (&priv->dhcp6.ip6_config)) {
+			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.
 				 */
@@ -9659,26 +8941,43 @@ nm_device_activate_ip6_state_done (NMDevice *self)
 /*****************************************************************************/
 
 static void
+act_request_set_cb (NMActRequest *act_request,
+                    GParamSpec *pspec,
+                    NMDevice *self)
+{
+	_notify (self, PROP_ACTIVE_CONNECTION);
+}
+
+static void
 act_request_set (NMDevice *self, NMActRequest *act_request)
 {
 	NMDevicePrivate *priv;
+	gs_unref_object NMActRequest *old_act_requst = NULL;
 
 	nm_assert (NM_IS_DEVICE (self));
 	nm_assert (!act_request || NM_IS_ACT_REQUEST (act_request));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (   !priv->act_request.visible
-	    && priv->act_request.obj == act_request)
+	if (   !priv->act_request_public
+	    && priv->act_request == act_request)
 		return;
 
 	/* always clear the public flag. The few callers that set a new @act_request
 	 * don't want that the property is public yet.  */
-	nm_dbus_track_obj_path_set (&priv->act_request,
-	                            act_request,
-	                            FALSE);
+	priv->act_request_public = FALSE;
+
+	nm_clear_g_signal_handler (priv->act_request, &priv->act_request_id);
+
+	old_act_requst = priv->act_request;
+	priv->act_request = nm_g_object_ref (act_request);
 
 	if (act_request) {
+		priv->act_request_id = g_signal_connect (act_request,
+		                                         "notify::"NM_EXPORTED_OBJECT_PATH,
+		                                         G_CALLBACK (act_request_set_cb),
+		                                         self);
+
 		switch (nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (act_request))) {
 		case NM_ACTIVATION_TYPE_EXTERNAL:
 			break;
@@ -9695,6 +8994,8 @@ act_request_set (NMDevice *self, NMActRequest *act_request)
 			break;
 		}
 	}
+
+	_notify (self, PROP_ACTIVE_CONNECTION);
 }
 
 static void
@@ -9720,9 +9021,9 @@ _update_ip4_address (NMDevice *self)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	if (   priv->ip_config_4
+	if (   priv->ip4_config
 	    && ip_config_valid (priv->state)
-	    && (address = nm_ip4_config_get_first_address (priv->ip_config_4))) {
+	    && (address = nm_ip4_config_get_first_address (priv->ip4_config))) {
 		if (address->address != priv->ip4_address) {
 			priv->ip4_address = address->address;
 			_notify (self, PROP_IP4_ADDRESS);
@@ -9816,31 +9117,37 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex)
 }
 
 static void
-_cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type)
+_cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const gboolean IS_IPv4 = (addr_family == AF_INET);
 
-	_set_ip_state (self, addr_family, IP_NONE);
+	_set_ip_state (self, AF_INET, IP_NONE);
 
-	if (nm_clear_g_source (&priv->queued_ip_config_id_x[IS_IPv4])) {
-		_LOGD (LOGD_DEVICE, "clearing queued IP%c config change",
-		       nm_utils_addr_family_to_char (addr_family));
-	}
+	if (nm_clear_g_source (&priv->queued_ip4_config_id))
+		_LOGD (LOGD_DEVICE, "clearing queued IP4 config change");
+	priv->queued_ip4_config_pending = FALSE;
 
-	if (IS_IPv4) {
-		priv->queued_ip4_config_pending = FALSE;
-		dhcp4_cleanup (self, cleanup_type, FALSE);
-		arp_cleanup (self);
-		dnsmasq_cleanup (self);
-		ipv4ll_cleanup (self);
-	} else {
-		priv->queued_ip6_config_pending = FALSE;
-		g_clear_object (&priv->dad6_ip6_config);
-		dhcp6_cleanup (self, cleanup_type, FALSE);
-		nm_clear_g_source (&priv->linklocal6_timeout_id);
-		addrconf6_cleanup (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);
+
+	_set_ip_state (self, AF_INET6, IP_NONE);
+
+	if (nm_clear_g_source (&priv->queued_ip6_config_id))
+		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
+	priv->queued_ip6_config_pending = FALSE;
+
+	g_clear_object (&priv->dad6_ip6_config);
+	dhcp6_cleanup (self, cleanup_type, FALSE);
+	linklocal6_cleanup (self);
+	addrconf6_cleanup (self);
 }
 
 gboolean
@@ -9859,7 +9166,7 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
 		gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL);
 
 		for (i = 0; argv[i]; i++) {
-			if (!g_hash_table_add (check_dups, (char *) argv[i]))
+			if (!nm_g_hash_table_add (check_dups, (char *) argv[i]))
 				nm_assert (FALSE);
 		}
 		nm_assert (g_hash_table_size (check_dups) > 0);
@@ -9910,7 +9217,8 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
 void
 nm_device_reactivate_ip4_config (NMDevice *self,
                                  NMSettingIPConfig *s_ip4_old,
-                                 NMSettingIPConfig *s_ip4_new)
+                                 NMSettingIPConfig *s_ip4_new,
+                                 gboolean force_restart)
 {
 	NMDevicePrivate *priv;
 	const char *method_old, *method_new;
@@ -9919,31 +9227,31 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->ip4_state != IP_NONE) {
-		g_clear_object (&priv->con_ip_config_4);
-		g_clear_object (&priv->ext_ip_config_4);
-		g_clear_object (&priv->dev_ip4_config.current);
-		g_clear_object (&priv->wwan_ip_config_4.current);
-		priv->con_ip_config_4 = _ip4_config_new (self);
-		nm_ip4_config_merge_setting (priv->con_ip_config_4,
+		g_clear_object (&priv->con_ip4_config);
+		g_clear_object (&priv->ext_ip4_config);
+		priv->con_ip4_config = _ip4_config_new (self);
+		nm_ip4_config_merge_setting (priv->con_ip4_config,
 		                             s_ip4_new,
-		                             _get_mdns (self),
 		                             nm_device_get_route_table (self, AF_INET, TRUE),
 		                             nm_device_get_route_metric (self, AF_INET));
 
-		method_old = s_ip4_old
-		             ? nm_setting_ip_config_get_method (s_ip4_old)
-		             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
-		method_new = s_ip4_new
-		             ? nm_setting_ip_config_get_method (s_ip4_new)
-		             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+		if (!force_restart) {
+			method_old = s_ip4_old
+			             ? nm_setting_ip_config_get_method (s_ip4_old)
+			             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+			method_new = s_ip4_new
+			             ? nm_setting_ip_config_get_method (s_ip4_new)
+			             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+			force_restart = !nm_streq0 (method_old, method_new);
+		}
 
-		if (!nm_streq0 (method_old, method_new)) {
-			_cleanup_ip_pre (self, AF_INET, CLEANUP_TYPE_DECONFIGURE);
+		if (force_restart) {
+			_cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE);
 			_set_ip_state (self, AF_INET, IP_WAIT);
 			if (!nm_device_activate_stage3_ip4_start (self))
 				_LOGW (LOGD_IP4, "Failed to apply IPv4 configuration");
 		} else {
-			if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+			if (!ip4_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
 		}
 	}
@@ -9952,7 +9260,8 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 void
 nm_device_reactivate_ip6_config (NMDevice *self,
                                  NMSettingIPConfig *s_ip6_old,
-                                 NMSettingIPConfig *s_ip6_new)
+                                 NMSettingIPConfig *s_ip6_new,
+                                 gboolean force_restart)
 {
 	NMDevicePrivate *priv;
 	const char *method_old, *method_new;
@@ -9961,34 +9270,31 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->ip6_state != IP_NONE) {
-		g_clear_object (&priv->con_ip_config_6);
-		g_clear_object (&priv->ext_ip_config_6);
-		g_clear_object (&priv->ac_ip6_config.current);
-		g_clear_object (&priv->dhcp6.ip6_config.current);
-		g_clear_object (&priv->wwan_ip_config_6.current);
-		if (   priv->ipv6ll_handle
-		    && !IN6_IS_ADDR_UNSPECIFIED (&priv->ipv6ll_addr))
-			priv->ipv6ll_has = TRUE;
-		priv->con_ip_config_6 = _ip6_config_new (self);
-		nm_ip6_config_merge_setting (priv->con_ip_config_6,
+		g_clear_object (&priv->con_ip6_config);
+		g_clear_object (&priv->ext_ip6_config);
+		priv->con_ip6_config = _ip6_config_new (self);
+		nm_ip6_config_merge_setting (priv->con_ip6_config,
 		                             s_ip6_new,
 		                             nm_device_get_route_table (self, AF_INET6, TRUE),
 		                             nm_device_get_route_metric (self, AF_INET6));
 
-		method_old = s_ip6_old
-		             ? nm_setting_ip_config_get_method (s_ip6_old)
-		             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
-		method_new = s_ip6_new
-		             ? nm_setting_ip_config_get_method (s_ip6_new)
-		             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+		if (!force_restart) {
+			method_old = s_ip6_old
+			             ? nm_setting_ip_config_get_method (s_ip6_old)
+			             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+			method_new = s_ip6_new
+			             ? nm_setting_ip_config_get_method (s_ip6_new)
+			             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+			force_restart = !nm_streq0 (method_old, method_new);
+		}
 
-		if (!nm_streq0 (method_old, method_new)) {
-			_cleanup_ip_pre (self, AF_INET6, CLEANUP_TYPE_DECONFIGURE);
+		if (force_restart) {
+			_cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE);
 			_set_ip_state (self, AF_INET6, IP_WAIT);
 			if (!nm_device_activate_stage3_ip6_start (self))
 				_LOGW (LOGD_IP6, "Failed to apply IPv6 configuration");
 		} else {
-			if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+			if (!ip6_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
 		}
 	}
@@ -10033,7 +9339,7 @@ can_reapply_change (NMDevice *self, const char *setting_name,
 		 * allowed to differ.
 		 *
 		 * This includes UUID, there is no principal problem with reapplying a
-		 * connection and changing its UUID. In fact, disallowing it makes it
+		 * connection and changing it's UUID. In fact, disallowing it makes it
 		 * cumbersome for the user to reapply any connection but the original
 		 * settings-connection. */
 		return nm_device_hash_check_invalid_keys (diffs,
@@ -10159,7 +9465,7 @@ check_and_reapply_connection (NMDevice *self,
 	}
 
 	if (   version_id != 0
-	    && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request.obj)) {
+	    && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)) {
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_VERSION_ID_MISMATCH,
@@ -10172,10 +9478,10 @@ check_and_reapply_connection (NMDevice *self,
 	 *************************************************************************/
 
 	if (diffs)
-		nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request.obj);
+		nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request);
 
 	_LOGD (LOGD_DEVICE, "reapply (version-id %llu%s)",
-	       (unsigned long long) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request.obj)),
+	       (unsigned long long) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request)),
 	       diffs ? "" : " (unmodified)");
 
 	if (diffs) {
@@ -10231,8 +9537,8 @@ check_and_reapply_connection (NMDevice *self,
 	s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
 	s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
 
-	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new);
-	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new);
+	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new, TRUE);
+	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new, TRUE);
 
 	reactivate_proxy_config (self);
 
@@ -10301,33 +9607,25 @@ reapply_cb (NMDevice *self,
 }
 
 static void
-impl_device_reapply (NMDBusObject *obj,
-                     const NMDBusInterfaceInfoExtended *interface_info,
-                     const NMDBusMethodInfoExtended *method_info,
-                     GDBusConnection *dbus_connection,
-                     const char *sender,
-                     GDBusMethodInvocation *invocation,
-                     GVariant *parameters)
+impl_device_reapply (NMDevice *self,
+                     GDBusMethodInvocation *context,
+                     GVariant *settings,
+                     guint64 version_id,
+                     guint32 flags)
 {
-	NMDevice *self = NM_DEVICE (obj);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection;
 	NMConnection *connection = NULL;
 	GError *error = NULL;
 	ReapplyData *reapply_data;
-	gs_unref_variant GVariant *settings = NULL;
-	guint64 version_id;
-	guint32 flags;
-
-	g_variant_get (parameters, "(@a{sa{sv}}tu)", &settings, &version_id, &flags);
 
 	/* No flags supported as of now. */
 	if (flags != 0) {
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_FAILED,
 		                             "Invalid flags specified");
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, invocation, error->message);
-		g_dbus_method_invocation_take_error (invocation, error);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -10335,8 +9633,8 @@ impl_device_reapply (NMDBusObject *obj,
 		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, NULL, invocation, error->message);
-		g_dbus_method_invocation_take_error (invocation, error);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -10351,8 +9649,8 @@ impl_device_reapply (NMDBusObject *obj,
 		                                                  &error);
 		if (!connection) {
 			g_prefix_error (&error, "The settings specified are invalid: ");
-			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, invocation, error->message);
-			g_dbus_method_invocation_take_error (invocation, error);
+			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
+			g_dbus_method_invocation_take_error (context, error);
 			return;
 		}
 		nm_connection_clear_secrets (connection);
@@ -10365,8 +9663,9 @@ impl_device_reapply (NMDBusObject *obj,
 	} else
 		reapply_data = NULL;
 
+	/* Ask the manager to authenticate this request for us */
 	g_signal_emit (self, signals[AUTH_REQUEST], 0,
-	               invocation,
+	               context,
 	               nm_device_get_applied_connection (self),
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	               TRUE,
@@ -10425,44 +9724,40 @@ get_applied_connection_cb (NMDevice *self,
 	g_dbus_method_invocation_return_value (context,
 	                                       g_variant_new ("(@a{sa{sv}}t)",
 	                                                      settings,
-	                                                      nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request.obj)));
+	                                                      nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)));
 }
 
 static void
-impl_device_get_applied_connection (NMDBusObject *obj,
-                                    const NMDBusInterfaceInfoExtended *interface_info,
-                                    const NMDBusMethodInfoExtended *method_info,
-                                    GDBusConnection *connection,
-                                    const char *sender,
-                                    GDBusMethodInvocation *invocation,
-                                    GVariant *parameters)
+impl_device_get_applied_connection (NMDevice *self,
+                                    GDBusMethodInvocation *context,
+                                    guint32 flags)
 {
-	NMDevice *self = NM_DEVICE (obj);
 	NMConnection *applied_connection;
-	guint32 flags;
+	GError *error = NULL;
 
-	g_variant_get (parameters, "(u)", &flags);
+	g_return_if_fail (NM_IS_DEVICE (self));
 
 	/* No flags supported as of now. */
 	if (flags != 0) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_FAILED,
-		                                               "Invalid flags specified");
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_FAILED,
+		                             "Invalid flags specified");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
 	applied_connection = nm_device_get_applied_connection (self);
 	if (!applied_connection) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_ACTIVE,
-		                                               "Device is not activated");
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "Device is not activated");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
+	/* Ask the manager to authenticate this request for us */
 	g_signal_emit (self, signals[AUTH_REQUEST], 0,
-	               invocation,
+	               context,
 	               applied_connection,
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	               TRUE,
@@ -10611,31 +9906,25 @@ _clear_queued_act_request (NMDevicePrivate *priv)
 }
 
 static void
-impl_device_disconnect (NMDBusObject *obj,
-                        const NMDBusInterfaceInfoExtended *interface_info,
-                        const NMDBusMethodInfoExtended *method_info,
-                        GDBusConnection *dbus_connection,
-                        const char *sender,
-                        GDBusMethodInvocation *invocation,
-                        GVariant *parameters)
+impl_device_disconnect (NMDevice *self, GDBusMethodInvocation *context)
 {
-	NMDevice *self = NM_DEVICE (obj);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
+	GError *error = NULL;
 
-	if (!priv->act_request.obj) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_ACTIVE,
-		                                               "This device is not active");
+	if (NM_DEVICE_GET_PRIVATE (self)->act_request == NULL) {
+		error = g_error_new_literal (NM_DEVICE_ERROR,
+		                             NM_DEVICE_ERROR_NOT_ACTIVE,
+		                             "This device is not active");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
 	connection = nm_device_get_applied_connection (self);
-	nm_assert (connection);
+	g_assert (connection);
 
+	/* Ask the manager to authenticate this request for us */
 	g_signal_emit (self, signals[AUTH_REQUEST], 0,
-	               invocation,
+	               context,
 	               connection,
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	               TRUE,
@@ -10667,27 +9956,21 @@ delete_cb (NMDevice *self,
 }
 
 static void
-impl_device_delete (NMDBusObject *obj,
-                    const NMDBusInterfaceInfoExtended *interface_info,
-                    const NMDBusMethodInfoExtended *method_info,
-                    GDBusConnection *connection,
-                    const char *sender,
-                    GDBusMethodInvocation *invocation,
-                    GVariant *parameters)
+impl_device_delete (NMDevice *self, GDBusMethodInvocation *context)
 {
-	NMDevice *self = NM_DEVICE (obj);
+	GError *error = NULL;
 
-	if (   !nm_device_is_software (self)
-	    || !nm_device_is_real (self)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_SOFTWARE,
-		                                               "This device is not a software device or is not realized");
+	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 or is not realized");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
+	/* Ask the manager to authenticate this request for us */
 	g_signal_emit (self, signals[AUTH_REQUEST], 0,
-	               invocation,
+	               context,
 	               NULL,
 	               NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	               TRUE,
@@ -10811,8 +10094,8 @@ nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection)
 	    && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request)))
 		_clear_queued_act_request (priv);
 
-	if (   priv->act_request.obj
-	    && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request.obj))
+	if (   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,
@@ -10828,7 +10111,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 
 	must_queue = _carrier_wait_check_act_request_must_queue (self, req);
 
-	if (   !priv->act_request.obj
+	if (   !priv->act_request
 	    && !must_queue
 	    && nm_device_is_real (self)) {
 		_device_activate (self, req);
@@ -10843,7 +10126,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 	_LOGD (LOGD_DEVICE, "queue activation request waiting for %s", must_queue ? "carrier" : "currently active connection to disconnect");
 
 	/* Deactivate existing activation request first */
-	if (priv->act_request.obj) {
+	if (priv->act_request) {
 		_LOGI (LOGD_DEVICE, "disconnecting for new activation request.");
 		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
@@ -10928,155 +10211,116 @@ nm_device_get_ip4_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->ip_config_4;
+	return NM_DEVICE_GET_PRIVATE (self)->ip4_config;
 }
 
 
 static gboolean
-nm_device_set_ip_config (NMDevice *self,
-                         int addr_family,
-                         NMIPConfig *new_config,
-                         gboolean commit,
-                         GPtrArray *ip4_dev_route_blacklist)
+nm_device_set_ip4_config (NMDevice *self,
+                          NMIP4Config *new_config,
+                          gboolean commit,
+                          GPtrArray *ip4_dev_route_blacklist)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const gboolean IS_IPv4 = (addr_family == AF_INET);
-	NMIPConfig *old_config;
+	NMDevicePrivate *priv;
+	NMIP4Config *old_config = NULL;
 	gboolean has_changes = FALSE;
 	gboolean success = TRUE;
-	NMSettingsConnection *settings_connection;
 
-	nm_assert_addr_family (addr_family);
-	nm_assert (!new_config || nm_ip_config_get_addr_family (new_config) == addr_family);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	_LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)",
+	       commit, new_config);
+
 	nm_assert (   !new_config
 	           || (   new_config
 	               && ({
 	                    int ip_ifindex = nm_device_get_ip_ifindex (self);
 
 	                    (   ip_ifindex > 0
-	                     && ip_ifindex == nm_ip_config_get_ifindex (new_config));
+	                     && ip_ifindex == nm_ip4_config_get_ifindex (new_config));
 	                  })));
-	nm_assert (IS_IPv4 || !ip4_dev_route_blacklist);
 
-	_LOGD (LOGD_IP_from_af (addr_family),
-	       "ip%c-config: update (commit=%d, new-config=%p)",
-	       nm_utils_addr_family_to_char (addr_family),
-	       commit,
-	       new_config);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	old_config = priv->ip4_config;
 
 	/* Always commit to nm-platform to update lifetimes */
 	if (commit && new_config) {
-
-		_commit_mtu (self,
-		             IS_IPv4
-		               ? NM_IP4_CONFIG (new_config)
-		               : priv->ip_config_4);
-
-		if (IS_IPv4) {
-			success = nm_ip4_config_commit (NM_IP4_CONFIG (new_config),
-			                                nm_device_get_platform (self),
-			                                nm_device_get_route_table (self, addr_family, FALSE)
-			                                  ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL
-			                                  : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN);
-			nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self),
-			                                         nm_ip_config_get_ifindex (new_config),
-			                                         ip4_dev_route_blacklist);
+		_commit_mtu (self, new_config);
+		success = nm_ip4_config_commit (new_config,
+		                                nm_device_get_platform (self),
+		                                nm_device_get_route_table (self, AF_INET, FALSE)
+		                                  ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL
+		                                  : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN);
+		nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self),
+		                                         nm_ip4_config_get_ifindex (new_config),
+		                                         ip4_dev_route_blacklist);
+	}
+
+	if (new_config) {
+		if (old_config) {
+			/* has_changes is set only on relevant changes, because when the configuration changes,
+			 * this causes a re-read and reset. This should only happen for relevant changes */
+			nm_ip4_config_replace (old_config, new_config, &has_changes);
+			if (has_changes) {
+				_LOGD (LOGD_IP4, "ip4-config: update IP4Config instance (%s)",
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
+			}
 		} else {
-			gs_unref_ptrarray GPtrArray *temporary_not_available = NULL;
-
-			success = nm_ip6_config_commit (NM_IP6_CONFIG (new_config),
-			                                nm_device_get_platform (self),
-			                                nm_device_get_route_table (self, addr_family, FALSE)
-			                                  ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL
-			                                  : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
-			                                &temporary_not_available);
-
-			if (!_rt6_temporary_not_available_set (self, temporary_not_available))
-				success = FALSE;
-		}
-	}
+			has_changes = TRUE;
+			priv->ip4_config = g_object_ref (new_config);
 
-	old_config = priv->ip_config_x[IS_IPv4];
+			if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config)))
+				nm_exported_object_export (NM_EXPORTED_OBJECT (new_config));
 
-	if (new_config && old_config) {
-		/* has_changes is set only on relevant changes, because when the configuration changes,
-		 * this causes a re-read and reset. This should only happen for relevant changes */
-		nm_ip_config_replace (old_config, new_config, &has_changes);
-		if (has_changes) {
-			_LOGD (LOGD_IP_from_af (addr_family),
-			       "ip%c-config: update IP Config instance (%s)",
-			       nm_utils_addr_family_to_char (addr_family),
-			       nm_dbus_object_get_path (NM_DBUS_OBJECT (old_config)));
+			_LOGD (LOGD_IP4, "ip4-config: set IP4Config instance (%s)",
+			       nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config)));
 		}
-	} else if (new_config /*&& !old_config*/) {
-		has_changes = TRUE;
-		priv->ip_config_x[IS_IPv4] = g_object_ref (new_config);
-		if (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (new_config)))
-			nm_dbus_object_export (NM_DBUS_OBJECT (new_config));
-
-		_LOGD (LOGD_IP_from_af (addr_family),
-		       "ip%c-config: set IP Config instance (%s)",
-		       nm_utils_addr_family_to_char (addr_family),
-		       nm_dbus_object_get_path (NM_DBUS_OBJECT (new_config)));
-	} else if (old_config /*&& !new_config*/) {
+	} else if (old_config) {
 		has_changes = TRUE;
-		priv->ip_config_x[IS_IPv4] = NULL;
-		_LOGD (LOGD_IP_from_af (addr_family),
-		       "ip%c-config: clear IP Config instance (%s)",
-		       nm_utils_addr_family_to_char (addr_family),
-		       nm_dbus_object_get_path (NM_DBUS_OBJECT (old_config)));
-		if (IS_IPv4) {
-			/* Device config is invalid if combined config is invalid */
-			applied_config_clear (&priv->dev_ip4_config);
-		} else
-			priv->needs_ip6_subnet = FALSE;
+		priv->ip4_config = NULL;
+		_LOGD (LOGD_IP4, "ip4-config: clear IP4Config instance (%s)",
+		       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);
 	}
 
-	if (IS_IPv4) {
-		if (!nm_device_sys_iface_state_is_external_or_assume (self))
-			ip4_rp_filter_update (self);
-	}
+	concheck_periodic_update (self);
 
-	if (has_changes) {
+	if (!nm_device_sys_iface_state_is_external_or_assume (self))
+		ip4_rp_filter_update (self);
 
-		if (IS_IPv4)
-			_update_ip4_address (self);
+	if (has_changes) {
+		NMSettingsConnection *settings_connection;
 
-		if (old_config != priv->ip_config_x[IS_IPv4])
-			_notify (self, IS_IPv4 ? PROP_IP4_CONFIG : PROP_IP6_CONFIG);
+		_update_ip4_address (self);
 
-		g_signal_emit (self,
-		               signals[IS_IPv4 ? IP4_CONFIG_CHANGED : IP6_CONFIG_CHANGED],
-		               0,
-		               priv->ip_config_x[IS_IPv4],
-		               old_config);
+		if (old_config != priv->ip4_config)
+			_notify (self, PROP_IP4_CONFIG);
+		g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, old_config);
 
-		if (old_config != priv->ip_config_x[IS_IPv4])
-			nm_dbus_object_clear_and_unexport (&old_config);
+		if (old_config != priv->ip4_config)
+			nm_exported_object_clear_and_unexport (&old_config);
 
 		if (   nm_device_sys_iface_state_is_external (self)
 		    && (settings_connection = nm_device_get_settings_connection (self))
 		    && NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
-		                     NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
-		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) {
+		                     NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)
+		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
+			NMSetting *s_ip4;
+
 			g_object_freeze_notify (G_OBJECT (settings_connection));
-			nm_connection_add_setting (NM_CONNECTION (settings_connection),
-			                           IS_IPv4
-			                             ? nm_ip4_config_create_setting (priv->ip_config_4)
-			                             : nm_ip6_config_create_setting (priv->ip_config_6));
+
+			nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP4_CONFIG);
+			s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
+			nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip4);
+
 			g_object_thaw_notify (G_OBJECT (settings_connection));
 		}
 
 		nm_device_queue_recheck_assume (self);
-
-		if (!IS_IPv4) {
-			if (priv->ndisc)
-				ndisc_set_router_config (priv->ndisc, self);
-		}
 	}
 
-	nm_assert (!old_config || old_config == priv->ip_config_x[IS_IPv4]);
-
 	return success;
 }
 
@@ -11123,11 +10367,11 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co
 	nm_assert (!old || nm_ip4_config_get_ifindex (old) == nm_device_get_ip_ifindex (self));
 	nm_assert (!config || nm_ip4_config_get_ifindex (config) == nm_device_get_ip_ifindex (self));
 
-	if (!_replace_vpn_config_in_list (&priv->vpn_configs_4, (GObject *) old, (GObject *) config))
+	if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config))
 		return;
 
 	/* NULL to use existing configs */
-	if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+	if (!ip4_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP4, "failed to set VPN routes for device");
 }
 
@@ -11136,11 +10380,125 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	applied_config_init (&priv->wwan_ip_config_4, config);
-	if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+	if (priv->wwan_ip4_config == config)
+		return;
+
+	g_clear_object (&priv->wwan_ip4_config);
+	if (config)
+		priv->wwan_ip4_config = g_object_ref (config);
+
+	/* NULL to use existing configs */
+	if (!ip4_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration");
 }
 
+static gboolean
+nm_device_set_ip6_config (NMDevice *self,
+                          NMIP6Config *new_config,
+                          gboolean commit)
+{
+	NMDevicePrivate *priv;
+	NMIP6Config *old_config = NULL;
+	gboolean has_changes = FALSE;
+	gboolean success = TRUE;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	_LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)",
+	       commit, new_config);
+
+	nm_assert (   !new_config
+	           || (   new_config
+	               && ({
+	                    int ip_ifindex = nm_device_get_ip_ifindex (self);
+
+	                    (   ip_ifindex > 0
+	                     && ip_ifindex == nm_ip6_config_get_ifindex (new_config));
+	                  })));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	old_config = priv->ip6_config;
+
+	/* Always commit to nm-platform to update lifetimes */
+	if (commit && new_config) {
+		gs_unref_ptrarray GPtrArray *temporary_not_available = NULL;
+
+		_commit_mtu (self, priv->ip4_config);
+
+		success = nm_ip6_config_commit (new_config,
+		                                nm_device_get_platform (self),
+		                                nm_device_get_route_table (self, AF_INET6, FALSE)
+		                                  ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL
+		                                  : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
+		                                &temporary_not_available);
+
+		if (!_rt6_temporary_not_available_set (self, temporary_not_available))
+			success = FALSE;
+	}
+
+	if (new_config) {
+		if (old_config) {
+			/* has_changes is set only on relevant changes, because when the configuration changes,
+			 * this causes a re-read and reset. This should only happen for relevant changes */
+			nm_ip6_config_replace (old_config, new_config, &has_changes);
+			if (has_changes) {
+				_LOGD (LOGD_IP6, "ip6-config: update IP6Config instance (%s)",
+				       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_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config)))
+				nm_exported_object_export (NM_EXPORTED_OBJECT (new_config));
+
+			_LOGD (LOGD_IP6, "ip6-config: set IP6Config instance (%s)",
+			       nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config)));
+		}
+	} else if (old_config) {
+		has_changes = TRUE;
+		priv->ip6_config = NULL;
+		priv->needs_ip6_subnet = FALSE;
+		_LOGD (LOGD_IP6, "ip6-config: clear IP6Config instance (%s)",
+		       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
+	}
+
+	if (has_changes) {
+		NMSettingsConnection *settings_connection;
+
+		if (old_config != priv->ip6_config)
+			_notify (self, PROP_IP6_CONFIG);
+		g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config);
+
+		if (old_config != priv->ip6_config)
+			nm_exported_object_clear_and_unexport (&old_config);
+
+		if (   nm_device_sys_iface_state_is_external (self)
+		    && (settings_connection = nm_device_get_settings_connection (self))
+		    && NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
+		                     NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)
+		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
+			NMSetting *s_ip6;
+
+			g_object_freeze_notify (G_OBJECT (settings_connection));
+
+			nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP6_CONFIG);
+			s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
+			nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip6);
+
+			g_object_thaw_notify (G_OBJECT (settings_connection));
+		}
+
+		nm_device_queue_recheck_assume (self);
+
+		if (priv->ndisc)
+			ndisc_set_router_config (priv->ndisc, self);
+	}
+
+	return success;
+}
+
 void
 nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config)
 {
@@ -11151,11 +10509,11 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co
 	nm_assert (!old || nm_ip6_config_get_ifindex (old) == nm_device_get_ip_ifindex (self));
 	nm_assert (!config || nm_ip6_config_get_ifindex (config) == nm_device_get_ip_ifindex (self));
 
-	if (!_replace_vpn_config_in_list (&priv->vpn_configs_6, (GObject *) old, (GObject *) config))
+	if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config))
 		return;
 
 	/* NULL to use existing configs */
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "failed to set VPN routes for device");
 }
 
@@ -11164,8 +10522,15 @@ nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	applied_config_init (&priv->wwan_ip_config_6, config);
-	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+	if (priv->wwan_ip6_config == config)
+		return;
+
+	g_clear_object (&priv->wwan_ip6_config);
+	if (config)
+		priv->wwan_ip6_config = g_object_ref (config);
+
+	/* NULL to use existing configs */
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "failed to set WWAN IPv6 configuration");
 }
 
@@ -11182,7 +10547,7 @@ nm_device_get_ip6_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->ip_config_6;
+	return NM_DEVICE_GET_PRIVATE (self)->ip6_config;
 }
 
 /*****************************************************************************/
@@ -11373,7 +10738,7 @@ start_ping (NMDevice *self,
 	priv->gw_ping.log_domain = log_domain;
 	priv->gw_ping.address = g_strdup (address);
 	priv->gw_ping.binary = g_strdup (binary);
-	priv->gw_ping.deadline = timeout + 10; /* the proper termination is enforced by a timer */
+	priv->gw_ping.deadline = timeout + 10;	/* the proper termination is enforced by a timer */
 
 	if (spawn_ping (self)) {
 		priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self);
@@ -11416,15 +10781,15 @@ nm_device_start_ip_check (NMDevice *self)
 	if (timeout) {
 		const NMPObject *gw;
 
-		if (priv->ip_config_4 && priv->ip4_state == IP_DONE) {
-			gw = nm_ip4_config_best_default_route_get (priv->ip_config_4);
+		if (priv->ip4_config && priv->ip4_state == IP_DONE) {
+			gw = nm_ip4_config_best_default_route_get (priv->ip4_config);
 			if (gw) {
 				nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (gw)->gateway, buf);
 				ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL);
 				log_domain = LOGD_IP4;
 			}
-		} else if (priv->ip_config_6 && priv->ip6_state == IP_DONE) {
-			gw = nm_ip6_config_best_default_route_get (priv->ip_config_6);
+		} else if (priv->ip6_config && priv->ip6_state == IP_DONE) {
+			gw = nm_ip6_config_best_default_route_get (priv->ip6_config);
 			if (gw) {
 				nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (gw)->gateway, buf);
 				ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL);
@@ -11564,11 +10929,11 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 	/* when the link comes up, we must restore IP configuration if necessary. */
 	if (priv->ip4_state == IP_DONE) {
-		if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+		if (!ip4_config_merge_and_apply (self, TRUE))
 			_LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up");
 	}
 	if (priv->ip6_state == IP_DONE) {
-		if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+		if (!ip6_config_merge_and_apply (self, TRUE))
 			_LOGW (LOGD_IP6, "failed applying IP6 config after bringing link up");
 	}
 
@@ -11633,37 +10998,122 @@ nm_device_get_firmware_missing (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->firmware_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;
+
+	g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+	leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (),
+	                                               nm_device_get_multi_index (self),
+	                                               AF_INET,
+	                                               ip_iface,
+	                                               ip_ifindex,
+	                                               nm_connection_get_uuid (connection),
+	                                               nm_device_get_route_table (self, AF_INET, TRUE),
+	                                               nm_device_get_route_metric (self, AF_INET));
+	for (liter = leases; liter && !found; liter = liter->next) {
+		NMIP4Config *lease_config = liter->data;
+		const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (lease_config);
+		const NMPObject *gw1, *gw2;
+
+		g_assert (address);
+		if (!nm_ip4_config_address_exists (ext_ip4_config, address))
+			continue;
+		gw1 = nm_ip4_config_best_default_route_get (lease_config);
+		if (!gw1)
+			continue;
+		gw2 = nm_ip4_config_best_default_route_get (ext_ip4_config);
+		if (!gw2)
+			continue;
+		if (NMP_OBJECT_CAST_IP4_ROUTE (gw1)->gateway != NMP_OBJECT_CAST_IP4_ROUTE (gw2)->gateway)
+			continue;
+		found = g_object_ref (lease_config);
+	}
+
+	g_slist_free_full (leases, g_object_unref);
+	return found;
+}
+
 static void
-intersect_ext_config (NMDevice *self, AppliedConfig *config)
+capture_lease_config (NMDevice *self,
+                      NMIP4Config *ext_ip4_config,
+                      NMIP4Config **out_ip4_config,
+                      NMIP6Config *ext_ip6_config,
+                      NMIP6Config **out_ip6_config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMIPConfig *ext;
-	guint32 penalty;
-	int family;
+	NMSettingsConnection *const*connections;
+	guint i;
+	gboolean dhcp_used = FALSE;
+	NMDedupMultiIter ipconf_iter;
+
+	/* Ensure at least one address on the device has a non-infinite lifetime,
+	 * otherwise DHCP cannot possibly be active on the device right now.
+	 */
+	if (ext_ip4_config && out_ip4_config) {
+		const NMPlatformIP4Address *addr;
+
+		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ext_ip4_config, &addr) {
+			if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
+				dhcp_used = TRUE;
+				break;
+			}
+		}
+	} else if (ext_ip6_config && out_ip6_config) {
+		const NMPlatformIP6Address *addr;
+
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ext_ip6_config, &addr) {
+			if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
+				dhcp_used = TRUE;
+				break;
+			}
+		}
+	} else {
+		g_return_if_fail (   (ext_ip6_config && out_ip6_config)
+		                  || (ext_ip4_config && out_ip4_config));
+	}
 
-	if (!config->orig)
+	if (!dhcp_used)
 		return;
 
-	family = nm_ip_config_get_addr_family (config->orig);
-	penalty = default_route_metric_penalty_get (self, family);
-	ext = family == AF_INET
-	      ? (NMIPConfig *) priv->ext_ip_config_4
-	      : (NMIPConfig *) priv->ext_ip_config_6;
+	connections = nm_settings_get_connections (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMConnection *candidate = (NMConnection *) connections[i];
+		const char *method;
+
+		if (!nm_device_check_connection_compatible (self, candidate))
+			continue;
 
-	if (config->current)
-		nm_ip_config_intersect (config->current, ext, penalty);
-	else {
-		config->current = nm_ip_config_intersect_alloc (config->orig,
-		                                                ext,
-		                                                penalty);
+		/* IPv4 leases */
+		method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
+		if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
+			*out_ip4_config = find_ip4_lease_config (self, candidate, ext_ip4_config);
+			if (*out_ip4_config)
+				return;
+		}
+
+		/* IPv6 leases */
+		method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
+		if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
+			/* FIXME: implement find_ip6_lease_config() */
+		}
 	}
 }
 
 static gboolean
-update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_configs)
+update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex;
+	gboolean capture_resolv_conf;
 	GSList *iter;
 
 	nm_assert_addr_family (addr_family);
@@ -11672,110 +11122,122 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 	if (!ifindex)
 		return FALSE;
 
+	capture_resolv_conf =    initial
+	                      && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ());
+
 	if (addr_family == AF_INET) {
 
-		g_clear_object (&priv->ext_ip_config_4);
-		priv->ext_ip_config_4 = nm_ip4_config_capture (nm_device_get_multi_index (self),
+		g_clear_object (&priv->ext_ip4_config);
+		priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self),
 		                                              nm_device_get_platform (self),
-		                                              ifindex);
-		if (priv->ext_ip_config_4) {
+		                                              ifindex,
+		                                              capture_resolv_conf);
+		if (priv->ext_ip4_config) {
+			if (initial) {
+				g_clear_object (&priv->dev_ip4_config);
+				capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL);
+			}
+
 			if (intersect_configs) {
 				/* This function was called upon external changes. Remove the configuration
 				 * (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_ip_config_4) {
-					nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4,
+				if (priv->con_ip4_config) {
+					nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config,
 					                         default_route_metric_penalty_get (self, AF_INET));
 				}
-
-				intersect_ext_config (self, &priv->dev_ip4_config);
-				intersect_ext_config (self, &priv->wwan_ip_config_4);
-
-				for (iter = priv->vpn_configs_4; iter; iter = iter->next)
-					nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, 0);
+				if (priv->dev_ip4_config) {
+					nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config,
+					                         default_route_metric_penalty_get (self, AF_INET));
+				}
+				if (priv->wwan_ip4_config) {
+					nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config,
+					                         default_route_metric_penalty_get (self, AF_INET));
+				}
+				for (iter = priv->vpn4_configs; iter; iter = iter->next)
+					nm_ip4_config_intersect (iter->data, priv->ext_ip4_config, 0);
 			}
 
-			/* Remove parts from ext_ip_config_4 to only contain the information that
+			/* Remove parts from ext_ip4_config to only contain the information that
 			 * was configured externally -- we already have the same configuration from
 			 * internal origins. */
-			if (priv->con_ip_config_4) {
-				nm_ip4_config_subtract (priv->ext_ip_config_4, priv->con_ip_config_4,
+			if (priv->con_ip4_config) {
+				nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config,
 				                        default_route_metric_penalty_get (self, AF_INET));
 			}
-			if (applied_config_get_current (&priv->dev_ip4_config)) {
-				nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4,
-				                       applied_config_get_current (&priv->dev_ip4_config),
-				                       default_route_metric_penalty_get (self, AF_INET));
+			if (priv->dev_ip4_config) {
+				nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config,
+				                        default_route_metric_penalty_get (self, AF_INET));
 			}
-			if (applied_config_get_current (&priv->wwan_ip_config_4)) {
-				nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4,
-				                       applied_config_get_current (&priv->wwan_ip_config_4),
-				                       default_route_metric_penalty_get (self, AF_INET));
+			if (priv->wwan_ip4_config) {
+				nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config,
+				                        default_route_metric_penalty_get (self, AF_INET));
 			}
-			for (iter = priv->vpn_configs_4; iter; iter = iter->next)
-				nm_ip4_config_subtract (priv->ext_ip_config_4, iter->data, 0);
+			for (iter = priv->vpn4_configs; iter; iter = iter->next)
+				nm_ip4_config_subtract (priv->ext_ip4_config, iter->data, 0);
 		}
 
 	} else {
 		nm_assert (addr_family == AF_INET6);
 
-		g_clear_object (&priv->ext_ip_config_6);
+		g_clear_object (&priv->ext_ip6_config);
 		g_clear_object (&priv->ext_ip6_config_captured);
 		priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self),
 		                                                       nm_device_get_platform (self),
 		                                                       ifindex,
+		                                                       capture_resolv_conf,
 		                                                       NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 		if (priv->ext_ip6_config_captured) {
 
-			priv->ext_ip_config_6 = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured);
+			priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured);
 
 			if (intersect_configs) {
 				/* This function was called upon external changes. Remove the configuration
 				 * (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_ip_config_6) {
-					nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6,
+				if (priv->con_ip6_config) {
+					nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config,
 					                         default_route_metric_penalty_get (self, AF_INET6));
 				}
-
-				intersect_ext_config (self, &priv->ac_ip6_config);
-				intersect_ext_config (self, &priv->dhcp6.ip6_config);
-				intersect_ext_config (self, &priv->wwan_ip_config_6);
-
-				for (iter = priv->vpn_configs_6; iter; iter = iter->next)
-					nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, 0);
-
-				if (   priv->ipv6ll_has
-				    && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr))
-					priv->ipv6ll_has = FALSE;
+				if (priv->ac_ip6_config) {
+					nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config,
+					                         default_route_metric_penalty_get (self, AF_INET6));
+				}
+				if (priv->dhcp6.ip6_config) {
+					nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config,
+					                         default_route_metric_penalty_get (self, AF_INET6));
+				}
+				if (priv->wwan_ip6_config) {
+					nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config,
+					                         default_route_metric_penalty_get (self, AF_INET6));
+				}
+				for (iter = priv->vpn6_configs; iter; iter = iter->next)
+					nm_ip6_config_intersect (iter->data, priv->ext_ip6_config, 0);
 			}
 
-			/* Remove parts from ext_ip_config_6 to only contain the information that
+			/* Remove parts from ext_ip6_config to only contain the information that
 			 * was configured externally -- we already have the same configuration from
 			 * internal origins. */
-			if (priv->con_ip_config_6) {
-				nm_ip6_config_subtract (priv->ext_ip_config_6, priv->con_ip_config_6,
+			if (priv->con_ip6_config) {
+				nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config,
 				                        default_route_metric_penalty_get (self, AF_INET6));
 			}
-			if (applied_config_get_current (&priv->ac_ip6_config)) {
-				nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_6,
-				                       applied_config_get_current (&priv->ac_ip6_config),
-				                       default_route_metric_penalty_get (self, AF_INET6));
+			if (priv->ac_ip6_config) {
+				nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config,
+				                        default_route_metric_penalty_get (self, AF_INET6));
 			}
-			if (applied_config_get_current (&priv->dhcp6.ip6_config)) {
-				nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_6,
-				                       applied_config_get_current (&priv->dhcp6.ip6_config),
-				                       default_route_metric_penalty_get (self, AF_INET6));
+			if (priv->dhcp6.ip6_config) {
+				nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.ip6_config,
+				                        default_route_metric_penalty_get (self, AF_INET6));
 			}
-			if (applied_config_get_current (&priv->wwan_ip_config_6)) {
-				nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_6,
-				                       applied_config_get_current (&priv->wwan_ip_config_6),
-				                       default_route_metric_penalty_get (self, AF_INET6));
+			if (priv->wwan_ip6_config) {
+				nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config,
+				                        default_route_metric_penalty_get (self, AF_INET6));
 			}
-			for (iter = priv->vpn_configs_6; iter; iter = iter->next)
-				nm_ip6_config_subtract (priv->ext_ip_config_6, iter->data, 0);
+			for (iter = priv->vpn6_configs; iter; iter = iter->next)
+				nm_ip6_config_subtract (priv->ext_ip6_config, iter->data, 0);
 		}
 	}
 
@@ -11783,139 +11245,156 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config
 }
 
 static void
-update_ip_config (NMDevice *self, int addr_family)
+update_ip_config (NMDevice *self, int addr_family, gboolean initial)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	nm_assert_addr_family (addr_family);
 
-	if (addr_family == AF_INET)
-		priv->update_ip_config_completed_v4 = TRUE;
-	else
-		priv->update_ip_config_completed_v6 = TRUE;
-
-	if (update_ext_ip_config (self, addr_family, TRUE)) {
+	if (update_ext_ip_config (self, addr_family, initial, TRUE)) {
 		if (addr_family == AF_INET) {
-			if (priv->ext_ip_config_4)
-				ip_config_merge_and_apply (self, AF_INET, FALSE);
+			if (priv->ext_ip4_config)
+				ip4_config_merge_and_apply (self, FALSE);
 		} else {
 			if (priv->ext_ip6_config_captured)
-				ip_config_merge_and_apply (self, AF_INET6, FALSE);
+				ip6_config_merge_and_apply (self, FALSE);
 		}
 	}
+
+	if (   addr_family == AF_INET6
+	    && priv->linklocal6_timeout_id
+	    && priv->ext_ip6_config_captured
+	    && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) {
+		/* linklocal6 is ready now, do the state transition... we are also
+		 * invoked as g_idle_add, so no problems with reentrance doing it now.
+		 */
+		linklocal6_complete (self);
+	}
 }
 
 void
 nm_device_capture_initial_config (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	if (!priv->update_ip_config_completed_v4)
-		update_ip_config (self, AF_INET);
-	if (!priv->update_ip_config_completed_v6)
-		update_ip_config (self, AF_INET6);
+	update_ip_config (self, AF_INET,  TRUE);
+	update_ip_config (self, AF_INET6, TRUE);
 }
 
 static gboolean
-queued_ip_config_change (NMDevice *self, int addr_family)
+queued_ip4_config_change (gpointer user_data)
 {
+	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
-	gboolean need_ipv6ll = FALSE;
-	const gboolean IS_IPv4 = (addr_family == AF_INET);
-	NMPlatform *platform;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_assert (IS_IPv4 ? !priv->queued_ip4_config_pending : !priv->queued_ip6_config_pending);
+	nm_assert (!priv->queued_ip4_config_pending);
 
 	/* Wait for any queued state changes */
 	if (priv->queued_state.id)
-		return G_SOURCE_CONTINUE;
+		return TRUE;
 
-	priv->queued_ip_config_id_x[IS_IPv4] = 0;
+	priv->queued_ip4_config_id = 0;
 
 	/* If a commit is scheduled, this function would potentially interfere with
 	 * it changing IP configurations before they are applied. Postpone the
 	 * update in such case.
 	 */
 	if (activation_source_is_scheduled (self,
-	                                    IS_IPv4
-	                                      ? activate_stage5_ip4_config_result
-	                                      : activate_stage5_ip6_config_commit,
-	                                    addr_family)) {
-		if (IS_IPv4) {
-			priv->queued_ip4_config_pending = FALSE;
-			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
-		} else {
-			priv->queued_ip6_config_pending = FALSE;
-			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
-		}
-		_LOGT (LOGD_DEVICE, "IP%c update was postponed",
-		       nm_utils_addr_family_to_char (addr_family));
-	} else {
-		update_ip_config (self, addr_family);
+	                                    activate_stage5_ip4_config_result,
+	                                    AF_INET)) {
+		priv->queued_ip4_config_pending = FALSE;
+		priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
+		_LOGT (LOGD_DEVICE, "IP4 update was postponed");
+	} else
+		update_ip_config (self, AF_INET, FALSE);
 
-		if (!IS_IPv4) {
-			/* Check whether we need to complete waiting for link-local.
-			 * We are also called from an idle handler, so no problem doing state transitions
-			 * now. */
-			linklocal6_check_complete (self);
-		}
+	set_unmanaged_external_down (self, TRUE);
+
+	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
+		priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);;
+		ip4_rp_filter_update (self);
 	}
 
-	if (!IS_IPv4) {
-		if (   priv->state < NM_DEVICE_STATE_DEACTIVATING
-		    && (platform = nm_device_get_platform (self))
-		    && nm_platform_link_get (platform, priv->ifindex)) {
-			/* Handle DAD failures */
-			while (priv->dad6_failed_addrs) {
-				nm_auto_nmpobj const NMPObject *obj = NULL;
-				const NMPlatformIP6Address *addr;
+	return FALSE;
+}
 
-				obj = priv->dad6_failed_addrs->data;
-				priv->dad6_failed_addrs = g_slist_delete_link (priv->dad6_failed_addrs, priv->dad6_failed_addrs);
+static gboolean
+queued_ip6_config_change (gpointer user_data)
+{
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+	GSList *iter;
+	gboolean need_ipv6ll = FALSE;
 
-				if (!nm_ndisc_dad_addr_is_fail_candidate (platform, obj))
-					continue;
+	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
 
-				addr = NMP_OBJECT_CAST_IP6_ADDRESS (obj);
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-				_LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address",
-				       nm_platform_ip6_address_to_string (addr, NULL, 0));
+	nm_assert (!priv->queued_ip4_config_pending);
 
-				if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
-					need_ipv6ll = TRUE;
-				else if (priv->ndisc)
-					nm_ndisc_dad_failed (priv->ndisc, &addr->address);
-			}
+	/* Wait for any queued state changes */
+	if (priv->queued_state.id)
+		return TRUE;
 
-			/* 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->ip_config_6
-			    && nm_ip6_config_get_num_addresses (priv->ip_config_6))
-				need_ipv6ll = TRUE;
+	priv->queued_ip6_config_id = 0;
 
-			if (need_ipv6ll)
-				check_and_add_ipv6ll_addr (self);
-		} else {
-			g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
-			priv->dad6_failed_addrs = NULL;
+	/* If a commit is scheduled, this function would potentially interfere with
+	 * it changing IP configurations before they are applied. Postpone the
+	 * update in such case.
+	 */
+	if (activation_source_is_scheduled (self,
+	                                    activate_stage5_ip6_config_commit,
+	                                    AF_INET6)) {
+		priv->queued_ip6_config_pending = FALSE;
+		priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
+		_LOGT (LOGD_DEVICE, "IP6 update was postponed");
+	} else
+		update_ip_config (self, AF_INET6, FALSE);
+
+	if (priv->state < NM_DEVICE_STATE_DEACTIVATING
+	    && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) {
+		/* Handle DAD failures */
+		for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
+			NMPlatformIP6Address *addr = iter->data;
+
+			if (addr->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->ndisc)
+				nm_ndisc_dad_failed (priv->ndisc, &addr->address);
 		}
 
-		/* Check if DAD is still pending */
-		if (   priv->ip6_state == IP_CONF
-		    && priv->dad6_ip6_config
-		    && priv->ext_ip6_config_captured
-		    && !nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured,
-		                                           priv->dad6_ip6_config)) {
+		/* 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;
+
+	/* Check if DAD is still pending */
+	if (   priv->ip6_state == IP_CONF
+	    && priv->dad6_ip6_config
+	    && priv->ext_ip6_config_captured) {
+		if (!nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured,
+		                                        priv->dad6_ip6_config)) {
 			_LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated");
 			g_clear_object (&priv->dad6_ip6_config);
-			_set_ip_state (self, addr_family, IP_DONE);
+			_set_ip_state (self, AF_INET6, IP_DONE);
 			check_ip_state (self, FALSE, TRUE);
 			if (priv->rt6_temporary_not_available)
 				nm_device_activate_schedule_ip6_config_result (self);
@@ -11924,40 +11403,21 @@ queued_ip_config_change (NMDevice *self, int addr_family)
 
 	set_unmanaged_external_down (self, TRUE);
 
-	if (IS_IPv4) {
-		if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
-			priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);;
-			ip4_rp_filter_update (self);
-		}
-	}
-
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-queued_ip4_config_change (gpointer user_data)
-{
-	return queued_ip_config_change (user_data, AF_INET);
-}
-
-static gboolean
-queued_ip6_config_change (gpointer user_data)
-{
-	return queued_ip_config_change (user_data, AF_INET6);
+	return FALSE;
 }
 
 static void
 device_ipx_changed (NMPlatform *platform,
                     int obj_type_i,
                     int ifindex,
-                    gconstpointer platform_object,
+                    gpointer platform_object,
                     int change_type_i,
                     NMDevice *self)
 {
 	const NMPObjectType obj_type = obj_type_i;
 	const NMPlatformSignalChangeType change_type = change_type_i;
 	NMDevicePrivate *priv;
-	const NMPlatformIP6Address *addr;
+	NMPlatformIP6Address *addr;
 
 	if (nm_device_get_ip_ifindex (self) != ifindex)
 		return;
@@ -11969,10 +11429,10 @@ device_ipx_changed (NMPlatform *platform,
 	case NMP_OBJECT_TYPE_IP4_ROUTE:
 		if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
 			priv->queued_ip4_config_pending = TRUE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4));
-		} else if (!priv->queued_ip_config_id_4) {
+			nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id));
+		} else if (!priv->queued_ip4_config_id) {
 			priv->queued_ip4_config_pending = FALSE;
-			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
+			priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
 			_LOGD (LOGD_DEVICE, "queued IP4 config change");
 		}
 		break;
@@ -11981,18 +11441,19 @@ device_ipx_changed (NMPlatform *platform,
 
 		if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
 		    && priv->state < NM_DEVICE_STATE_DEACTIVATING
-		    && nm_ndisc_dad_addr_is_fail_candidate_event (change_type, addr)) {
-			priv->dad6_failed_addrs = g_slist_prepend (priv->dad6_failed_addrs,
-			                                           (gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr)));
+		    && (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
+		        || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))) {
+			priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs,
+			                                          g_memdup (addr, sizeof (NMPlatformIP6Address)));
 		}
 		/* fall through */
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
 		if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
 			priv->queued_ip6_config_pending = TRUE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6));
-		} else if (!priv->queued_ip_config_id_6) {
+			nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id));
+		} else if (!priv->queued_ip6_config_id) {
 			priv->queued_ip6_config_pending = FALSE;
-			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
+			priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
 			_LOGD (LOGD_DEVICE, "queued IP6 config change");
 		}
 		break;
@@ -12260,14 +11721,14 @@ _set_unmanaged_flags (NMDevice *self,
 
 		if (priv->queued_ip4_config_pending) {
 			priv->queued_ip4_config_pending = FALSE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4));
-			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
+			nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id));
+			priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self);
 		}
 
 		if (priv->queued_ip6_config_pending) {
 			priv->queued_ip6_config_pending = FALSE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6));
-			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
+			nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id));
+			priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
 		}
 
 		if (!priv->pending_actions) {
@@ -12533,7 +11994,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)),
 	               nm_setting_connection_get_zone (s_con_applied)) != 0) {
 
-		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request.obj);
+		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request);
 		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s (version-id %llu)", NM_PRINT_FMT_QUOTE_STRING (zone), (unsigned long long) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
@@ -12545,7 +12006,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 
 	if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) {
 
-		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request.obj);
+		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request);
 		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d (version-id %llu)", (int) metered, (unsigned long long) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
@@ -12601,27 +12062,13 @@ nm_device_update_metered (NMDevice *self)
 
 	/* Try to guess a value using the metered flag in IP configuration */
 	if (value == NM_METERED_INVALID) {
-		if (   priv->ip_config_4
+		if (   priv->ip4_config
 		    && priv->ip4_state == IP_DONE
-		    && nm_ip4_config_get_metered (priv->ip_config_4))
-			value = NM_METERED_GUESS_YES;
-	}
-
-	/* Otherwise look at connection type. For Bluetooth, we look at the type of
-	 * Bluetooth sharing: for PANU/DUN (where we are receiving internet from
-	 * another device) we set GUESS_YES; for NAP (where we are sharing internet
-	 * to another device) we set GUESS_NO. We ignore WiMAX here as it’s no
-	 * longer supported by NetworkManager. */
-	if (   value == NM_METERED_INVALID
-	    && nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
-
-		if (_nm_connection_get_setting_bluetooth_for_nap (connection)) {
-			/* NAP types are not metered, but other types are. */
-			value = NM_METERED_GUESS_NO;
-		} else
+		    && nm_ip4_config_get_metered (priv->ip4_config))
 			value = NM_METERED_GUESS_YES;
 	}
 
+	/* Otherwise look at connection type */
 	if (value == NM_METERED_INVALID) {
 		if (   nm_connection_is_type (connection, NM_SETTING_GSM_SETTING_NAME)
 		    || nm_connection_is_type (connection, NM_SETTING_CDMA_SETTING_NAME))
@@ -12712,7 +12159,7 @@ nm_device_check_connection_available (NMDevice *self,
 
 		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_ANY (i, j)) {
+				if (NM_FLAGS_HAS (i, j)) {
 					k = i & ~j;
 					nm_assert (   available_all[i] == available_all[k]
 					           || available_all[i]);
@@ -12737,7 +12184,7 @@ available_connections_del_all (NMDevice *self)
 static gboolean
 available_connections_add (NMDevice *self, NMConnection *connection)
 {
-	return g_hash_table_add (self->_priv->available_connections, g_object_ref (connection));
+	return nm_g_hash_table_add (self->_priv->available_connections, g_object_ref (connection));
 }
 
 static gboolean
@@ -12795,7 +12242,7 @@ nm_device_recheck_available_connections (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE(self);
 
 	if (g_hash_table_size (priv->available_connections) > 0) {
-		prune_list = g_hash_table_new (nm_direct_hash, NULL);
+		prune_list = g_hash_table_new (g_direct_hash, g_direct_equal);
 		g_hash_table_iter_init (&h_iter, priv->available_connections);
 		while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL))
 			g_hash_table_add (prune_list, connection);
@@ -12904,19 +12351,19 @@ cp_connection_added_or_updated (NMDevice *self, NMConnection *connection)
 }
 
 static void
-cp_connection_added (NMSettings *settings, NMConnection *connection, gpointer user_data)
+cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
 	cp_connection_added_or_updated (user_data, connection);
 }
 
 static void
-cp_connection_updated (NMSettings *settings, NMConnection *connection, gboolean by_user, gpointer user_data)
+cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gboolean by_user, gpointer user_data)
 {
 	cp_connection_added_or_updated (user_data, connection);
 }
 
 static void
-cp_connection_removed (NMSettings *settings, NMConnection *connection, gpointer user_data)
+cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
 	NMDevice *self = user_data;
 
@@ -13098,8 +12545,8 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
 
 	queued_state_clear (self);
 
-	_cleanup_ip_pre (self, AF_INET, cleanup_type);
-	_cleanup_ip_pre (self, AF_INET6, cleanup_type);
+	_cleanup_ip4_pre (self, cleanup_type);
+	_cleanup_ip6_pre (self, cleanup_type);
 }
 
 static void
@@ -13121,41 +12568,39 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface; the caller must flush routes and addresses explicitly.
 	 */
-	nm_device_set_ip_config (self, AF_INET, NULL, TRUE, NULL);
-	nm_device_set_ip_config (self, AF_INET6, NULL, TRUE, NULL);
+	nm_device_set_ip4_config (self, NULL, TRUE, NULL);
+	nm_device_set_ip6_config (self, NULL, TRUE);
 	g_clear_object (&priv->proxy_config);
-	g_clear_object (&priv->con_ip_config_4);
-	applied_config_clear (&priv->dev_ip4_config);
-	applied_config_clear (&priv->wwan_ip_config_4);
-	g_clear_object (&priv->ext_ip_config_4);
-	g_clear_object (&priv->ip_config_4);
-	g_clear_object (&priv->con_ip_config_6);
-	applied_config_clear (&priv->ac_ip6_config);
-	g_clear_object (&priv->ext_ip_config_6);
+	g_clear_object (&priv->con_ip4_config);
+	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->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->ext_ip6_config_captured);
-	applied_config_clear (&priv->wwan_ip_config_6);
-	g_clear_object (&priv->ip_config_6);
+	g_clear_object (&priv->wwan_ip6_config);
+	g_clear_object (&priv->ip6_config);
 	g_clear_object (&priv->dad6_ip6_config);
-	priv->ipv6ll_has = FALSE;
-	memset (&priv->ipv6ll_addr, 0, sizeof (priv->ipv6ll_addr));
 
 	g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref);
 	nm_clear_g_source (&priv->rt6_temporary_not_available_id);
 
-	g_slist_free_full (priv->vpn_configs_4, g_object_unref);
-	priv->vpn_configs_4 = NULL;
-	g_slist_free_full (priv->vpn_configs_6, g_object_unref);
-	priv->vpn_configs_6 = NULL;
+	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;
 
-	/* We no longer accept the delegations. nm_device_set_ip_config(NULL)
+	/* We no longer accept the delegations. nm_device_set_ip6_config(NULL)
 	 * above disables them. */
 	nm_assert (priv->needs_ip6_subnet == FALSE);
 
-	if (priv->act_request.obj) {
-		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request.obj), AF_INET, FALSE);
+	if (priv->act_request) {
+		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), AF_INET, FALSE);
 
 		priv->master_ready_handled = FALSE;
-		nm_clear_g_signal_handler (priv->act_request.obj, &priv->master_ready_id);
+		nm_clear_g_signal_handler (priv->act_request, &priv->master_ready_id);
 
 		act_request_set (self, NULL);
 	}
@@ -13177,7 +12622,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	 * those are identified by ip_iface, not by iface (which might be a tty
 	 * or ATM device).
 	 */
-	_set_ip_ifindex (self, 0, NULL);
+	nm_device_set_ip_iface (self, NULL);
 }
 
 /*
@@ -13297,10 +12742,10 @@ find_dhcp4_address (NMDevice *self)
 	const NMPlatformIP4Address *a;
 	NMDedupMultiIter ipconf_iter;
 
-	if (!priv->ip_config_4)
+	if (!priv->ip4_config)
 		return NULL;
 
-	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip_config_4, &a) {
+	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip4_config, &a) {
 		if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP)
 			return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
 	}
@@ -13607,7 +13052,7 @@ _set_state_full (NMDevice *self,
 		g_cancellable_cancel (priv->deactivating_cancellable);
 
 	/* Cache the activation request for the dispatcher */
-	req = nm_g_object_ref (priv->act_request.obj);
+	req = nm_g_object_ref (priv->act_request);
 
 	if (   state >  NM_DEVICE_STATE_UNMANAGED
 	    && state <= NM_DEVICE_STATE_ACTIVATED
@@ -13715,8 +13160,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, AF_INET, CLEANUP_TYPE_DECONFIGURE);
-			_cleanup_ip_pre (self, AF_INET6, CLEANUP_TYPE_DECONFIGURE);
+			_cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE);
+			_cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE);
 		}
 		break;
 	default:
@@ -13730,13 +13175,6 @@ _set_state_full (NMDevice *self,
 
 	_notify (self, PROP_STATE);
 	_notify (self, PROP_STATE_REASON);
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_device,
-	                            &signal_info_state_changed,
-	                            "(uuu)",
-	                            (guint32) state,
-	                            (guint32) old_state,
-	                            (guint32) reason);
 	g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) state, (guint) old_state, (guint) reason);
 
 	/* Post-process the event after internal notification */
@@ -13887,7 +13325,7 @@ _set_state_full (NMDevice *self,
 	if (ip_config_valid (old_state) && !ip_config_valid (state))
 	    notify_ip_properties (self);
 
-	nm_device_check_connectivity_update_interval (self);
+	concheck_periodic_update (self);
 
 	/* Dispose of the cached activation request */
 	if (req)
@@ -14849,15 +14287,8 @@ nm_device_init (NMDevice *self)
 
 	self->_priv = priv;
 
-	c_list_init (&priv->concheck_lst_head);
-	c_list_init (&self->devices_lst);
 	c_list_init (&priv->slaves);
 
-	priv->connectivity_state = NM_CONNECTIVITY_UNKNOWN;
-
-	nm_dbus_track_obj_path_init (&priv->parent_device, G_OBJECT (self), obj_properties[PROP_PARENT]);
-	nm_dbus_track_obj_path_init (&priv->act_request, G_OBJECT (self), obj_properties[PROP_ACTIVE_CONNECTION]);
-
 	priv->netns = g_object_ref (NM_NETNS_GET);
 
 	priv->autoconnect_blocked_flags = DEFAULT_AUTOCONNECT
@@ -14872,7 +14303,7 @@ nm_device_init (NMDevice *self)
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
 	priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT;
 	priv->unmanaged_mask = priv->unmanaged_flags;
-	priv->available_connections = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL);
+	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
 	priv->ip6_saved_properties = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free);
 	priv->sys_iface_state = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL;
 
@@ -14971,19 +14402,9 @@ dispose (GObject *object)
 	NMDevice *self = NM_DEVICE (object);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatform *platform;
-	NMDeviceConnectivityHandle *con_handle;
-	gs_free_error GError *cancelled_error = NULL;
 
 	_LOGD (LOGD_DEVICE, "disposing");
 
-	nm_assert (c_list_is_empty (&self->devices_lst));
-
-	while ((con_handle = c_list_first_entry (&priv->concheck_lst_head, NMDeviceConnectivityHandle, concheck_lst))) {
-		if (!cancelled_error)
-			nm_utils_error_set_cancelled (&cancelled_error, FALSE, "NMDevice");
-		concheck_handle_complete (con_handle, cancelled_error);
-	}
-
 	nm_clear_g_cancellable (&priv->deactivating_cancellable);
 
 	nm_device_assume_state_reset (self);
@@ -14994,8 +14415,8 @@ dispose (GObject *object)
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
 
-	g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_destroy);
-	priv->acd.dad_list = NULL;
+	g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy);
+	priv->arping.dad_list = NULL;
 
 	arp_cleanup (self);
 
@@ -15057,8 +14478,6 @@ dispose (GObject *object)
 		g_clear_object (&priv->lldp_listener);
 	}
 
-	nm_clear_g_source (&priv->concheck_p_cur_id);
-
 	G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
 
 	if (nm_clear_g_source (&priv->queued_state.id)) {
@@ -15081,7 +14500,7 @@ finalize (GObject *object)
 	g_free (priv->hw_addr_perm);
 	g_free (priv->hw_addr_initial);
 	g_slist_free (priv->pending_actions);
-	g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
+	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->iface);
@@ -15090,22 +14509,19 @@ finalize (GObject *object)
 	g_free (priv->driver_version);
 	g_free (priv->firmware_version);
 	g_free (priv->type_desc);
+	g_free (priv->type_description);
 	g_free (priv->dhcp_anycast_address);
 	g_free (priv->current_stable_id);
 
 	g_hash_table_unref (priv->ip6_saved_properties);
 	g_hash_table_unref (priv->available_connections);
 
-	nm_dbus_track_obj_path_deinit (&priv->parent_device);
-	nm_dbus_track_obj_path_deinit (&priv->act_request);
-
 	G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
 
 	/* for testing, NMDeviceTest does not invoke NMDevice::constructed,
 	 * and thus @settings might be unset. */
-	nm_g_object_unref (priv->settings);
-
-	nm_g_object_unref (priv->concheck_mgr);
+	if (priv->settings)
+		g_object_unref (priv->settings);
 
 	g_object_unref (priv->netns);
 }
@@ -15215,6 +14631,9 @@ get_property (GObject *object, guint prop_id,
 {
 	NMDevice *self = NM_DEVICE (object);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	GPtrArray *array;
+	GHashTableIter iter;
+	NMConnection *connection;
 	GVariantBuilder array_builder;
 
 	switch (prop_id) {
@@ -15270,16 +14689,16 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->mtu);
 		break;
 	case PROP_IP4_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip_config_4 : NULL);
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL);
 		break;
 	case PROP_DHCP4_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4.config : NULL);
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4.config : NULL);
 		break;
 	case PROP_IP6_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip_config_6 : NULL);
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL);
 		break;
 	case PROP_DHCP6_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6.config : NULL);
+		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);
@@ -15289,7 +14708,7 @@ get_property (GObject *object, guint prop_id,
 		                      g_variant_new ("(uu)", priv->state, priv->state_reason));
 		break;
 	case PROP_ACTIVE_CONNECTION:
-		g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->act_request));
+		nm_utils_g_value_set_object_path (value, priv->act_request_public ? priv->act_request : NULL);
 		break;
 	case PROP_DEVICE_TYPE:
 		g_value_set_uint (value, priv->type);
@@ -15320,9 +14739,12 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->rfkill_type);
 		break;
 	case PROP_AVAILABLE_CONNECTIONS:
-		nm_dbus_utils_g_value_set_object_path_from_hash (value,
-		                                                 priv->available_connections,
-		                                                 TRUE);
+		array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections));
+		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_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);
@@ -15331,7 +14753,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_object (value, nm_device_get_master (self));
 		break;
 	case PROP_PARENT:
-		g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->parent_device));
+		nm_utils_g_value_set_object_path (value, priv->parent_device);
 		break;
 	case PROP_HW_ADDRESS:
 		g_value_set_string (value, priv->hw_addr);
@@ -15376,7 +14798,7 @@ get_property (GObject *object, guint prop_id,
 
 			if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved)
 				continue;
-			path = nm_dbus_object_get_path (NM_DBUS_OBJECT (info->slave));
+			path = nm_exported_object_get_path ((NMExportedObject *) info->slave);
 			if (path)
 				slave_list[i++] = g_strdup (path);
 		}
@@ -15403,115 +14825,15 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
-static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"StateChanged",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("new_state", "u"),
-		NM_DEFINE_GDBUS_ARG_INFO ("old_state", "u"),
-		NM_DEFINE_GDBUS_ARG_INFO ("reason",    "u"),
-	),
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_device = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Reapply",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
-						NM_DEFINE_GDBUS_ARG_INFO ("version_id", "t"),
-						NM_DEFINE_GDBUS_ARG_INFO ("flags",      "u"),
-					),
-				),
-				.handle = impl_device_reapply,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetAppliedConnection",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
-						NM_DEFINE_GDBUS_ARG_INFO ("version_id", "t"),
-					),
-				),
-				.handle = impl_device_get_applied_connection,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Disconnect",
-				),
-				.handle = impl_device_disconnect,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Delete",
-				),
-				.handle = impl_device_delete,
-			),
-		),
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&signal_info_state_changed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Udi",                  "s",      NM_DEVICE_UDI),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Interface",            "s",      NM_DEVICE_IFACE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("IpInterface",          "s",      NM_DEVICE_IP_IFACE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Driver",               "s",      NM_DEVICE_DRIVER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("DriverVersion",        "s",      NM_DEVICE_DRIVER_VERSION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("FirmwareVersion",      "s",      NM_DEVICE_FIRMWARE_VERSION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Capabilities",         "u",      NM_DEVICE_CAPABILITIES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Ip4Address",           "u",      NM_DEVICE_IP4_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("State",                "u",      NM_DEVICE_STATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("StateReason",          "(uu)",   NM_DEVICE_STATE_REASON),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActiveConnection",     "o",      NM_DEVICE_ACTIVE_CONNECTION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Ip4Config",            "o",      NM_DEVICE_IP4_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Dhcp4Config",          "o",      NM_DEVICE_DHCP4_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Ip6Config",            "o",      NM_DEVICE_IP6_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Dhcp6Config",          "o",      NM_DEVICE_DHCP6_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("Managed",              "b",      NM_DEVICE_MANAGED,               NM_AUTH_PERMISSION_NETWORK_CONTROL, NM_AUDIT_OP_DEVICE_MANAGED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("Autoconnect",          "b",      NM_DEVICE_AUTOCONNECT,           NM_AUTH_PERMISSION_NETWORK_CONTROL, NM_AUDIT_OP_DEVICE_AUTOCONNECT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("FirmwareMissing",      "b",      NM_DEVICE_FIRMWARE_MISSING),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("NmPluginMissing",      "b",      NM_DEVICE_NM_PLUGIN_MISSING),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("DeviceType",           "u",      NM_DEVICE_DEVICE_TYPE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("AvailableConnections", "ao",     NM_DEVICE_AVAILABLE_CONNECTIONS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PhysicalPortId",       "s",      NM_DEVICE_PHYSICAL_PORT_ID),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Mtu",                  "u",      NM_DEVICE_MTU),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Metered",              "u",      NM_DEVICE_METERED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("LldpNeighbors",        "aa{sv}", NM_DEVICE_LLDP_NEIGHBORS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Real",                 "b",      NM_DEVICE_REAL),
-		),
-	),
-};
-
-const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_STATISTICS,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE ("RefreshRateMs", "u", NM_DEVICE_STATISTICS_REFRESH_RATE_MS, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, NM_AUDIT_OP_STATISTICS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE     ("TxBytes",       "t", NM_DEVICE_STATISTICS_TX_BYTES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE     ("RxBytes",       "t", NM_DEVICE_STATISTICS_RX_BYTES),
-		),
-	),
-};
-
 static void
 nm_device_class_init (NMDeviceClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass);
 
 	g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Devices");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device,
-	                                                              &nm_interface_info_device_statistics);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Devices");
 
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
@@ -15743,6 +15065,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
+	/* Statistics */
 	obj_properties[PROP_REFRESH_RATE_MS] =
 	    g_param_spec_uint (NM_DEVICE_STATISTICS_REFRESH_RATE_MS, "", "",
 	                       0, UINT32_MAX, 0,
@@ -15759,6 +15082,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	                         G_PARAM_READABLE |
 	                         G_PARAM_STATIC_STRINGS);
 
+	/* Connectivity */
 	obj_properties[PROP_CONNECTIVITY] =
 	     g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "",
 	                        NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
@@ -15841,11 +15165,15 @@ nm_device_class_init (NMDeviceClass *klass)
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
 
-	signals[CONNECTIVITY_CHANGED] =
-	    g_signal_new (NM_DEVICE_CONNECTIVITY_CHANGED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_SKELETON,
+	                                        "Reapply", impl_device_reapply,
+	                                        "GetAppliedConnection", impl_device_get_applied_connection,
+	                                        "Disconnect", impl_device_disconnect,
+	                                        "Delete", impl_device_delete,
+	                                        NULL);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_STATISTICS_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 66720f01..ac73ee0c 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -24,8 +24,7 @@
 
 #include <netinet/in.h>
 
-#include "nm-setting-connection.h"
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 #include "nm-rfkill-manager.h"
@@ -114,7 +113,8 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
 #define NM_DEVICE_PARENT           "parent"
 
 /* the "slaves" property is internal in the parent class, but exposed
- * by the derived classes NMDeviceBond, NMDeviceBridge and NMDeviceTeam. */
+ * 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 */
@@ -135,7 +135,6 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
 #define NM_DEVICE_STATE_CHANGED         "state-changed"
 #define NM_DEVICE_LINK_INITIALIZED      "link-initialized"
 #define NM_DEVICE_AUTOCONNECT_ALLOWED   "autoconnect-allowed"
-#define NM_DEVICE_CONNECTIVITY_CHANGED  "connectivity-changed"
 
 #define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms"
 #define NM_DEVICE_STATISTICS_TX_BYTES        "tx-bytes"
@@ -173,9 +172,10 @@ typedef enum { /*< skip >*/
 struct _NMDevicePrivate;
 
 struct _NMDevice {
-	NMDBusObject parent;
+	NMExportedObject parent;
+
+	/* private */
 	struct _NMDevicePrivate *_priv;
-	CList devices_lst;
 };
 
 /* The flags have an relaxing meaning, that means, specifying more flags, can make
@@ -190,9 +190,7 @@ typedef enum { /*< skip >*/
 } NMDeviceCheckDevAvailableFlags;
 
 typedef struct {
-	NMDBusObjectClass parent;
-
-	const char *default_type_description;
+	NMExportedObjectClass parent;
 
 	const char *connection_type;
 	const NMLinkType *link_types;
@@ -314,7 +312,7 @@ typedef struct {
 	gboolean    (* complete_connection)         (NMDevice *self,
 	                                             NMConnection *connection,
 	                                             const char *specific_object,
-	                                             NMConnection *const*existing_connections,
+	                                             const GSList *existing_connections,
 	                                             GError **error);
 
 	NMActStageReturn    (* act_stage1_prepare)  (NMDevice *self,
@@ -522,7 +520,7 @@ gboolean nm_device_can_auto_connect (NMDevice *self,
 gboolean nm_device_complete_connection (NMDevice *device,
                                         NMConnection *connection,
                                         const char *specific_object,
-                                        NMConnection *const*existing_connections,
+                                        const GSList *existing_connection,
                                         GError **error);
 
 gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
@@ -756,10 +754,12 @@ 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);
+                                      NMSettingIPConfig *s_ip4_new,
+                                      gboolean force_restart);
 void nm_device_reactivate_ip6_config (NMDevice *device,
                                       NMSettingIPConfig *s_ip6_old,
-                                      NMSettingIPConfig *s_ip6_new);
+                                      NMSettingIPConfig *s_ip6_new,
+                                      gboolean force_restart);
 
 gboolean nm_device_update_hw_address (NMDevice *self);
 void nm_device_update_initial_hw_address (NMDevice *self);
@@ -776,22 +776,12 @@ gboolean nm_device_hw_addr_get_cloned (NMDevice *self,
                                        gboolean *preserve,
                                        GError **error);
 
-typedef struct _NMDeviceConnectivityHandle NMDeviceConnectivityHandle;
-
 typedef void (*NMDeviceConnectivityCallback) (NMDevice *self,
-                                              NMDeviceConnectivityHandle *handle,
                                               NMConnectivityState state,
-                                              GError *error,
                                               gpointer user_data);
-
-void nm_device_check_connectivity_update_interval (NMDevice *self);
-
-NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self,
-                                                          NMDeviceConnectivityCallback callback,
-                                                          gpointer user_data);
-
-void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle);
-
+void nm_device_check_connectivity (NMDevice *self,
+                                   NMDeviceConnectivityCallback callback,
+                                   gpointer user_data);
 NMConnectivityState nm_device_get_connectivity_state (NMDevice *self);
 
 typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer;
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index f637825b..2ed2a7d9 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -286,21 +286,19 @@ lldp_neighbor_id_hash (gconstpointer ptr)
 }
 
 static int
-lldp_neighbor_id_cmp (const LldpNeighbor *x, const LldpNeighbor *y)
+lldp_neighbor_id_cmp (gconstpointer a, gconstpointer b)
 {
-	NM_CMP_SELF (x, y);
-	NM_CMP_FIELD (x, y, chassis_id_type);
-	NM_CMP_FIELD (x, y, port_id_type);
-	NM_CMP_FIELD_STR0 (x, y, chassis_id);
-	NM_CMP_FIELD_STR0 (x, y, port_id);
-	return 0;
-}
-
-static int
-lldp_neighbor_id_cmp_p (gconstpointer a, gconstpointer b, gpointer user_data)
-{
-	return lldp_neighbor_id_cmp (*((const LldpNeighbor *const*) a),
-	                             *((const LldpNeighbor *const*) b));
+	const LldpNeighbor *x = a, *y = b;
+	int c;
+
+	if (x->chassis_id_type != y->chassis_id_type)
+		return x->chassis_id_type < y->chassis_id_type ? -1 : 1;
+	if (x->port_id_type != y->port_id_type)
+		return x->port_id_type < y->port_id_type ? -1 : 1;
+	c = g_strcmp0 (x->chassis_id, y->chassis_id);
+	if (c == 0)
+		c = g_strcmp0 (x->port_id, y->port_id);
+	return c < 0 ? -1 : (c > 0 ? 1 : 0);
 }
 
 static gboolean
@@ -840,23 +838,20 @@ GVariant *
 nm_lldp_listener_get_neighbors (NMLldpListener *self)
 {
 	NMLldpListenerPrivate *priv;
+	GVariantBuilder array_builder;
+	GList *neighbors, *iter;
 
 	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 
 	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 
-	if (G_UNLIKELY (!priv->variant)) {
-		GVariantBuilder array_builder;
-		gs_free LldpNeighbor **neighbors = NULL;
-		guint i, n;
-
+	if (!priv->variant) {
 		g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
-		neighbors = (LldpNeighbor **) nm_utils_hash_keys_to_array (priv->lldp_neighbors,
-		                                                           lldp_neighbor_id_cmp_p,
-		                                                           NULL,
-		                                                           &n);
-		for (i = 0; i < n; i++)
-			g_variant_builder_add_value (&array_builder, lldp_neighbor_to_variant (neighbors[i]));
+		neighbors = g_hash_table_get_keys (priv->lldp_neighbors);
+		neighbors = g_list_sort (neighbors, lldp_neighbor_id_cmp);
+		for (iter = neighbors; iter; iter = iter->next)
+			g_variant_builder_add_value (&array_builder, lldp_neighbor_to_variant (iter->data));
+		g_list_free (neighbors);
 		priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder));
 	}
 	return priv->variant;
diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build
deleted file mode 100644
index 7b1c4617..00000000
--- a/src/devices/ovs/meson.build
+++ /dev/null
@@ -1,38 +0,0 @@
-sources = files(
-  'nm-device-ovs-bridge.c',
-  'nm-device-ovs-interface.c',
-  'nm-device-ovs-port.c',
-  'nm-ovsdb.c',
-  'nm-ovs-factory.c'
-)
-
-deps = [
-  jansson_dep,
-  nm_dep
-]
-
-libnm_device_plugin_ovs = shared_module(
-  'nm-device-plugin-ovs',
-  sources: sources,
-  dependencies: deps,
-  c_args: '-DRUNSTATEDIR="@0@"'.format(nm_runstatedir),
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_ovs
-
-run_target(
-  'check-local-devices-ovs',
-  command: [check_exports, libnm_device_plugin_ovs.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_ovs
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-ovs: src/devices/ovs/libnm-device-plugin-ovs.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/ovs/.libs/libnm-device-plugin-ovs.so)
-'''
diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c
index 5244ca8b..53ea2b82 100644
--- a/src/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/devices/ovs/nm-device-ovs-bridge.c
@@ -28,8 +28,10 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-ovs-bridge.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsBridge.h"
+
 #include "devices/nm-device-logging.h"
-_LOG_DECLARE_SELF (NMDeviceOvsBridge);
+_LOG_DECLARE_SELF(NMDeviceOvsBridge);
 
 /*****************************************************************************/
 
@@ -131,24 +133,11 @@ nm_device_ovs_bridge_init (NMDeviceOvsBridge *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_ovs_bridge = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_bridge);
-
 	device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
 	device_class->is_master = TRUE;
 	device_class->get_type_description = get_type_description;
@@ -160,4 +149,8 @@ nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass)
 	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	device_class->enslave_slave = enslave_slave;
 	device_class->release_slave = release_slave;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_OVS_BRIDGE_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
index eefe38c8..ce32c2dd 100644
--- a/src/devices/ovs/nm-device-ovs-interface.c
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -28,6 +28,8 @@
 #include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-port.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsInterface.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceOvsInterface);
 
@@ -183,26 +185,13 @@ nm_device_ovs_interface_init (NMDeviceOvsInterface *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_ovs_interface = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH);
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_interface);
-
 	device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
@@ -213,4 +202,8 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_OVS_INTERFACE_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
index 3f1fe974..cb0915af 100644
--- a/src/devices/ovs/nm-device-ovs-port.c
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -28,8 +28,10 @@
 #include "nm-setting-ovs-port.h"
 #include "nm-setting-ovs-port.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.OvsPort.h"
+
 #include "devices/nm-device-logging.h"
-_LOG_DECLARE_SELF (NMDeviceOvsPort);
+_LOG_DECLARE_SELF(NMDeviceOvsPort);
 
 /*****************************************************************************/
 
@@ -178,24 +180,11 @@ nm_device_ovs_port_init (NMDeviceOvsPort *self)
 {
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_ovs_port = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_OVS_PORT,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass)
 {
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_port);
-
 	device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME;
 	device_class->is_master = TRUE;
 	device_class->get_type_description = get_type_description;
@@ -206,4 +195,8 @@ nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass)
 	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	device_class->enslave_slave = enslave_slave;
 	device_class->release_slave = release_slave;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_OVS_PORT_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
index b8f5a935..92fcfa01 100644
--- a/src/devices/ovs/nm-ovsdb.c
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -22,20 +22,37 @@
 #include "nm-ovsdb.h"
 
 #include <string.h>
+#include <jansson.h>
 #include <gmodule.h>
 #include <gio/gunixsocketaddress.h>
 
-#include "nm-utils/nm-jansson.h"
 #include "devices/nm-device.h"
 #include "platform/nm-platform.h"
 #include "nm-core-internal.h"
 
-/*****************************************************************************/
+/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */
+#ifndef json_boolean
+#define json_boolean(val) ((val) ? json_true() : json_false())
+#endif
+
+/* Added in Jansson v2.5 (released Sep 19 2013), but travis.ci has v2.2. */
+#ifndef json_array_foreach
+#define json_array_foreach(array, index, value) \
+	for (index = 0; \
+	     index < json_array_size(array) && (value = json_array_get(array, index)); \
+	     index++)
+#endif
 
-#if JANSSON_VERSION_HEX < 0x020400
-#warning "requires at least libjansson 2.4"
+/* Added in Jansson v2.3 (released Jan 27 2012) */
+#ifndef json_object_foreach
+#define json_object_foreach(object, key, value) \
+    for(key = json_object_iter_key(json_object_iter(object)); \
+        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+        key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
 #endif
 
+/*****************************************************************************/
+
 typedef struct {
 	char *name;
 	char *connection_uuid;
@@ -78,7 +95,7 @@ typedef struct {
 	GHashTable *interfaces;         /* interface uuid => OpenvswitchInterface */
 	GHashTable *ports;              /* port uuid => OpenvswitchPort */
 	GHashTable *bridges;            /* bridge uuid => OpenvswitchBridge */
-	char *db_uuid;
+	const char *db_uuid;
 } NMOvsdbPrivate;
 
 struct _NMOvsdb {
@@ -127,7 +144,7 @@ typedef struct {
 	OvsdbMethodCallback callback;
 	gpointer user_data;
 	union {
-		char *ifname;
+		const char *ifname;
 		struct {
 			NMConnection *bridge;
 			NMConnection *port;
@@ -867,7 +884,7 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg)
 
 	if (ovs) {
 		iter = json_object_iter (ovs);
-		priv->db_uuid = iter ? g_strdup (json_object_iter_key (iter)) : NULL;
+		priv->db_uuid = g_strdup (iter ? json_object_iter_key (iter) : NULL);
 	}
 
 	/* Interfaces */
diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build
deleted file mode 100644
index 4a533bc5..00000000
--- a/src/devices/team/meson.build
+++ /dev/null
@@ -1,35 +0,0 @@
-sources = files(
-  'nm-device-team.c',
-  'nm-team-factory.c'
-)
-
-deps = [
-  jansson_dep,
-  libteamdctl_dep,
-  nm_dep
-]
-
-libnm_device_plugin_team = shared_module(
-  'nm-device-plugin-team',
-  sources: sources,
-  dependencies: deps,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_team
-
-run_target(
-  'check-local-devices-team',
-  command: [check_exports, libnm_device_plugin_team.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_team
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-team: src/devices/team/libnm-device-plugin-team.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/team/.libs/libnm-device-plugin-team.so)
-'''
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index f83ba22b..098cd437 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -28,8 +28,8 @@
 #include <sys/wait.h>
 #include <teamdctl.h>
 #include <stdlib.h>
+#include <jansson.h>
 
-#include "nm-utils/nm-jansson.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
 #include "platform/nm-platform.h"
@@ -37,6 +37,8 @@
 #include "nm-ip4-config.h"
 #include "nm-dbus-compat.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Team.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceTeam);
 
@@ -104,7 +106,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingTeam *s_team;
@@ -889,27 +891,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_team = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_TEAM,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress", "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Carrier",   "b",  NM_DEVICE_CARRIER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Slaves",    "ao", NM_DEVICE_SLAVES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Config",    "s",  NM_DEVICE_TEAM_CONFIG),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_team_class_init (NMDeviceTeamClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_TEAM_SETTING_NAME, NM_LINK_TYPE_TEAM)
@@ -918,8 +903,6 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_team);
-
 	parent_class->is_master = TRUE;
 	parent_class->create_and_realize = create_and_realize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
@@ -941,4 +924,8 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	                         G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_TEAM_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build
deleted file mode 100644
index 02c61ced..00000000
--- a/src/devices/tests/meson.build
+++ /dev/null
@@ -1,18 +0,0 @@
-test_units = [
-  'test-acd',
-  'test-lldp'
-]
-
-foreach test_unit: test_units
-  exe = executable(
-    test_unit,
-    test_unit + '.c',
-    dependencies: test_nm_dep
-  )
-
-  test(
-    'devices/' + test_unit,
-    test_script,
-    args: test_args + [exe.full_path()]
-  )
-endforeach
diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-arping.c
index 8a2852a2..f0537545 100644
--- a/src/devices/tests/test-acd.c
+++ b/src/devices/tests/test-arping.c
@@ -20,7 +20,7 @@
 
 #include "nm-default.h"
 
-#include "devices/nm-acd-manager.h"
+#include "devices/nm-arping-manager.h"
 #include "platform/tests/test-common.h"
 
 #define IFACE_VETH0 "nm-test-veth0"
@@ -34,10 +34,6 @@
 typedef struct {
 	int ifindex0;
 	int ifindex1;
-	const guint8 *hwaddr0;
-	const guint8 *hwaddr1;
-	size_t hwaddr0_len;
-	size_t hwaddr1_len;
 } test_fixture;
 
 static void
@@ -49,9 +45,6 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL));
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL));
-
-	fixture->hwaddr0 = nm_platform_link_get_address (NM_PLATFORM_GET, fixture->ifindex0, &fixture->hwaddr0_len);
-	fixture->hwaddr1 = nm_platform_link_get_address (NM_PLATFORM_GET, fixture->ifindex1, &fixture->hwaddr1_len);
 }
 
 typedef struct {
@@ -61,21 +54,26 @@ typedef struct {
 } TestInfo;
 
 static void
-acd_manager_probe_terminated (NMAcdManager *acd_manager, GMainLoop *loop)
+arping_manager_probe_terminated (NMArpingManager *arping_manager, GMainLoop *loop)
 {
 	g_main_loop_quit (loop);
 }
 
 static void
-test_acd_common (test_fixture *fixture, TestInfo *info)
+test_arping_common (test_fixture *fixture, TestInfo *info)
 {
-	gs_unref_object NMAcdManager *manager = NULL;
+	gs_unref_object NMArpingManager *manager = NULL;
 	GMainLoop *loop;
 	int i;
 	const guint WAIT_TIME_OPTIMISTIC = 50;
 	guint wait_time;
 	gulong signal_id;
 
+	if (!nm_utils_find_helper ("arping", NULL, NULL)) {
+		g_test_skip ("arping binary is missing");
+		return;
+	}
+
 	/* first, try with a short waittime. We hope that this is long enough
 	 * to successfully complete the test. Only if that's not the case, we
 	 * assume the computer is currently busy (high load) and we retry with
@@ -83,11 +81,11 @@ test_acd_common (test_fixture *fixture, TestInfo *info)
 	wait_time = WAIT_TIME_OPTIMISTIC;
 again:
 
-	manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len);
+	manager = nm_arping_manager_new (fixture->ifindex0);
 	g_assert (manager != NULL);
 
 	for (i = 0; info->addresses[i]; i++)
-		g_assert (nm_acd_manager_add_address (manager, info->addresses[i]));
+		g_assert (nm_arping_manager_add_address (manager, info->addresses[i]));
 
 	for (i = 0; info->peer_addresses[i]; i++) {
 		nmtstp_ip4_address_add (NULL, FALSE, fixture->ifindex1, info->peer_addresses[i],
@@ -95,9 +93,9 @@ again:
 	}
 
 	loop = g_main_loop_new (NULL, FALSE);
-	signal_id = g_signal_connect (manager, NM_ACD_MANAGER_PROBE_TERMINATED,
-	                              G_CALLBACK (acd_manager_probe_terminated), loop);
-	g_assert (nm_acd_manager_start_probe (manager, wait_time));
+	signal_id = g_signal_connect (manager, NM_ARPING_MANAGER_PROBE_TERMINATED,
+	                              G_CALLBACK (arping_manager_probe_terminated), loop);
+	g_assert (nm_arping_manager_start_probe (manager, wait_time, NULL));
 	g_assert (nmtst_main_loop_run (loop, 2000));
 	g_signal_handler_disconnect (manager, signal_id);
 	g_main_loop_unref (loop);
@@ -105,7 +103,7 @@ again:
 	for (i = 0; info->addresses[i]; i++) {
 		gboolean val;
 
-		val = nm_acd_manager_check_address (manager, info->addresses[i]);
+		val = nm_arping_manager_check_address (manager, info->addresses[i]);
 		if (val == info->expected_result[i])
 			continue;
 
@@ -124,41 +122,23 @@ again:
 }
 
 static void
-test_acd_probe_1 (test_fixture *fixture, gconstpointer user_data)
+test_arping_1 (test_fixture *fixture, gconstpointer user_data)
 {
 	TestInfo info = { .addresses       = { ADDR1, ADDR2, ADDR3 },
 	                  .peer_addresses  = { ADDR4 },
 	                  .expected_result = { TRUE, TRUE, TRUE } };
 
-	test_acd_common (fixture, &info);
+	test_arping_common (fixture, &info);
 }
 
 static void
-test_acd_probe_2 (test_fixture *fixture, gconstpointer user_data)
+test_arping_2 (test_fixture *fixture, gconstpointer user_data)
 {
 	TestInfo info = { .addresses       = { ADDR1, ADDR2, ADDR3, ADDR4 },
 	                  .peer_addresses  = { ADDR3, ADDR2 },
 	                  .expected_result = { TRUE, FALSE, FALSE, TRUE } };
 
-	test_acd_common (fixture, &info);
-}
-
-static void
-test_acd_announce (test_fixture *fixture, gconstpointer user_data)
-{
-	gs_unref_object NMAcdManager *manager = NULL;
-	GMainLoop *loop;
-
-	manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len);
-	g_assert (manager != NULL);
-
-	g_assert (nm_acd_manager_add_address (manager, ADDR1));
-	g_assert (nm_acd_manager_add_address (manager, ADDR2));
-
-	loop = g_main_loop_new (NULL, FALSE);
-	nm_acd_manager_announce_addresses (manager);
-	g_assert (!nmtst_main_loop_run (loop, 200));
-	g_main_loop_unref (loop);
+	test_arping_common (fixture, &info);
 }
 
 static void
@@ -179,7 +159,6 @@ _nmtstp_init_tests (int *argc, char ***argv)
 void
 _nmtstp_setup_tests (void)
 {
-	g_test_add ("/acd/probe/1", test_fixture, NULL, fixture_setup, test_acd_probe_1, fixture_teardown);
-	g_test_add ("/acd/probe/2", test_fixture, NULL, fixture_setup, test_acd_probe_2, fixture_teardown);
-	g_test_add ("/acd/announce", test_fixture, NULL, fixture_setup, test_acd_announce, fixture_teardown);
+	g_test_add ("/arping/1", test_fixture, NULL, fixture_setup, test_arping_1, fixture_teardown);
+	g_test_add ("/arping/2", test_fixture, NULL, fixture_setup, test_arping_2, fixture_teardown);
 }
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index 5d0625f8..c3c4f0d2 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -347,7 +347,8 @@ static void
 _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 {
 	const NMPlatformLink *link;
-	nm_auto_close int fd = -1;
+	struct ifreq ifr = { };
+	int fd, s;
 
 	fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
 	if (fd == -1) {
@@ -356,45 +357,20 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 		return;
 	}
 
-	if (nmtst_get_rand_bool ()) {
-		const NMPlatformLnkTun lnk = {
-			.type = IFF_TAP,
-			.pi = FALSE,
-			.vnet_hdr = FALSE,
-			.multi_queue = FALSE,
-			.persist = FALSE,
-		};
-
-		nm_close (nm_steal_fd (&fd));
-
-		link = nmtstp_link_tun_add (NM_PLATFORM_GET,
-		                            FALSE,
-		                            TEST_IFNAME,
-		                            &lnk,
-		                            &fd);
-		g_assert (link);
-		nmtstp_link_set_updown (NM_PLATFORM_GET, -1, link->ifindex, TRUE);
-		link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 0);
-	} else {
-		int s;
-		struct ifreq ifr = { };
-
-		ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-		nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
-		g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
-
-		/* Bring the interface up */
-		s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-		g_assert (s >= 0);
-		ifr.ifr_flags |= IFF_UP;
-		g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
-		nm_close (s);
-
-		link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TUN, 100);
-	}
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
+	g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
+
+	/* Bring the interface up */
+	s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	g_assert (s >= 0);
+	ifr.ifr_flags |= IFF_UP;
+	g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
+	nm_close (s);
 
+	link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TAP, 100);
 	fixture->ifindex = link->ifindex;
-	fixture->fd = nm_steal_fd (&fd);
+	fixture->fd = fd;
 	memcpy (fixture->mac, link->addr.data, ETH_ALEN);
 }
 
diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build
deleted file mode 100644
index 27eaeea6..00000000
--- a/src/devices/wifi/meson.build
+++ /dev/null
@@ -1,51 +0,0 @@
-common_sources = files(
-  'nm-wifi-ap.c',
-  'nm-wifi-utils.c'
-)
-
-sources = common_sources + files(
-  'nm-wifi-factory.c',
-  'nm-wifi-common.c',
-  'nm-device-wifi.c',
-  'nm-device-olpc-mesh.c'
-)
-
-if enable_iwd
-  sources += files(
-    'nm-device-iwd.c',
-    'nm-iwd-manager.c',
-  )
-endif
-
-deps = [
-  nm_dep
-]
-
-libnm_device_plugin_wifi = shared_module(
-  'nm-device-plugin-wifi',
-  sources: sources,
-  dependencies: deps,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_wifi
-
-run_target(
-  'check-local-devices-wifi',
-  command: [check_exports, libnm_device_plugin_wifi.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_wifi
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-wifi: src/devices/wifi/libnm-device-plugin-wifi.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wifi/.libs/libnm-device-plugin-wifi.so)
-'''
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
deleted file mode 100644
index eeff5bd3..00000000
--- a/src/devices/wifi/nm-device-iwd.c
+++ /dev/null
@@ -1,1921 +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) 2017 Intel Corporation
- */
-
-#include "nm-default.h"
-
-#include "nm-device-iwd.h"
-
-#include <string.h>
-
-#include "nm-common-macros.h"
-#include "devices/nm-device.h"
-#include "devices/nm-device-private.h"
-#include "nm-utils.h"
-#include "nm-act-request.h"
-#include "nm-setting-connection.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-setting-8021x.h"
-#include "settings/nm-settings-connection.h"
-#include "settings/nm-settings.h"
-#include "nm-wifi-utils.h"
-#include "nm-wifi-common.h"
-#include "nm-core-internal.h"
-#include "nm-config.h"
-#include "nm-iwd-manager.h"
-#include "nm-dbus-manager.h"
-
-#include "devices/nm-device-logging.h"
-_LOG_DECLARE_SELF(NMDeviceIwd);
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIwd,
-	PROP_MODE,
-	PROP_BITRATE,
-	PROP_ACCESS_POINTS,
-	PROP_ACTIVE_ACCESS_POINT,
-	PROP_CAPABILITIES,
-	PROP_SCANNING,
-);
-
-enum {
-	SCANNING_PROHIBITED,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
-	GDBusObject *   dbus_obj;
-	GDBusProxy *    dbus_proxy;
-	CList           aps_lst_head;
-	NMWifiAP *      current_ap;
-	GCancellable *  cancellable;
-	NMDeviceWifiCapabilities capabilities;
-	NMActRequestGetSecretsCallId *wifi_secrets_id;
-	GDBusMethodInvocation *secrets_request;
-	guint           periodic_scan_id;
-	bool            enabled:1;
-	bool            can_scan:1;
-	bool            can_connect:1;
-	bool            scanning:1;
-	bool            scan_requested:1;
-} NMDeviceIwdPrivate;
-
-struct _NMDeviceIwd {
-	NMDevice parent;
-	NMDeviceIwdPrivate _priv;
-};
-
-struct _NMDeviceIwdClass {
-	NMDeviceClass parent;
-
-	/* Signals */
-	gboolean (*scanning_prohibited) (NMDeviceIwd *device, gboolean periodic);
-};
-
-/*****************************************************************************/
-
-G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE)
-
-#define NM_DEVICE_IWD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceIwd, NM_IS_DEVICE_IWD)
-
-/*****************************************************************************/
-
-static void schedule_periodic_scan (NMDeviceIwd *self,
-                                    NMDeviceState current_state);
-
-/*****************************************************************************/
-
-static void
-_ap_dump (NMDeviceIwd *self,
-          NMLogLevel log_level,
-          const NMWifiAP *ap,
-          const char *prefix,
-          gint32 now_s)
-{
-	char buf[1024];
-
-	buf[0] = '\0';
-	_NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
-	        prefix,
-	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
-}
-
-/* Callers ensure we're not removing current_ap */
-static void
-ap_add_remove (NMDeviceIwd *self,
-               gboolean is_adding, /* or else is removing */
-               NMWifiAP *ap,
-               gboolean recheck_available_connections)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	if (is_adding) {
-		g_object_ref (ap);
-		ap->wifi_device = NM_DEVICE (self);
-		c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst);
-		nm_dbus_object_export (NM_DBUS_OBJECT (ap));
-		_ap_dump (self, LOGL_DEBUG, ap, "added", 0);
-		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE);
-	} else {
-		ap->wifi_device = NULL;
-		c_list_unlink (&ap->aps_lst);
-		_ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
-	}
-
-	_notify (self, PROP_ACCESS_POINTS);
-
-	if (!is_adding) {
-		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, FALSE);
-		nm_dbus_object_clear_and_unexport (&ap);
-	}
-
-	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
-	if (recheck_available_connections)
-		nm_device_recheck_available_connections (NM_DEVICE (self));
-}
-
-static void
-set_current_ap (NMDeviceIwd *self, NMWifiAP *new_ap, gboolean recheck_available_connections)
-{
-	NMDeviceIwdPrivate *priv;
-	NMWifiAP *old_ap;
-
-	g_return_if_fail (NM_IS_DEVICE_IWD (self));
-
-	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	old_ap = priv->current_ap;
-
-	if (old_ap == new_ap)
-		return;
-
-	if (new_ap)
-		priv->current_ap = g_object_ref (new_ap);
-	else
-		priv->current_ap = NULL;
-
-	if (old_ap) {
-		if (nm_wifi_ap_get_fake (old_ap))
-			ap_add_remove (self, FALSE, old_ap, recheck_available_connections);
-		g_object_unref (old_ap);
-	}
-
-	_notify (self, PROP_ACTIVE_ACCESS_POINT);
-	_notify (self, PROP_MODE);
-}
-
-static void
-remove_all_aps (NMDeviceIwd *self)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMWifiAP *ap, *ap_safe;
-
-	if (c_list_is_empty (&priv->aps_lst_head))
-		return;
-
-	set_current_ap (self, NULL, FALSE);
-
-	c_list_for_each_entry_safe (ap, ap_safe, &priv->aps_lst_head, aps_lst)
-		ap_add_remove (self, FALSE, ap, FALSE);
-
-	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
-	nm_device_recheck_available_connections (NM_DEVICE (self));
-}
-
-static GVariant *
-vardict_from_network_type (const gchar *type)
-{
-	GVariantBuilder builder;
-	const gchar *key_mgmt = "";
-	const gchar *pairwise = "ccmp";
-
-	if (!strcmp (type, "psk"))
-		key_mgmt = "wpa-psk";
-	else if (!strcmp (type, "8021x"))
-		key_mgmt = "wpa-eap";
-	else
-		return NULL;
-
-	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-	g_variant_builder_add (&builder, "{sv}", "KeyMgmt",
-	                       g_variant_new_strv (&key_mgmt, 1));
-	g_variant_builder_add (&builder, "{sv}", "Pairwise",
-	                       g_variant_new_strv (&pairwise, 1));
-	g_variant_builder_add (&builder, "{sv}", "Group",
-	                       g_variant_new_string ("ccmp"));
-	return g_variant_new ("a{sv}", &builder);
-}
-
-static void
-get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	NMDeviceIwd *self = user_data;
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *variant = NULL;
-	GVariantIter *networks;
-	const gchar *path, *name, *type;
-	int16_t signal;
-	NMWifiAP *ap, *ap_safe;
-	gboolean changed = FALSE;
-	GHashTableIter ap_iter;
-	gs_unref_hashtable GHashTable *new_aps = NULL;
-
-	variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                      G_VARIANT_TYPE ("(a(osns))"),
-	                                      &error);
-	if (!variant) {
-		_LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s",
-		       error->message);
-		return;
-	}
-
-	new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
-
-	g_variant_get (variant, "(a(osns))", &networks);
-
-	while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) {
-		GVariantBuilder builder;
-		gs_unref_variant GVariant *props = NULL;
-		GVariant *rsn;
-		static uint32_t ap_id = 0;
-		uint8_t bssid[6];
-
-		/*
-		 * What we get from IWD are networks, or ESSs, that may
-		 * contain multiple APs, or BSSs, each.  We don't get
-		 * information about any specific BSSs within an ESS but
-		 * we can safely present each ESS as an individual BSS to
-		 * NM, which will be seen as ESSs comprising a single BSS
-		 * each.  NM won't be able to handle roaming but IWD already
-		 * does that.  We fake the BSSIDs as they don't play any
-		 * role either.
-		 */
-		bssid[0] = 0x00;
-		bssid[1] = 0x01;
-		bssid[2] = 0x02;
-		bssid[3] = ap_id >> 16;
-		bssid[4] = ap_id >> 8;
-		bssid[5] = ap_id++;
-
-		/* WEP not supported */
-		if (!strcmp (type, "wep"))
-			continue;
-
-		g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-		g_variant_builder_add (&builder, "{sv}", "BSSID",
-		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
-		g_variant_builder_add (&builder, "{sv}", "Mode",
-		                       g_variant_new_string ("infrastructure"));
-
-		rsn = vardict_from_network_type (type);
-		if (rsn)
-			g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
-
-		props = g_variant_new ("a{sv}", &builder);
-
-		ap = nm_wifi_ap_new_from_properties (path, props);
-		if (name[0] != '\0')
-			nm_wifi_ap_set_ssid (ap, (const guint8 *) name, strlen (name));
-		nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100));
-		nm_wifi_ap_set_freq (ap, 2417);
-		nm_wifi_ap_set_max_bitrate (ap, 65000);
-		g_hash_table_insert (new_aps,
-		                     (gpointer) nm_wifi_ap_get_supplicant_path (ap),
-		                     ap);
-	}
-
-	g_variant_iter_free (networks);
-
-	c_list_for_each_entry_safe (ap, ap_safe, &priv->aps_lst_head, aps_lst) {
-
-		ap = g_hash_table_lookup (new_aps,
-		                          nm_wifi_ap_get_supplicant_path (ap));
-		if (ap) {
-			if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (ap))) {
-				_ap_dump (self, LOGL_TRACE, ap, "updated", 0);
-				changed = TRUE;
-			}
-			g_hash_table_remove (new_aps,
-			                     nm_wifi_ap_get_supplicant_path (ap));
-			continue;
-		}
-
-		if (ap == priv->current_ap) {
-			/* Normally IWD will prevent the current AP from being
-			 * removed from the list and set a low signal strength,
-			 * but just making sure.
-			 */
-			continue;
-		}
-
-		ap_add_remove (self, FALSE, ap, FALSE);
-		changed = TRUE;
-	}
-
-	g_hash_table_iter_init (&ap_iter, new_aps);
-	while (g_hash_table_iter_next (&ap_iter, NULL, (gpointer) &ap)) {
-		ap_add_remove (self, TRUE, ap, FALSE);
-		g_hash_table_iter_remove (&ap_iter);
-		changed = TRUE;
-	}
-
-	if (changed) {
-		nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
-		nm_device_recheck_available_connections (NM_DEVICE (self));
-	}
-}
-
-static void
-update_aps (NMDeviceIwd *self)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	if (!priv->cancellable)
-		priv->cancellable = g_cancellable_new ();
-
-	g_dbus_proxy_call (priv->dbus_proxy, "GetOrderedNetworks",
-	                   g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE,
-	                   2000, priv->cancellable,
-	                   get_ordered_networks_cb, self);
-}
-
-static void
-send_disconnect (NMDeviceIwd *self)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
-}
-
-static void
-wifi_secrets_cancel (NMDeviceIwd *self)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	if (priv->wifi_secrets_id)
-		nm_act_request_cancel_secrets (NULL, priv->wifi_secrets_id);
-	nm_assert (!priv->wifi_secrets_id);
-
-	if (priv->secrets_request) {
-		g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                                               "NM secrets request cancelled");
-		priv->secrets_request = NULL;
-	}
-
-}
-
-static void
-cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	wifi_secrets_cancel (self);
-
-	set_current_ap (self, NULL, TRUE);
-
-	if (disconnect && priv->dbus_obj)
-		send_disconnect (self);
-}
-
-static void
-deactivate (NMDevice *device)
-{
-	cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE);
-}
-
-static gboolean
-deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device));
-	gs_unref_variant GVariant *variant = NULL;
-
-	variant = g_dbus_proxy_call_finish (priv->dbus_proxy, res, error);
-	return variant != NULL;
-}
-
-typedef struct {
-	NMDeviceIwd *self;
-	GAsyncReadyCallback callback;
-	gpointer user_data;
-} DeactivateContext;
-
-static void
-disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	DeactivateContext *ctx = user_data;
-
-	ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data);
-
-	g_object_unref (ctx->self);
-	g_slice_free (DeactivateContext, ctx);
-}
-
-static void
-deactivate_async (NMDevice *device,
-                  GCancellable *cancellable,
-                  GAsyncReadyCallback callback,
-                  gpointer user_data)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	DeactivateContext *ctx;
-
-	ctx = g_slice_new0 (DeactivateContext);
-	ctx->self = g_object_ref (self);
-	ctx->callback = callback;
-	ctx->user_data = user_data;
-
-	g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx);
-}
-
-static NMIwdNetworkSecurity
-get_connection_iwd_security (NMConnection *connection)
-{
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const char *key_mgmt = NULL;
-
-	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
-	if (!s_wireless_sec)
-		return NM_IWD_NETWORK_SECURITY_NONE;
-
-	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
-	nm_assert (key_mgmt);
-
-	if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x"))
-		return NM_IWD_NETWORK_SECURITY_WEP;
-
-	if (!strcmp (key_mgmt, "wpa-psk"))
-		return NM_IWD_NETWORK_SECURITY_PSK;
-
-	nm_assert (!strcmp (key_mgmt, "wpa-eap"));
-	return NM_IWD_NETWORK_SECURITY_8021X;
-}
-
-static gboolean
-is_connection_known_network (NMConnection *connection)
-{
-	NMSettingWireless *s_wireless;
-	GBytes *ssid;
-	gs_free gchar *str_ssid = NULL;
-
-	s_wireless = nm_connection_get_setting_wireless (connection);
-	if (!s_wireless)
-		return FALSE;
-
-	ssid = nm_setting_wireless_get_ssid (s_wireless);
-	if (!ssid)
-		return FALSE;
-
-	str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
-	                                  g_bytes_get_size (ssid));
-
-	return nm_iwd_manager_is_known_network (nm_iwd_manager_get (),
-	                                        str_ssid,
-	                                        get_connection_iwd_security (connection));
-}
-
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingConnection *s_con;
-	NMSettingWireless *s_wireless;
-	const char *mac;
-	const char * const *mac_blacklist;
-	int i;
-	const char *mode;
-	const char *perm_hw_addr;
-
-	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
-		return FALSE;
-
-	s_wireless = nm_connection_get_setting_wireless (connection);
-	if (!s_wireless)
-		return FALSE;
-
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
-	mac = nm_setting_wireless_get_mac_address (s_wireless);
-	if (perm_hw_addr) {
-		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
-			return FALSE;
-
-		/* Check for MAC address blacklist */
-		mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
-		for (i = 0; mac_blacklist[i]; i++) {
-			if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
-				g_warn_if_reached ();
-				return FALSE;
-			}
-
-			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
-				return FALSE;
-		}
-	} else if (mac)
-		return FALSE;
-
-	mode = nm_setting_wireless_get_mode (s_wireless);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
-		return FALSE;
-
-	/* 8021x networks can only be used if they've been provisioned on the IWD side and
-	 * thus are Known Networks.
-	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection))
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean
-check_connection_available (NMDevice *device,
-                            NMConnection *connection,
-                            NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMSettingWireless *s_wifi;
-	const char *mode;
-
-	s_wifi = nm_connection_get_setting_wireless (connection);
-	g_return_val_if_fail (s_wifi, FALSE);
-
-	/* Only Infrastrusture mode at this time */
-	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
-		return FALSE;
-
-	/* Hidden SSIDs not supported yet */
-	if (nm_setting_wireless_get_hidden (s_wifi))
-		return FALSE;
-
-	/* 8021x networks can only be used if they've been provisioned on the IWD side and
-	 * thus are Known Networks.
-	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection))
-			return FALSE;
-	}
-
-	/* a connection that is available for a certain @specific_object, MUST
-	 * also be available in general (without @specific_object). */
-
-	if (specific_object) {
-		NMWifiAP *ap;
-
-		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
-		return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
-	}
-
-	if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
-		return TRUE;
-
-	/* Check at least one AP is compatible with this connection */
-	return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
-}
-
-static gboolean
-complete_connection (NMDevice *device,
-                     NMConnection *connection,
-                     const char *specific_object,
-                     NMConnection *const*existing_connections,
-                     GError **error)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMSettingWireless *s_wifi;
-	const char *setting_mac;
-	char *str_ssid = NULL;
-	NMWifiAP *ap;
-	const GByteArray *ssid = NULL;
-	GByteArray *tmp_ssid = NULL;
-	GBytes *setting_ssid = NULL;
-	const char *perm_hw_addr;
-	const char *mode;
-
-	s_wifi = nm_connection_get_setting_wireless (connection);
-
-	mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL;
-
-	if (s_wifi && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
-		g_set_error_literal (error,
-		                     NM_DEVICE_ERROR,
-		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                     "Only Infrastructure mode is supported.");
-		return FALSE;
-	}
-
-	if (!specific_object) {
-		/* If not given a specific object, we need at minimum an SSID */
-		if (!s_wifi) {
-			g_set_error_literal (error,
-			                     NM_DEVICE_ERROR,
-			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-			                     "A 'wireless' setting is required if no AP path was given.");
-			return FALSE;
-		}
-
-		setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
-		if (!setting_ssid || g_bytes_get_size (setting_ssid) == 0) {
-			g_set_error_literal (error,
-			                     NM_DEVICE_ERROR,
-			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-			                     "A 'wireless' setting with a valid SSID is required if no AP path was given.");
-			return FALSE;
-		}
-
-		/* Find a compatible AP in the scan list */
-		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
-		if (!ap) {
-			g_set_error_literal (error,
-			                     NM_DEVICE_ERROR,
-			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-			                     "No compatible AP in the scan list and hidden SSIDs not supported.");
-			return FALSE;
-		}
-	} else {
-		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
-		if (!ap) {
-			g_set_error (error,
-			             NM_DEVICE_ERROR,
-			             NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
-			             "The access point %s was not in the scan list.",
-			             specific_object);
-			return FALSE;
-		}
-	}
-
-	/* Add a wifi setting if one doesn't exist yet */
-	if (!s_wifi) {
-		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
-		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
-	}
-
-	ssid = nm_wifi_ap_get_ssid (ap);
-
-	if (ssid == NULL) {
-		g_set_error_literal (error,
-		                     NM_DEVICE_ERROR,
-		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                     "A 'wireless' setting with a valid SSID is required.");
-		return FALSE;
-	}
-
-	if (!nm_wifi_ap_complete_connection (ap,
-	                                     connection,
-	                                     nm_wifi_utils_is_manf_default_ssid (ssid),
-	                                     error)) {
-		if (tmp_ssid)
-			g_byte_array_unref (tmp_ssid);
-		return FALSE;
-	}
-
-	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
-
-	nm_utils_complete_generic (nm_device_get_platform (device),
-	                           connection,
-	                           NM_SETTING_WIRELESS_SETTING_NAME,
-	                           existing_connections,
-	                           str_ssid,
-	                           str_ssid,
-	                           NULL,
-	                           TRUE);
-	g_free (str_ssid);
-	if (tmp_ssid)
-		g_byte_array_unref (tmp_ssid);
-
-	/* 8021x networks can only be used if they've been provisioned on the IWD side and
-	 * thus are Known Networks.
-	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection)) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_DEVICE_ERROR_INVALID_CONNECTION,
-			                     "This 8021x network has not been provisioned on this machine");
-			return FALSE;
-		}
-	}
-
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
-	if (perm_hw_addr) {
-		setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
-		if (setting_mac) {
-			/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-			if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     "connection does not match device");
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
-				return FALSE;
-			}
-		} else {
-			guint8 tmp[ETH_ALEN];
-
-			/* Lock the connection to this device by default if it uses a
-			 * permanent MAC address (ie not a 'locally administered' one)
-			 */
-			nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN);
-			if (!(tmp[0] & 0x02)) {
-				g_object_set (G_OBJECT (s_wifi),
-				              NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr,
-				              NULL);
-			}
-		}
-	}
-
-	return TRUE;
-}
-
-static gboolean
-is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	gs_unref_variant GVariant *value = NULL;
-
-	if (!priv->enabled || !priv->dbus_obj)
-		return FALSE;
-
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Powered");
-	return g_variant_get_boolean (value);
-}
-
-static gboolean
-get_autoconnect_allowed (NMDevice *device)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device));
-
-	return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)
-	       && priv->can_connect;
-}
-
-static gboolean
-can_auto_connect (NMDevice *device,
-                  NMConnection *connection,
-                  char **specific_object)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMSettingWireless *s_wifi;
-	NMWifiAP *ap;
-	const char *mode;
-	guint64 timestamp = 0;
-
-	nm_assert (!specific_object || !*specific_object);
-
-	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->can_auto_connect (device, connection, NULL))
-		return FALSE;
-
-	s_wifi = nm_connection_get_setting_wireless (connection);
-	g_return_val_if_fail (s_wifi, FALSE);
-
-	/* Only Infrastrusture mode */
-	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
-		return FALSE;
-
-	/* Don't autoconnect to networks that have been tried at least once
-	 * but haven't been successful, since these are often accidental choices
-	 * from the menu and the user may not know the password.
-	 */
-	if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp)) {
-		if (timestamp == 0)
-			return FALSE;
-	}
-
-	/* 8021x networks can only be used if they've been provisioned on the IWD side and
-	 * thus are Known Networks.
-	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection))
-			return FALSE;
-	}
-
-	ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
-	if (ap) {
-		/* All good; connection is usable */
-		NM_SET_OUT (specific_object, g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))));
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-const CList *
-_nm_device_iwd_get_aps (NMDeviceIwd *self)
-{
-	return &NM_DEVICE_IWD_GET_PRIVATE (self)->aps_lst_head;
-}
-
-static gboolean
-check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
-{
-	gboolean prohibited = FALSE;
-
-	g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited);
-	return prohibited;
-}
-
-static void
-scan_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	NMDeviceIwd *self = user_data;
-	NMDeviceIwdPrivate *priv;
-	gs_free_error GError *error = NULL;
-
-	if (   !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                   G_VARIANT_TYPE ("()"), &error)
-	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	priv->scan_requested = FALSE;
-
-	/* On success, priv->scanning becomes true right before or right
-	 * after this callback, so the next automatic scan will be
-	 * scheduled when priv->scanning goes back to false.  On error,
-	 * schedule a retry now.
-	 */
-	if (error && !priv->scanning) {
-		NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
-
-		schedule_periodic_scan (self, state);
-	}
-}
-
-static void
-dbus_request_scan_cb (NMDevice *device,
-                      GDBusMethodInvocation *context,
-                      NMAuthSubject *subject,
-                      GError *error,
-                      gpointer user_data)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv;
-	gs_unref_variant GVariant *scan_options = user_data;
-
-	if (error) {
-		g_dbus_method_invocation_return_gerror (context, error);
-		return;
-	}
-
-	if (check_scanning_prohibited (self, FALSE)) {
-		g_dbus_method_invocation_return_error_literal (context,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
-		                                               "Scanning not allowed at this time");
-		return;
-	}
-
-	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	if (   !priv->enabled
-	    || !priv->dbus_obj
-	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
-	    || nm_device_is_activating (device)) {
-		g_dbus_method_invocation_return_error_literal (context,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
-		                                               "Scanning not allowed while unavailable");
-		return;
-	}
-
-	if (scan_options) {
-		gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL);
-
-		if (val) {
-			g_dbus_method_invocation_return_error_literal (context,
-			                                               NM_DEVICE_ERROR,
-			                                               NM_DEVICE_ERROR_NOT_ALLOWED,
-			                                               "'ssid' scan option not supported");
-			return;
-		}
-	}
-
-	if (!priv->scanning && !priv->scan_requested) {
-		g_dbus_proxy_call (priv->dbus_proxy, "Scan",
-		                   g_variant_new ("()"),
-		                   G_DBUS_CALL_FLAGS_NONE, -1,
-		                   priv->cancellable, scan_cb, self);
-		priv->scan_requested = TRUE;
-	}
-
-	g_dbus_method_invocation_return_value (context, NULL);
-}
-
-void
-_nm_device_iwd_request_scan (NMDeviceIwd *self,
-                             GVariant *options,
-                             GDBusMethodInvocation *invocation)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMDevice *device = NM_DEVICE (self);
-
-	if (   !priv->enabled
-	    || !priv->dbus_obj
-	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
-	    || nm_device_is_activating (device)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
-		                                               "Scanning not allowed while unavailable");
-		return;
-	}
-
-	g_signal_emit_by_name (device,
-	                       NM_DEVICE_AUTH_REQUEST,
-	                       invocation,
-	                       NULL,
-	                       NM_AUTH_PERMISSION_NETWORK_CONTROL,
-	                       TRUE,
-	                       dbus_request_scan_cb,
-	                       options ? g_variant_ref (options) : NULL);
-}
-
-static gboolean
-scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	g_return_val_if_fail (priv->dbus_obj != NULL, TRUE);
-
-	switch (nm_device_get_state (NM_DEVICE (self))) {
-	case NM_DEVICE_STATE_UNKNOWN:
-	case NM_DEVICE_STATE_UNMANAGED:
-	case NM_DEVICE_STATE_UNAVAILABLE:
-	case NM_DEVICE_STATE_PREPARE:
-	case NM_DEVICE_STATE_CONFIG:
-	case NM_DEVICE_STATE_NEED_AUTH:
-	case NM_DEVICE_STATE_IP_CONFIG:
-	case NM_DEVICE_STATE_IP_CHECK:
-	case NM_DEVICE_STATE_SECONDARIES:
-	case NM_DEVICE_STATE_DEACTIVATING:
-		/* Prohibit scans when unusable or activating */
-		return TRUE;
-	case NM_DEVICE_STATE_DISCONNECTED:
-	case NM_DEVICE_STATE_FAILED:
-		/* Can always scan when disconnected */
-		return FALSE;
-	case NM_DEVICE_STATE_ACTIVATED:
-		break;
-	}
-
-	/* Prohibit scans if IWD is busy */
-	return !priv->can_scan;
-}
-
-static void
-wifi_secrets_cb (NMActRequest *req,
-                 NMActRequestGetSecretsCallId *call_id,
-                 NMSettingsConnection *s_connection,
-                 GError *error,
-                 gpointer user_data)
-{
-	NMDevice *device = user_data;
-	NMDeviceIwd *self = user_data;
-	NMDeviceIwdPrivate *priv;
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const gchar *psk;
-
-	g_return_if_fail (NM_IS_DEVICE_IWD (self));
-	g_return_if_fail (NM_IS_ACT_REQUEST (req));
-
-	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	g_return_if_fail (priv->wifi_secrets_id == call_id);
-
-	priv->wifi_secrets_id = NULL;
-
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	g_return_if_fail (priv->secrets_request);
-	g_return_if_fail (req == nm_device_get_act_request (device));
-	g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection);
-
-	if (nm_device_get_state (device) != NM_DEVICE_STATE_NEED_AUTH)
-		goto secrets_error;
-
-	if (error) {
-		_LOGW (LOGD_WIFI, "%s", error->message);
-		goto secrets_error;
-	}
-
-	s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req));
-	if (!s_wireless_sec)
-		goto secrets_error;
-
-	psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
-	if (!psk)
-		goto secrets_error;
-
-	_LOGD (LOGD_DEVICE | LOGD_WIFI,
-	       "Returning a new PSK to the IWD Agent");
-
-	g_dbus_method_invocation_return_value (priv->secrets_request,
-	                                       g_variant_new ("(s)", psk));
-	priv->secrets_request = NULL;
-
-	/* Change state back to what it was before NEED_AUTH */
-	nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
-	return;
-
-secrets_error:
-	if (priv->secrets_request) {
-		g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                                               "NM secrets request failed");
-		priv->secrets_request = NULL;
-	}
-
-	nm_device_state_changed (device,
-	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-
-	cleanup_association_attempt (self, TRUE);
-}
-
-static void
-wifi_secrets_get_secrets (NMDeviceIwd *self,
-                          const char *setting_name,
-                          NMSecretAgentGetSecretsFlags flags)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActRequest *req;
-
-	wifi_secrets_cancel (self);
-
-	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_return_if_fail (NM_IS_ACT_REQUEST (req));
-
-	priv->wifi_secrets_id = nm_act_request_get_secrets (req,
-	                                                    TRUE,
-	                                                    setting_name,
-	                                                    flags,
-	                                                    NULL,
-	                                                    wifi_secrets_cb,
-	                                                    self);
-}
-
-static void
-network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	NMDeviceIwd *self = user_data;
-	NMDevice *device = NM_DEVICE (self);
-	gs_free_error GError *error = NULL;
-	NMConnection *connection;
-	NMSettingWireless *s_wifi;
-	GBytes *ssid;
-	gs_free gchar *str_ssid = NULL;
-
-	if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                 G_VARIANT_TYPE ("()"),
-	                                 &error)) {
-		gs_free gchar *dbus_error = NULL;
-
-		/* Connection failed; radio problems or if the network wasn't
-		 * open, the passwords or certificates may be wrong.
-		 */
-
-		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) Network.Connect failed: %s",
-		       error->message);
-
-		connection = nm_device_get_applied_connection (device);
-		if (!connection)
-			goto failed;
-
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR))
-			dbus_error = g_dbus_error_get_remote_error (error);
-
-		/* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
-		if (nm_streq0 (dbus_error, "net.connman.iwd.Failed")) {
-			nm_connection_clear_secrets (connection);
-
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-		} else if (   !nm_utils_error_is_cancelled (error, TRUE)
-		           && nm_device_is_activating (device))
-			goto failed;
-
-		/* Call Disconnect to make sure IWD's autoconnect is disabled */
-		cleanup_association_attempt (self, TRUE);
-
-		return;
-	}
-
-	nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
-
-	connection = nm_device_get_applied_connection (device);
-	if (!connection)
-		goto failed;
-
-	s_wifi = nm_connection_get_setting_wireless (connection);
-	if (!s_wifi)
-		goto failed;
-
-	ssid = nm_setting_wireless_get_ssid (s_wifi);
-	if (!ssid)
-		goto failed;
-
-	str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
-	                                  g_bytes_get_size (ssid));
-
-	_LOGI (LOGD_DEVICE | LOGD_WIFI,
-	       "Activation: (wifi) Stage 2 of 5 (Device Configure) successful.  Connected to '%s'.",
-	       str_ssid);
-	nm_device_activate_schedule_stage3_ip_config_start (device);
-
-	nm_iwd_manager_network_connected (nm_iwd_manager_get (), str_ssid,
-	                                  get_connection_iwd_security (connection));
-
-	return;
-
-failed:
-	cleanup_association_attempt (self, FALSE);
-	nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-}
-
-static void
-set_powered (NMDeviceIwd *self, gboolean powered)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	g_dbus_proxy_call (priv->dbus_proxy,
-	                   "org.freedesktop.DBus.Properties.Set",
-	                   g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE,
-	                                  "Powered",
-	                                  g_variant_new ("b", powered)),
-	                   G_DBUS_CALL_FLAGS_NONE, 2000,
-	                   NULL, NULL, NULL);
-}
-
-/*****************************************************************************/
-
-static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActStageReturn ret;
-	NMWifiAP *ap = NULL;
-	NMActRequest *req;
-	NMConnection *connection;
-	NMSettingWireless *s_wireless;
-	const char *ap_path;
-
-	ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason);
-	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
-		return ret;
-
-	req = nm_device_get_act_request (device);
-	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
-
-	connection = nm_act_request_get_applied_connection (req);
-	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
-
-	s_wireless = nm_connection_get_setting_wireless (connection);
-	g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
-	ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
-	if (!ap) {
-		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
-
-		/* TODO: assuming hidden networks aren't supported do we need
-		 * to consider the case of APs that are not in the scan list
-		 * yet, for which nm-device-wifi.c creates the temporary fake
-		 * AP object?
-		 */
-
-		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-	                                              nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
-	}
-
-	set_current_ap (self, ap, FALSE);
-	return NM_ACT_STAGE_RETURN_SUCCESS;
-}
-
-static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMActRequest *req;
-	NMWifiAP *ap;
-	NMConnection *connection;
-	GError *error = NULL;
-	GDBusProxy *network_proxy;
-
-	req = nm_device_get_act_request (device);
-	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ap = priv->current_ap;
-	if (!ap) {
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		goto out;
-	}
-
-	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
-
-	/* 802.1x networks that are not IWD Known Networks will definitely
-	 * fail, for other combinations we will let the Connect call fail
-	 * or ask us for any missing secrets through the Agent.
-	 */
-	if (   !is_connection_known_network (connection)
-	    && nm_connection_get_setting_802_1x (connection)) {
-		_LOGI (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.",
-		       nm_connection_get_id (connection));
-
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
-		goto out;
-	}
-
-	/* Locate the IWD Network object */
-	network_proxy = g_dbus_proxy_new_for_bus_sync (NM_IWD_BUS_TYPE,
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                                               NULL,
-	                                               NM_IWD_SERVICE,
-	                                               nm_wifi_ap_get_supplicant_path (ap),
-	                                               NM_IWD_NETWORK_INTERFACE,
-	                                               NULL, &error);
-	if (!network_proxy) {
-		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) could not get Network interface proxy for %s: %s",
-		       nm_wifi_ap_get_supplicant_path (ap),
-		       error->message);
-		g_clear_error (&error);
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		goto out;
-	}
-
-	if (!priv->cancellable)
-		priv->cancellable = g_cancellable_new ();
-
-	/* Call Network.Connect.  No timeout because IWD already handles
-	 * timeouts.
-	 */
-	g_dbus_proxy_call (network_proxy, "Connect",
-	                   g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
-	                   priv->cancellable, network_connect_cb, self);
-
-	g_object_unref (network_proxy);
-
-	/* We'll get stage3 started when the supplicant connects */
-	ret = NM_ACT_STAGE_RETURN_POSTPONE;
-
-out:
-	if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-		cleanup_association_attempt (self, FALSE);
-
-	return ret;
-}
-
-static guint32
-get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
-{
-	NMSettingWireless *setting;
-	gint64 mtu_default;
-	guint32 mtu;
-
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (out_is_user_config);
-
-	setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS));
-	if (!setting)
-		g_return_val_if_reached (0);
-
-	mtu = nm_setting_wireless_get_mtu (setting);
-	if (mtu == 0) {
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "wifi.mtu");
-		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
-			return (guint32) mtu_default;
-		}
-	}
-	*out_is_user_config = (mtu != 0);
-	return mtu;
-}
-
-static gboolean
-periodic_scan_timeout_cb (gpointer user_data)
-{
-	NMDeviceIwd *self = user_data;
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	priv->periodic_scan_id = 0;
-
-	if (priv->scanning || priv->scan_requested)
-		return FALSE;
-
-	g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   priv->cancellable, scan_cb, self);
-	priv->scan_requested = TRUE;
-
-	return FALSE;
-}
-
-static void
-schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	guint interval;
-
-	if (current_state <= NM_DEVICE_STATE_UNAVAILABLE)
-		return;
-
-	if (current_state == NM_DEVICE_STATE_DISCONNECTED)
-		interval = 10;
-	else
-		interval = 20;
-
-	nm_clear_g_source (&priv->periodic_scan_id);
-	priv->periodic_scan_id = g_timeout_add_seconds (interval,
-	                                                periodic_scan_timeout_cb,
-	                                                self);
-}
-
-static void
-device_state_changed (NMDevice *device,
-                      NMDeviceState new_state,
-                      NMDeviceState old_state,
-                      NMDeviceStateReason reason)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) {
-		remove_all_aps (self);
-		nm_clear_g_source (&priv->periodic_scan_id);
-	} else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) {
-		update_aps (self);
-		schedule_periodic_scan (self, new_state);
-	}
-
-	switch (new_state) {
-	case NM_DEVICE_STATE_UNMANAGED:
-		break;
-	case NM_DEVICE_STATE_UNAVAILABLE:
-		/*
-		 * If the device is enabled and the IWD manager is ready,
-		 * transition to DISCONNECTED because the device is now
-		 * ready to use.
-		 */
-		if (priv->enabled && priv->dbus_obj) {
-			nm_device_queue_recheck_available (device,
-			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-		}
-		break;
-	case NM_DEVICE_STATE_NEED_AUTH:
-		break;
-	case NM_DEVICE_STATE_IP_CHECK:
-		break;
-	case NM_DEVICE_STATE_ACTIVATED:
-		break;
-	case NM_DEVICE_STATE_FAILED:
-		break;
-	case NM_DEVICE_STATE_DISCONNECTED:
-		break;
-	default:
-		break;
-	}
-}
-
-static gboolean
-get_enabled (NMDevice *device)
-{
-	return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)->enabled;
-}
-
-static void
-set_enabled (NMDevice *device, gboolean enabled)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (device);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMDeviceState state;
-
-	enabled = !!enabled;
-
-	if (priv->enabled == enabled)
-		return;
-
-	priv->enabled = enabled;
-
-	_LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" : "disabled");
-
-	state = nm_device_get_state (device);
-	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
-		_LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED state",
-		       enabled ? "enable" : "disable");
-		return;
-	}
-
-	if (priv->dbus_proxy)
-		set_powered (self, enabled);
-
-	if (enabled) {
-		if (state != NM_DEVICE_STATE_UNAVAILABLE)
-			_LOGW (LOGD_CORE, "not in expected unavailable state!");
-
-		if (priv->dbus_obj)
-			nm_device_queue_recheck_available (device,
-			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-	} else {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         NM_DEVICE_STATE_REASON_NONE);
-	}
-}
-
-static gboolean
-can_reapply_change (NMDevice *device,
-                    const char *setting_name,
-                    NMSetting *s_old,
-                    NMSetting *s_new,
-                    GHashTable *diffs,
-                    GError **error)
-{
-	NMDeviceClass *device_class;
-
-	/* Only handle wireless setting here, delegate other settings to parent class */
-	if (nm_streq (setting_name, NM_SETTING_WIRELESS_SETTING_NAME)) {
-		return nm_device_hash_check_invalid_keys (diffs,
-		                                          NM_SETTING_WIRELESS_SETTING_NAME,
-		                                          error,
-		                                          NM_SETTING_WIRELESS_MTU); /* reapplied with IP config */
-	}
-
-	device_class = NM_DEVICE_CLASS (nm_device_iwd_parent_class);
-	return device_class->can_reapply_change (device,
-	                                         setting_name,
-	                                         s_old,
-	                                         s_new,
-	                                         diffs,
-	                                         error);
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (object);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	const char **list;
-
-	switch (prop_id) {
-	case PROP_MODE:
-		if (priv->current_ap)
-			g_value_set_uint (value, NM_802_11_MODE_INFRA);
-		else
-			g_value_set_uint (value, NM_802_11_MODE_UNKNOWN);
-		break;
-	case PROP_BITRATE:
-		g_value_set_uint (value, 65000);
-		break;
-	case PROP_CAPABILITIES:
-		g_value_set_uint (value, priv->capabilities);
-		break;
-	case PROP_ACCESS_POINTS:
-		list = nm_wifi_aps_get_paths (&priv->aps_lst_head, TRUE);
-		g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list));
-		break;
-	case PROP_ACTIVE_ACCESS_POINT:
-		nm_dbus_utils_g_value_set_object_path (value, priv->current_ap);
-		break;
-	case PROP_SCANNING:
-		g_value_set_boolean (value, priv->scanning);
-		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)
-{
-	NMDeviceIwd *device = NM_DEVICE_IWD (object);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_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;
-	}
-}
-
-/*****************************************************************************/
-
-static void
-state_changed (NMDeviceIwd *self, const gchar *new_state)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMDevice *device = NM_DEVICE (self);
-	NMDeviceState dev_state = nm_device_get_state (device);
-	gboolean iwd_connection = FALSE;
-	gboolean can_connect;
-
-	_LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state);
-
-	if (   dev_state >= NM_DEVICE_STATE_CONFIG
-	    && dev_state <= NM_DEVICE_STATE_ACTIVATED)
-		iwd_connection = TRUE;
-
-	/* Don't allow scanning while connecting, disconnecting or roaming */
-	priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected");
-
-	/* Don't allow new connection until iwd exits disconnecting */
-	can_connect = NM_IN_STRSET (new_state, "disconnected");
-	if (can_connect != priv->can_connect) {
-		priv->can_connect = can_connect;
-		nm_device_emit_recheck_auto_activate (device);
-	}
-
-	if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) {
-		/* If we were connecting, do nothing, the confirmation of
-		 * a connection success is handled in the Device.Connect
-		 * method return callback.  Otherwise IWD must have connected
-		 * without Network Manager's will so for simplicity force a
-		 * disconnect.
-		 */
-		if (iwd_connection)
-			return;
-
-		_LOGW (LOGD_DEVICE | LOGD_WIFI,
-		       "Unsolicited connection success, asking IWD to disconnect");
-		send_disconnect (self);
-
-		return;
-	} else if (NM_IN_STRSET (new_state, "disconnecting", "disconnected")) {
-		if (!iwd_connection)
-			return;
-
-		/* Call Disconnect on the IWD device object to make sure it
-		 * disables its own autoconnect.
-		 *
-		 * Note we could instead call net.connman.iwd.KnownNetworks.ForgetNetwork
-		 * and leave the device in autoconnect.  This way if NetworkManager
-		 * changes any settings for this connection, they'd be taken into
-		 * account on the next connection attempt.  But both methods are
-		 * a hack, we'll perhaps need an IWD API to "connect once" without
-		 * storing anything.
-		 */
-		send_disconnect (self);
-
-		/*
-		 * If IWD is still handling the Connect call, let our callback
-		 * for the dbus method handle the failure.
-		 */
-		if (dev_state == NM_DEVICE_STATE_CONFIG)
-			return;
-
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-	                                 NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
-
-		return;
-	}
-
-	_LOGE (LOGD_WIFI, "State %s unknown", new_state);
-}
-
-static void
-scanning_changed (NMDeviceIwd *self, gboolean new_scanning)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
-
-	if (new_scanning == priv->scanning)
-		return;
-
-	priv->scanning = new_scanning;
-
-	_notify (self, PROP_SCANNING);
-
-	if (!priv->scanning) {
-		update_aps (self);
-
-		if (!priv->scan_requested)
-			schedule_periodic_scan (self, state);
-	}
-}
-
-static void
-powered_changed (NMDeviceIwd *self, gboolean new_powered)
-{
-	nm_device_queue_recheck_available (NM_DEVICE (self),
-	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-}
-
-static void
-properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
-                    GStrv invalidate_properties, gpointer user_data)
-{
-	NMDeviceIwd *self = user_data;
-	GVariantIter *iter;
-	const gchar *key;
-	GVariant *value;
-
-	g_variant_get (changed_properties, "a{sv}", &iter);
-	while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
-		if (!strcmp (key, "State"))
-			state_changed (self, g_variant_get_string (value, NULL));
-
-		if (!strcmp (key, "Scanning"))
-			scanning_changed (self, g_variant_get_boolean (value));
-
-		if (!strcmp (key, "Powered"))
-			powered_changed (self, g_variant_get_boolean (value));
-
-		g_variant_unref (value);
-	}
-
-	g_variant_iter_free (iter);
-}
-
-void
-nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	GDBusInterface *interface;
-	GVariant *value;
-
-	if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object))
-		return;
-
-	if (priv->dbus_proxy) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_proxy,
-		                                      properties_changed, self);
-
-		g_clear_object (&priv->dbus_proxy);
-	}
-
-	if (priv->enabled)
-		nm_device_queue_recheck_available (NM_DEVICE (self),
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
-		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
-
-	if (!object) {
-		priv->can_scan = FALSE;
-
-		cleanup_association_attempt (self, FALSE);
-		return;
-	}
-
-	interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE);
-	priv->dbus_proxy = G_DBUS_PROXY (interface);
-
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning");
-	priv->scanning = g_variant_get_boolean (value);
-	g_variant_unref (value);
-	priv->scan_requested = FALSE;
-
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State");
-	state_changed (self, g_variant_get_string (value, NULL));
-	g_variant_unref (value);
-
-	g_signal_connect (priv->dbus_proxy, "g-properties-changed",
-	                  G_CALLBACK (properties_changed), self);
-
-	set_powered (self, priv->enabled);
-
-	/* Call Disconnect to make sure IWD's autoconnect is disabled.
-	 * Autoconnect is the default state after device is brought UP.
-	 */
-	if (priv->enabled)
-		send_disconnect (self);
-}
-
-gboolean
-nm_device_iwd_agent_psk_query (NMDeviceIwd *self,
-                               GDBusMethodInvocation *invocation)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	NMActRequest *req;
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const gchar *psk;
-
-	req = nm_device_get_act_request (NM_DEVICE (self));
-	if (!req)
-		return FALSE;
-
-	s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req));
-	if (!s_wireless_sec)
-		return FALSE;
-
-	psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
-	if (psk) {
-		_LOGD (LOGD_DEVICE | LOGD_WIFI,
-		       "Returning the PSK to the IWD Agent");
-
-		g_dbus_method_invocation_return_value (invocation,
-		                                       g_variant_new ("(s)", psk));
-		return TRUE;
-	}
-
-	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH,
-	                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	wifi_secrets_get_secrets (self,
-	                          NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-	                          NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
-	                            | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
-
-	priv->secrets_request = invocation;
-	return TRUE;
-}
-
-/*****************************************************************************/
-
-static const char *
-get_type_description (NMDevice *device)
-{
-	nm_assert (NM_IS_DEVICE_IWD (device));
-
-	return "wifi";
-}
-
-/*****************************************************************************/
-
-static void
-nm_device_iwd_init (NMDeviceIwd *self)
-{
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	c_list_init (&priv->aps_lst_head);
-
-	/* Make sure the manager is running */
-	(void) nm_iwd_manager_get ();
-}
-
-NMDevice *
-nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities)
-{
-	return g_object_new (NM_TYPE_DEVICE_IWD,
-	                     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_IWD_CAPABILITIES, (guint) capabilities,
-	                     NULL);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMDeviceIwd *self = NM_DEVICE_IWD (object);
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
-	nm_clear_g_cancellable (&priv->cancellable);
-
-	nm_clear_g_source (&priv->periodic_scan_id);
-
-	cleanup_association_attempt (self, TRUE);
-
-	g_clear_object (&priv->dbus_proxy);
-	g_clear_object (&priv->dbus_obj);
-
-	remove_all_aps (self);
-
-	G_OBJECT_CLASS (nm_device_iwd_parent_class)->dispose (object);
-
-	nm_assert (c_list_is_empty (&priv->aps_lst_head));
-}
-
-static void
-nm_device_iwd_class_init (NMDeviceIwdClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
-
-	object_class->get_property = get_property;
-	object_class->set_property = set_property;
-	object_class->dispose = dispose;
-
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless);
-
-	parent_class->can_auto_connect = can_auto_connect;
-	parent_class->is_available = is_available;
-	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->check_connection_available = check_connection_available;
-	parent_class->complete_connection = complete_connection;
-	parent_class->get_enabled = get_enabled;
-	parent_class->set_enabled = set_enabled;
-	parent_class->get_type_description = get_type_description;
-
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->get_configured_mtu = get_configured_mtu;
-	parent_class->deactivate = deactivate;
-	parent_class->deactivate_async = deactivate_async;
-	parent_class->deactivate_async_finish = deactivate_async_finish;
-	parent_class->can_reapply_change = can_reapply_change;
-
-	parent_class->state_changed = device_state_changed;
-
-	klass->scanning_prohibited = scanning_prohibited;
-
-	obj_properties[PROP_MODE] =
-	    g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "",
-	                       NM_802_11_MODE_UNKNOWN,
-	                       NM_802_11_MODE_AP,
-	                       NM_802_11_MODE_INFRA,
-	                       G_PARAM_READABLE |
-	                       G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_BITRATE] =
-	    g_param_spec_uint (NM_DEVICE_IWD_BITRATE, "", "",
-	                       0, G_MAXUINT32, 0,
-	                       G_PARAM_READABLE |
-	                       G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_ACCESS_POINTS] =
-	    g_param_spec_boxed (NM_DEVICE_IWD_ACCESS_POINTS, "", "",
-	                        G_TYPE_STRV,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_ACTIVE_ACCESS_POINT] =
-	    g_param_spec_string (NM_DEVICE_IWD_ACTIVE_ACCESS_POINT, "", "",
-	                         NULL,
-	                         G_PARAM_READABLE |
-	                         G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_CAPABILITIES] =
-	    g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "",
-	                       0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
-	                       G_PARAM_READWRITE |
-	                       G_PARAM_CONSTRUCT_ONLY |
-	                       G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_SCANNING] =
-	    g_param_spec_boolean (NM_DEVICE_IWD_SCANNING, "", "",
-	                          FALSE,
-	                          G_PARAM_READABLE |
-	                          G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[SCANNING_PROHIBITED] =
-	    g_signal_new (NM_DEVICE_IWD_SCANNING_PROHIBITED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST,
-	                  G_STRUCT_OFFSET (NMDeviceIwdClass, scanning_prohibited),
-	                  NULL, NULL, NULL,
-	                  G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
-}
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
deleted file mode 100644
index 699ba1bb..00000000
--- a/src/devices/wifi/nm-device-iwd.h
+++ /dev/null
@@ -1,62 +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) 2017 Intel Corporation
- */
-
-#ifndef __NETWORKMANAGER_DEVICE_IWD_H__
-#define __NETWORKMANAGER_DEVICE_IWD_H__
-
-#include "devices/nm-device.h"
-#include "nm-wifi-ap.h"
-#include "nm-device-wifi.h"
-
-#define NM_TYPE_DEVICE_IWD              (nm_device_iwd_get_type ())
-#define NM_DEVICE_IWD(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwd))
-#define NM_DEVICE_IWD_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_IWD, NMDeviceIwdClass))
-#define NM_IS_DEVICE_IWD(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_IWD))
-#define NM_IS_DEVICE_IWD_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_IWD))
-#define NM_DEVICE_IWD_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_IWD, NMDeviceIwdClass))
-
-#define NM_DEVICE_IWD_MODE                NM_DEVICE_WIFI_MODE
-#define NM_DEVICE_IWD_BITRATE             NM_DEVICE_WIFI_BITRATE
-#define NM_DEVICE_IWD_ACCESS_POINTS       NM_DEVICE_WIFI_ACCESS_POINTS
-#define NM_DEVICE_IWD_ACTIVE_ACCESS_POINT NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT
-#define NM_DEVICE_IWD_CAPABILITIES        NM_DEVICE_WIFI_CAPABILITIES
-#define NM_DEVICE_IWD_SCANNING            NM_DEVICE_WIFI_SCANNING
-
-#define NM_DEVICE_IWD_SCANNING_PROHIBITED  NM_DEVICE_WIFI_SCANNING_PROHIBITED
-
-typedef struct _NMDeviceIwd NMDeviceIwd;
-typedef struct _NMDeviceIwdClass NMDeviceIwdClass;
-
-GType nm_device_iwd_get_type (void);
-
-NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities);
-
-void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object);
-
-gboolean nm_device_iwd_agent_psk_query (NMDeviceIwd *device,
-                                        GDBusMethodInvocation *invocation);
-
-const CList *_nm_device_iwd_get_aps (NMDeviceIwd *self);
-
-void _nm_device_iwd_request_scan (NMDeviceIwd *self,
-                                  GVariant *options,
-                                  GDBusMethodInvocation *invocation);
-
-#endif /* __NETWORKMANAGER_DEVICE_IWD_H__ */
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index cd2c68af..3a4a027a 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -48,6 +48,11 @@
 #include "nm-manager.h"
 #include "platform/nm-platform.h"
 
+/* This is a bug; but we can't really change API now... */
+#include "nm-vpn-dbus-interface.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.OlpcMesh.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceOlpcMesh);
 
@@ -113,7 +118,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMSettingOlpcMesh *s_mesh;
@@ -387,8 +392,7 @@ static void
 find_companion (NMDeviceOlpcMesh *self)
 {
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
-	const CList *tmp_lst;
-	NMDevice *candidate;
+	const GSList *list;
 
 	if (priv->companion)
 		return;
@@ -396,8 +400,8 @@ find_companion (NMDeviceOlpcMesh *self)
 	nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE);
 
 	/* Try to find the companion if it's already known to the NMManager */
-	nm_manager_for_each_device (priv->manager, candidate, tmp_lst) {
-		if (check_companion (self, candidate)) {
+	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,
 			                                   NM_DEVICE_STATE_REASON_NONE);
@@ -436,7 +440,7 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_COMPANION:
-		nm_dbus_utils_g_value_set_object_path (value, priv->companion);
+		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_device_get_platform (device), nm_device_get_ifindex (device)));
@@ -496,26 +500,10 @@ dispose (GObject *object)
 	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_olpc_mesh = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_OLPC_MESH,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",     "s", NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Companion",     "o", NM_DEVICE_OLPC_MESH_COMPANION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveChannel", "u", NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_OLPC_MESH_SETTING_NAME, NM_LINK_TYPE_OLPC_MESH)
@@ -524,8 +512,6 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_olpc_mesh);
-
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
 	parent_class->complete_connection = complete_connection;
@@ -548,5 +534,9 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 	                        G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	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-wifi.c b/src/devices/wifi/nm-device-wifi.c
index de4af42c..bf021095 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -28,11 +28,9 @@
 #include <unistd.h>
 #include <errno.h>
 
-#include "nm-wifi-ap.h"
 #include "nm-common-macros.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
-#include "nm-dbus-manager.h"
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-act-request.h"
@@ -50,11 +48,11 @@
 #include "nm-auth-utils.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
-#include "nm-wifi-utils.h"
-#include "nm-wifi-common.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Wireless.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceWifi);
 
@@ -77,6 +75,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi,
 );
 
 enum {
+	ACCESS_POINT_ADDED,
+	ACCESS_POINT_REMOVED,
 	SCANNING_PROHIBITED,
 
 	LAST_SIGNAL
@@ -87,8 +87,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 typedef struct {
 	gint8             invalid_strength_counter;
 
-	CList             aps_lst_head;
-
+	GHashTable *      aps;
 	NMWifiAP *        current_ap;
 	guint32           rate;
 	bool              enabled:1; /* rfkilled or not */
@@ -188,7 +187,7 @@ static void request_wireless_scan (NMDeviceWifi *self,
                                    const GPtrArray *ssids);
 
 static void ap_add_remove (NMDeviceWifi *self,
-                           gboolean is_adding,
+                           guint signum,
                            NMWifiAP *ap,
                            gboolean recheck_available_connections);
 
@@ -346,6 +345,30 @@ supplicant_interface_release (NMDeviceWifi *self)
 	_notify_scanning (self);
 }
 
+static NMWifiAP *
+get_ap_by_path (NMDeviceWifi *self, const char *path)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+	return g_hash_table_lookup (NM_DEVICE_WIFI_GET_PRIVATE (self)->aps, path);
+
+}
+
+static NMWifiAP *
+get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
+{
+	GHashTableIter iter;
+	NMWifiAP *ap;
+
+	g_return_val_if_fail (path != 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 (g_strcmp0 (path, nm_wifi_ap_get_supplicant_path (ap)) == 0)
+			return ap;
+	}
+	return NULL;
+}
+
 static void
 update_seen_bssids_cache (NMDeviceWifi *self, NMWifiAP *ap)
 {
@@ -392,7 +415,7 @@ set_current_ap (NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available
 
 		/* 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_wifi_ap_get_fake (old_ap))
-			ap_add_remove (self, FALSE, old_ap, recheck_available_connections);
+			ap_add_remove (self, ACCESS_POINT_REMOVED, old_ap, recheck_available_connections);
 		g_object_unref (old_ap);
 	}
 
@@ -459,32 +482,32 @@ periodic_update_cb (gpointer user_data)
 
 static void
 ap_add_remove (NMDeviceWifi *self,
-               gboolean is_adding, /* or else removing */
+               guint signum,
                NMWifiAP *ap,
                gboolean recheck_available_connections)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	if (is_adding) {
-		g_object_ref (ap);
-		ap->wifi_device = NM_DEVICE (self);
-		c_list_link_tail (&priv->aps_lst_head, &ap->aps_lst);
-		nm_dbus_object_export (NM_DBUS_OBJECT (ap));
+	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));
 		_ap_dump (self, LOGL_DEBUG, ap, "added", 0);
-		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, TRUE);
-	} else {
-		ap->wifi_device = NULL;
-		c_list_unlink (&ap->aps_lst);
+	} else
 		_ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
-	}
 
-	_notify (self, PROP_ACCESS_POINTS);
+	g_signal_emit (self, signals[signum], 0, ap);
 
-	if (!is_adding) {
-		nm_device_wifi_emit_signal_access_point (NM_DEVICE (self), ap, FALSE);
-		nm_dbus_object_clear_and_unexport (&ap);
+	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);
 	}
 
+	_notify (self, PROP_ACCESS_POINTS);
+
 	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
 	if (recheck_available_connections)
 		nm_device_recheck_available_connections (NM_DEVICE (self));
@@ -494,15 +517,20 @@ static void
 remove_all_aps (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GHashTableIter iter;
 	NMWifiAP *ap;
 
-	if (c_list_is_empty (&priv->aps_lst_head))
+	if (!g_hash_table_size (priv->aps))
 		return;
 
 	set_current_ap (self, NULL, FALSE);
 
-	while ((ap = c_list_first_entry (&priv->aps_lst_head, NMWifiAP, aps_lst)))
-		ap_add_remove (self, FALSE, ap, FALSE);
+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));
 }
@@ -652,14 +680,35 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	return TRUE;
 }
 
+static NMWifiAP *
+find_first_compatible_ap (NMDeviceWifi *self,
+                          NMConnection *connection,
+                          gboolean allow_unstable_order)
+{
+	GHashTableIter iter;
+	NMWifiAP *ap;
+	NMWifiAP *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_wifi_ap_check_compatible (ap, connection))
+			continue;
+		if (allow_unstable_order)
+			return ap;
+		if (!cand_ap || (nm_wifi_ap_get_id (cand_ap) < nm_wifi_ap_get_id (ap)))
+			cand_ap = ap;
+	}
+	return cand_ap;
+}
+
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
 	const char *mode;
 
@@ -672,7 +721,7 @@ check_connection_available (NMDevice *device,
 	if (specific_object) {
 		NMWifiAP *ap;
 
-		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
+		ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object);
 		return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
 	}
 
@@ -696,18 +745,50 @@ check_connection_available (NMDevice *device,
 		return TRUE;
 
 	/* check at least one AP is compatible with this connection */
-	return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+	return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE);
+}
+
+static gboolean
+is_manf_default_ssid (const GByteArray *ssid)
+{
+	int i;
+	/*
+	 * List of manufacturer default SSIDs that are often unchanged by users.
+	 *
+	 * NOTE: this list should *not* contain networks that you would like to
+	 * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot".
+	 */
+	static const char *manf_defaults[] = {
+		"linksys",
+		"linksys-a",
+		"linksys-g",
+		"default",
+		"belkin54g",
+		"NETGEAR",
+		"o2DSL",
+		"WLAN",
+		"ALICE-WLAN",
+		"Speedport W 501V",
+		"TURBONETT",
+	};
+
+	for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) {
+		if (ssid->len == strlen (manf_defaults[i])) {
+			if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0)
+				return TRUE;
+		}
+	}
+	return FALSE;
 }
 
 static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      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;
@@ -744,7 +825,7 @@ complete_connection (NMDevice *device,
 
 		if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) {
 			/* Find a compatible AP in the scan list */
-			ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+			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
@@ -766,7 +847,7 @@ complete_connection (NMDevice *device,
 			return FALSE;
 		ap = NULL;
 	} else {
-		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
+		ap = get_ap_by_path (self, specific_object);
 		if (!ap) {
 			g_set_error (error,
 			             NM_DEVICE_ERROR,
@@ -819,7 +900,7 @@ complete_connection (NMDevice *device,
 		 */
 		if (!nm_wifi_ap_complete_connection (ap,
 		                                     connection,
-		                                     nm_wifi_utils_is_manf_default_ssid (ssid),
+		                                     is_manf_default_ssid (ssid),
 		                                     error)) {
 			if (tmp_ssid)
 				g_byte_array_unref (tmp_ssid);
@@ -926,7 +1007,6 @@ can_auto_connect (NMDevice *device,
                   char **specific_object)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
 	NMWifiAP *ap;
 	const char *method, *mode;
@@ -958,20 +1038,102 @@ can_auto_connect (NMDevice *device,
 			return FALSE;
 	}
 
-	ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+	ap = find_first_compatible_ap (self, connection, FALSE);
 	if (ap) {
 		/* All good; connection is usable */
-		NM_SET_OUT (specific_object, g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))));
+		NM_SET_OUT (specific_object, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap))));
 		return TRUE;
 	}
 
 	return FALSE;
 }
 
-const CList *
-_nm_device_wifi_get_aps (NMDeviceWifi *self)
+static int
+ap_id_compare (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+	guint64 a_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_a));
+	guint64 b_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_b));
+
+	return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1);
+}
+
+static NMWifiAP **
+ap_list_get_sorted (NMDeviceWifi *self, gboolean include_without_ssid)
+{
+	NMDeviceWifiPrivate *priv;
+	NMWifiAP **list;
+	GHashTableIter iter;
+	NMWifiAP *ap;
+	gsize i, n;
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	n = g_hash_table_size (priv->aps);
+	list = g_new (NMWifiAP *, n + 1);
+
+	i = 0;
+	if (n > 0) {
+		g_hash_table_iter_init (&iter, priv->aps);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+			nm_assert (i < n);
+			if (   include_without_ssid
+			    || nm_wifi_ap_get_ssid (ap))
+				list[i++] = ap;
+		}
+		nm_assert (i <= n);
+		nm_assert (!include_without_ssid || i == n);
+
+		g_qsort_with_data (list,
+		                   i,
+		                   sizeof (gpointer),
+		                   ap_id_compare,
+		                   NULL);
+	}
+	list[i] = NULL;
+	return list;
+}
+
+static const char **
+ap_list_get_sorted_paths (NMDeviceWifi *self, gboolean include_without_ssid)
 {
-	return &NM_DEVICE_WIFI_GET_PRIVATE (self)->aps_lst_head;
+	gpointer *list;
+	gsize i, j;
+
+	list = (gpointer *) ap_list_get_sorted (self, include_without_ssid);
+	for (i = 0, j = 0; list[i]; i++) {
+		NMWifiAP *ap = list[i];
+		const char *path;
+
+		/* update @list inplace to hold instead the export-path. */
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap));
+		nm_assert (path);
+		list[j++] = (gpointer) path;
+	}
+	return (const char **) list;
+}
+
+static void
+impl_device_wifi_get_access_points (NMDeviceWifi *self,
+                                    GDBusMethodInvocation *context)
+{
+	gs_free const char **list = NULL;
+	GVariant *v;
+
+	list = ap_list_get_sorted_paths (self, FALSE);
+	v = g_variant_new_objv (list, -1);
+	g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1));
+}
+
+static void
+impl_device_wifi_get_all_access_points (NMDeviceWifi *self,
+                                        GDBusMethodInvocation *context)
+{
+	gs_free const char **list = NULL;
+	GVariant *v;
+
+	list = ap_list_get_sorted_paths (self, TRUE);
+	v = g_variant_new_objv (list, -1);
+	g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1));
 }
 
 static void
@@ -991,7 +1153,7 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	randomize = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA,
-	                                                      NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS,
+	                                                      "wifi.scan-rand-mac-address",
 	                                                      device,
 	                                                      TRUE, TRUE);
 
@@ -1077,6 +1239,7 @@ dbus_request_scan_cb (NMDevice *device,
                       gpointer user_data)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv;
 	gs_unref_variant GVariant *scan_options = user_data;
 	gs_unref_ptrarray GPtrArray *ssids = NULL;
 
@@ -1093,6 +1256,8 @@ dbus_request_scan_cb (NMDevice *device,
 		return;
 	}
 
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
 	if (scan_options) {
 		gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL);
 
@@ -1119,10 +1284,10 @@ dbus_request_scan_cb (NMDevice *device,
 	g_dbus_method_invocation_return_value (context, NULL);
 }
 
-void
-_nm_device_wifi_request_scan (NMDeviceWifi *self,
-                              GVariant *options,
-                              GDBusMethodInvocation *invocation)
+static void
+impl_device_wifi_request_scan (NMDeviceWifi *self,
+                               GDBusMethodInvocation *context,
+                               GVariant *options)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
@@ -1132,7 +1297,7 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self,
 	    || !priv->sup_iface
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
+		g_dbus_method_invocation_return_error_literal (context,
 		                                               NM_DEVICE_ERROR,
 		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
 		                                               "Scanning not allowed while unavailable or activating");
@@ -1140,7 +1305,7 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self,
 	}
 
 	if (nm_supplicant_interface_get_scanning (priv->sup_iface)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
+		g_dbus_method_invocation_return_error_literal (context,
 		                                               NM_DEVICE_ERROR,
 		                                               NM_DEVICE_ERROR_NOT_ALLOWED,
 		                                               "Scanning not allowed while already scanning");
@@ -1149,16 +1314,17 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self,
 
 	last_scan = nm_supplicant_interface_get_last_scan_time (priv->sup_iface);
 	if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) {
-		g_dbus_method_invocation_return_error_literal (invocation,
+		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 */
 	g_signal_emit_by_name (device,
 	                       NM_DEVICE_AUTH_REQUEST,
-	                       invocation,
+	                       context,
 	                       NULL,
 	                       NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	                       TRUE,
@@ -1442,15 +1608,17 @@ ap_list_dump (gpointer user_data)
 	priv->ap_dump_id = 0;
 
 	if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) {
-		NMWifiAP *ap;
+		gs_free NMWifiAP **list = NULL;
+		gsize i;
 		gint32 now_s = nm_utils_get_monotonic_timestamp_s ();
 
 		_LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]",
 		       now_s,
 		       priv->last_scan,
 		       priv->scheduled_scan_time);
-		c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst)
-			_ap_dump (self, LOGL_DEBUG, ap, "dump", now_s);
+		list = ap_list_get_sorted (self, TRUE);
+		for (i = 0; list[i]; i++)
+			_ap_dump (self, LOGL_DEBUG, list[i], "dump", now_s);
 	}
 	return G_SOURCE_REMOVE;
 }
@@ -1521,7 +1689,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
 		return;
 
-	found_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path);
+	found_ap = get_ap_by_supplicant_path (self, object_path);
 	if (found_ap) {
 		if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties))
 			return;
@@ -1553,7 +1721,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 			}
 		}
 
-		ap_add_remove (self, TRUE, ap, TRUE);
+		ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
 	}
 
 	/* Update the current AP if the supplicant notified a current BSS change
@@ -1577,7 +1745,7 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
 	g_return_if_fail (object_path != NULL);
 
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, object_path);
+	ap = get_ap_by_supplicant_path (self, object_path);
 	if (!ap)
 		return;
 
@@ -1590,7 +1758,7 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
 		if (nm_wifi_ap_set_fake (ap, TRUE))
 			_ap_dump (self, LOGL_DEBUG, ap, "updated", 0);
 	} else {
-		ap_add_remove (self, FALSE, ap, TRUE);
+		ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
 		schedule_ap_list_dump (self);
 	}
 }
@@ -2121,7 +2289,7 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
 
 	current_bss = nm_supplicant_interface_get_current_bss (iface);
 	if (current_bss)
-		new_ap = nm_wifi_aps_find_by_supplicant_path (&priv->aps_lst_head, current_bss);
+		new_ap = get_ap_by_supplicant_path (self, current_bss);
 
 	if (new_ap != priv->current_ap) {
 		const char *new_bssid = NULL;
@@ -2329,7 +2497,6 @@ build_supplicant_config (NMDeviceWifi *self,
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
 	NMSettingWirelessSecurityPmf pmf;
-	NMSettingWirelessSecurityFils fils;
 	gs_free char *value = NULL;
 
 	g_return_val_if_fail (priv->sup_iface, NULL);
@@ -2337,9 +2504,7 @@ build_supplicant_config (NMDeviceWifi *self,
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wireless != NULL, NULL);
 
-	config = nm_supplicant_config_new (
-		nm_supplicant_interface_get_pmf_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
-		nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES);
+	config = nm_supplicant_config_new ();
 
 	/* Warn if AP mode may not be supported */
 	if (   g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0
@@ -2381,16 +2546,24 @@ build_supplicant_config (NMDeviceWifi *self,
 			                                    NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL);
 		}
 
-		/* Configure FILS (802.11ai) */
-		fils = nm_setting_wireless_security_get_fils (s_wireless_sec);
-		if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) {
-			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-			                                               "wifi-sec.fils",
-			                                               NM_DEVICE (self));
-			fils = _nm_utils_ascii_str_to_int64 (value, 10,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL);
+		/* Don't try to enable PMF on non-WPA networks */
+		if (!NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wireless_sec),
+		                   "wpa-eap",
+		                   "wpa-psk"))
+			pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE;
+
+		/* Check if we actually support PMF */
+		if (nm_supplicant_interface_get_pmf_support (priv->sup_iface) != NM_SUPPLICANT_FEATURE_YES) {
+			if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) {
+				g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
+				                     "Supplicant does not support PMF");
+				goto error;
+			} else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) {
+				/* To be on the safe side, assume no support if we can't determine
+				 * capabilities.
+				 */
+				pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE;
+			}
 		}
 
 		s_8021x = nm_connection_get_setting_802_1x (connection);
@@ -2400,7 +2573,6 @@ build_supplicant_config (NMDeviceWifi *self,
 		                                                         con_uuid,
 		                                                         mtu,
 		                                                         pmf,
-		                                                         fils,
 		                                                         error)) {
 			g_prefix_error (error, "802-11-wireless-security: ");
 			goto error;
@@ -2482,16 +2654,16 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_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 ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
+		ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
 		if (ap)
 			goto done;
 
-		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+		ap = find_first_compatible_ap (self, connection, FALSE);
 	}
 
 	if (ap) {
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-		                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
+		                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)));
 		goto done;
 	}
 
@@ -2508,11 +2680,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device));
 
 	g_object_freeze_notify (G_OBJECT (self));
-	ap_add_remove (self, TRUE, ap, TRUE);
+	ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
 	g_object_thaw_notify (G_OBJECT (self));
 	set_current_ap (self, ap, FALSE);
 	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
+	                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap)));
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 
 done:
@@ -2883,10 +3055,13 @@ activation_success_handler (NMDevice *device)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ifindex (device);
 	NMActRequest *req;
+	NMConnection *applied_connection;
 
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
+	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_device_get_platform (device), ifindex, FALSE);
 
@@ -2924,7 +3099,7 @@ activation_success_handler (NMDevice *device)
 		}
 
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-		                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (priv->current_ap)));
+		                                          nm_exported_object_get_path (NM_EXPORTED_OBJECT (priv->current_ap)));
 	}
 
 	periodic_update (self);
@@ -3102,7 +3277,8 @@ get_property (GObject *object, guint prop_id,
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	const char **list;
+	gsize i;
+	char **list;
 
 	switch (prop_id) {
 	case PROP_MODE:
@@ -3115,11 +3291,13 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->capabilities);
 		break;
 	case PROP_ACCESS_POINTS:
-		list = nm_wifi_aps_get_paths (&priv->aps_lst_head, TRUE);
-		g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list));
+		list = (char **) ap_list_get_sorted_paths (self, TRUE);
+		for (i = 0; list[i]; i++)
+			list[i] = g_strdup (list[i]);
+		g_value_take_boxed (value, list);
 		break;
 	case PROP_ACTIVE_ACCESS_POINT:
-		nm_dbus_utils_g_value_set_object_path (value, priv->current_ap);
+		nm_utils_g_value_set_object_path (value, priv->current_ap);
 		break;
 	case PROP_SCANNING:
 		g_value_set_boolean (value, priv->is_scanning);
@@ -3155,9 +3333,8 @@ nm_device_wifi_init (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	c_list_init (&priv->aps_lst_head);
-
 	priv->mode = NM_802_11_MODE_INFRA;
+	priv->aps = g_hash_table_new (nm_str_hash, g_str_equal);
 }
 
 static void
@@ -3215,7 +3392,9 @@ finalize (GObject *object)
 	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
-	nm_assert (c_list_is_empty (&priv->aps_lst_head));
+	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);
 }
@@ -3224,7 +3403,6 @@ static void
 nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
@@ -3235,8 +3413,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless);
-
 	parent_class->can_auto_connect = can_auto_connect;
 	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
 	parent_class->is_available = is_available;
@@ -3303,6 +3479,24 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
+	signals[ACCESS_POINT_ADDED] =
+	    g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_ADDED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1,
+	                  NM_TYPE_WIFI_AP);
+
+	signals[ACCESS_POINT_REMOVED] =
+	    g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_REMOVED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1,
+	                  NM_TYPE_WIFI_AP);
+
 	signals[SCANNING_PROHIBITED] =
 	    g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -3310,4 +3504,13 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	                  G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited),
 	                  NULL, NULL, NULL,
 	                  G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
+
+	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 c13b00de..09707d4f 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -23,6 +23,7 @@
 #define __NETWORKMANAGER_DEVICE_WIFI_H__
 
 #include "devices/nm-device.h"
+#include "nm-wifi-ap.h"
 
 #define NM_TYPE_DEVICE_WIFI             (nm_device_wifi_get_type ())
 #define NM_DEVICE_WIFI(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi))
@@ -38,6 +39,11 @@
 #define NM_DEVICE_WIFI_CAPABILITIES        "wireless-capabilities"
 #define NM_DEVICE_WIFI_SCANNING            "scanning"
 
+/* signals */
+#define NM_DEVICE_WIFI_ACCESS_POINT_ADDED  "access-point-added"
+#define NM_DEVICE_WIFI_ACCESS_POINT_REMOVED "access-point-removed"
+
+/* internal signals */
 #define NM_DEVICE_WIFI_SCANNING_PROHIBITED    "scanning-prohibited"
 
 typedef struct _NMDeviceWifi NMDeviceWifi;
@@ -47,10 +53,4 @@ GType nm_device_wifi_get_type (void);
 
 NMDevice * nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities);
 
-const CList *_nm_device_wifi_get_aps (NMDeviceWifi *self);
-
-void _nm_device_wifi_request_scan (NMDeviceWifi *self,
-                                   GVariant *options,
-                                   GDBusMethodInvocation *invocation);
-
 #endif /* __NETWORKMANAGER_DEVICE_WIFI_H__ */
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
deleted file mode 100644
index 450009f0..00000000
--- a/src/devices/wifi/nm-iwd-manager.c
+++ /dev/null
@@ -1,694 +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) 2017 Intel Corporation
- */
-
-#include "nm-default.h"
-
-#include "nm-iwd-manager.h"
-
-#include <string.h>
-#include <net/if.h>
-
-#include "nm-logging.h"
-#include "nm-core-internal.h"
-#include "nm-manager.h"
-#include "nm-device-iwd.h"
-#include "nm-utils/nm-random-utils.h"
-
-/*****************************************************************************/
-
-typedef struct {
-	gchar *name;
-	NMIwdNetworkSecurity security;
-} KnownNetworkData;
-
-typedef struct {
-	NMManager *manager;
-	GCancellable *cancellable;
-	gboolean running;
-	GDBusObjectManager *object_manager;
-	guint agent_id;
-	gchar *agent_path;
-	GSList *known_networks;
-} NMIwdManagerPrivate;
-
-struct _NMIwdManager {
-	GObject parent;
-	NMIwdManagerPrivate _priv;
-};
-
-struct _NMIwdManagerClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT)
-
-#define NM_IWD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIwdManager, NM_IS_IWD_MANAGER)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME                "iwd-manager"
-#define _NMLOG_DOMAIN                     LOGD_WIFI
-
-#define _NMLOG(level, ...) \
-	G_STMT_START { \
-		if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \
-			char __prefix[32]; \
-			\
-			if (self) \
-				g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
-			else \
-				g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-			_nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
-			          "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-			          __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-		} \
-	} G_STMT_END
-
-/*****************************************************************************/
-
-static void
-psk_agent_dbus_method_cb (GDBusConnection *connection,
-                          const gchar *sender, const gchar *object_path,
-                          const gchar *interface_name, const gchar *method_name,
-                          GVariant *parameters,
-                          GDBusMethodInvocation *invocation,
-                          gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager);
-	const gchar *network_path, *device_path, *ifname;
-	gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL;
-	gs_unref_variant GVariant *value = NULL;
-	gint ifindex;
-	NMDevice *device;
-
-	/* Be paranoid and check the sender address */
-	if (!nm_streq0 (g_dbus_object_manager_client_get_name_owner (omc), sender))
-		goto return_error;
-
-	g_variant_get (parameters, "(&o)", &network_path);
-
-	network = g_dbus_object_manager_get_interface (priv->object_manager,
-	                                               network_path,
-	                                               NM_IWD_NETWORK_INTERFACE);
-	value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (network), "Device");
-	device_path = g_variant_get_string (value, NULL);
-
-	if (!device_path) {
-		_LOGE ("Device not cached for network %s in IWD Agent request",
-		       network_path);
-		goto return_error;
-	}
-
-	device_obj = g_dbus_object_manager_get_interface (priv->object_manager,
-	                                                  device_path,
-	                                                  NM_IWD_DEVICE_INTERFACE);
-	g_variant_unref (value);
-	value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (device_obj), "Name");
-	ifname = g_variant_get_string (value, NULL);
-
-	if (!ifname) {
-		_LOGE ("Name not cached for device %s in IWD Agent request",
-		       device_path);
-		goto return_error;
-	}
-
-	ifindex = if_nametoindex (ifname);
-	if (!ifindex) {
-		_LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
-		       ifname, device_path, errno);
-		goto return_error;
-	}
-
-	device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
-	if (!NM_IS_DEVICE_IWD (device)) {
-		_LOGE ("IWD device named %s is not a Wifi device in IWD Agent request",
-                       ifname);
-		goto return_error;
-	}
-
-	if (nm_device_iwd_agent_psk_query (NM_DEVICE_IWD (device), invocation))
-		return;
-
-	_LOGE ("Device %s did not handle the IWD Agent request", ifname);
-
-return_error:
-	/* IWD doesn't look at the specific error */
-	g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
-	                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-	                                               "No PSK available for this connection");
-}
-
-
-static guint
-psk_agent_export (GDBusConnection *connection, gpointer user_data,
-                  gchar **agent_path, GError **error)
-{
-	static const GDBusArgInfo request_passphrase_arg_network = {
-		-1,
-		(gchar *) "network",
-		(gchar *) "o",
-		NULL,
-	};
-	static const GDBusArgInfo *const request_passphrase_in_args[] = {
-		&request_passphrase_arg_network,
-		NULL,
-	};
-	static const GDBusArgInfo request_passphrase_arg_passphrase = {
-		-1,
-		(gchar *) "passphrase",
-		(gchar *) "s",
-		NULL,
-	};
-	static const GDBusArgInfo *const request_passphrase_out_args[] = {
-		&request_passphrase_arg_passphrase,
-		NULL,
-	};
-	static const GDBusMethodInfo request_passphrase_info = {
-		-1,
-		(gchar *) "RequestPassphrase",
-		(GDBusArgInfo **) &request_passphrase_in_args,
-		(GDBusArgInfo **) &request_passphrase_out_args,
-		NULL,
-	};
-	static const GDBusMethodInfo *const method_info[] = {
-		&request_passphrase_info,
-		NULL,
-	};
-	static GDBusInterfaceInfo interface_info = {
-		-1,
-		(gchar *) "net.connman.iwd.Agent",
-		(GDBusMethodInfo **) &method_info,
-		NULL,
-		NULL,
-		NULL,
-	};
-	static GDBusInterfaceVTable vtable = {
-		psk_agent_dbus_method_cb,
-		NULL,
-		NULL,
-	};
-
-	gchar path[50];
-	unsigned int rnd;
-	guint id;
-
-	if  (!nm_utils_random_bytes (&rnd, sizeof (rnd))) {
-		g_set_error_literal (error,
-		                     NM_DEVICE_ERROR,
-		                     NM_DEVICE_ERROR_FAILED,
-		                     "Can't read urandom.");
-		return 0;
-	}
-
-	nm_sprintf_buf (path, "/agent/%u", rnd);
-
-	id = g_dbus_connection_register_object (connection, path,
-	                                        &interface_info, &vtable,
-	                                        user_data, NULL, error);
-
-	if (id)
-		*agent_path = g_strdup (path);
-	return id;
-}
-
-static void
-register_agent (NMIwdManager *self)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusInterface *agent_manager;
-
-	agent_manager = g_dbus_object_manager_get_interface (priv->object_manager,
-	                                                     "/",
-	                                                     NM_IWD_AGENT_MANAGER_INTERFACE);
-
-	/* Register our agent */
-	g_dbus_proxy_call (G_DBUS_PROXY (agent_manager),
-	                   "RegisterAgent",
-	                   g_variant_new ("(o)", priv->agent_path),
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   NULL, NULL, NULL);
-
-	g_object_unref (agent_manager);
-}
-
-/*****************************************************************************/
-
-static void
-set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface,
-                        GDBusObject *object)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusProxy *proxy;
-	GVariant *value;
-	const char *ifname;
-	gint ifindex;
-	NMDevice *device;
-
-	if (!priv->running)
-		return;
-
-	g_return_if_fail (G_IS_DBUS_PROXY (interface));
-
-	proxy = G_DBUS_PROXY (interface);
-
-	if (strcmp (g_dbus_proxy_get_interface_name (proxy),
-	            NM_IWD_DEVICE_INTERFACE))
-		return;
-
-	value = g_dbus_proxy_get_cached_property (proxy, "Name");
-	if (!value) {
-		_LOGE ("Name not cached for Device at %s",
-		       g_dbus_proxy_get_object_path (proxy));
-		return;
-	}
-
-	ifname = g_variant_get_string (value, NULL);
-	ifindex = if_nametoindex (ifname);
-	g_variant_unref (value);
-
-	if (!ifindex) {
-		_LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
-		       ifname, g_dbus_proxy_get_object_path (proxy), errno);
-		return;
-	}
-
-	device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
-	if (!NM_IS_DEVICE_IWD (device)) {
-		_LOGE ("IWD device named %s is not a Wifi device", ifname);
-		return;
-	}
-
-	nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object);
-}
-
-static void
-interface_added (GDBusObjectManager *object_manager, GDBusObject *object,
-                 GDBusInterface *interface, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-
-	set_device_dbus_object (self, interface, object);
-}
-
-static void
-interface_removed (GDBusObjectManager *object_manager, GDBusObject *object,
-                   GDBusInterface *interface, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-
-	/*
-	 * TODO: we may need to save the GDBusInterface or GDBusObject
-	 * pointer in the hash table because we may be no longer able to
-	 * access the Name property or map the name to ifindex with
-	 * if_nametoindex at this point.
-	 */
-
-	set_device_dbus_object (self, interface, NULL);
-}
-
-static gboolean
-_om_has_name_owner (GDBusObjectManager *object_manager)
-{
-	gs_free char *name_owner = NULL;
-
-	nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
-
-	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
-	return !!name_owner;
-}
-
-static void
-object_added (NMIwdManager *self, GDBusObject *object)
-{
-	GList *interfaces, *iter;
-
-	interfaces = g_dbus_object_get_interfaces (object);
-	for (iter = interfaces; iter; iter = iter->next) {
-		GDBusInterface *interface = G_DBUS_INTERFACE (iter->data);
-
-		set_device_dbus_object (self, interface, object);
-	}
-
-	g_list_free_full (interfaces, g_object_unref);
-}
-
-static void
-known_network_free (KnownNetworkData *network)
-{
-	g_free (network->name);
-	g_free (network);
-}
-
-static void
-list_known_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *variant = NULL;
-	GVariantIter *networks, *props;
-
-	variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                      G_VARIANT_TYPE ("(aa{sv})"),
-	                                      &error);
-	if (!variant) {
-		_LOGE ("ListKnownNetworks() failed: %s", error->message);
-		return;
-	}
-
-	g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free);
-	priv->known_networks = NULL;
-
-	g_variant_get (variant, "(aa{sv})", &networks);
-
-	while (g_variant_iter_next (networks, "a{sv}", &props)) {
-		const gchar *key;
-		const gchar *name = NULL;
-		const gchar *type = NULL;
-		GVariant *val;
-		KnownNetworkData *network_data;
-
-		while (g_variant_iter_next (props, "{&sv}", &key, &val)) {
-			if (!strcmp (key, "Name"))
-				name = g_variant_get_string (val, NULL);
-
-			if (!strcmp (key, "Type"))
-				type = g_variant_get_string (val, NULL);
-
-			g_variant_unref (val);
-		}
-
-		if (!name || !type)
-			goto next;
-
-		network_data = g_new (KnownNetworkData, 1);
-		network_data->name = g_strdup (name);
-		if (!strcmp (type, "open"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_NONE;
-		else if (!strcmp (type, "psk"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_PSK;
-		else if (!strcmp (type, "8021x"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_8021X;
-
-		priv->known_networks = g_slist_append (priv->known_networks,
-		                                       network_data);
-
-next:
-		g_variant_iter_free (props);
-	}
-
-	g_variant_iter_free (networks);
-
-	/* For completness we may want to call nm_device_emit_recheck_auto_activate
-	 * and nm_device_recheck_available_connections for all affected devices
-	 * now but the ListKnownNetworks call should have been really fast,
-	 * faster than any scan on any newly created devices could have happened.
-	 */
-}
-
-static void
-update_known_networks (NMIwdManager *self)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusInterface *known_networks_if;
-
-	known_networks_if = g_dbus_object_manager_get_interface (priv->object_manager,
-	                                                         "/",
-	                                                         NM_IWD_KNOWN_NETWORKS_INTERFACE);
-
-	g_dbus_proxy_call (G_DBUS_PROXY (known_networks_if),
-	                   "ListKnownNetworks",
-	                   g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   priv->cancellable, list_known_networks_cb, self);
-
-	g_object_unref (known_networks_if);
-}
-
-static void prepare_object_manager (NMIwdManager *self);
-
-static void
-name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusObjectManager *object_manager = G_DBUS_OBJECT_MANAGER (object);
-
-	nm_assert (object_manager == priv->object_manager);
-
-	if (_om_has_name_owner (object_manager)) {
-		g_signal_handlers_disconnect_by_data (object_manager, self);
-		g_clear_object (&priv->object_manager);
-		prepare_object_manager (self);
-	} else {
-		const CList *tmp_lst;
-		NMDevice *device;
-
-		if (!priv->running)
-			return;
-
-		priv->running = false;
-
-		nm_manager_for_each_device (priv->manager, device, tmp_lst) {
-			if (NM_IS_DEVICE_IWD (device)) {
-				nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device),
-				                               NULL);
-			}
-		}
-	}
-}
-
-static void
-device_added (NMManager *manager, NMDevice *device, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GList *objects, *iter;
-
-	if (!NM_IS_DEVICE_IWD (device))
-		return;
-
-	if (!priv->running)
-		return;
-
-	objects = g_dbus_object_manager_get_objects (priv->object_manager);
-	for (iter = objects; iter; iter = iter->next) {
-		GDBusObject *object = G_DBUS_OBJECT (iter->data);
-		GDBusInterface *interface;
-		GDBusProxy *proxy;
-		GVariant *value;
-		const char *obj_ifname;
-
-		interface = g_dbus_object_get_interface (object,
-		                                         NM_IWD_DEVICE_INTERFACE);
-		if (!interface)
-			continue;
-
-		proxy = G_DBUS_PROXY (interface);
-		value = g_dbus_proxy_get_cached_property (proxy, "Name");
-		if (!value) {
-			g_object_unref (interface);
-			continue;
-		}
-
-		obj_ifname = g_variant_get_string (value, NULL);
-		g_variant_unref (value);
-		g_object_unref (interface);
-
-		if (strcmp (nm_device_get_iface (device), obj_ifname))
-			continue;
-
-		nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object);
-		break;
-	}
-
-	g_list_free_full (objects, g_object_unref);
-}
-
-static void
-got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
-{
-	NMIwdManager *self = user_data;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
-	GDBusObjectManager *object_manager;
-	GDBusConnection *connection;
-
-	object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error);
-	if (object_manager == NULL) {
-		_LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)",
-		       NM_G_ERROR_MSG (error));
-		g_clear_error (&error);
-		return;
-	}
-
-	priv->object_manager = object_manager;
-
-	g_signal_connect (priv->object_manager, "notify::name-owner",
-	                  G_CALLBACK (name_owner_changed), self);
-
-	nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
-
-	connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
-
-	priv->agent_id = psk_agent_export (connection, self,
-	                                   &priv->agent_path, &error);
-	if (!priv->agent_id) {
-		_LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks will not work: %s",
-		       NM_G_ERROR_MSG (error));
-		g_clear_error (&error);
-	}
-
-	if (_om_has_name_owner (object_manager)) {
-		GList *objects, *iter;
-
-		priv->running = true;
-
-		g_signal_connect (priv->object_manager, "interface-added",
-		                  G_CALLBACK (interface_added), self);
-		g_signal_connect (priv->object_manager, "interface-removed",
-		                  G_CALLBACK (interface_removed), self);
-
-		objects = g_dbus_object_manager_get_objects (object_manager);
-		for (iter = objects; iter; iter = iter->next)
-			object_added (self, G_DBUS_OBJECT (iter->data));
-
-		g_list_free_full (objects, g_object_unref);
-
-		if (priv->agent_id)
-			register_agent (self);
-
-		update_known_networks (self);
-	}
-}
-
-static void
-prepare_object_manager (NMIwdManager *self)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-
-	g_dbus_object_manager_client_new_for_bus (NM_IWD_BUS_TYPE,
-	                                          G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
-	                                          NM_IWD_SERVICE, "/",
-	                                          NULL, NULL, NULL,
-	                                          priv->cancellable,
-	                                          got_object_manager, self);
-}
-
-gboolean
-nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name,
-                                 NMIwdNetworkSecurity security)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	const GSList *iter;
-
-	for (iter = priv->known_networks; iter; iter = g_slist_next (iter)) {
-		const KnownNetworkData *network = iter->data;
-
-		if (!strcmp (network->name, name) && network->security == security)
-			return true;
-	}
-
-	return false;
-}
-
-void
-nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name,
-                                  NMIwdNetworkSecurity security)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	KnownNetworkData *network_data;
-
-	if (nm_iwd_manager_is_known_network (self, name, security))
-		return;
-
-	network_data = g_new (KnownNetworkData, 1);
-	network_data->name = g_strdup (name);
-	network_data->security = security;
-	priv->known_networks = g_slist_append (priv->known_networks, network_data);
-}
-
-/*****************************************************************************/
-
-NM_DEFINE_SINGLETON_GETTER (NMIwdManager, nm_iwd_manager_get,
-                            NM_TYPE_IWD_MANAGER);
-
-static void
-nm_iwd_manager_init (NMIwdManager *self)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-
-	priv->manager = g_object_ref (nm_manager_get ());
-	g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
-	                  G_CALLBACK (device_added), self);
-
-	priv->cancellable = g_cancellable_new ();
-
-	prepare_object_manager (self);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMIwdManager *self = (NMIwdManager *) object;
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-
-	if (priv->object_manager) {
-		if (priv->agent_id) {
-			GDBusConnection *connection;
-			GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager);
-
-			/* No need to unregister the agent as IWD will detect
-			 * our DBus connection being closed.
-			 */
-
-			connection = g_dbus_object_manager_client_get_connection (omc);
-
-			g_dbus_connection_unregister_object (connection, priv->agent_id);
-			priv->agent_id = 0;
-		}
-
-		g_clear_object (&priv->object_manager);
-	}
-
-	nm_clear_g_free (&priv->agent_path);
-
-	nm_clear_g_cancellable (&priv->cancellable);
-
-	g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free);
-	priv->known_networks = NULL;
-
-	if (priv->manager) {
-		g_signal_handlers_disconnect_by_data (priv->manager, self);
-		g_clear_object (&priv->manager);
-	}
-
-	G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object);
-}
-
-static void
-nm_iwd_manager_class_init (NMIwdManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = dispose;
-}
diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h
deleted file mode 100644
index 8e6b66ff..00000000
--- a/src/devices/wifi/nm-iwd-manager.h
+++ /dev/null
@@ -1,65 +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) 2017 Intel Corporation
- */
-
-#ifndef __NETWORKMANAGER_IWD_MANAGER_H__
-#define __NETWORKMANAGER_IWD_MANAGER_H__
-
-#include "devices/nm-device.h"
-
-#define NM_IWD_BUS_TYPE                 G_BUS_TYPE_SYSTEM
-#define NM_IWD_SERVICE                  "net.connman.iwd"
-
-#define NM_IWD_AGENT_MANAGER_INTERFACE  "net.connman.iwd.AgentManager"
-#define NM_IWD_WIPHY_INTERFACE          "net.connman.iwd.Adapter"
-#define NM_IWD_DEVICE_INTERFACE         "net.connman.iwd.Device"
-#define NM_IWD_NETWORK_INTERFACE        "net.connman.iwd.Network"
-#define NM_IWD_AGENT_INTERFACE          "net.connman.iwd.Agent"
-#define NM_IWD_WSC_INTERFACE            \
-	"net.connman.iwd.WiFiSimpleConfiguration"
-#define NM_IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks"
-#define NM_IWD_SIGNAL_AGENT_INTERFACE   "net.connman.iwd.SignalLevelAgent"
-
-typedef enum {
-	NM_IWD_NETWORK_SECURITY_NONE,
-	NM_IWD_NETWORK_SECURITY_WEP,
-	NM_IWD_NETWORK_SECURITY_PSK,
-	NM_IWD_NETWORK_SECURITY_8021X,
-} NMIwdNetworkSecurity;
-
-#define NM_TYPE_IWD_MANAGER              (nm_iwd_manager_get_type ())
-#define NM_IWD_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IWD_MANAGER, NMIwdManager))
-#define NM_IWD_MANAGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_IWD_MANAGER, NMIwdManagerClass))
-#define NM_IS_IWD_MANAGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IWD_MANAGER))
-#define NM_IS_IWD_MANAGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_IWD_MANAGER))
-#define NM_IWD_MANAGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_IWD_MANAGER, NMIwdManagerClass))
-
-typedef struct _NMIwdManager NMIwdManager;
-typedef struct _NMIwdManagerClass NMIwdManagerClass;
-
-GType nm_iwd_manager_get_type (void);
-
-NMIwdManager *nm_iwd_manager_get (void);
-
-gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name,
-                                          NMIwdNetworkSecurity security);
-void nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name,
-                                       NMIwdNetworkSecurity security);
-
-#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index dd6d1deb..bc823af0 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -21,20 +21,19 @@
 
 #include "nm-default.h"
 
-#include "nm-wifi-ap.h"
-
 #include <string.h>
 #include <stdlib.h>
 
-#include "nm-setting-wireless.h"
-
+#include "nm-wifi-ap.h"
 #include "nm-wifi-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
-#include "devices/nm-device.h"
-#include "nm-dbus-manager.h"
+
+#include "nm-setting-wireless.h"
+
+#include "introspection/org.freedesktop.NetworkManager.AccessPoint.h"
 
 #define PROTO_WPA "wpa"
 #define PROTO_RSN "rsn"
@@ -54,7 +53,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMWifiAP,
 	PROP_LAST_SEEN,
 );
 
-struct _NMWifiAPPrivate {
+typedef struct {
 	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
 
 	/* Scanned or cached values */
@@ -73,17 +72,20 @@ struct _NMWifiAPPrivate {
 	bool                fake:1;       /* Whether or not the AP is from a scan */
 	bool                hotspot:1;    /* Whether the AP is a local device's hotspot network */
 	gint32              last_seen;    /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
-};
+} NMWifiAPPrivate;
 
-typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
+struct _NMWifiAP {
+	NMExportedObject parent;
+	NMWifiAPPrivate _priv;
+};
 
 struct _NMWifiAPClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMWifiAP, nm_wifi_ap, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMWifiAP, nm_wifi_ap, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_WIFI_AP_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMWifiAP, NM_IS_WIFI_AP)
+#define NM_WIFI_AP_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMWifiAP, NM_IS_WIFI_AP)
 
 /*****************************************************************************/
 
@@ -95,24 +97,30 @@ nm_wifi_ap_get_supplicant_path (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path;
 }
 
-const GByteArray *
-nm_wifi_ap_get_ssid (const NMWifiAP *ap)
+guint64
+nm_wifi_ap_get_id (NMWifiAP *ap)
 {
-	g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL);
+	const char *path;
+	guint64 i;
 
-	return NM_WIFI_AP_GET_PRIVATE (ap)->ssid;
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), 0);
+
+	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap));
+	g_return_val_if_fail (path, 0);
+
+	nm_assert (g_str_has_prefix (path, NM_DBUS_PATH_ACCESS_POINT"/"));
+
+	i = _nm_utils_ascii_str_to_int64 (&path[NM_STRLEN (NM_DBUS_PATH_ACCESS_POINT"/")], 10, 1, G_MAXINT64, 0);
+
+	nm_assert (i);
+	return i;
 }
 
-static GVariant *
-nm_wifi_ap_get_ssid_as_variant (const NMWifiAP *self)
+const GByteArray * nm_wifi_ap_get_ssid (const NMWifiAP *ap)
 {
-	const NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self);
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL);
 
-	if (priv->ssid) {
-		return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                  priv->ssid->data, priv->ssid->len, 1);
-	} else
-		return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0);
+	return NM_WIFI_AP_GET_PRIVATE (ap)->ssid;
 }
 
 gboolean
@@ -322,7 +330,7 @@ guint32
 nm_wifi_ap_get_max_bitrate (NMWifiAP *ap)
 {
 	g_return_val_if_fail (NM_IS_WIFI_AP (ap), 0);
-	g_return_val_if_fail (nm_dbus_object_is_exported (NM_DBUS_OBJECT (ap)), 0);
+	g_return_val_if_fail (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (ap)), 0);
 
 	return NM_WIFI_AP_GET_PRIVATE (ap)->max_bitrate;
 }
@@ -407,9 +415,7 @@ security_from_vardict (GVariant *security)
 	    && array) {
 		if (g_strv_contains (array, "wpa-psk"))
 			flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-		if (g_strv_contains (array, "wpa-eap") ||
-		    g_strv_contains (array, "wpa-fils-sha256") ||
-		    g_strv_contains (array, "wpa-fils-sha384"))
+		if (g_strv_contains (array, "wpa-eap"))
 			flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
 		g_free (array);
 	}
@@ -630,7 +636,7 @@ get_max_rate_vht_160_ss3 (int mcs)
 static gboolean
 get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate)
 {
-	guint32 i;
+	guint32 mcs, i;
 	guint8 ht_cap_info;
 	const guint8 *supported_mcs_set;
 	guint32 rate;
@@ -647,6 +653,7 @@ get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate)
 	*out_maxrate = 0;
 
 	/* Find the maximum supported mcs rate */
+	mcs = -1;
 	for (i = 0; i <= 76; i++) {
 		unsigned int mcs_octet = i / 8;
 		unsigned int MCS_RATE_BIT = 1 << i % 8;
@@ -970,7 +977,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	if (priv->supplicant_path)
 		supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id;
 
-	export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+	export_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
 	if (export_path)
 		export_path = strrchr (export_path, '/') ?: export_path;
 	else
@@ -1112,8 +1119,8 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMWifiAP *self = NM_WIFI_AP (object);
-	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE ((NMWifiAP *) object);
+	GVariant *ssid;
 
 	switch (prop_id) {
 	case PROP_FLAGS:
@@ -1126,7 +1133,12 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->rsn_flags);
 		break;
 	case PROP_SSID:
-		g_value_take_variant (value, nm_wifi_ap_get_ssid_as_variant (self));
+		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);
@@ -1158,15 +1170,9 @@ get_property (GObject *object, guint prop_id,
 /*****************************************************************************/
 
 static void
-nm_wifi_ap_init (NMWifiAP *self)
+nm_wifi_ap_init (NMWifiAP *ap)
 {
-	NMWifiAPPrivate *priv;
-
-	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_WIFI_AP, NMWifiAPPrivate);
-
-	self->_priv = priv;
-
-	c_list_init (&self->aps_lst);
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	priv->mode = NM_802_11_MODE_INFRA;
 	priv->flags = NM_802_11_AP_FLAGS_NONE;
@@ -1326,11 +1332,7 @@ error:
 static void
 finalize (GObject *object)
 {
-	NMWifiAP *self = NM_WIFI_AP (object);
-	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self);
-
-	nm_assert (!self->wifi_device);
-	nm_assert (c_list_is_empty (&self->aps_lst));
+	NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE ((NMWifiAP *) object);
 
 	g_free (priv->supplicant_path);
 	if (priv->ssid)
@@ -1340,28 +1342,6 @@ finalize (GObject *object)
 	G_OBJECT_CLASS (nm_wifi_ap_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_access_point = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_ACCESS_POINT,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Flags",      "u",  NM_WIFI_AP_FLAGS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WpaFlags",   "u",  NM_WIFI_AP_WPA_FLAGS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RsnFlags",   "u",  NM_WIFI_AP_RSN_FLAGS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ssid",       "ay", NM_WIFI_AP_SSID),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Frequency",  "u",  NM_WIFI_AP_FREQUENCY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",  "s",  NM_WIFI_AP_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",       "u",  NM_WIFI_AP_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("MaxBitrate", "u",  NM_WIFI_AP_MAX_BITRATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Strength",   "y",  NM_WIFI_AP_STRENGTH),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LastSeen",   "i",  NM_WIFI_AP_LAST_SEEN),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_wifi_ap_class_init (NMWifiAPClass *ap_class)
 {
@@ -1379,12 +1359,9 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class)
 	| NM_802_11_AP_SEC_KEY_MGMT_802_1X )
 
 	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ap_class);
-
-	g_type_class_add_private (object_class, sizeof (NMWifiAPPrivate));
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (ap_class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_ACCESS_POINT);
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_access_point);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_ACCESS_POINT);
 
 	object_class->get_property = get_property;
 	object_class->finalize = finalize;
@@ -1447,85 +1424,9 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class)
 	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-}
-
-/*****************************************************************************/
 
-const char **
-nm_wifi_aps_get_paths (const CList *aps_lst_head, gboolean include_without_ssid)
-{
-	NMWifiAP *ap;
-	gsize i, n;
-	const char **list;
-	const char *path;
-
-	n = c_list_length (aps_lst_head);
-	list = g_new (const char *, n + 1);
-
-	i = 0;
-	if (n > 0) {
-		c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
-			nm_assert (i < n);
-			if (   !include_without_ssid
-			    && !nm_wifi_ap_get_ssid (ap))
-				continue;
-
-			path = nm_dbus_object_get_path (NM_DBUS_OBJECT (ap));
-			nm_assert (path);
-
-			list[i++] = path;
-		}
-		nm_assert (i <= n);
-		nm_assert (!include_without_ssid || i == n);
-	}
-	list[i] = NULL;
-	return list;
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ap_class),
+	                                        NMDBUS_TYPE_ACCESS_POINT_SKELETON,
+	                                        NULL);
 }
 
-NMWifiAP *
-nm_wifi_aps_find_first_compatible (const CList *aps_lst_head,
-                                   NMConnection *connection)
-{
-	NMWifiAP *ap;
-
-	g_return_val_if_fail (connection, NULL);
-
-	c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
-		if (nm_wifi_ap_check_compatible (ap, connection))
-			return ap;
-	}
-	return NULL;
-}
-
-NMWifiAP *
-nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path)
-{
-	NMWifiAP *ap;
-
-	g_return_val_if_fail (path != NULL, NULL);
-
-	c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
-		if (nm_streq0 (path, nm_wifi_ap_get_supplicant_path (ap)))
-			return ap;
-	}
-	return NULL;
-}
-
-/*****************************************************************************/
-
-NMWifiAP *
-nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path)
-{
-	NMWifiAP *ap;
-
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-
-	ap = (NMWifiAP *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)),
-	                                                 exported_path);
-	if (   !ap
-	    || !NM_IS_WIFI_AP (ap)
-	    || ap->wifi_device != device)
-		return NULL;
-
-	return ap;
-}
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 4fdeee93..dd5a4ad1 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -22,7 +22,7 @@
 #ifndef __NM_WIFI_AP_H__
 #define __NM_WIFI_AP_H__
 
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 
@@ -44,13 +44,7 @@
 #define NM_WIFI_AP_STRENGTH             "strength"
 #define NM_WIFI_AP_LAST_SEEN            "last-seen"
 
-typedef struct {
-	NMDBusObject parent;
-	NMDevice *wifi_device;
-	CList aps_lst;
-	struct _NMWifiAPPrivate *_priv;
-} NMWifiAP;
-
+typedef struct _NMWifiAP NMWifiAP;
 typedef struct _NMWifiAPClass NMWifiAPClass;
 
 GType nm_wifi_ap_get_type (void);
@@ -72,6 +66,7 @@ gboolean          nm_wifi_ap_complete_connection      (NMWifiAP *self,
                                                        GError **error);
 
 const char *      nm_wifi_ap_get_supplicant_path      (NMWifiAP *ap);
+guint64           nm_wifi_ap_get_id                   (NMWifiAP *ap);
 const GByteArray *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
 gboolean          nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
                                                        const guint8 *ssid,
@@ -100,14 +95,4 @@ const char       *nm_wifi_ap_to_string                (const NMWifiAP *self,
                                                        gulong buf_len,
                                                        gint32 now_s);
 
-const char      **nm_wifi_aps_get_paths        (const CList *aps_lst_head,
-                                                gboolean include_without_ssid);
-
-NMWifiAP         *nm_wifi_aps_find_first_compatible (const CList *aps_lst_head,
-                                                     NMConnection *connection);
-
-NMWifiAP         *nm_wifi_aps_find_by_supplicant_path (const CList *aps_lst_head, const char *path);
-
-NMWifiAP         *nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path);
-
 #endif /* __NM_WIFI_AP_H__ */
diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c
deleted file mode 100644
index 47c0ce67..00000000
--- a/src/devices/wifi/nm-wifi-common.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*-*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-wifi-common.h"
-
-#include "devices/nm-device.h"
-#include "nm-wifi-ap.h"
-#include "nm-device-wifi.h"
-#include "nm-dbus-manager.h"
-
-#if WITH_IWD
-#include "nm-device-iwd.h"
-#endif
-
-/*****************************************************************************/
-
-void
-nm_device_wifi_emit_signal_access_point (NMDevice *device,
-                                         NMWifiAP *ap,
-                                         gboolean is_added /* or else is_removed */)
-{
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (device),
-	                            &nm_interface_info_device_wireless,
-	                            is_added
-	                              ? &nm_signal_info_wireless_access_point_added
-	                              : &nm_signal_info_wireless_access_point_removed,
-	                            "(o)",
-	                            nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
-}
-
-/*****************************************************************************/
-
-static const CList *
-_dispatch_get_aps (NMDevice *device)
-{
-#if WITH_IWD
-	if (NM_IS_DEVICE_IWD (device))
-		return _nm_device_iwd_get_aps (NM_DEVICE_IWD (device));
-#endif
-	return _nm_device_wifi_get_aps (NM_DEVICE_WIFI (device));
-}
-
-static void
-_dispatch_request_scan (NMDevice *device,
-                        GVariant *options,
-                        GDBusMethodInvocation *invocation)
-{
-#if WITH_IWD
-	if (NM_IS_DEVICE_IWD (device)) {
-		_nm_device_iwd_request_scan (NM_DEVICE_IWD (device),
-		                             options,
-		                             invocation);
-	}
-#endif
-	_nm_device_wifi_request_scan (NM_DEVICE_WIFI (device),
-	                              options,
-	                              invocation);
-}
-
-static void
-impl_device_wifi_get_access_points (NMDBusObject *obj,
-                                    const NMDBusInterfaceInfoExtended *interface_info,
-                                    const NMDBusMethodInfoExtended *method_info,
-                                    GDBusConnection *connection,
-                                    const char *sender,
-                                    GDBusMethodInvocation *invocation,
-                                    GVariant *parameters)
-{
-	gs_free const char **list = NULL;
-	GVariant *v;
-	const CList *all_aps;
-
-	/* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */
-
-	all_aps = _dispatch_get_aps (NM_DEVICE (obj));
-	list = nm_wifi_aps_get_paths (all_aps, FALSE);
-	v = g_variant_new_objv (list, -1);
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new_tuple (&v, 1));
-}
-
-static void
-impl_device_wifi_get_all_access_points (NMDBusObject *obj,
-                                        const NMDBusInterfaceInfoExtended *interface_info,
-                                        const NMDBusMethodInfoExtended *method_info,
-                                        GDBusConnection *connection,
-                                        const char *sender,
-                                        GDBusMethodInvocation *invocation,
-                                        GVariant *parameters)
-{
-	gs_free const char **list = NULL;
-	GVariant *v;
-	const CList *all_aps;
-
-	/* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */
-
-	all_aps = _dispatch_get_aps (NM_DEVICE (obj));
-	list = nm_wifi_aps_get_paths (all_aps, TRUE);
-	v = g_variant_new_objv (list, -1);
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new_tuple (&v, 1));
-}
-
-static void
-impl_device_wifi_request_scan (NMDBusObject *obj,
-                               const NMDBusInterfaceInfoExtended *interface_info,
-                               const NMDBusMethodInfoExtended *method_info,
-                               GDBusConnection *connection,
-                               const char *sender,
-                               GDBusMethodInvocation *invocation,
-                               GVariant *parameters)
-{
-	gs_unref_variant GVariant *options = NULL;
-
-	/* NOTE: this handler is called both for NMDevicwWifi and NMDeviceIwd. */
-
-	g_variant_get (parameters, "(@a{sv})", &options);
-
-	_dispatch_request_scan (NM_DEVICE (obj),
-	                        options,
-	                        invocation);
-}
-
-const GDBusSignalInfo nm_signal_info_wireless_access_point_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"AccessPointAdded",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("access_point", "o"),
-	),
-);
-
-const GDBusSignalInfo nm_signal_info_wireless_access_point_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"AccessPointRemoved",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("access_point", "o"),
-	),
-);
-
-const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_WIRELESS,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetAccessPoints",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("access_points", "ao"),
-					),
-				),
-				.handle = impl_device_wifi_get_access_points,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetAllAccessPoints",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("access_points", "ao"),
-					),
-				),
-				.handle = impl_device_wifi_get_all_access_points,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"RequestScan",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"),
-					),
-				),
-				.handle = impl_device_wifi_request_scan,
-			),
-		),
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&nm_signal_info_wireless_access_point_added,
-			&nm_signal_info_wireless_access_point_removed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",            "s",  NM_DEVICE_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PermHwAddress",        "s",  NM_DEVICE_PERM_HW_ADDRESS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",                 "u",  NM_DEVICE_WIFI_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("BitRate",              "u",  NM_DEVICE_WIFI_BITRATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AccessPoints",         "ao", NM_DEVICE_WIFI_ACCESS_POINTS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveAccessPoint",    "o",  NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WirelessCapabilities", "u",  NM_DEVICE_WIFI_CAPABILITIES),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
diff --git a/src/devices/wifi/nm-wifi-common.h b/src/devices/wifi/nm-wifi-common.h
deleted file mode 100644
index 91cbeb55..00000000
--- a/src/devices/wifi/nm-wifi-common.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_WIFI_COMMON_H__
-#define __NM_WIFI_COMMON_H__
-
-#include "nm-dbus-utils.h"
-#include "nm-wifi-ap.h"
-
-/*****************************************************************************/
-
-void nm_device_wifi_emit_signal_access_point (NMDevice *device,
-                                              NMWifiAP *ap,
-                                              gboolean is_added /* or else is_removed */);
-
-extern const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless;
-extern const GDBusSignalInfo nm_signal_info_wireless_access_point_added;
-extern const GDBusSignalInfo nm_signal_info_wireless_access_point_removed;
-
-#endif  /* __NM_WIFI_COMMON_H__ */
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 6b8e5fb8..a1752634 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -27,10 +27,8 @@
 #include "nm-setting-olpc-mesh.h"
 #include "nm-device-wifi.h"
 #include "nm-device-olpc-mesh.h"
-#include "nm-device-iwd.h"
 #include "settings/nm-settings-connection.h"
 #include "platform/nm-platform.h"
-#include "nm-config.h"
 
 /*****************************************************************************/
 
@@ -77,7 +75,6 @@ create_device (NMDeviceFactory *factory,
 {
 	NMDeviceWifiCapabilities capabilities;
 	NM80211Mode mode;
-	gs_free char *backend = NULL;
 
 	g_return_val_if_fail (iface != NULL, NULL);
 	g_return_val_if_fail (plink != NULL, NULL);
@@ -101,30 +98,10 @@ create_device (NMDeviceFactory *factory,
 		return NULL;
 	}
 
-	if (plink->type != NM_LINK_TYPE_WIFI)
-		return nm_device_olpc_mesh_new (iface);
-
-	backend = nm_config_data_get_device_config_by_pllink (NM_CONFIG_GET_DATA,
-	                                                      NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND,
-	                                                      plink,
-	                                                      "wifi",
-	                                                      NULL);
-	nm_strstrip (backend);
-
-	nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI,
-	            "(%s) config: backend is %s%s%s%s",
-	            iface,
-	            NM_PRINT_FMT_QUOTE_STRING (backend),
-	            WITH_IWD ? " (iwd support enabled)" : "");
-	if (!backend || !strcasecmp (backend, "wpa_supplicant"))
+	if (plink->type == NM_LINK_TYPE_WIFI)
 		return nm_device_wifi_new (iface, capabilities);
-#if WITH_IWD
-	else if (!strcasecmp (backend, "iwd"))
-		return nm_device_iwd_new (iface, capabilities);
-#endif
-
-	nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend);
-	return NULL;
+	else
+		return nm_device_olpc_mesh_new (iface);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c
index 044bd392..3ff82004 100644
--- a/src/devices/wifi/nm-wifi-utils.c
+++ b/src/devices/wifi/nm-wifi-utils.c
@@ -782,35 +782,3 @@ nm_wifi_utils_level_to_quality (gint val)
 	return CLAMP (val, 0, 100);
 }
 
-gboolean
-nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid)
-{
-	int i;
-	/*
-	 * List of manufacturer default SSIDs that are often unchanged by users.
-	 *
-	 * NOTE: this list should *not* contain networks that you would like to
-	 * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot".
-	 */
-	static const char *manf_defaults[] = {
-		"linksys",
-		"linksys-a",
-		"linksys-g",
-		"default",
-		"belkin54g",
-		"NETGEAR",
-		"o2DSL",
-		"WLAN",
-		"ALICE-WLAN",
-		"Speedport W 501V",
-		"TURBONETT",
-	};
-
-	for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) {
-		if (ssid->len == strlen (manf_defaults[i])) {
-			if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0)
-				return TRUE;
-		}
-	}
-	return FALSE;
-}
diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h
index def64dd6..1b6c2f4b 100644
--- a/src/devices/wifi/nm-wifi-utils.h
+++ b/src/devices/wifi/nm-wifi-utils.h
@@ -39,6 +39,4 @@ gboolean nm_wifi_utils_complete_connection (const GByteArray *ssid,
 
 guint32 nm_wifi_utils_level_to_quality (gint val);
 
-gboolean nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid);
-
 #endif  /* __NM_WIFI_UTILS_H__ */
diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build
deleted file mode 100644
index bb8f7c27..00000000
--- a/src/devices/wifi/tests/meson.build
+++ /dev/null
@@ -1,13 +0,0 @@
-test_unit = 'test-general'
-
-exe = executable(
-  'wifi-' + test_unit,
-  [test_unit + '.c'] + common_sources,
-  dependencies: test_nm_dep
-)
-
-test(
-  'devices/wifi/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 70b954c5..6efcb03f 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -11,10 +11,10 @@ global:
 	nm_modem_get_capabilities;
 	nm_modem_get_configured_mtu;
 	nm_modem_get_control_port;
+	nm_modem_get_data_port;
 	nm_modem_get_driver;
 	nm_modem_get_iid;
 	nm_modem_get_path;
-	nm_modem_get_ip_ifindex;
 	nm_modem_get_secrets;
 	nm_modem_get_state;
 	nm_modem_get_type;
diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build
deleted file mode 100644
index 032b3585..00000000
--- a/src/devices/wwan/meson.build
+++ /dev/null
@@ -1,75 +0,0 @@
-sources = files(
-  'nm-modem-broadband.c',
-  'nm-modem.c',
-  'nm-modem-manager.c'
-)
-
-deps = [
-  libsystemd_dep,
-  mm_glib_dep,
-  nm_dep
-]
-
-if enable_ofono
-  sources += files('nm-modem-ofono.c')
-endif
-
-linker_script = join_paths(meson.current_source_dir(), 'libnm-wwan.ver')
-
-libnm_wwan = shared_module(
-  'nm-wwan',
-  sources: sources,
-  dependencies: deps,
-  link_args: [
-    '-Wl,--version-script,@0@'.format(linker_script),
-  ],
-  link_depends: linker_script,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-libnm_wwan_dep = declare_dependency(
-  include_directories: include_directories('.'),
-  link_with: libnm_wwan
-)
-
-core_plugins += libnm_wwan
-
-run_target(
-  'check-wwan',
-  command: [check_exports, libnm_wwan.full_path(), linker_script],
-  depends: libnm_wwan
-)
-
-sources = files(
-  'nm-device-modem.c',
-  'nm-wwan-factory.c'
-)
-
-libnm_device_plugin_wwan = shared_module(
-  'nm-device-plugin-wwan',
-  sources: sources,
-  dependencies: deps,
-  link_with: libnm_wwan,
-  link_args: ldflags_linker_script_devices,
-  link_depends: linker_script_devices,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_device_plugin_wwan
-
-run_target(
-  'check-local-devices-wwan',
-  command: [check_exports, libnm_device_plugin_wwan.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_wwan
-)
-
-# FIXME: check_so_symbols replacement
-'''
-check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devices/wwan/libnm-wwan.la
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so "$(srcdir)/linker-script-devices.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so)
-  $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver"
-  $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so)
-'''
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 2a3e9ebe..b79d145d 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -32,6 +32,8 @@
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Device.Modem.h"
+
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceModem);
 
@@ -258,26 +260,21 @@ modem_ip6_config_result (NMModem *modem,
 }
 
 static void
-ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
+data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 {
-	NMDevice *device = NM_DEVICE (user_data);
-
-	if (!nm_device_is_activating (device))
-		return;
+	NMDevice *self = NM_DEVICE (user_data);
+	gboolean changed;
 
-	if (!nm_device_set_ip_ifindex (device,
-	                               nm_modem_get_ip_ifindex (modem))) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-		return;
-	}
+	/* We set the IP iface in the device as soon as we know it, so that we
+	 * properly ifup it if needed */
+	changed = nm_device_set_ip_iface (self, nm_modem_get_data_port (modem));
 
 	/* Disable IPv6 immediately on the interface since NM handles IPv6
 	 * internally, and leaving it enabled could allow the kernel's IPv6
 	 * RA handling code to run before NM is ready.
 	 */
-	nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+	if (changed)
+		nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
 }
 
 static void
@@ -434,7 +431,7 @@ static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device);
@@ -632,7 +629,11 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 	g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
 	g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
 
-	g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
+	/* In the old ModemManager the data port is known from the very beginning;
+	 * while in the new ModemManager the data port is set afterwards when the bearer gets
+	 * created */
+	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);
@@ -707,23 +708,34 @@ nm_device_modem_new (NMModem *modem)
 {
 	NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
 	NMDeviceModemCapabilities current_caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
+	NMDevice *device;
+	const char *data_port;
 
 	g_return_val_if_fail (NM_IS_MODEM (modem), NULL);
 
 	/* Load capabilities */
 	nm_modem_get_capabilities (modem, &caps, &current_caps);
 
-	return g_object_new (NM_TYPE_DEVICE_MODEM,
-	                     NM_DEVICE_UDI, nm_modem_get_path (modem),
-	                     NM_DEVICE_IFACE, nm_modem_get_uid (modem),
-	                     NM_DEVICE_DRIVER, nm_modem_get_driver (modem),
-	                     NM_DEVICE_TYPE_DESC, "Broadband",
-	                     NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM,
-	                     NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
-	                     NM_DEVICE_MODEM_MODEM, modem,
-	                     NM_DEVICE_MODEM_CAPABILITIES, caps,
-	                     NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
-	                     NULL);
+	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_MODEM,
+	                                    NM_DEVICE_UDI, nm_modem_get_path (modem),
+	                                    NM_DEVICE_IFACE, nm_modem_get_uid (modem),
+	                                    NM_DEVICE_DRIVER, nm_modem_get_driver (modem),
+	                                    NM_DEVICE_TYPE_DESC, "Broadband",
+	                                    NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM,
+	                                    NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
+	                                    NM_DEVICE_MODEM_MODEM, modem,
+	                                    NM_DEVICE_MODEM_CAPABILITIES, caps,
+	                                    NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
+	                                    NULL);
+
+	/* If the data port is known, set it as the IP interface immediately */
+	data_port = nm_modem_get_data_port (modem);
+	if (data_port) {
+		nm_device_set_ip_iface (device, data_port);
+		nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1");
+	}
+
+	return device;
 }
 
 static void
@@ -731,41 +743,23 @@ dispose (GObject *object)
 {
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object);
 
-	if (priv->modem) {
+	if (priv->modem)
 		g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object));
-		g_clear_object (&priv->modem);
-	}
+	g_clear_object (&priv->modem);
 
 	G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DEVICE_MODEM,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ModemCapabilities",   "u",  NM_DEVICE_MODEM_CAPABILITIES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CurrentCapabilities", "u",  NM_DEVICE_MODEM_CURRENT_CAPABILITIES),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
-nm_device_modem_class_init (NMDeviceModemClass *klass)
+nm_device_modem_class_init (NMDeviceModemClass *mclass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (mclass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (mclass);
 
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_modem);
-
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->get_type_description = get_type_description;
 	device_class->check_connection_compatible = check_connection_compatible;
@@ -808,4 +802,8 @@ nm_device_modem_class_init (NMDeviceModemClass *klass)
 	                        G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (mclass),
+	                                        NMDBUS_TYPE_DEVICE_MODEM_SKELETON,
+	                                        NULL);
 }
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 9a3744db..dc0ce303 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -339,7 +339,7 @@ connect_context_clear (NMModemBroadband *self)
 		ConnectContext *ctx = self->_priv.ctx;
 
 		g_clear_error (&ctx->first_error);
-		g_clear_pointer (&ctx->ip_types, g_array_unref);
+		g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
 		nm_clear_g_cancellable (&ctx->cancellable);
 		g_clear_object (&ctx->connection);
 		g_clear_object (&ctx->connect_properties);
@@ -385,10 +385,9 @@ connect_ready (MMModemSimple *simple_iface,
 				g_dbus_error_strip_remote_error (error);
 			ctx->first_error = error;
 		} else
-			g_clear_error (&error);
+			g_error_free (error);
 
-		if (   ctx->ip_type_tries == 0
-		    && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
+		if (ctx->ip_type_tries == 0 && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
 			/* Try one more time */
 			ctx->ip_type_tries++;
 		} else {
@@ -411,20 +410,21 @@ connect_ready (MMModemSimple *simple_iface,
 	if (self->_priv.ipv6_config)
 		ip6_method = get_bearer_ip_method (self->_priv.ipv6_config);
 
-	if (!nm_modem_set_data_port (NM_MODEM (self),
-	                             NM_PLATFORM_GET,
-	                             mm_bearer_get_interface (self->_priv.bearer),
-	                             ip4_method,
-	                             ip6_method,
-	                             mm_bearer_get_ip_timeout (self->_priv.bearer),
-	                             &error)) {
-		_LOGW ("failed to connect modem: %s", error->message);
-		g_error_free (error);
+	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
+	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
+		_LOGW ("failed to connect modem: invalid bearer IP configuration");
 		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		connect_context_clear (self);
 		return;
 	}
 
+	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 (self->_priv.bearer),
+	              NULL);
+
 	ctx->step++;
 	connect_context_step (self);
 }
@@ -663,7 +663,7 @@ check_connection_compatible (NMModem *_self, NMConnection *connection)
 static gboolean
 complete_connection (NMModem *_self,
                      NMConnection *connection,
-                     NMConnection *const*existing_connections,
+                     const GSList *existing_connections,
                      GError **error)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
@@ -1409,34 +1409,35 @@ nm_modem_broadband_init (NMModemBroadband *self)
 NMModem *
 nm_modem_broadband_new (GObject *object, GError **error)
 {
+	NMModem *modem;
 	MMObject *modem_object;
 	MMModem *modem_iface;
-	const char *const*drivers;
-	gs_free char *driver = NULL;
+	gchar *drivers;
 
 	g_return_val_if_fail (MM_IS_OBJECT (object), NULL);
 	modem_object = MM_OBJECT (object);
 
 	/* Ensure we have the 'Modem' interface and the primary port at least */
 	modem_iface = mm_object_peek_modem (modem_object);
-	g_return_val_if_fail (modem_iface, NULL);
-	g_return_val_if_fail (mm_modem_get_primary_port (modem_iface), NULL);
+	g_return_val_if_fail (!!modem_iface, NULL);
+	g_return_val_if_fail (!!mm_modem_get_primary_port (modem_iface), NULL);
 
 	/* Build a single string with all drivers listed */
-	drivers = mm_modem_get_drivers (modem_iface);
-	if (drivers)
-		driver = g_strjoinv (", ", (char **) drivers);
-
-	return g_object_new (NM_TYPE_MODEM_BROADBAND,
-	                     NM_MODEM_PATH, mm_object_get_path (modem_object),
-	                     NM_MODEM_UID, mm_modem_get_primary_port (modem_iface),
-	                     NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
-	                     NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
-	                     NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)),
-	                     NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
-	                     NM_MODEM_BROADBAND_MODEM, modem_object,
-	                     NM_MODEM_DRIVER, driver,
-	                     NULL);
+	drivers = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (modem_iface));
+
+	modem = g_object_new (NM_TYPE_MODEM_BROADBAND,
+	                      NM_MODEM_PATH, mm_object_get_path (modem_object),
+	                      NM_MODEM_UID, mm_modem_get_primary_port (modem_iface),
+	                      NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface),
+	                      NM_MODEM_DATA_PORT, NULL, /* We don't know it until bearer created */
+	                      NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)),
+	                      NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)),
+	                      NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface),
+	                      NM_MODEM_BROADBAND_MODEM, modem_object,
+	                      NM_MODEM_DRIVER, drivers,
+	                      NULL);
+	g_free (drivers);
+	return modem;
 }
 
 static void
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index a1c6aef2..811c3afb 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -836,7 +836,6 @@ context_property_changed (GDBusProxy *proxy,
 	guint32 address_network, gateway_network;
 	guint32 ip4_route_table, ip4_route_metric;
 	int ifindex;
-	GError *error = NULL;
 
 	_LOGD ("PropertyChanged: %s", property);
 
@@ -861,26 +860,27 @@ context_property_changed (GDBusProxy *proxy,
 		_LOGW ("Settings 'Interface' missing");
 		goto out;
 	}
+	if (!interface || !interface[0]) {
+		_LOGW ("Settings 'Interface'; empty");
+		goto out;
+	}
 
-	_LOGD ("Interface: %s", interface);
-	if (!nm_modem_set_data_port (NM_MODEM (self),
-	                             NM_PLATFORM_GET,
-	                             interface,
-	                             NM_MODEM_IP_METHOD_STATIC,
-	                             NM_MODEM_IP_METHOD_UNKNOWN,
-	                             0,
-	                             &error)) {
-		_LOGW ("failed to connect to modem: %s", error->message);
-		g_clear_error (&error);
+	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, interface);
+	if (ifindex <= 0) {
+		_LOGW ("Interface \"%s\" not found", interface);
 		goto out;
 	}
 
-	ifindex = nm_modem_get_ip_ifindex (NM_MODEM (self));
-	nm_assert (ifindex > 0);
+	_LOGD ("Interface: %s", interface);
+	g_object_set (self,
+	              NM_MODEM_DATA_PORT, interface,
+	              NM_MODEM_IP4_METHOD, NM_MODEM_IP_METHOD_STATIC,
+	              NULL);
 
 	/* TODO: verify handling of ip4_config; check other places it's used... */
 	g_clear_object (&priv->ip4_config);
 
+
 	priv->ip4_config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET),
 	                                      ifindex);
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 61b7247e..010a2b60 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -44,10 +44,13 @@
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMModem,
 	PROP_CONTROL_PORT,
-	PROP_IP_IFINDEX,
+	PROP_DATA_PORT,
 	PROP_PATH,
 	PROP_UID,
 	PROP_DRIVER,
+	PROP_IP4_METHOD,
+	PROP_IP6_METHOD,
+	PROP_IP_TIMEOUT,
 	PROP_STATE,
 	PROP_DEVICE_ID,
 	PROP_SIM_ID,
@@ -76,12 +79,7 @@ typedef struct _NMModemPrivate {
 	char *driver;
 	char *control_port;
 	char *data_port;
-
-	/* TODO: ip_iface is solely used for nm_modem_owns_port().
-	 * We should rework the code that it's not necessary */
-	char *ip_iface;
-
-	int ip_ifindex;
+	char *ppp_iface;
 	NMModemIPMethod ip4_method;
 	NMModemIPMethod ip6_method;
 	NMUtilsIPv6IfaceId iid;
@@ -98,7 +96,7 @@ typedef struct _NMModemPrivate {
 	guint32 secrets_tries;
 	NMActRequestGetSecretsCallId *secrets_id;
 
-	guint mm_ip_timeout;
+	guint32 mm_ip_timeout;
 
 	guint32 ip4_route_table;
 	guint32 ip4_route_metric;
@@ -155,10 +153,6 @@ _nmlog_prefix (char *prefix, NMModem *self)
     } G_STMT_END
 
 /*****************************************************************************/
-
-static void _set_ip_ifindex (NMModem *self, int ifindex, const char *ifname);
-
-/*****************************************************************************/
 /* State/enabled/connected */
 
 static const char *state_table[] = {
@@ -456,28 +450,20 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 }
 
 static void
-ppp_ifindex_set (NMPPPManager *ppp_manager,
-                 int ifindex,
-                 const char *iface,
-                 gpointer user_data)
+set_data_port (NMModem *self, const char *new_data_port)
 {
-	NMModem *self = NM_MODEM (user_data);
-
-	nm_assert (ifindex >= 0);
-	nm_assert (NM_MODEM_GET_PRIVATE (self)->ppp_manager == ppp_manager);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
-	if (ifindex <= 0 && iface) {
-		/* this might happen, if the ifname was already deleted
-		 * and we failed to resolve ifindex.
-		 *
-		 * Forget about the name. */
-		iface = NULL;
+	if (g_strcmp0 (priv->data_port, new_data_port) != 0) {
+		g_free (priv->data_port);
+		priv->data_port = g_strdup (new_data_port);
+		_notify (self, PROP_DATA_PORT);
 	}
-	_set_ip_ifindex (self, ifindex, iface);
 }
 
 static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 NMIP4Config *config,
                 gpointer user_data)
 {
@@ -489,6 +475,9 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 	guint32 good_dns2 = htonl (0x04020202);  /* GTE nameserver */
 	gboolean dns_workaround = FALSE;
 
+	/* Notify about the new data port to use */
+	set_data_port (self, iface);
+
 	/* Work around a PPP bug (#1732) which causes many mobile broadband
 	 * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
 	 * Apparently fixed in ppp-2.4.5 but we've had some reports that this is
@@ -530,12 +519,16 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 
 static void
 ppp_ip6_config (NMPPPManager *ppp_manager,
+                const char *iface,
                 const NMUtilsIPv6IfaceId *iid,
                 NMIP6Config *config,
                 gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 
+	/* Notify about the new data port to use */
+	set_data_port (self, iface);
+
 	NM_MODEM_GET_PRIVATE (self)->iid = *iid;
 
 	nm_modem_emit_ip6_config_result (self, config, NULL);
@@ -564,18 +557,6 @@ port_speed_is_zero (const char *port)
 {
 	struct termios options;
 	nm_auto_close int fd = -1;
-	gs_free char *path = NULL;
-
-	nm_assert (port);
-
-	if (port[0] != '/') {
-		if (   !port[0]
-		    || strchr (port, '/')
-		    || NM_IN_STRSET (port, ".", ".."))
-			return FALSE;
-		path = g_build_path ("/sys/class/tty", port, NULL);
-		port = path;
-	}
 
 	fd = open (port, O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC);
 	if (fd < 0)
@@ -617,12 +598,6 @@ ppp_stage3_ip_config_start (NMModem *self,
 			return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	if (!priv->data_port) {
-		_LOGE ("error starting PPP (no data port)");
-		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
-		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
-
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
 	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
@@ -654,7 +629,9 @@ ppp_stage3_ip_config_start (NMModem *self,
 	                              ip_timeout, baud_override, &error)) {
 		_LOGE ("error starting PPP: %s", error->message);
 		g_error_free (error);
+
 		g_clear_object (&priv->ppp_manager);
+
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
@@ -662,9 +639,6 @@ ppp_stage3_ip_config_start (NMModem *self,
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
 	                  G_CALLBACK (ppp_state_changed),
 	                  self);
-	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_CALLBACK (ppp_ifindex_set),
-	                  self);
 	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_CALLBACK (ppp_ip4_config),
 	                  self);
@@ -1091,20 +1065,12 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 gboolean
 nm_modem_complete_connection (NMModem *self,
                               NMConnection *connection,
-                              NMConnection *const*existing_connections,
+                              const GSList *existing_connections,
                               GError **error)
 {
-	NMModemClass *klass;
-
-	klass = NM_MODEM_GET_CLASS (self);
-	if (!klass->complete_connection) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
-		             "Modem class %s had no complete_connection method",
-		             G_OBJECT_TYPE_NAME (self));
-		return FALSE;
-	}
-
-	return klass->complete_connection (self, connection, existing_connections, error);
+	if (NM_MODEM_GET_CLASS (self)->complete_connection)
+		return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error);
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -1129,10 +1095,7 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 
 	priv->in_bytes = priv->out_bytes = 0;
 
-	if (priv->ppp_manager) {
-		g_signal_handlers_disconnect_by_data (priv->ppp_manager, self);
-		g_clear_object (&priv->ppp_manager);
-	}
+	g_clear_object (&priv->ppp_manager);
 
 	if (device) {
 		g_return_if_fail (NM_IS_DEVICE (device));
@@ -1151,12 +1114,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 			}
 		}
 	}
-
-	nm_clear_g_free (&priv->data_port);
-	priv->mm_ip_timeout = 0;
 	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
 	priv->ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
-	_set_ip_ifindex (self, -1, NULL);
+
+	g_free (priv->ppp_iface);
+	priv->ppp_iface = NULL;
 }
 
 /*****************************************************************************/
@@ -1407,117 +1369,17 @@ nm_modem_get_control_port (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->control_port;
 }
 
-int
-nm_modem_get_ip_ifindex (NMModem *self)
-{
-	NMModemPrivate *priv;
-
-	g_return_val_if_fail (NM_IS_MODEM (self), 0);
-
-	priv = NM_MODEM_GET_PRIVATE (self);
-
-	/* internally we track an unset ip_ifindex as -1.
-	 * For the caller of nm_modem_get_ip_ifindex(), this
-	 * shall be zero too. */
-	return priv->ip_ifindex != -1 ? priv->ip_ifindex : 0;
-}
-
-static void
-_set_ip_ifindex (NMModem *self, int ifindex, const char *ifname)
-{
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-
-	nm_assert (ifindex >= -1);
-	nm_assert ((ifindex > 0) == !!ifname);
-
-	if (!nm_streq0 (priv->ip_iface, ifname)) {
-		g_free (priv->ip_iface);
-		priv->ip_iface = g_strdup (ifname);
-	}
-
-	if (priv->ip_ifindex != ifindex) {
-		priv->ip_ifindex = ifindex;
-		_notify (self, PROP_IP_IFINDEX);
-	}
-}
-
-gboolean
-nm_modem_set_data_port (NMModem *self,
-                        NMPlatform *platform,
-                        const char *data_port,
-                        NMModemIPMethod ip4_method,
-                        NMModemIPMethod ip6_method,
-                        guint timeout,
-                        GError **error)
+const char *
+nm_modem_get_data_port (NMModem *self)
 {
-	NMModemPrivate *priv;
-	gboolean is_ppp;
-	int ifindex = -1;
-
-	g_return_val_if_fail (NM_IS_MODEM (self), FALSE);
-	g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-
-	priv = NM_MODEM_GET_PRIVATE (self);
-
-	if (   priv->ppp_manager
-	    || priv->data_port
-	    || priv->ip_ifindex != -1) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "cannot set data port in activated state");
-		/* this really shouldn't happen. Assert. */
-		g_return_val_if_reached (FALSE);
-	}
-
-	if (!data_port) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "missing data port");
-		return FALSE;
-	}
-
-	is_ppp =    (ip4_method == NM_MODEM_IP_METHOD_PPP)
-	         || (ip6_method == NM_MODEM_IP_METHOD_PPP);
-	if (is_ppp) {
-		if (   !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP)
-		    || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_PPP)) {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "conflicting ip methods");
-			return FALSE;
-		}
-	} else if (   !NM_IN_SET (ip4_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO)
-	           || !NM_IN_SET (ip6_method, NM_MODEM_IP_METHOD_UNKNOWN, NM_MODEM_IP_METHOD_STATIC, NM_MODEM_IP_METHOD_AUTO)
-	           || (   ip4_method == NM_MODEM_IP_METHOD_UNKNOWN
-	               && ip6_method == NM_MODEM_IP_METHOD_UNKNOWN)) {
-		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-		                     "invalid ip methods");
-		return FALSE;
-	}
-
-	if (!is_ppp) {
-		ifindex = nm_platform_if_nametoindex (platform, data_port);
-		if (ifindex <= 0) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "cannot find network interface %s", data_port);
-			return FALSE;
-		}
-		if (!nm_platform_process_events_ensure_link (platform, ifindex, data_port)) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "cannot find network interface %s in platform cache", data_port);
-			return FALSE;
-		}
-	}
+	g_return_val_if_fail (NM_IS_MODEM (self), NULL);
 
-	priv->mm_ip_timeout = timeout;
-	priv->ip4_method = ip4_method;
-	priv->ip6_method = ip6_method;
-	if (is_ppp) {
-		priv->data_port = g_strdup (data_port);
-		_set_ip_ifindex (self, -1, NULL);
-	} else {
-		priv->data_port = NULL;
-		_set_ip_ifindex (self, ifindex, data_port);
-	}
-	return TRUE;
+	/* The ppp_iface takes precedence over the data interface when PPP is used,
+	 * since data_iface is the TTY over which PPP is run, and that TTY can't
+	 * do IP.  The caller really wants the thing that's doing IP.
+	 */
+	return NM_MODEM_GET_PRIVATE (self)->ppp_iface ?
+		NM_MODEM_GET_PRIVATE (self)->ppp_iface : NM_MODEM_GET_PRIVATE (self)->data_port;
 }
 
 gboolean
@@ -1530,10 +1392,15 @@ nm_modem_owns_port (NMModem *self, const char *iface)
 	if (NM_MODEM_GET_CLASS (self)->owns_port)
 		return NM_MODEM_GET_CLASS (self)->owns_port (self, iface);
 
-	return NM_IN_STRSET (iface,
-	                     priv->ip_iface,
-	                     priv->data_port,
-	                     priv->control_port);
+	/* Fall back to data/control ports */
+	if (priv->ppp_iface && (strcmp (priv->ppp_iface, iface) == 0))
+		return TRUE;
+	if (priv->data_port && (strcmp (priv->data_port, iface) == 0))
+		return TRUE;
+	if (priv->control_port && (strcmp (priv->control_port, iface) == 0))
+		return TRUE;
+
+	return FALSE;
 }
 
 gboolean
@@ -1633,8 +1500,7 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMModem *self = NM_MODEM (object);
-	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE ((NMModem *) object);
 
 	switch (prop_id) {
 	case PROP_PATH:
@@ -1646,12 +1512,21 @@ get_property (GObject *object, guint prop_id,
 	case PROP_CONTROL_PORT:
 		g_value_set_string (value, priv->control_port);
 		break;
-	case PROP_IP_IFINDEX:
-		g_value_set_int (value, nm_modem_get_ip_ifindex (self));
+	case PROP_DATA_PORT:
+		g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object)));
 		break;
 	case PROP_UID:
 		g_value_set_string (value, priv->uid);
 		break;
+	case PROP_IP4_METHOD:
+		g_value_set_uint (value, priv->ip4_method);
+		break;
+	case PROP_IP6_METHOD:
+		g_value_set_uint (value, priv->ip6_method);
+		break;
+	case PROP_IP_TIMEOUT:
+		g_value_set_uint (value, priv->mm_ip_timeout);
+		break;
 	case PROP_STATE:
 		g_value_set_int (value, priv->state);
 		break;
@@ -1694,10 +1569,23 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->control_port = g_value_dup_string (value);
 		break;
+	case PROP_DATA_PORT:
+		g_free (priv->data_port);
+		priv->data_port = g_value_dup_string (value);
+		break;
 	case PROP_UID:
 		/* construct-only */
 		priv->uid = g_value_dup_string (value);
 		break;
+	case PROP_IP4_METHOD:
+		priv->ip4_method = g_value_get_uint (value);
+		break;
+	case PROP_IP6_METHOD:
+		priv->ip6_method = g_value_get_uint (value);
+		break;
+	case PROP_IP_TIMEOUT:
+		priv->mm_ip_timeout = g_value_get_uint (value);
+		break;
 	case PROP_STATE:
 		/* construct-only */
 		priv->state = g_value_get_int (value);
@@ -1735,7 +1623,6 @@ nm_modem_init (NMModem *self)
 	self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_MODEM, NMModemPrivate);
 	priv = self->_priv;
 
-	priv->ip_ifindex = -1;
 	priv->ip4_route_table = RT_TABLE_MAIN;
 	priv->ip4_route_metric = 700;
 	priv->ip6_route_table = RT_TABLE_MAIN;
@@ -1751,7 +1638,7 @@ constructed (GObject *object)
 
 	priv = NM_MODEM_GET_PRIVATE (NM_MODEM (object));
 
-	g_return_if_fail (priv->control_port);
+	g_return_if_fail (priv->data_port || priv->control_port);
 }
 
 /*****************************************************************************/
@@ -1776,7 +1663,6 @@ finalize (GObject *object)
 	g_free (priv->driver);
 	g_free (priv->control_port);
 	g_free (priv->data_port);
-	g_free (priv->ip_iface);
 	g_free (priv->device_id);
 	g_free (priv->sim_id);
 	g_free (priv->sim_operator_id);
@@ -1825,11 +1711,33 @@ nm_modem_class_init (NMModemClass *klass)
 	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                          G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_IP_IFINDEX] =
-	     g_param_spec_int (NM_MODEM_IP_IFINDEX, "", "",
-	                       0, G_MAXINT, 0,
-	                       G_PARAM_READABLE |
-	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DATA_PORT] =
+	     g_param_spec_string (NM_MODEM_DATA_PORT, "", "",
+	                          NULL,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_IP4_METHOD] =
+	     g_param_spec_uint (NM_MODEM_IP4_METHOD, "", "",
+	                        NM_MODEM_IP_METHOD_UNKNOWN,
+	                        NM_MODEM_IP_METHOD_AUTO,
+	                        NM_MODEM_IP_METHOD_UNKNOWN,
+	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                        G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_IP6_METHOD] =
+	     g_param_spec_uint (NM_MODEM_IP6_METHOD, "", "",
+	                        NM_MODEM_IP_METHOD_UNKNOWN,
+	                        NM_MODEM_IP_METHOD_AUTO,
+	                        NM_MODEM_IP_METHOD_UNKNOWN,
+	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+	                        G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_IP_TIMEOUT] =
+	     g_param_spec_uint (NM_MODEM_IP_TIMEOUT, "", "",
+	                        0, 360, 20,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_STATE] =
 	     g_param_spec_int (NM_MODEM_STATE, "", "",
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 3e281c0c..9546e4a1 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -37,7 +37,10 @@
 #define NM_MODEM_PATH            "path"
 #define NM_MODEM_DRIVER          "driver"
 #define NM_MODEM_CONTROL_PORT    "control-port"
-#define NM_MODEM_IP_IFINDEX      "ip-ifindex"
+#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"
@@ -126,7 +129,7 @@ typedef struct {
 
 	gboolean (*complete_connection)            (NMModem *modem,
 	                                            NMConnection *connection,
-	                                            NMConnection *const*existing_connections,
+	                                            const GSList *existing_connections,
 	                                            GError **error);
 
 	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
@@ -164,21 +167,13 @@ 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);
-int         nm_modem_get_ip_ifindex      (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_set_data_port (NMModem *self,
-                                    NMPlatform *platform,
-                                    const char *data_port,
-                                    NMModemIPMethod ip4_method,
-                                    NMModemIPMethod ip6_method,
-                                    guint timeout,
-                                    GError **error);
-
 gboolean    nm_modem_owns_port        (NMModem *modem, const char *iface);
 
 void        nm_modem_get_capabilities (NMModem *self,
@@ -189,7 +184,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *conn
 
 gboolean nm_modem_complete_connection (NMModem *self,
                                        NMConnection *connection,
-                                       NMConnection *const*existing_connections,
+                                       const GSList *existing_connections,
                                        GError **error);
 
 void nm_modem_get_route_parameters (NMModem *self,
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index f0aae040..663102de 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -80,7 +80,7 @@ modem_added_cb (NMModemManager *manager,
 {
 	NMWwanFactory *self = NM_WWAN_FACTORY (user_data);
 	NMDevice *device;
-	const char *driver;
+	const char *driver, *port;
 
 	/* Do nothing if the modem was consumed by some other plugin */
 	if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem)))
@@ -93,8 +93,10 @@ modem_added_cb (NMModemManager *manager,
 	 * by the Bluetooth code during the connection process.
 	 */
 	if (driver && strstr (driver, "bluetooth")) {
-		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
-		             nm_modem_get_control_port (modem));
+		port = nm_modem_get_data_port (modem);
+		if (!port)
+			port = nm_modem_get_control_port (modem);
+		nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", port);
 		return;
 	}
 
diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build
deleted file mode 100644
index 289a16ca..00000000
--- a/src/dhcp/meson.build
+++ /dev/null
@@ -1,22 +0,0 @@
-name = 'nm-dhcp-helper'
-
-cflags = [
-  '-DG_LOG_DOMAIN="@0@"'.format(name),
-  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
-  '-DNMRUNDIR="@0@"'.format(nm_pkgrundir),
-]
-
-executable(
-  name,
-  name + '.c',
-  dependencies: nm_core_dep,
-  c_args: cflags,
-  link_args: ldflags_linker_script_binary,
-  link_depends: linker_script_binary,
-  install: true,
-  install_dir: nm_libexecdir
-)
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 96c02653..ea3938d6 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -52,24 +52,23 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_MULTI_IDX,
 	PROP_ADDR_FAMILY,
-	PROP_FLAGS,
-	PROP_HWADDR,
 	PROP_IFACE,
 	PROP_IFINDEX,
-	PROP_MULTI_IDX,
-	PROP_ROUTE_METRIC,
+	PROP_HWADDR,
+	PROP_UUID,
 	PROP_ROUTE_TABLE,
+	PROP_ROUTE_METRIC,
 	PROP_TIMEOUT,
-	PROP_UUID,
 );
 
 typedef struct _NMDhcpClientPrivate {
 	NMDedupMultiIndex *multi_idx;
 	char *       iface;
-	GBytes *     hwaddr;
+	GByteArray * hwaddr;
 	char *       uuid;
-	GBytes *     duid;
+	GByteArray * duid;
 	GBytes *     client_id;
 	char *       hostname;
 	pid_t        pid;
@@ -139,7 +138,7 @@ nm_dhcp_client_get_uuid (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->uuid;
 }
 
-GBytes *
+const GByteArray *
 nm_dhcp_client_get_duid (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
@@ -147,7 +146,7 @@ nm_dhcp_client_get_duid (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid;
 }
 
-GBytes *
+const GByteArray *
 nm_dhcp_client_get_hw_addr (NMDhcpClient *self)
 {
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
@@ -239,20 +238,26 @@ nm_dhcp_client_set_client_id_bin (NMDhcpClient *self,
 	_set_client_id (self, b, TRUE);
 }
 
-const char *
-nm_dhcp_client_get_hostname (NMDhcpClient *self)
+void
+nm_dhcp_client_set_client_id_str (NMDhcpClient *self,
+                                  const char *dhcp_client_id)
 {
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+	g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+	g_return_if_fail (!dhcp_client_id || dhcp_client_id[0]);
 
-	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
+	_set_client_id (self,
+	                dhcp_client_id
+	                  ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id)
+	                  : NULL,
+	                TRUE);
 }
 
-gboolean
-nm_dhcp_client_get_info_only (NMDhcpClient *self)
+const char *
+nm_dhcp_client_get_hostname (NMDhcpClient *self)
 {
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
 
-	return NM_DHCP_CLIENT_GET_PRIVATE (self)->info_only;
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
 }
 
 gboolean
@@ -340,7 +345,7 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface)
 }
 
 static void
-stop (NMDhcpClient *self, gboolean release, GBytes *duid)
+stop (NMDhcpClient *self, gboolean release, const GByteArray *duid)
 {
 	NMDhcpClientPrivate *priv;
 
@@ -354,6 +359,7 @@ stop (NMDhcpClient *self, gboolean release, GBytes *duid)
 		nm_dhcp_client_stop_pid (priv->pid, priv->iface);
 	}
 	priv->pid = -1;
+	priv->info_only = FALSE;
 }
 
 void
@@ -486,9 +492,10 @@ nm_dhcp_client_watch_child (NMDhcpClient *self, pid_t pid)
 
 gboolean
 nm_dhcp_client_start_ip4 (NMDhcpClient *self,
-                          GBytes *client_id,
+                          const char *dhcp_client_id,
                           const char *dhcp_anycast_addr,
                           const char *hostname,
+                          gboolean use_fqdn,
                           const char *last_ip4_address)
 {
 	NMDhcpClientPrivate *priv;
@@ -505,19 +512,19 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 	else
 		_LOGI ("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
 
-	nm_dhcp_client_set_client_id (self, client_id);
+	nm_dhcp_client_set_client_id_str (self, dhcp_client_id);
 
 	g_clear_pointer (&priv->hostname, g_free);
 	priv->hostname = g_strdup (hostname);
+	priv->use_fqdn = use_fqdn;
 
 	return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
 }
 
-static GBytes *
+static GByteArray *
 generate_duid_from_machine_id (void)
 {
-	const int DUID_SIZE = 18;
-	guint8 *duid_buffer;
+	GByteArray *duid;
 	GChecksum *sum;
 	guint8 buffer[32]; /* SHA256 digest size */
 	gsize sumlen = sizeof (buffer);
@@ -525,7 +532,6 @@ generate_duid_from_machine_id (void)
 	uuid_t uuid;
 	gs_free char *machine_id_s = NULL;
 	gs_free char *str = NULL;
-	GBytes *duid;
 
 	machine_id_s = nm_utils_machine_id_read ();
 	if (nm_utils_machine_id_parse (machine_id_s, uuid)) {
@@ -548,31 +554,36 @@ generate_duid_from_machine_id (void)
 	 * u16: type (DUID-UUID = 4)
 	 * u8[16]: UUID bytes
 	 */
-	duid_buffer = g_malloc (DUID_SIZE);
-
-	G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2);
-	memcpy (&duid_buffer[0], &duid_type, 2);
+	duid = g_byte_array_sized_new (18);
+	g_byte_array_append (duid, (guint8 *) &duid_type, sizeof (duid_type));
 
 	/* Since SHA256 is 256 bits, but UUID is 128 bits, we just take the first
 	 * 128 bits of the SHA256 as the DUID-UUID.
 	 */
-	memcpy (&duid_buffer[2], buffer, 16);
+	g_byte_array_append (duid, buffer, 16);
 
-	duid = g_bytes_new_take (duid_buffer, DUID_SIZE);
 	nm_log_dbg (LOGD_DHCP, "dhcp: generated DUID %s",
 	            (str = nm_dhcp_utils_duid_to_string (duid)));
 	return duid;
 }
 
-static GBytes *
+static GByteArray *
 get_duid (NMDhcpClient *self)
 {
-	static GBytes *duid = NULL;
+	static GByteArray *duid = NULL;
+	GByteArray *copy = NULL;
 
-	if (G_UNLIKELY (!duid))
+	if (G_UNLIKELY (duid == NULL)) {
 		duid = generate_duid_from_machine_id ();
+		g_assert (duid);
+	}
+
+	if (G_LIKELY (duid)) {
+		copy = g_byte_array_sized_new (duid->len);
+		g_byte_array_append (copy, duid->data, duid->len);
+	}
 
-	return g_bytes_ref (duid);
+	return copy;
 }
 
 gboolean
@@ -580,6 +591,7 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
                           const char *dhcp_anycast_addr,
                           const struct in6_addr *ll_addr,
                           const char *hostname,
+                          gboolean info_only,
                           NMSettingIP6ConfigPrivacy privacy,
                           guint needed_prefixes)
 {
@@ -604,6 +616,8 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 	g_clear_pointer (&priv->hostname, g_free);
 	priv->hostname = g_strdup (hostname);
 
+	priv->info_only = info_only;
+
 	if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
 		_LOGI ("activation: beginning transaction (no timeout)");
 	else
@@ -612,6 +626,7 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 	return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
 	                                                   dhcp_anycast_addr,
 	                                                   ll_addr,
+	                                                   info_only,
 	                                                   privacy,
 	                                                   priv->duid,
 	                                                   needed_prefixes);
@@ -796,8 +811,8 @@ nm_dhcp_client_handle_event (gpointer unused,
 
 	old_state = priv->state;
 	new_state = reason_to_state (self, priv->iface, reason);
-	_LOGD ("DHCP state '%s' -> '%s' (reason: '%s')",
-	       state_to_string (old_state), state_to_string (new_state), reason);
+	_LOGD ("DHCP reason '%s' -> state '%s'",
+	       reason, state_to_string (new_state));
 
 	if (new_state == NM_DHCP_STATE_BOUND) {
 		GVariantIter iter;
@@ -908,16 +923,8 @@ set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
 	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE ((NMDhcpClient *) object);
-	guint flags;
 
 	switch (prop_id) {
-	case PROP_FLAGS:
-		/* construct-only */
-		flags = g_value_get_uint (value);
-		nm_assert ((flags & ~((guint) (NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN))) == 0);
-		priv->info_only = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY);
-		priv->use_fqdn = NM_FLAGS_HAS (flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN);
-		break;
 	case PROP_MULTI_IDX:
 		/* construct-only */
 		priv->multi_idx = g_value_get_pointer (value);
@@ -976,8 +983,6 @@ nm_dhcp_client_init (NMDhcpClient *self)
 	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate);
 	self->_priv = priv;
 
-	c_list_init (&self->dhcp_client_lst);
-
 	priv->pid = -1;
 }
 
@@ -992,8 +997,6 @@ dispose (GObject *object)
 	 * the DHCP client.
 	 */
 
-	nm_assert (c_list_is_empty (&self->dhcp_client_lst));
-
 	watch_cleanup (self);
 	timeout_cleanup (self);
 
@@ -1001,8 +1004,16 @@ dispose (GObject *object)
 	g_clear_pointer (&priv->hostname, g_free);
 	g_clear_pointer (&priv->uuid, g_free);
 	g_clear_pointer (&priv->client_id, g_bytes_unref);
-	g_clear_pointer (&priv->hwaddr, g_bytes_unref);
-	g_clear_pointer (&priv->duid, g_bytes_unref);
+
+	if (priv->hwaddr) {
+		g_byte_array_free (priv->hwaddr, TRUE);
+		priv->hwaddr = NULL;
+	}
+
+	if (priv->duid) {
+		g_byte_array_free (priv->duid, TRUE);
+		priv->duid = NULL;
+	}
 
 	G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object);
 
@@ -1043,7 +1054,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 
 	obj_properties[PROP_HWADDR] =
 	    g_param_spec_boxed (NM_DHCP_CLIENT_HWADDR, "", "",
-	                        G_TYPE_BYTES,
+	                        G_TYPE_BYTE_ARRAY,
 	                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                        G_PARAM_STATIC_STRINGS);
 
@@ -1077,12 +1088,6 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                       G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_FLAGS] =
-	    g_param_spec_uint (NM_DHCP_CLIENT_FLAGS, "", "",
-	                       0, G_MAXUINT32, 0,
-	                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
-	                       G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[SIGNAL_STATE_CHANGED] =
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 0d92d743..2c634168 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -34,16 +34,15 @@
 #define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_CLIENT))
 #define NM_DHCP_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
 
-#define NM_DHCP_CLIENT_ADDR_FAMILY  "addr-family"
-#define NM_DHCP_CLIENT_FLAGS        "flags"
-#define NM_DHCP_CLIENT_HWADDR       "hwaddr"
-#define NM_DHCP_CLIENT_IFINDEX      "ifindex"
-#define NM_DHCP_CLIENT_INTERFACE    "iface"
-#define NM_DHCP_CLIENT_MULTI_IDX    "multi-idx"
-#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric"
+#define NM_DHCP_CLIENT_INTERFACE "iface"
+#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family"
+#define NM_DHCP_CLIENT_IFINDEX   "ifindex"
+#define NM_DHCP_CLIENT_HWADDR    "hwaddr"
+#define NM_DHCP_CLIENT_UUID      "uuid"
 #define NM_DHCP_CLIENT_ROUTE_TABLE  "route-table"
-#define NM_DHCP_CLIENT_TIMEOUT      "timeout"
-#define NM_DHCP_CLIENT_UUID         "uuid"
+#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric"
+#define NM_DHCP_CLIENT_TIMEOUT   "timeout"
+#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx"
 
 #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
 #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated"
@@ -65,14 +64,8 @@ struct _NMDhcpClientPrivate;
 typedef struct {
 	GObject parent;
 	struct _NMDhcpClientPrivate *_priv;
-	CList dhcp_client_lst;
 } NMDhcpClient;
 
-typedef enum {
-	NM_DHCP_CLIENT_FLAGS_INFO_ONLY = (1LL <<  0),
-	NM_DHCP_CLIENT_FLAGS_USE_FQDN  = (1LL <<  1),
-} NMDhcpClientFlags;
-
 typedef struct {
 	GObjectClass parent;
 
@@ -85,13 +78,14 @@ typedef struct {
 	gboolean (*ip6_start)     (NMDhcpClient *self,
 	                           const char *anycast_addr,
 	                           const struct in6_addr *ll_addr,
+	                           gboolean info_only,
 	                           NMSettingIP6ConfigPrivacy privacy,
-	                           GBytes *duid,
+	                           const GByteArray *duid,
 	                           guint needed_prefixes);
 
 	void (*stop)              (NMDhcpClient *self,
 	                           gboolean release,
-	                           GBytes *duid);
+	                           const GByteArray *duid);
 
 	/**
 	 * get_duid:
@@ -102,7 +96,7 @@ typedef struct {
 	 * representation of the DUID.  If no DUID is found, %NULL should be
 	 * returned.
 	 */
-	GBytes *(*get_duid) (NMDhcpClient *self);
+	GByteArray * (*get_duid) (NMDhcpClient *self);
 
 	/* Signals */
 	void (*state_changed) (NMDhcpClient *self,
@@ -125,9 +119,9 @@ int         nm_dhcp_client_get_ifindex (NMDhcpClient *self);
 
 const char *nm_dhcp_client_get_uuid (NMDhcpClient *self);
 
-GBytes *nm_dhcp_client_get_duid (NMDhcpClient *self);
+const GByteArray *nm_dhcp_client_get_duid (NMDhcpClient *self);
 
-GBytes *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
+const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
 
 guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self);
 
@@ -139,20 +133,20 @@ GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
 
 const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
 
-gboolean nm_dhcp_client_get_info_only (NMDhcpClient *self);
-
 gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self);
 
 gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
-                                   GBytes *client_id,
+                                   const char *dhcp_client_id,
                                    const char *dhcp_anycast_addr,
                                    const char *hostname,
+                                   gboolean use_fqdn,
                                    const char *last_ip4_address);
 
 gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
                                    const char *dhcp_anycast_addr,
                                    const struct in6_addr *ll_addr,
                                    const char *hostname,
+                                   gboolean info_only,
                                    NMSettingIP6ConfigPrivacy privacy,
                                    guint needed_prefixes);
 
@@ -185,6 +179,8 @@ void nm_dhcp_client_set_client_id_bin (NMDhcpClient *self,
                                        guint8 type,
                                        const guint8 *client_id,
                                        gsize len);
+void nm_dhcp_client_set_client_id_str (NMDhcpClient *self,
+                                       const char *dhcp_client_id);
 
 /*****************************************************************************
  * Client data
@@ -194,6 +190,13 @@ typedef struct {
 	GType (*get_type)(void);
 	const char *name;
 	const char *(*get_path) (void);
+	GSList *(*get_lease_ip_configs) (struct _NMDedupMultiIndex *multi_idx,
+	                                 int addr_family,
+	                                 const char *iface,
+	                                 int ifindex,
+	                                 const char *uuid,
+	                                 guint32 route_table,
+	                                 guint32 route_metric);
 } NMDhcpClientFactory;
 
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon;
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index 52923310..4df90d76 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -125,7 +125,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 		 * as long as all the characters are printable.
 		 */
 		for (i = 1; (p[0] == 0) && i < l; i++) {
-			if (!g_ascii_isprint (p[i]) || p[i] == '\\' || p[i] == '"')
+			if (!g_ascii_isprint (p[i]))
 				break;
 		}
 
@@ -138,9 +138,8 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 				g_string_append_printf (str, "%02x", (guint8) p[i]);
 			}
 		} else {
-			/* Printable; just add to the line with type 0 */
+			/* Printable; just add to the line minus the 'type' */
 			g_string_append_c (str, '"');
-			g_string_append (str, "\\x00");
 			g_string_append_len (str, p + 1, l - 1);
 			g_string_append_c (str, '"');
 		}
@@ -178,60 +177,31 @@ read_client_id (const char *str)
 {
 	gs_free char *s = NULL;
 	char *p;
-	int i = 0, j = 0;
 
 	nm_assert (!strncmp (str, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG)));
-	str += NM_STRLEN (CLIENTID_TAG);
 
-	if (!g_ascii_isspace (*str))
-		return NULL;
+	str += NM_STRLEN (CLIENTID_TAG);
 	while (g_ascii_isspace (*str))
 		str++;
 
 	if (*str == '"') {
-		/* Parse string literal with escape sequences */
 		s = g_strdup (str + 1);
 		p = strrchr (s, '"');
 		if (p)
 			*p = '\0';
 		else
 			return NULL;
+	} else
+		s = g_strdup (str);
 
-		if (!s[0])
-			return NULL;
-
-		while (s[i]) {
-			if (   s[i] == '\\'
-			    && s[i + 1] == 'x'
-			    && g_ascii_isxdigit (s[i + 2])
-			    && g_ascii_isxdigit (s[i + 3])) {
-				s[j++] =  (g_ascii_xdigit_value (s[i + 2]) << 4)
-				         + g_ascii_xdigit_value (s[i + 3]);
-				i += 4;
-				continue;
-			}
-			if (   s[i] == '\\'
-			    && s[i + 1] >= '0' && s[i + 1] <= '7'
-			    && s[1 + 2] >= '0' && s[i + 2] <= '7'
-			    && s[1 + 3] >= '0' && s[i + 3] <= '7') {
-				s[j++] =    ((s[i + 1] - '0') << 6)
-				          + ((s[i + 2] - '0') << 3)
-				          + ( s[i + 3] - '0');
-				i += 4;
-				continue;
-			}
-			s[j++] = s[i++];
-		}
-		return g_bytes_new_take (g_steal_pointer (&s), j);
-	}
-
-	/* Otherwise, try to read a hexadecimal sequence */
-	s = g_strdup (str);
 	g_strchomp (s);
 	if (s[strlen (s) - 1] == ';')
 		s[strlen (s) - 1] = '\0';
 
-	return nm_utils_hexstr2bin (s);
+	if (!s[0])
+		return NULL;
+
+	return nm_dhcp_utils_client_id_string_to_bytes (s);
 }
 
 GBytes *
@@ -309,7 +279,6 @@ nm_dhcp_dhclient_create_config (const char *interface,
 
 	g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL);
 	g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL);
-	nm_assert (!out_new_client_id || !*out_new_client_id);
 
 	new_contents = g_string_new (_("# Created by NetworkManager\n"));
 	fqdn_opts = g_ptr_array_sized_new (5);
@@ -363,8 +332,6 @@ nm_dhcp_dhclient_create_config (const char *interface,
 					continue;
 
 				/* Otherwise capture and return the existing client id */
-				if (out_new_client_id)
-					g_clear_pointer (out_new_client_id, g_bytes_unref);
 				NM_SET_OUT (out_new_client_id, read_client_id (p));
 			}
 
@@ -477,20 +444,14 @@ nm_dhcp_dhclient_create_config (const char *interface,
 
 /* Roughly follow what dhclient's quotify_buf() and pretty_escape() functions do */
 char *
-nm_dhcp_dhclient_escape_duid (GBytes *duid)
+nm_dhcp_dhclient_escape_duid (const GByteArray *duid)
 {
 	char *escaped;
-	const guint8 *s, *s0;
-	gsize len;
+	const guint8 *s = duid->data;
 	char *d;
 
-	g_return_val_if_fail (duid, NULL);
-
-	s0 = g_bytes_get_data (duid, &len);
-	s = s0;
-
-	d = escaped = g_malloc ((len * 4) + 1);
-	while (s < (s0 + len)) {
+	d = escaped = g_malloc0 ((duid->len * 4) + 1);
+	while (s < (duid->data + duid->len)) {
 		if (!g_ascii_isprint (*s)) {
 			*d++ = '\\';
 			*d++ = '0' + ((*s >> 6) & 0x7);
@@ -504,7 +465,6 @@ nm_dhcp_dhclient_escape_duid (GBytes *duid)
 		} else
 			*d++ = *s++;
 	}
-	*d++ = '\0';
 	return escaped;
 }
 
@@ -516,7 +476,7 @@ isoctal (const guint8 *p)
 	        && p[2] >= '0' && p[2] <= '7');
 }
 
-GBytes *
+GByteArray *
 nm_dhcp_dhclient_unescape_duid (const char *duid)
 {
 	GByteArray *unescaped;
@@ -547,7 +507,7 @@ nm_dhcp_dhclient_unescape_duid (const char *duid)
 			g_byte_array_append (unescaped, &p[i], 1);
 	}
 
-	return g_byte_array_free_to_bytes (unescaped);
+	return unescaped;
 
 error:
 	g_byte_array_free (unescaped, TRUE);
@@ -556,10 +516,10 @@ error:
 
 #define DUID_PREFIX "default-duid \""
 
-GBytes *
+GByteArray *
 nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error)
 {
-	GBytes *duid = NULL;
+	GByteArray *duid = NULL;
 	char *contents;
 	char **line, **split, *p, *e;
 
@@ -643,3 +603,259 @@ nm_dhcp_dhclient_save_duid (const char *leasefile,
 	g_string_free (s, TRUE);
 	return success;
 }
+
+static void
+add_lease_option (GHashTable *hash, char *line)
+{
+	char *spc;
+	size_t len;
+
+	/* Find the space after "option" */
+	spc = strchr (line, ' ');
+	if (!spc)
+		return;
+
+	/* Find the option tag's data, which is after the second space */
+	if (g_str_has_prefix (line, "option ")) {
+		while (g_ascii_isspace (*spc))
+			spc++;
+		spc = strchr (spc + 1, ' ');
+		if (!spc)
+			return;
+	}
+
+	/* Split the line at the space */
+	*spc = '\0';
+	spc++;
+
+	/* Kill the ';' at the end of the line, if any */
+	len = strlen (spc);
+	if (*(spc + len - 1) == ';')
+		*(spc + len - 1) = '\0';
+
+	/* Strip leading quote */
+	while (g_ascii_isspace (*spc))
+		spc++;
+	if (*spc == '"')
+		spc++;
+
+	/* Strip trailing quote */
+	len = strlen (spc);
+	if (len > 0 && spc[len - 1] == '"')
+		spc[len - 1] = '\0';
+
+	if (spc[0])
+		g_hash_table_insert (hash, g_strdup (line), g_strdup (spc));
+}
+
+#define LEASE_INVALID    G_MININT64
+static GTimeSpan
+lease_validity_span (const char *str_expire, GDateTime *now)
+{
+	GDateTime *expire = NULL;
+	struct tm expire_tm;
+	GTimeSpan span;
+
+	g_return_val_if_fail (now != NULL, LEASE_INVALID);
+	g_return_val_if_fail (str_expire != NULL, LEASE_INVALID);
+
+	/* Skip initial number (day of week?) */
+	if (!isdigit (*str_expire++))
+		return LEASE_INVALID;
+	if (!isspace (*str_expire++))
+		return LEASE_INVALID;
+	/* Read lease expiration (in UTC) */
+	if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm))
+		return LEASE_INVALID;
+
+	expire = g_date_time_new_utc (expire_tm.tm_year + 1900,
+	                              expire_tm.tm_mon + 1,
+	                              expire_tm.tm_mday,
+	                              expire_tm.tm_hour,
+	                              expire_tm.tm_min,
+	                              expire_tm.tm_sec);
+	if (!expire)
+		return LEASE_INVALID;
+
+	span = g_date_time_difference (expire, now);
+	g_date_time_unref (expire);
+
+	/* GDateTime only supports a range of less then 10000 years, so span can
+	 * not overflow or be equal to LEASE_INVALID */
+	return span;
+}
+
+/**
+ * nm_dhcp_dhclient_read_lease_ip_configs:
+ * @multi_idx: the multi index instance for the ip config object
+ * @addr_family: whether to read IPv4 or IPv6 leases
+ * @iface: the interface name to match leases with
+ * @ifindex: interface index of @iface
+ * @route_table: the route table for the default route.
+ * @route_metric: the route metric for the default route.
+ * @contents: the contents of a dhclient leasefile
+ * @now: the current UTC date/time; pass %NULL to automatically use current
+ *  UTC time.  Testcases may need a different value for 'now'
+ *
+ * Reads dhclient leases from @contents and parses them into either
+ * #NMIP4Config or #NMIP6Config objects depending on the value of @addr_family.
+ *
+ * Returns: a #GSList of #NMIP4Config objects (if @addr_family is %AF_INET) or a list of
+ * #NMIP6Config objects (if @addr_family is %AF_INET6) containing the lease data.
+ */
+GSList *
+nm_dhcp_dhclient_read_lease_ip_configs (NMDedupMultiIndex *multi_idx,
+                                        int addr_family,
+                                        const char *iface,
+                                        int ifindex,
+                                        guint32 route_table,
+                                        guint32 route_metric,
+                                        const char *contents,
+                                        GDateTime *now)
+{
+	GSList *parsed = NULL, *iter, *leases = NULL;
+	char **line, **split = NULL;
+	GHashTable *hash = NULL;
+	gint32 now_monotonic_ts;
+
+	g_return_val_if_fail (contents != NULL, NULL);
+	nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
+
+	split = g_strsplit_set (contents, "\n\r", -1);
+	if (!split)
+		return NULL;
+
+	for (line = split; line && *line; line++) {
+		*line = g_strstrip (*line);
+
+		if (*line[0] == '#') {
+			/* Comment */
+		} else if (!strcmp (*line, "}")) {
+			/* Lease ends */
+			parsed = g_slist_append (parsed, hash);
+			hash = NULL;
+		} else if (!strcmp (*line, "lease {")) {
+			/* Beginning of a new lease */
+			if (hash) {
+				/* Ignore malformed lease that doesn't end before new one starts */
+				g_hash_table_destroy (hash);
+			}
+
+			hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+		} else if (hash && strlen (*line))
+			add_lease_option (hash, *line);
+	}
+	g_strfreev (split);
+
+	/* Check if the last lease in the file was properly ended */
+	if (hash) {
+		/* Ignore malformed lease that doesn't end before new one starts */
+		g_hash_table_destroy (hash);
+		hash = NULL;
+	}
+
+	if (now)
+		g_date_time_ref (now);
+	else
+		now = g_date_time_new_now_utc ();
+	now_monotonic_ts = nm_utils_get_monotonic_timestamp_s ();
+
+	for (iter = parsed; iter; iter = g_slist_next (iter)) {
+		NMIP4Config *ip4;
+		NMPlatformIP4Address address;
+		const char *value;
+		GTimeSpan expiry;
+		guint32 tmp, gw = 0;
+
+		hash = iter->data;
+
+		/* Make sure this lease is for the interface we want */
+		value = g_hash_table_lookup (hash, "interface");
+		if (!value || strcmp (value, iface))
+			continue;
+
+		value = g_hash_table_lookup (hash, "expire");
+		if (!value)
+			continue;
+		expiry = lease_validity_span (value, now);
+		if (expiry == LEASE_INVALID)
+			continue;
+
+		/* scale expiry to seconds (and CLAMP into the range of guint32) */
+		expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, NM_PLATFORM_LIFETIME_PERMANENT-1);
+		if (expiry <= 0) {
+			/* the address is already expired. Don't even add it. */
+			continue;
+		}
+
+		memset (&address, 0, sizeof (address));
+
+		/* IP4 address */
+		value = g_hash_table_lookup (hash, "fixed-address");
+		if (!value)
+			continue;
+		if (!inet_pton (AF_INET, value, &address.address))
+			continue;
+		address.peer_address = address.address;
+
+		/* Gateway */
+		value = g_hash_table_lookup (hash, "option routers");
+		if (!value)
+			continue;
+		if (!inet_pton (AF_INET, value, &gw))
+			continue;
+
+		/* Netmask */
+		value = g_hash_table_lookup (hash, "option subnet-mask");
+		if (value && inet_pton (AF_INET, value, &tmp))
+			address.plen = nm_utils_ip4_netmask_to_prefix (tmp);
+
+		/* Get default netmask for the IP according to appropriate class. */
+		if (!address.plen)
+			address.plen = _nm_utils_ip4_get_default_prefix (address.address);
+
+		address.timestamp = now_monotonic_ts;
+		address.lifetime = address.preferred = expiry;
+		address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
+
+		ip4 = nm_ip4_config_new (multi_idx, ifindex);
+		nm_ip4_config_add_address (ip4, &address);
+
+		{
+			const NMPlatformIP4Route r = {
+				.rt_source = NM_IP_CONFIG_SOURCE_DHCP,
+				.gateway = gw,
+				.table_coerced = nm_platform_route_table_coerce (route_table),
+				.metric = route_metric,
+			};
+
+			nm_ip4_config_add_route (ip4, &r, NULL);
+		}
+
+		value = g_hash_table_lookup (hash, "option domain-name-servers");
+		if (value) {
+			char **dns, **dns_iter;
+
+			dns = g_strsplit_set (value, ",", -1);
+			for (dns_iter = dns; dns_iter && *dns_iter; dns_iter++) {
+				if (inet_pton (AF_INET, *dns_iter, &tmp))
+					nm_ip4_config_add_nameserver (ip4, tmp);
+			}
+			if (dns)
+				g_strfreev (dns);
+		}
+
+		value = g_hash_table_lookup (hash, "option domain-name");
+		if (value && value[0])
+			nm_ip4_config_add_domain (ip4, value);
+
+		/* FIXME: static routes */
+
+		leases = g_slist_append (leases, ip4);
+	}
+
+	g_date_time_unref (now);
+	g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy);
+	return leases;
+}
+
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index fab9196a..94de1963 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -33,16 +33,25 @@ char *nm_dhcp_dhclient_create_config (const char *interface,
                                       const char *orig_contents,
                                       GBytes **out_new_client_id);
 
-char *nm_dhcp_dhclient_escape_duid (GBytes *duid);
+char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid);
 
-GBytes *nm_dhcp_dhclient_unescape_duid (const char *duid);
+GByteArray *nm_dhcp_dhclient_unescape_duid (const char *duid);
 
-GBytes *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error);
+GByteArray *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error);
 
 gboolean nm_dhcp_dhclient_save_duid (const char *leasefile,
                                      const char *escaped_duid,
                                      GError **error);
 
+GSList *nm_dhcp_dhclient_read_lease_ip_configs (struct _NMDedupMultiIndex *multi_idx,
+                                                int addr_family,
+                                                const char *iface,
+                                                int ifindex,
+                                                guint32 route_table,
+                                                guint32 route_metric,
+                                                const char *contents,
+                                                GDateTime *now);
+
 GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path);
 
 #endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 738e9f91..74d920a8 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -158,6 +158,32 @@ get_dhclient_leasefile (int addr_family,
 	return NULL;
 }
 
+static GSList *
+nm_dhcp_dhclient_get_lease_ip_configs (NMDedupMultiIndex *multi_idx,
+                                       int addr_family,
+                                       const char *iface,
+                                       int ifindex,
+                                       const char *uuid,
+                                       guint32 route_table,
+                                       guint32 route_metric)
+{
+	gs_free char *contents = NULL;
+	gs_free char *leasefile = NULL;
+
+	leasefile = get_dhclient_leasefile (addr_family, iface, uuid, NULL);
+	if (!leasefile)
+		return NULL;
+
+	if (   g_file_test (leasefile, G_FILE_TEST_EXISTS)
+	    && g_file_get_contents (leasefile, &contents, NULL, NULL)
+	    && contents
+	    && contents[0]) {
+		return nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, addr_family, iface, ifindex,
+		                                               route_table, route_metric, contents, NULL);
+	}
+	return NULL;
+}
+
 static gboolean
 merge_dhclient_config (NMDhcpDhclient *self,
                        int addr_family,
@@ -312,7 +338,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 static gboolean
 dhclient_start (NMDhcpClient *client,
                 const char *mode_opt,
-                GBytes *duid,
+                const GByteArray *duid,
                 gboolean release,
                 pid_t *out_pid,
                 int prefixes)
@@ -413,19 +439,19 @@ dhclient_start (NMDhcpClient *client,
 		while (prefixes--)
 			g_ptr_array_add (argv, (gpointer) "-P");
 	}
-	g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */
+	g_ptr_array_add (argv, (gpointer) "-sf");	/* Set script file */
 	g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path);
 
 	if (pid_file) {
-		g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */
+		g_ptr_array_add (argv, (gpointer) "-pf");	/* Set pid file */
 		g_ptr_array_add (argv, (gpointer) pid_file);
 	}
 
-	g_ptr_array_add (argv, (gpointer) "-lf"); /* Set lease file */
+	g_ptr_array_add (argv, (gpointer) "-lf");	/* Set lease file */
 	g_ptr_array_add (argv, (gpointer) priv->lease_file);
 
 	if (priv->conf_file) {
-		g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */
+		g_ptr_array_add (argv, (gpointer) "-cf");	/* Set interface config file */
 		g_ptr_array_add (argv, (gpointer) priv->conf_file);
 	}
 
@@ -492,10 +518,8 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	priv->conf_file = create_dhclient_config (self, AF_INET, iface, uuid, client_id, dhcp_anycast_addr,
 	                                          hostname, timeout, use_fqdn, &new_client_id);
 	if (priv->conf_file) {
-		if (new_client_id) {
-			nm_assert (!client_id);
+		if (new_client_id)
 			nm_dhcp_client_set_client_id (client, new_client_id);
-		}
 		success = dhclient_start (client, NULL, NULL, FALSE, NULL, 0);
 	} else
 		_LOGW ("error creating dhclient configuration file");
@@ -507,8 +531,9 @@ static gboolean
 ip6_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
            const struct in6_addr *ll_addr,
+           gboolean info_only,
            NMSettingIP6ConfigPrivacy privacy,
-           GBytes *duid,
+           const GByteArray *duid,
            guint needed_prefixes)
 {
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
@@ -528,19 +553,16 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	return dhclient_start (client,
-	                       nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self))
-	                         ? "-S"
-	                         : "-N",
-	                       duid, FALSE, NULL, needed_prefixes);
+	return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL, needed_prefixes);
 }
 
 static void
-stop (NMDhcpClient *client, gboolean release, GBytes *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 
+	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid);
 
 	if (priv->conf_file)
@@ -581,12 +603,12 @@ state_changed (NMDhcpClient *client,
 	nm_dhcp_client_set_client_id (client, client_id);
 }
 
-static GBytes *
+static GByteArray *
 get_duid (NMDhcpClient *client)
 {
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
-	GBytes *duid = NULL;
+	GByteArray *duid = NULL;
 	char *leasefile;
 	GError *error = NULL;
 
@@ -620,7 +642,7 @@ get_duid (NMDhcpClient *client)
 	}
 
 	/* return our DUID, otherwise let the parent class make a default DUID */
-	return duid ?: NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client);
+	return duid ? duid : NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client);
 }
 
 /*****************************************************************************/
@@ -695,6 +717,7 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = {
 	.name = "dhclient",
 	.get_type = nm_dhcp_dhclient_get_type,
 	.get_path = nm_dhcp_dhclient_get_path,
+	.get_lease_ip_configs = nm_dhcp_dhclient_get_lease_ip_configs,
 };
 
 #endif /* WITH_DHCLIENT */
diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c
index 82b3db4f..d7ddd194 100644
--- a/src/dhcp/nm-dhcp-dhcpcanon.c
+++ b/src/dhcp/nm-dhcp-dhcpcanon.c
@@ -80,7 +80,7 @@ nm_dhcp_dhcpcanon_get_path (void)
 static gboolean
 dhcpcanon_start (NMDhcpClient *client,
                 const char *mode_opt,
-                GBytes *duid,
+                const GByteArray *duid,
                 gboolean release,
                 pid_t *out_pid,
                 int prefixes)
@@ -118,16 +118,16 @@ dhcpcanon_start (NMDhcpClient *client,
 	argv = g_ptr_array_new ();
 	g_ptr_array_add (argv, (gpointer) dhcpcanon_path);
 
-	g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */
+	g_ptr_array_add (argv, (gpointer) "-sf");	/* Set script file */
 	g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path);
 
 	if (pid_file) {
-		g_ptr_array_add (argv, (gpointer) "-pf"); /* Set pid file */
+		g_ptr_array_add (argv, (gpointer) "-pf");	/* Set pid file */
 		g_ptr_array_add (argv, (gpointer) pid_file);
 	}
 
 	if (priv->conf_file) {
-		g_ptr_array_add (argv, (gpointer) "-cf"); /* Set interface config file */
+		g_ptr_array_add (argv, (gpointer) "-cf");	/* Set interface config file */
 		g_ptr_array_add (argv, (gpointer) priv->conf_file);
 	}
 
@@ -179,8 +179,9 @@ static gboolean
 ip6_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
            const struct in6_addr *ll_addr,
+           gboolean info_only,
            NMSettingIP6ConfigPrivacy privacy,
-           GBytes *duid,
+           const GByteArray *duid,
            guint needed_prefixes)
 {
 	NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client);
@@ -189,7 +190,7 @@ ip6_start (NMDhcpClient *client,
 	return FALSE;
 }
 static void
-stop (NMDhcpClient *client, gboolean release, GBytes *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
 	NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client);
 	NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self);
@@ -265,6 +266,7 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = {
 	.name = "dhcpcanon",
 	.get_type = nm_dhcp_dhcpcanon_get_type,
 	.get_path = nm_dhcp_dhcpcanon_get_path,
+	.get_lease_ip_configs = NULL,
 };
 
 #endif /* WITH_DHCPCANON */
diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c
index c4bcb084..66a31acf 100644
--- a/src/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/dhcp/nm-dhcp-dhcpcd.c
@@ -114,18 +114,18 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	argv = g_ptr_array_new ();
 	g_ptr_array_add (argv, (gpointer) dhcpcd_path);
 
-	g_ptr_array_add (argv, (gpointer) "-B");    /* Don't background on lease (disable fork()) */
+	g_ptr_array_add (argv, (gpointer) "-B");	/* Don't background on lease (disable fork()) */
 
-	g_ptr_array_add (argv, (gpointer) "-K");    /* Disable built-in carrier detection */
+	g_ptr_array_add (argv, (gpointer) "-K");	/* Disable built-in carrier detection */
 
-	g_ptr_array_add (argv, (gpointer) "-L");    /* Disable built-in IPv4LL */
+	g_ptr_array_add (argv, (gpointer) "-L");	/* Disable built-in IPv4LL */
 
 	/* --noarp. Don't request or claim the address by ARP; this also disables IPv4LL. */
 	g_ptr_array_add (argv, (gpointer) "-A");
 
-	g_ptr_array_add (argv, (gpointer) "-G");    /* Let NM handle routing */
+	g_ptr_array_add (argv, (gpointer) "-G");	/* Let NM handle routing */
 
-	g_ptr_array_add (argv, (gpointer) "-c");    /* Set script file */
+	g_ptr_array_add (argv, (gpointer) "-c");	/* Set script file */
 	g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path);
 
 #ifdef DHCPCD_SUPPORTS_IPV6
@@ -177,8 +177,9 @@ static gboolean
 ip6_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
            const struct in6_addr *ll_addr,
+           gboolean info_only,
            NMSettingIP6ConfigPrivacy privacy,
-           GBytes *duid,
+           const GByteArray *duid,
            guint needed_prefixes)
 {
 	NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
@@ -188,11 +189,12 @@ ip6_start (NMDhcpClient *client,
 }
 
 static void
-stop (NMDhcpClient *client, gboolean release, GBytes *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
 	NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
 	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self);
 
+	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid);
 
 	if (priv->pid_file) {
@@ -251,6 +253,7 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd = {
 	.name = "dhcpcd",
 	.get_type = nm_dhcp_dhcpcd_get_type,
 	.get_path = nm_dhcp_dhcpcd_get_path,
+	.get_lease_ip_configs = NULL,
 };
 
 #endif /* WITH_DHCPCD */
diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c
index 8ea55061..f50c5cec 100644
--- a/src/dhcp/nm-dhcp-helper.c
+++ b/src/dhcp/nm-dhcp-helper.c
@@ -134,6 +134,8 @@ main (int argc, char *argv[])
 	guint try_count = 0;
 	gint64 time_end;
 
+	nm_g_type_init ();
+
 	/* FIXME: g_dbus_connection_new_for_address_sync() tries to connect to the socket in
 	 * non-blocking mode, which can easily fail with EAGAIN, causing the creation of the
 	 * socket to fail with "Could not connect: Resource temporarily unavailable".
diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c
index d7d38e54..1cce5a1c 100644
--- a/src/dhcp/nm-dhcp-listener.c
+++ b/src/dhcp/nm-dhcp-listener.c
@@ -33,7 +33,7 @@
 #include "nm-dhcp-client.h"
 #include "nm-dhcp-manager.h"
 #include "nm-core-internal.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "NetworkManagerUtils.h"
 
 #define PRIV_SOCK_PATH            NMRUNDIR "/private-dhcp"
@@ -60,7 +60,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = {
 /*****************************************************************************/
 
 typedef struct {
-	NMDBusManager *      dbus_mgr;
+	NMBusManager *      dbus_mgr;
 	gulong              new_conn_id;
 	gulong              dis_conn_id;
 	GHashTable *        connections;
@@ -192,52 +192,70 @@ _method_call (GDBusConnection *connection,
 {
 	NMDhcpListener *self = NM_DHCP_LISTENER (user_data);
 
-	if (   !nm_streq (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME)
-	    || !nm_streq (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY)) {
-		g_dbus_method_invocation_return_error (invocation,
-		                                       G_DBUS_ERROR,
-		                                       G_DBUS_ERROR_UNKNOWN_METHOD,
-		                                       "Unknown method %s",
-		                                       method_name);
-		return;
-	}
+	if (!nm_streq0 (interface_name, NM_DHCP_HELPER_SERVER_INTERFACE_NAME))
+		g_return_if_reached ();
+	if (!nm_streq0 (method_name, NM_DHCP_HELPER_SERVER_METHOD_NOTIFY))
+		g_return_if_reached ();
+	if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})")))
+		g_return_if_reached ();
 
 	_method_call_handle (self, parameters);
+
 	g_dbus_method_invocation_return_value (invocation, NULL);
 }
 
-static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
-	NM_DHCP_HELPER_SERVER_INTERFACE_NAME,
-	.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-		NM_DEFINE_GDBUS_METHOD_INFO (
-			NM_DHCP_HELPER_SERVER_METHOD_NOTIFY,
-			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-				NM_DEFINE_GDBUS_ARG_INFO ("data", "a{sv}"),
-			),
-		),
-	),
-);
-
 static guint
 _dbus_connection_register_object (NMDhcpListener *self,
                                   GDBusConnection *connection,
                                   GError **error)
 {
-	static const GDBusInterfaceVTable interface_vtable = {
+	static GDBusArgInfo arg_info_notify_in = {
+		.ref_count = -1,
+		.name = "data",
+		.signature = "a{sv}",
+		.annotations = NULL,
+	};
+	static GDBusArgInfo *arg_infos_notify[] = {
+		&arg_info_notify_in,
+		NULL,
+	};
+	static GDBusMethodInfo method_info_notify = {
+		.ref_count = -1,
+		.name = NM_DHCP_HELPER_SERVER_METHOD_NOTIFY,
+		.in_args = arg_infos_notify,
+		.out_args = NULL,
+		.annotations = NULL,
+	};
+	static GDBusMethodInfo *method_infos[] = {
+		&method_info_notify,
+		NULL,
+	};
+	static GDBusInterfaceInfo interface_info = {
+		.ref_count = -1,
+		.name = NM_DHCP_HELPER_SERVER_INTERFACE_NAME,
+		.methods = method_infos,
+		.signals = NULL,
+		.properties = NULL,
+		.annotations = NULL,
+	};
+
+	static GDBusInterfaceVTable interface_vtable = {
 		.method_call = _method_call,
+		.get_property = NULL,
+		.set_property = NULL,
 	};
 
 	return g_dbus_connection_register_object (connection,
 	                                          NM_DHCP_HELPER_SERVER_OBJECT_PATH,
-	                                          interface_info,
-	                                          NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable),
+	                                          &interface_info,
+	                                          &interface_vtable,
 	                                          self,
 	                                          NULL,
 	                                          error);
 }
 
 static void
-new_connection_cb (NMDBusManager *mgr,
+new_connection_cb (NMBusManager *mgr,
                    GDBusConnection *connection,
                    GDBusObjectManager *manager,
                    NMDhcpListener *self)
@@ -260,7 +278,7 @@ new_connection_cb (NMDBusManager *mgr,
 }
 
 static void
-dis_connection_cb (NMDBusManager *mgr,
+dis_connection_cb (NMBusManager *mgr,
                    GDBusConnection *connection,
                    NMDhcpListener *self)
 {
@@ -282,18 +300,18 @@ nm_dhcp_listener_init (NMDhcpListener *self)
 	NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
 
 	/* Maps GDBusConnection :: signal-id */
-	priv->connections = g_hash_table_new (nm_direct_hash, NULL);
+	priv->connections = g_hash_table_new (NULL, NULL);
 
-	priv->dbus_mgr = nm_dbus_manager_get ();
+	priv->dbus_mgr = nm_bus_manager_get ();
 
 	/* Register the socket our DHCP clients will return lease info on */
-	nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
+	nm_bus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
 	priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
-	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
+	                                      NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
 	                                      G_CALLBACK (new_connection_cb),
 	                                      self);
 	priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
-	                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
+	                                      NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
 	                                      G_CALLBACK (dis_connection_cb),
 	                                      self);
 }
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index bf22872d..f5c7c84b 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -43,8 +43,8 @@
 
 typedef struct {
 	const NMDhcpClientFactory *client_factory;
-	char *default_hostname;
-	CList dhcp_client_lst_head;
+	GHashTable *        clients;
+	char *              default_hostname;
 } NMDhcpManagerPrivate;
 
 struct _NMDhcpManager {
@@ -98,17 +98,21 @@ static NMDhcpClient *
 get_client_for_ifindex (NMDhcpManager *manager, int addr_family, int ifindex)
 {
 	NMDhcpManagerPrivate *priv;
-	NMDhcpClient *client;
+	GHashTableIter iter;
+	gpointer value;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
 	g_return_val_if_fail (ifindex > 0, NULL);
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
 
-	c_list_for_each_entry (client, &priv->dhcp_client_lst_head, dhcp_client_lst) {
-		if (   nm_dhcp_client_get_ifindex (client) == ifindex
-		    && nm_dhcp_client_get_addr_family (client) == addr_family)
-			return client;
+	g_hash_table_iter_init (&iter, priv->clients);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
+
+		if (   nm_dhcp_client_get_ifindex (candidate) == ifindex
+		    && nm_dhcp_client_get_addr_family (candidate) == addr_family)
+			return candidate;
 	}
 
 	return NULL;
@@ -125,19 +129,13 @@ static void
 remove_client (NMDhcpManager *self, NMDhcpClient *client)
 {
 	g_signal_handlers_disconnect_by_func (client, client_state_changed, self);
-	c_list_unlink (&client->dhcp_client_lst);
 
 	/* Stopping the client is left up to the controlling device
 	 * explicitly since we may want to quit NetworkManager but not terminate
 	 * the DHCP client.
 	 */
-}
 
-static void
-remove_client_unref (NMDhcpManager *self, NMDhcpClient *client)
-{
-	remove_client (self, client);
-	g_object_unref (client);
+	g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client);
 }
 
 static void
@@ -149,7 +147,7 @@ client_state_changed (NMDhcpClient *client,
                       NMDhcpManager *self)
 {
 	if (state >= NM_DHCP_STATE_TIMEOUT)
-		remove_client_unref (self, client);
+		remove_client (self, client);
 }
 
 static NMDhcpClient *
@@ -158,12 +156,12 @@ client_start (NMDhcpManager *self,
               NMDedupMultiIndex *multi_idx,
               const char *iface,
               int ifindex,
-              GBytes *hwaddr,
+              const GByteArray *hwaddr,
               const char *uuid,
               guint32 route_table,
               guint32 route_metric,
               const struct in6_addr *ipv6_ll_addr,
-              GBytes *dhcp_client_id,
+              const char *dhcp_client_id,
               guint32 timeout,
               const char *dhcp_anycast_addr,
               const char *hostname,
@@ -181,21 +179,23 @@ client_start (NMDhcpManager *self,
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 	g_return_val_if_fail (ifindex > 0, NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
-	g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL);
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
+	/* Ensure we have a usable DHCP client */
 	if (!priv->client_factory)
 		return NULL;
 
 	/* Kill any old client instance */
 	client = get_client_for_ifindex (self, addr_family, ifindex);
 	if (client) {
+		g_object_ref (client);
 		remove_client (self, client);
 		nm_dhcp_client_stop (client, FALSE);
 		g_object_unref (client);
 	}
 
+	/* And make a new one */
 	client = g_object_new (priv->client_factory->get_type (),
 	                       NM_DHCP_CLIENT_MULTI_IDX, multi_idx,
 	                       NM_DHCP_CLIENT_ADDR_FAMILY, addr_family,
@@ -206,26 +206,21 @@ client_start (NMDhcpManager *self,
 	                       NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table,
 	                       NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric,
 	                       NM_DHCP_CLIENT_TIMEOUT, (guint) timeout,
-	                       NM_DHCP_CLIENT_FLAGS, (guint) (0
-	                           | (hostname_use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN  : 0)
-	                           | (info_only         ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY : 0)
-	                       ),
 	                       NULL);
-	nm_assert (client && c_list_is_empty (&client->dhcp_client_lst));
-	c_list_link_tail (&priv->dhcp_client_lst_head, &client->dhcp_client_lst);
+	g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client));
 	g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
 
 	if (addr_family == AF_INET)
-		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address);
+		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, hostname_use_fqdn, last_ip4_address);
 	else
-		success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, privacy, needed_prefixes);
+		success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes);
 
 	if (!success) {
-		remove_client_unref (self, client);
-		return NULL;
+		remove_client (self, client);
+		client = NULL;
 	}
 
-	return g_object_ref (client);
+	return client;
 }
 
 /* Caller owns a reference to the NMDhcpClient on return */
@@ -234,14 +229,14 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            NMDedupMultiIndex *multi_idx,
                            const char *iface,
                            int ifindex,
-                           GBytes *hwaddr,
+                           const GByteArray *hwaddr,
                            const char *uuid,
                            guint32 route_table,
                            guint32 route_metric,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
                            const char *dhcp_fqdn,
-                           GBytes *dhcp_client_id,
+                           const char *dhcp_client_id,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
                            const char *last_ip_address)
@@ -290,7 +285,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            NMDedupMultiIndex *multi_idx,
                            const char *iface,
                            int ifindex,
-                           GBytes *hwaddr,
+                           const GByteArray *hwaddr,
                            const struct in6_addr *ll_addr,
                            const char *uuid,
                            guint32 route_table,
@@ -333,6 +328,31 @@ nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostna
 	priv->default_hostname = g_strdup (hostname);
 }
 
+GSList *
+nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
+                                      NMDedupMultiIndex *multi_idx,
+                                      int addr_family,
+                                      const char *iface,
+                                      int ifindex,
+                                      const char *uuid,
+                                      guint32 route_table,
+                                      guint32 route_metric)
+{
+	NMDhcpManagerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+	g_return_val_if_fail (iface != NULL, NULL);
+	g_return_val_if_fail (ifindex >= -1, NULL);
+	g_return_val_if_fail (uuid != NULL, NULL);
+	g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NULL);
+
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	if (   priv->client_factory
+	    && priv->client_factory->get_lease_ip_configs)
+		return priv->client_factory->get_lease_ip_configs (multi_idx, addr_family, iface, ifindex, uuid, route_table, route_metric);
+	return NULL;
+}
+
 const char *
 nm_dhcp_manager_get_config (NMDhcpManager *self)
 {
@@ -358,8 +378,6 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	int i;
 	const NMDhcpClientFactory *client_factory = NULL;
 
-	c_list_init (&priv->dhcp_client_lst_head);
-
 	for (i = 0; i < G_N_ELEMENTS (_nm_dhcp_manager_factories); i++) {
 		const NMDhcpClientFactory *f = _nm_dhcp_manager_factories[i];
 
@@ -411,21 +429,38 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name);
 
 	priv->client_factory = client_factory;
+	priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                       NULL,
+	                                       (GDestroyNotify) g_object_unref);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDhcpManager *self = NM_DHCP_MANAGER (object);
-	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-	NMDhcpClient *client, *client_safe;
-
-	c_list_for_each_entry_safe (client, client_safe, &priv->dhcp_client_lst_head, dhcp_client_lst)
-		remove_client_unref (self, client);
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object);
+	GList *values, *iter;
+
+	if (priv->clients) {
+		values = g_hash_table_get_values (priv->clients);
+		for (iter = values; iter; iter = g_list_next (iter))
+			remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data));
+		g_list_free (values);
+	}
 
 	G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE ((NMDhcpManager *) object);
+
+	g_free (priv->default_hostname);
+
+	if (priv->clients)
+		g_hash_table_destroy (priv->clients);
 
-	nm_clear_g_free (&priv->default_hostname);
+	G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object);
 }
 
 static void
@@ -433,5 +468,6 @@ nm_dhcp_manager_class_init (NMDhcpManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
 
+	object_class->finalize = finalize;
 	object_class->dispose = dispose;
 }
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index f8a7e31d..078117ff 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -49,14 +49,14 @@ NMDhcpClient * nm_dhcp_manager_start_ip4     (NMDhcpManager *manager,
                                               struct _NMDedupMultiIndex *multi_idx,
                                               const char *iface,
                                               int ifindex,
-                                              GBytes *hwaddr,
+                                              const GByteArray *hwaddr,
                                               const char *uuid,
                                               guint32 route_table,
                                               guint32 route_metric,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
                                               const char *dhcp_fqdn,
-                                              GBytes *dhcp_client_id,
+                                              const char *dhcp_client_id,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
                                               const char *last_ip_address);
@@ -65,7 +65,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               struct _NMDedupMultiIndex *multi_idx,
                                               const char *iface,
                                               int ifindex,
-                                              GBytes *hwaddr,
+                                              const GByteArray *hwaddr,
                                               const struct in6_addr *ll_addr,
                                               const char *uuid,
                                               guint32 route_table,
@@ -78,6 +78,15 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               NMSettingIP6ConfigPrivacy privacy,
                                               guint needed_prefixes);
 
+GSList *       nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
+                                                     struct _NMDedupMultiIndex *multi_idx,
+                                                     int addr_family,
+                                                     const char *iface,
+                                                     int ifindex,
+                                                     const char *uuid,
+                                                     guint32 route_table,
+                                                     guint32 route_metric);
+
 /* For testing only */
 extern const char* nm_dhcp_helper_path;
 
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 4f37f069..f79b7cb1 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -29,7 +29,6 @@
 #include <net/if_arp.h>
 
 #include "nm-utils/nm-dedup-multi.h"
-#include "nm-utils/unaligned.h"
 
 #include "nm-utils.h"
 #include "nm-dhcp-utils.h"
@@ -61,7 +60,8 @@ typedef struct {
 
 	guint request_count;
 
-	bool privacy:1;
+	gboolean privacy;
+	gboolean info_only;
 } NMDhcpSystemdPrivate;
 
 struct _NMDhcpSystemd {
@@ -451,6 +451,36 @@ get_leasefile_path (int addr_family, const char *iface, const char *uuid)
 	                        iface);
 }
 
+static GSList *
+nm_dhcp_systemd_get_lease_ip_configs (NMDedupMultiIndex *multi_idx,
+                                      int addr_family,
+                                      const char *iface,
+                                      int ifindex,
+                                      const char *uuid,
+                                      guint32 route_table,
+                                      guint32 route_metric)
+{
+	GSList *leases = NULL;
+	gs_free char *path = NULL;
+	sd_dhcp_lease *lease = NULL;
+	NMIP4Config *ip4_config;
+	int r;
+
+	if (addr_family != AF_INET)
+		return NULL;
+
+	path = get_leasefile_path (addr_family, iface, uuid);
+	r = dhcp_lease_load (&lease, path);
+	if (r == 0 && lease) {
+		ip4_config = lease_to_ip4_config (multi_idx, iface, ifindex, lease, NULL, route_table, route_metric, FALSE, NULL);
+		if (ip4_config)
+			leases = g_slist_append (leases, ip4_config);
+		sd_dhcp_lease_unref (lease);
+	}
+
+	return leases;
+}
+
 /*****************************************************************************/
 
 static void
@@ -554,16 +584,14 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
 }
 
 static guint16
-get_arp_type (GBytes *hwaddr)
+get_arp_type (const GByteArray *hwaddr)
 {
-	switch (g_bytes_get_size (hwaddr)) {
-	case ETH_ALEN:
+	if (hwaddr->len == ETH_ALEN)
 		return ARPHRD_ETHER;
-	case INFINIBAND_ALEN:
+	else if (hwaddr->len == INFINIBAND_ALEN)
 		return ARPHRD_INFINIBAND;
-	default:
+	else
 		return ARPHRD_NONE;
-	}
 }
 
 static gboolean
@@ -572,7 +600,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (client);
-	GBytes *hwaddr;
+	const GByteArray *hwaddr;
 	sd_dhcp_lease *lease = NULL;
 	GBytes *override_client_id;
 	const uint8_t *client_id = NULL;
@@ -581,6 +609,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	const char *hostname;
 	int r, i;
 	gboolean success = FALSE;
+	guint16 arp_type;
 
 	g_assert (priv->client4 == NULL);
 	g_assert (priv->client6 == NULL);
@@ -604,14 +633,16 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 
 	hwaddr = nm_dhcp_client_get_hw_addr (client);
 	if (hwaddr) {
-		const uint8_t *data;
-		gsize len;
+		arp_type= get_arp_type (hwaddr);
+		if (arp_type == ARPHRD_NONE) {
+			_LOGW ("failed to determine ARP type");
+			goto error;
+		}
 
-		data = g_bytes_get_data (hwaddr, &len);
 		r = sd_dhcp_client_set_mac (priv->client4,
-		                            data,
-		                            len,
-		                            get_arp_type (hwaddr));
+		                            hwaddr->data,
+		                            hwaddr->len,
+		                            arp_type);
 		if (r < 0) {
 			_LOGW ("failed to set MAC address (%d)", r);
 			goto error;
@@ -630,6 +661,12 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 		goto error;
 	}
 
+	r = sd_dhcp_client_set_request_broadcast (priv->client4, true);
+	if (r < 0) {
+		_LOGW ("failed to enable broadcast mode (%d)", r);
+		goto error;
+	}
+
 	dhcp_lease_load (&lease, priv->lease_file);
 
 	if (last_ip4_address)
@@ -817,7 +854,7 @@ bound6_handle (NMDhcpSystemd *self)
 	                                  lease,
 	                                  options,
 	                                  TRUE,
-	                                  nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)),
+	                                  priv->info_only,
 	                                  &error);
 
 	if (ip6_config) {
@@ -863,29 +900,24 @@ static gboolean
 ip6_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
            const struct in6_addr *ll_addr,
+           gboolean info_only,
            NMSettingIP6ConfigPrivacy privacy,
-           GBytes *duid,
+           const GByteArray *duid,
            guint needed_prefixes)
 {
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (client);
-	GBytes *hwaddr;
-	const char *hostname;
+	const GByteArray *hwaddr;
 	int r, i;
-	const guint8 *duid_arr;
-	gsize duid_len;
 
 	g_assert (priv->client4 == NULL);
 	g_assert (priv->client6 == NULL);
 	g_return_val_if_fail (duid != NULL, FALSE);
 
-	duid_arr = g_bytes_get_data (duid, &duid_len);
-	if (!duid_arr || duid_len < 2)
-		g_return_val_if_reached (FALSE);
-
 	g_free (priv->lease_file);
 	priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client));
+	priv->info_only = info_only;
 
 	r = sd_dhcp6_client_new (&priv->client6);
 	if (r < 0) {
@@ -900,13 +932,16 @@ ip6_start (NMDhcpClient *client,
 
 	_LOGT ("dhcp-client6: set %p", priv->client6);
 
-	if (nm_dhcp_client_get_info_only (client))
-		sd_dhcp6_client_set_information_request (priv->client6, 1);
+	if (info_only)
+	    sd_dhcp6_client_set_information_request (priv->client6, 1);
 
+	/* NM stores the entire DUID which includes the uint16 "type", while systemd
+	 * wants the type passed separately from the following data.
+	 */
 	r = sd_dhcp6_client_set_duid (priv->client6,
-	                              unaligned_read_be16 (&duid_arr[0]),
-	                              &duid_arr[2],
-	                              duid_len - 2);
+	                              ntohs (((const guint16 *) duid->data)[0]),
+	                              duid->data + 2,
+	                              duid->len - 2);
 	if (r < 0) {
 		_LOGW ("failed to set DUID (%d)", r);
 		return FALSE;
@@ -920,13 +955,9 @@ ip6_start (NMDhcpClient *client,
 
 	hwaddr = nm_dhcp_client_get_hw_addr (client);
 	if (hwaddr) {
-		const uint8_t *data;
-		gsize len;
-
-		data = g_bytes_get_data (hwaddr, &len);
 		r = sd_dhcp6_client_set_mac (priv->client6,
-		                             data,
-		                             len,
+		                             hwaddr->data,
+		                             hwaddr->len,
 		                             get_arp_type (hwaddr));
 		if (r < 0) {
 			_LOGW ("failed to set MAC address (%d)", r);
@@ -958,13 +989,6 @@ ip6_start (NMDhcpClient *client,
 		goto error;
 	}
 
-	hostname = nm_dhcp_client_get_hostname (client);
-	r = sd_dhcp6_client_set_fqdn (priv->client6, hostname);
-	if (r < 0) {
-		_LOGW ("failed to set DHCP hostname to '%s' (%d)", hostname, r);
-		goto error;
-	}
-
 	r = sd_dhcp6_client_start (priv->client6);
 	if (r < 0) {
 		_LOGW ("failed to start client (%d)", r);
@@ -982,14 +1006,12 @@ error:
 }
 
 static void
-stop (NMDhcpClient *client, gboolean release, GBytes *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	int r = 0;
 
-	NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release, duid);
-
 	_LOGT ("dhcp-client%d: stop %p",
 	       priv->client4 ? '4' : '6',
 	       priv->client4 ? (gpointer) priv->client4 : (gpointer) priv->client6);
@@ -1052,4 +1074,5 @@ const NMDhcpClientFactory _nm_dhcp_client_factory_internal = {
 	.name = "internal",
 	.get_type = nm_dhcp_systemd_get_type,
 	.get_path = NULL,
+	.get_lease_ip_configs = nm_dhcp_systemd_get_lease_ip_configs,
 };
diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c
index 9185a135..50ca2abe 100644
--- a/src/dhcp/nm-dhcp-utils.c
+++ b/src/dhcp/nm-dhcp-utils.c
@@ -721,15 +721,11 @@ error:
 }
 
 char *
-nm_dhcp_utils_duid_to_string (GBytes *duid)
+nm_dhcp_utils_duid_to_string (const GByteArray *duid)
 {
-	gconstpointer data;
-	gsize len;
-
 	g_return_val_if_fail (duid != NULL, NULL);
 
-	data = g_bytes_get_data (duid, &len);
-	return _nm_utils_bin2str (data, len, FALSE);
+	return _nm_utils_bin2str (duid->data, duid->len, FALSE);
 }
 
 /**
diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h
index 5c127bd1..32140f48 100644
--- a/src/dhcp/nm-dhcp-utils.h
+++ b/src/dhcp/nm-dhcp-utils.h
@@ -39,7 +39,7 @@ NMIP6Config *nm_dhcp_utils_ip6_config_from_options (struct _NMDedupMultiIndex *m
 
 NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options);
 
-char *nm_dhcp_utils_duid_to_string (GBytes *duid);
+char *       nm_dhcp_utils_duid_to_string          (const GByteArray *duid);
 
 GBytes *     nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
 
diff --git a/src/dhcp/tests/leases/basic.leases b/src/dhcp/tests/leases/basic.leases
new file mode 100644
index 00000000..703d9247
--- /dev/null
+++ b/src/dhcp/tests/leases/basic.leases
@@ -0,0 +1,31 @@
+lease {
+  interface "wlan0";
+  fixed-address 192.168.1.180;
+  option subnet-mask 255.255.255.0;
+  option routers 192.168.1.1;
+  option dhcp-lease-time 600;
+  option dhcp-message-type 5;
+  option domain-name-servers 192.168.1.1;
+  option dhcp-server-identifier 192.168.1.1;
+  option broadcast-address 192.168.1.255;
+  renew 5 2013/11/01 19:56:15;
+  rebind 5 2013/11/01 20:00:44;
+  expire 5 2013/11/01 20:01:59;
+}
+lease {
+  interface "wlan0";
+  fixed-address 10.77.52.141;
+  option subnet-mask 255.0.0.0;
+  option dhcp-lease-time 1200;
+  option routers 10.77.52.254;
+  option dhcp-message-type 5;
+  option dhcp-server-identifier 10.77.52.254;
+  option domain-name-servers 8.8.8.8,8.8.4.4;
+  option dhcp-renewal-time 600;
+  option dhcp-rebinding-time 1050;
+  option domain-name "morriesguest.local";
+  renew 5 2013/11/01 20:01:08;
+  rebind 5 2013/11/01 20:05:00;
+  expire 5 2013/11/01 20:06:15;
+}
+
diff --git a/src/dhcp/tests/leases/malformed1.leases b/src/dhcp/tests/leases/malformed1.leases
new file mode 100644
index 00000000..401d982a
--- /dev/null
+++ b/src/dhcp/tests/leases/malformed1.leases
@@ -0,0 +1,15 @@
+# missing fixed-address option
+lease {
+  interface "wlan0";
+  option subnet-mask 255.255.255.0;
+  option routers 192.168.1.1;
+  option dhcp-lease-time 600;
+  option dhcp-message-type 5;
+  option domain-name-servers 192.168.1.1;
+  option dhcp-server-identifier 192.168.1.1;
+  option broadcast-address 192.168.1.255;
+  renew 5 2013/11/01 19:56:15;
+  rebind 5 2013/11/01 20:00:44;
+  expire 5 2013/11/01 20:01:59;
+}
+
diff --git a/src/dhcp/tests/leases/malformed2.leases b/src/dhcp/tests/leases/malformed2.leases
new file mode 100644
index 00000000..adf5f6de
--- /dev/null
+++ b/src/dhcp/tests/leases/malformed2.leases
@@ -0,0 +1,15 @@
+# missing routers option
+lease {
+  interface "wlan0";
+  fixed-address 192.168.1.180;
+  option subnet-mask 255.255.255.0;
+  option dhcp-lease-time 600;
+  option dhcp-message-type 5;
+  option domain-name-servers 192.168.1.1;
+  option dhcp-server-identifier 192.168.1.1;
+  option broadcast-address 192.168.1.255;
+  renew 5 2013/11/01 19:56:15;
+  rebind 5 2013/11/01 20:00:44;
+  expire 5 2013/11/01 20:01:59;
+}
+
diff --git a/src/dhcp/tests/leases/malformed3.leases b/src/dhcp/tests/leases/malformed3.leases
new file mode 100644
index 00000000..a2afc8b6
--- /dev/null
+++ b/src/dhcp/tests/leases/malformed3.leases
@@ -0,0 +1,15 @@
+# missing expire time
+lease {
+  interface "wlan0";
+  fixed-address 192.168.1.180;
+  option subnet-mask 255.255.255.0;
+  option routers 192.168.1.1;
+  option dhcp-lease-time 600;
+  option dhcp-message-type 5;
+  option domain-name-servers 192.168.1.1;
+  option dhcp-server-identifier 192.168.1.1;
+  option broadcast-address 192.168.1.255;
+  renew 5 2013/11/01 19:56:15;
+  rebind 5 2013/11/01 20:00:44;
+}
+
diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build
deleted file mode 100644
index 32badae8..00000000
--- a/src/dhcp/tests/meson.build
+++ /dev/null
@@ -1,19 +0,0 @@
-test_units = [
-  'test-dhcp-dhclient',
-  'test-dhcp-utils'
-]
-
-foreach test_unit: test_units
-  exe = executable(
-    test_unit,
-    test_unit + '.c',
-    dependencies: test_nm_dep,
-    c_args: '-DTESTDIR="@0@"'.format(meson.current_source_dir())
-  )
-
-  test(
-    'dhcp/' + test_unit,
-    test_script,
-    args: test_args + [exe.full_path()]
-  )
-endforeach
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 25af51a1..f2e1f321 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -36,6 +36,12 @@
 
 #include "nm-test-utils-core.h"
 
+#define DEBUG 1
+
+static const int IFINDEX = 5;
+static const guint32 ROUTE_TABLE = RT_TABLE_MAIN;
+static const guint32 ROUTE_METRIC = 100;
+
 static void
 test_config (const char *orig,
              const char *expected,
@@ -148,7 +154,7 @@ test_override_client_id (void)
 static const char *quote_client_id_expected = \
 	"# Created by NetworkManager\n"
 	"\n"
-	"send dhcp-client-identifier \"\\x00abcd\"; # added by NetworkManager\n"
+	"send dhcp-client-identifier \"1234\"; # added by NetworkManager\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -166,65 +172,7 @@ test_quote_client_id (void)
 {
 	test_config (NULL, quote_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
-	             "abcd",
-	             NULL,
-	             "eth0",
-	             NULL);
-}
-
-/*****************************************************************************/
-
-static const char *quote_client_id_expected_2 = \
-	"# Created by NetworkManager\n"
-	"\n"
-	"send dhcp-client-identifier 00:61:5c:62:63; # added by NetworkManager\n"
-	"\n"
-	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
-	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
-	"option wpad code 252 = string;\n"
-	"\n"
-	"also request rfc3442-classless-static-routes;\n"
-	"also request ms-classless-static-routes;\n"
-	"also request static-routes;\n"
-	"also request wpad;\n"
-	"also request ntp-servers;\n"
-	"\n";
-
-static void
-test_quote_client_id_2 (void)
-{
-	test_config (NULL, quote_client_id_expected_2,
-	             AF_INET, NULL, 0, FALSE,
-	             "a\\bc",
-	             NULL,
-	             "eth0",
-	             NULL);
-}
-
-/*****************************************************************************/
-
-static const char *hex_zero_client_id_expected = \
-	"# Created by NetworkManager\n"
-	"\n"
-	"send dhcp-client-identifier 00:11:22:33; # added by NetworkManager\n"
-	"\n"
-	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
-	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
-	"option wpad code 252 = string;\n"
-	"\n"
-	"also request rfc3442-classless-static-routes;\n"
-	"also request ms-classless-static-routes;\n"
-	"also request static-routes;\n"
-	"also request wpad;\n"
-	"also request ntp-servers;\n"
-	"\n";
-
-static void
-test_hex_zero_client_id (void)
-{
-	test_config (NULL, hex_zero_client_id_expected,
-	             AF_INET, NULL, 0, FALSE,
-	             "00:11:22:33",
+	             "1234",
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -235,7 +183,7 @@ test_hex_zero_client_id (void)
 static const char *ascii_client_id_expected = \
 	"# Created by NetworkManager\n"
 	"\n"
-	"send dhcp-client-identifier \"\\x00qb:cd:ef:12:34:56\"; # added by NetworkManager\n"
+	"send dhcp-client-identifier \"qb:cd:ef:12:34:56\"; # added by NetworkManager\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -291,13 +239,13 @@ test_hex_single_client_id (void)
 /*****************************************************************************/
 
 static const char *existing_hex_client_id_orig = \
-	"send dhcp-client-identifier 10:30:04:20:7A:08;\n";
+	"send dhcp-client-identifier 00:30:04:20:7A:08;\n";
 
 static const char *existing_hex_client_id_expected = \
 	"# Created by NetworkManager\n"
 	"# Merged from /path/to/dhclient.conf\n"
 	"\n"
-	"send dhcp-client-identifier 10:30:04:20:7A:08;\n"
+	"send dhcp-client-identifier 00:30:04:20:7A:08;\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -314,7 +262,7 @@ static void
 test_existing_hex_client_id (void)
 {
 	gs_unref_bytes GBytes *new_client_id = NULL;
-	const guint8 bytes[] = { 0x10, 0x30, 0x04, 0x20, 0x7A, 0x08 };
+	const guint8 bytes[] = { 0x00, 0x30, 0x04,0x20, 0x7A, 0x08 };
 
 	new_client_id = g_bytes_new (bytes, sizeof (bytes));
 	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
@@ -327,52 +275,16 @@ test_existing_hex_client_id (void)
 
 /*****************************************************************************/
 
-static const char *existing_escaped_client_id_orig = \
-	"send dhcp-client-identifier \"\\044test\\xfe\";\n";
-
-static const char *existing_escaped_client_id_expected = \
-	"# Created by NetworkManager\n"
-	"# Merged from /path/to/dhclient.conf\n"
-	"\n"
-	"send dhcp-client-identifier \"\\044test\\xfe\";\n"
-	"\n"
-	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
-	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
-	"option wpad code 252 = string;\n"
-	"\n"
-	"also request rfc3442-classless-static-routes;\n"
-	"also request ms-classless-static-routes;\n"
-	"also request static-routes;\n"
-	"also request wpad;\n"
-	"also request ntp-servers;\n"
-	"\n";
-
-static void
-test_existing_escaped_client_id (void)
-{
-	gs_unref_bytes GBytes *new_client_id = NULL;
-
-	new_client_id = g_bytes_new ("$test\xfe", 6);
-	test_config (existing_escaped_client_id_orig, existing_escaped_client_id_expected,
-	             AF_INET, NULL, 0, FALSE,
-	             NULL,
-	             new_client_id,
-	             "eth0",
-	             NULL);
-}
-
-/*****************************************************************************/
-
 #define EACID "qb:cd:ef:12:34:56"
 
 static const char *existing_ascii_client_id_orig = \
-	"send dhcp-client-identifier \"\\x00" EACID "\";\n";
+	"send dhcp-client-identifier \"" EACID "\";\n";
 
 static const char *existing_ascii_client_id_expected = \
 	"# Created by NetworkManager\n"
 	"# Merged from /path/to/dhclient.conf\n"
 	"\n"
-	"send dhcp-client-identifier \"\\x00" EACID "\";\n"
+	"send dhcp-client-identifier \"" EACID "\";\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -673,26 +585,23 @@ test_existing_multiline_alsoreq (void)
 static void
 test_one_duid (const char *escaped, const guint8 *unescaped, guint len)
 {
-	GBytes *t;
+	GByteArray *t;
 	char *w;
-	gsize t_len;
-	gconstpointer t_arr;
 
 	t = nm_dhcp_dhclient_unescape_duid (escaped);
 	g_assert (t);
-	t_arr = g_bytes_get_data (t, &t_len);
-	g_assert (t_arr);
-	g_assert_cmpint (t_len, ==, len);
-	g_assert_cmpint (memcmp (t_arr, unescaped, len), ==, 0);
-	g_bytes_unref (t);
+	g_assert_cmpint (t->len, ==, len);
+	g_assert_cmpint (memcmp (t->data, unescaped, len), ==, 0);
+	g_byte_array_free (t, TRUE);
 
-	t = g_bytes_new_static (unescaped, len);
+	t = g_byte_array_sized_new (len);
+	g_byte_array_append (t, unescaped, len);
 	w = nm_dhcp_dhclient_escape_duid (t);
 	g_assert (w);
 	g_assert_cmpint (strlen (escaped), ==, strlen (w));
 	g_assert_cmpstr (escaped, ==, w);
 
-	g_bytes_unref (t);
+	g_byte_array_free (t, TRUE);
 	g_free (w);
 }
 
@@ -731,23 +640,22 @@ test_read_duid_from_leasefile (void)
 {
 	const guint8 expected[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0x79, 0xa6,
 	                            0x13, 0x60, 0x67, 0x20, 0xec, 0x4c, 0x70 };
-	gs_unref_bytes GBytes *duid = NULL;
+	GByteArray *duid;
 	GError *error = NULL;
-	gconstpointer duid_arr;
-	gsize duid_len;
 
 	duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-duid.leases", &error);
 	g_assert_no_error (error);
 	g_assert (duid);
-	duid_arr = g_bytes_get_data (duid, &duid_len);
-	g_assert_cmpint (duid_len, ==, sizeof (expected));
-	g_assert_cmpint (memcmp (duid_arr, expected, duid_len), ==, 0);
+	g_assert_cmpint (duid->len, ==, sizeof (expected));
+	g_assert_cmpint (memcmp (duid->data, expected, duid->len), ==, 0);
+
+	g_byte_array_free (duid, TRUE);
 }
 
 static void
 test_read_commented_duid_from_leasefile (void)
 {
-	GBytes *duid;
+	GByteArray *duid;
 	GError *error = NULL;
 
 	duid = nm_dhcp_dhclient_read_duid (TESTDIR "/test-dhclient-commented-duid.leases", &error);
@@ -846,12 +754,12 @@ test_write_existing_commented_duid (void)
 
 static const char *interface1_orig = \
 	"interface \"eth0\" {\n"
-	"\talso request my-option;\n"
-	"\tinitial-delay 5;\n"
+	"	also request my-option;\n"
+	"	initial-delay 5;\n"
 	"}\n"
 	"interface \"eth1\" {\n"
-	"\talso request another-option;\n"
-	"\tinitial-delay 0;\n"
+	"	also request another-option;\n"
+	"	initial-delay 0;\n"
 	"}\n"
 	"\n"
 	"also request yet-another-option;\n";
@@ -890,12 +798,12 @@ test_interface1 (void)
 
 static const char *interface2_orig = \
 	"interface eth0 {\n"
-	"\talso request my-option;\n"
-	"\tinitial-delay 5;\n"
+	"	also request my-option;\n"
+	"	initial-delay 5;\n"
 	" }\n"
 	"interface eth1 {\n"
-	"\tinitial-delay 0;\n"
-	"\trequest another-option;\n"
+	"	initial-delay 0;\n"
+	"	request another-option;\n"
 	" } \n"
 	"\n"
 	"also request yet-another-option;\n";
@@ -936,12 +844,12 @@ test_config_req_intf (void)
 {
 	static const char *const orig = \
 		"request subnet-mask, broadcast-address, routers,\n"
-		"\trfc3442-classless-static-routes,\n"
-		"\tinterface-mtu, host-name, domain-name, domain-search,\n"
-		"\tdomain-name-servers, nis-domain, nis-servers,\n"
-		"\tnds-context, nds-servers, nds-tree-name,\n"
-		"\tnetbios-name-servers, netbios-dd-server,\n"
-		"\tnetbios-node-type, netbios-scope, ntp-servers;\n"
+		"	rfc3442-classless-static-routes,\n"
+		"	interface-mtu, host-name, domain-name, domain-search,\n"
+		"	domain-name-servers, nis-domain, nis-servers,\n"
+		"	nds-context, nds-servers, nds-tree-name,\n"
+		"	netbios-name-servers, netbios-dd-server,\n"
+		"	netbios-node-type, netbios-scope, ntp-servers;\n"
 		"";
 	static const char *const expected = \
 		"# Created by NetworkManager\n"
@@ -987,6 +895,133 @@ test_config_req_intf (void)
 
 /*****************************************************************************/
 
+static void
+test_read_lease_ip4_config_basic (void)
+{
+	nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new ();
+	GError *error = NULL;
+	char *contents = NULL;
+	gboolean success;
+	const char *path = TESTDIR "/leases/basic.leases";
+	GSList *leases;
+	GDateTime *now;
+	NMIP4Config *config;
+	const NMPlatformIP4Address *addr;
+	guint32 expected_addr;
+
+	success = g_file_get_contents (path, &contents, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Date from before the least expiration */
+	now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32);
+	leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now);
+	g_assert_cmpint (g_slist_length (leases), ==, 2);
+
+	/* IP4Config #1 */
+	config = g_slist_nth_data (leases, 0);
+	g_assert (NM_IS_IP4_CONFIG (config));
+
+	/* Address */
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1);
+	expected_addr = nmtst_inet4_from_string ("192.168.1.180");
+	addr = _nmtst_ip4_config_get_address (config, 0);
+	g_assert_cmpint (addr->address, ==, expected_addr);
+	g_assert_cmpint (addr->peer_address, ==, expected_addr);
+	g_assert_cmpint (addr->plen, ==, 24);
+
+	/* Gateway */
+	expected_addr = nmtst_inet4_from_string ("192.168.1.1");
+	g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr);
+
+	/* DNS */
+	g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 1);
+	expected_addr = nmtst_inet4_from_string ("192.168.1.1");
+	g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr);
+
+	g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 0);
+
+	/* IP4Config #2 */
+	config = g_slist_nth_data (leases, 1);
+	g_assert (NM_IS_IP4_CONFIG (config));
+
+	/* Address */
+	g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1);
+	expected_addr = nmtst_inet4_from_string ("10.77.52.141");
+	addr = _nmtst_ip4_config_get_address (config, 0);
+	g_assert_cmpint (addr->address, ==, expected_addr);
+	g_assert_cmpint (addr->peer_address, ==, expected_addr);
+	g_assert_cmpint (addr->plen, ==, 8);
+
+	/* Gateway */
+	expected_addr = nmtst_inet4_from_string ("10.77.52.254");
+	g_assert_cmpint (nmtst_ip4_config_get_gateway (config), ==, expected_addr);
+
+	/* DNS */
+	g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 2);
+	expected_addr = nmtst_inet4_from_string ("8.8.8.8");
+	g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr);
+	expected_addr = nmtst_inet4_from_string ("8.8.4.4");
+	g_assert_cmpint (nm_ip4_config_get_nameserver (config, 1), ==, expected_addr);
+
+	/* Domains */
+	g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 1);
+	g_assert_cmpstr (nm_ip4_config_get_domain (config, 0), ==, "morriesguest.local");
+
+	g_slist_free_full (leases, g_object_unref);
+	g_date_time_unref (now);
+	g_free (contents);
+}
+
+static void
+test_read_lease_ip4_config_expired (void)
+{
+	nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new ();
+	GError *error = NULL;
+	char *contents = NULL;
+	gboolean success;
+	const char *path = TESTDIR "/leases/basic.leases";
+	GSList *leases;
+	GDateTime *now;
+
+	success = g_file_get_contents (path, &contents, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Date from *after* the lease expiration */
+	now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32);
+	leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now);
+	g_assert (leases == NULL);
+
+	g_date_time_unref (now);
+	g_free (contents);
+}
+
+static void
+test_read_lease_ip4_config_expect_failure (gconstpointer user_data)
+{
+	nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new ();
+	GError *error = NULL;
+	char *contents = NULL;
+	gboolean success;
+	GSList *leases;
+	GDateTime *now;
+
+	success = g_file_get_contents ((const char *) user_data, &contents, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Date from before the least expiration */
+	now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1);
+	leases = nm_dhcp_dhclient_read_lease_ip_configs (multi_idx, AF_INET, "wlan0", IFINDEX, ROUTE_TABLE, ROUTE_METRIC, contents, now);
+	g_assert (leases == NULL);
+
+	g_date_time_unref (now);
+	g_free (contents);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -996,13 +1031,10 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/dhcp/dhclient/orig_missing", test_orig_missing);
 	g_test_add_func ("/dhcp/dhclient/override_client_id", test_override_client_id);
-	g_test_add_func ("/dhcp/dhclient/quote_client_id/1", test_quote_client_id);
-	g_test_add_func ("/dhcp/dhclient/quote_client_id/2", test_quote_client_id_2);
-	g_test_add_func ("/dhcp/dhclient/hex_zero_client_id", test_hex_zero_client_id);
+	g_test_add_func ("/dhcp/dhclient/quote_client_id", test_quote_client_id);
 	g_test_add_func ("/dhcp/dhclient/ascii_client_id", test_ascii_client_id);
 	g_test_add_func ("/dhcp/dhclient/hex_single_client_id", test_hex_single_client_id);
 	g_test_add_func ("/dhcp/dhclient/existing-hex-client-id", test_existing_hex_client_id);
-	g_test_add_func ("/dhcp/dhclient/existing-client-id", test_existing_escaped_client_id);
 	g_test_add_func ("/dhcp/dhclient/existing-ascii-client-id", test_existing_ascii_client_id);
 	g_test_add_func ("/dhcp/dhclient/fqdn", test_fqdn);
 	g_test_add_func ("/dhcp/dhclient/fqdn_options_override", test_fqdn_options_override);
@@ -1024,6 +1056,18 @@ main (int argc, char **argv)
 	g_test_add_func ("/dhcp/dhclient/write_existing_duid", test_write_existing_duid);
 	g_test_add_func ("/dhcp/dhclient/write_existing_commented_duid", test_write_existing_commented_duid);
 
+	g_test_add_func ("/dhcp/dhclient/leases/ip4-config/basic", test_read_lease_ip4_config_basic);
+	g_test_add_func ("/dhcp/dhclient/leases/ip4-config/expired", test_read_lease_ip4_config_expired);
+	g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-address",
+	                      TESTDIR "/leases/malformed1.leases",
+	                      test_read_lease_ip4_config_expect_failure);
+	g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-gateway",
+	                      TESTDIR "/leases/malformed2.leases",
+	                      test_read_lease_ip4_config_expect_failure);
+	g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-expire",
+	                      TESTDIR "/leases/malformed3.leases",
+	                      test_read_lease_ip4_config_expect_failure);
+
 	return g_test_run ();
 }
 
diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c
index 617a3c6c..72f31191 100644
--- a/src/dhcp/tests/test-dhcp-utils.c
+++ b/src/dhcp/tests/test-dhcp-utils.c
@@ -349,7 +349,8 @@ test_dhclient_invalid_classless_routes_1 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring invalid classless static routes*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -379,7 +380,8 @@ test_dhcpcd_invalid_classless_routes_1 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring invalid classless static routes*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -411,7 +413,8 @@ test_dhclient_invalid_classless_routes_2 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring invalid classless static routes*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -443,7 +446,8 @@ test_dhcpcd_invalid_classless_routes_2 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring invalid classless static routes*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -475,7 +479,8 @@ test_dhclient_invalid_classless_routes_3 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid classless static routes*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring invalid classless static routes*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -502,7 +507,8 @@ test_dhcpcd_invalid_classless_routes_3 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*DHCP provided invalid classless static route*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*DHCP provided invalid classless static route*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
@@ -609,7 +615,8 @@ test_invalid_escaped_domain_searches (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	NMTST_EXPECT_NM_WARN ("*invalid domain search*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid domain search*");
 	ip4_config = _ip4_config_from_options (1, "eth0", options, 0);
 	g_test_assert_expected_messages ();
 
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c
index d0753078..e6436c79 100644
--- a/src/dns/nm-dns-dnsmasq.c
+++ b/src/dns/nm-dns-dnsmasq.c
@@ -35,7 +35,7 @@
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "NetworkManagerUtils.h"
 
 #define PIDFILE NMRUNDIR "/dnsmasq.pid"
@@ -76,41 +76,54 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
 /*****************************************************************************/
 
 static char **
-get_ip_rdns_domains (NMIPConfig *ip_config)
+get_ip4_rdns_domains (NMIP4Config *ip4)
 {
-	int addr_family = nm_ip_config_get_addr_family (ip_config);
 	char **strv;
 	GPtrArray *domains = NULL;
 	NMDedupMultiIter ipconf_iter;
+	const NMPlatformIP4Address *address;
+	const NMPlatformIP4Route *route;
 
-	nm_assert_addr_family (addr_family);
+	g_return_val_if_fail (ip4 != NULL, NULL);
 
 	domains = g_ptr_array_sized_new (5);
 
-	if (addr_family == AF_INET) {
-		NMIP4Config *ip4 = (gpointer) ip_config;
-		const NMPlatformIP4Address *address;
-		const NMPlatformIP4Route *route;
+	nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address)
+		nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains);
 
-		nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address)
-			nm_utils_get_reverse_dns_domains_ip4 (address->address, address->plen, domains);
+	nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) {
+		if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
+			nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains);
+	}
 
-		nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &route) {
-			if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
-				nm_utils_get_reverse_dns_domains_ip4 (route->network, route->plen, domains);
-		}
-	} else {
-		NMIP6Config *ip6 = (gpointer) ip_config;
-		const NMPlatformIP6Address *address;
-		const NMPlatformIP6Route *route;
+	/* Terminating NULL so we can use g_strfreev() to free it */
+	g_ptr_array_add (domains, NULL);
 
-		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address)
-			nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains);
+	/* Free the array and return NULL if the only element was the ending NULL */
+	strv = (char **) g_ptr_array_free (domains, (domains->len == 1));
 
-		nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) {
-			if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
-				nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains);
-		}
+	return _nm_utils_strv_cleanup (strv, FALSE, FALSE, TRUE);
+}
+
+static char **
+get_ip6_rdns_domains (NMIP6Config *ip6)
+{
+	char **strv;
+	GPtrArray *domains = NULL;
+	NMDedupMultiIter ipconf_iter;
+	const NMPlatformIP6Address *address;
+	const NMPlatformIP6Route *route;
+
+	g_return_val_if_fail (ip6 != NULL, NULL);
+
+	domains = g_ptr_array_sized_new (5);
+
+	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address)
+		nm_utils_get_reverse_dns_domains_ip6 (&address->address, address->plen, domains);
+
+	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &route) {
+		if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
+			nm_utils_get_reverse_dns_domains_ip6 (&route->network, route->plen, domains);
 	}
 
 	/* Terminating NULL so we can use g_strfreev() to free it */
@@ -142,43 +155,96 @@ add_dnsmasq_nameserver (NMDnsDnsmasq *self,
 	g_variant_builder_close (servers);
 }
 
-#define IP_ADDR_TO_STRING_BUFLEN (NM_UTILS_INET_ADDRSTRLEN + 1 + IFNAMSIZ)
-
-static const char *
-ip_addr_to_string (int addr_family, gconstpointer addr, const char *iface, char *out_buf)
+static gboolean
+add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4,
+                const char *iface, gboolean split)
 {
-	int n_written;
-	char buf2[NM_UTILS_INET_ADDRSTRLEN];
-	const char *separator;
+	char buf[INET_ADDRSTRLEN + 1 + IFNAMSIZ];
+	char buf2[INET_ADDRSTRLEN];
+	in_addr_t addr;
+	int nnameservers, i_nameserver, n, i;
+	gboolean added = FALSE;
 
-	nm_assert_addr_family (addr_family);
-	nm_assert (addr);
-	nm_assert (out_buf);
+	g_return_val_if_fail (iface, FALSE);
+	nnameservers = nm_ip4_config_get_num_nameservers (ip4);
 
-	if (addr_family == AF_INET) {
-		nm_utils_inet_ntop (addr_family, addr, buf2);
-		separator = "@";
-	} else {
-		if (IN6_IS_ADDR_V4MAPPED (addr))
-			nm_utils_inet4_ntop (((const struct in6_addr *) addr)->s6_addr32[3], buf2);
-		else
-			nm_utils_inet6_ntop (addr, buf2);
-		/* Need to scope link-local addresses with %<zone-id>. Before dnsmasq 2.58,
-		 * only '@' was supported as delimiter. Since 2.58, '@' and '%' are
-		 * supported. Due to a bug, since 2.73 only '%' works properly as "server"
-		 * address.
-		 */
-		separator = IN6_IS_ADDR_LINKLOCAL (addr) ? "%" : "@";
+	if (split) {
+		char **domains, **iter;
+
+		if (nnameservers == 0)
+			return FALSE;
+
+		for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) {
+			addr = nm_ip4_config_get_nameserver (ip4, i_nameserver);
+			g_snprintf (buf, sizeof (buf), "%s@%s",
+			            nm_utils_inet4_ntop (addr, buf2), iface);
+
+			/* searches are preferred over domains */
+			n = nm_ip4_config_get_num_searches (ip4);
+			for (i = 0; i < n; i++) {
+				add_dnsmasq_nameserver (self,
+				                        servers,
+				                        buf,
+				                        nm_ip4_config_get_search (ip4, i));
+				added = TRUE;
+			}
+
+			if (n == 0) {
+				/* If not searches, use any domains */
+				n = nm_ip4_config_get_num_domains (ip4);
+				for (i = 0; i < n; i++) {
+					add_dnsmasq_nameserver (self,
+					                        servers,
+					                        buf,
+					                        nm_ip4_config_get_domain (ip4, i));
+					added = TRUE;
+				}
+			}
+
+			/* Ensure reverse-DNS works by directing queries for in-addr.arpa
+			 * domains to the split domain's nameserver.
+			 */
+			domains = get_ip4_rdns_domains (ip4);
+			if (domains) {
+				for (iter = domains; iter && *iter; iter++)
+					add_dnsmasq_nameserver (self, servers, buf, *iter);
+				g_strfreev (domains);
+			}
+		}
+	}
+
+	/* If no searches or domains, just add the nameservers */
+	if (!added) {
+		for (i = 0; i < nnameservers; i++) {
+			addr = nm_ip4_config_get_nameserver (ip4, i);
+			g_snprintf (buf, sizeof (buf), "%s@%s",
+			            nm_utils_inet4_ntop (addr, buf2), iface);
+			add_dnsmasq_nameserver (self, servers, buf, NULL);
+		}
 	}
 
-	n_written = g_snprintf (out_buf,
-	                        IP_ADDR_TO_STRING_BUFLEN,
-	                        "%s%s%s",
-	                        buf2,
-	                        iface ? separator : "",
-	                        iface ?: "");
-	nm_assert (n_written < IP_ADDR_TO_STRING_BUFLEN);
-	return out_buf;
+	return TRUE;
+}
+
+static char *
+ip6_addr_to_string (const struct in6_addr *addr, const char *iface)
+{
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	if (IN6_IS_ADDR_V4MAPPED (addr))
+		nm_utils_inet4_ntop (addr->s6_addr32[3], buf);
+	else
+		nm_utils_inet6_ntop (addr, buf);
+
+	/* Need to scope link-local addresses with %<zone-id>. Before dnsmasq 2.58,
+	 * only '@' was supported as delimiter. Since 2.58, '@' and '%' are
+	 * supported. Due to a bug, since 2.73 only '%' works properly as "server"
+	 * address.
+	 */
+	return g_strdup_printf ("%s%c%s",
+	                        buf,
+	                        IN6_IS_ADDR_LINKLOCAL (addr) ? '%' : '@',
+	                        iface);
 }
 
 static void
@@ -205,100 +271,103 @@ add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const N
 	}
 }
 
-static void
-add_ip_config (NMDnsDnsmasq *self,
-               GVariantBuilder *servers,
-               int ifindex,
-               NMIPConfig *ip_config,
-               gboolean split)
+static gboolean
+add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6,
+                const char *iface, gboolean split)
 {
-	int addr_family;
-	gconstpointer addr;
+	const struct in6_addr *addr;
+	char *buf = NULL;
+	int nnameservers, i_nameserver, n, i;
 	gboolean added = FALSE;
-	guint nnameservers, i_nameserver, n, i;
-	char ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN];
-	char **domains, **iter;
-	gboolean iface_resolved = FALSE;
-	const char *iface = NULL, *domain;
 
-	addr_family = nm_ip_config_get_addr_family (ip_config);
-	g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6));
-
-	nm_assert (ifindex > 0);
-	nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config));
-
-	nnameservers = nm_ip_config_get_num_nameservers (ip_config);
+	g_return_val_if_fail (iface, FALSE);
+	nnameservers = nm_ip6_config_get_num_nameservers (ip6);
 
 	if (split) {
-		if (nnameservers == 0)
-			return;
-
-		if (!iface_resolved) {
-			iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex);
-			iface_resolved = TRUE;
-		}
-
-		if (iface) {
-			for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) {
-				addr = nm_ip_config_get_nameserver (ip_config, i_nameserver);
+		char **domains, **iter;
 
-				ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf);
+		if (nnameservers == 0)
+			return FALSE;
+
+		for (i_nameserver = 0; i_nameserver < nnameservers; i_nameserver++) {
+			addr = nm_ip6_config_get_nameserver (ip6, i_nameserver);
+			buf = ip6_addr_to_string (addr, iface);
+
+			/* searches are preferred over domains */
+			n = nm_ip6_config_get_num_searches (ip6);
+			for (i = 0; i < n; i++) {
+				add_dnsmasq_nameserver (self,
+				                        servers,
+				                        buf,
+				                        nm_ip6_config_get_search (ip6, i));
+				added = TRUE;
+			}
 
-				/* searches are preferred over domains */
-				n = nm_ip_config_get_num_searches (ip_config);
+			if (n == 0) {
+				/* If not searches, use any domains */
+				n = nm_ip6_config_get_num_domains (ip6);
 				for (i = 0; i < n; i++) {
-					domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (ip_config, i), NULL);
 					add_dnsmasq_nameserver (self,
 					                        servers,
-					                        ip_addr_to_string_buf,
-					                        domain);
+					                        buf,
+					                        nm_ip6_config_get_domain (ip6, i));
 					added = TRUE;
 				}
+			}
 
-				if (n == 0) {
-					/* If not searches, use any domains */
-					n = nm_ip_config_get_num_domains (ip_config);
-					domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (ip_config, i), NULL);
-					for (i = 0; i < n; i++) {
-						add_dnsmasq_nameserver (self,
-						                        servers,
-						                        ip_addr_to_string_buf,
-						                        domain);
-						added = TRUE;
-					}
-				}
-
-				/* Ensure reverse-DNS works by directing queries for in-addr4.arpa/ip6.arpa
-				 * domains to the split domain's nameserver.
-				 */
-				domains = get_ip_rdns_domains (ip_config);
-				if (domains) {
-					for (iter = domains; *iter; iter++)
-						add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, *iter);
-					g_strfreev (domains);
-				}
+			/* Ensure reverse-DNS works by directing queries for ip6.arpa
+			 * domains to the split domain's nameserver.
+			 */
+			domains = get_ip6_rdns_domains (ip6);
+			if (domains) {
+				for (iter = domains; iter && *iter; iter++)
+					add_dnsmasq_nameserver (self, servers, buf, *iter);
+				g_strfreev (domains);
 			}
+
+			g_free (buf);
 		}
 	}
 
 	/* If no searches or domains, just add the nameservers */
 	if (!added) {
-		if (!iface_resolved)
-			iface = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex);
-		if (iface) {
-			for (i = 0; i < nnameservers; i++) {
-				addr = nm_ip_config_get_nameserver (ip_config, i);
-				ip_addr_to_string (addr_family, addr, iface, ip_addr_to_string_buf);
-				add_dnsmasq_nameserver (self, servers, ip_addr_to_string_buf, NULL);
+		for (i = 0; i < nnameservers; i++) {
+			addr = nm_ip6_config_get_nameserver (ip6, i);
+			buf = ip6_addr_to_string (addr, iface);
+			if (buf) {
+				add_dnsmasq_nameserver (self, servers, buf, NULL);
+				g_free (buf);
 			}
 		}
 	}
+
+	return TRUE;
+}
+
+static gboolean
+add_ip_config_data (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigData *data)
+{
+	if (NM_IS_IP4_CONFIG (data->config)) {
+		return add_ip4_config (self,
+		                       servers,
+		                       (NMIP4Config *) data->config,
+		                       data->iface,
+		                       data->type == NM_DNS_IP_CONFIG_TYPE_VPN);
+	} else if (NM_IS_IP6_CONFIG (data->config)) {
+		return add_ip6_config (self,
+		                       servers,
+		                       (NMIP6Config *) data->config,
+		                       data->iface,
+		                       data->type == NM_DNS_IP_CONFIG_TYPE_VPN);
+	} else
+		g_return_val_if_reached (FALSE);
 }
 
 static void
 dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
 {
 	NMDnsDnsmasq *self;
+	NMDnsDnsmasqPrivate *priv;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *response = NULL;
 
@@ -307,6 +376,7 @@ dnsmasq_update_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
 		return;
 
 	self = NM_DNS_DNSMASQ (user_data);
+	priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 
 	if (!response)
 		_LOGW ("dnsmasq update failed: %s", error->message);
@@ -408,7 +478,7 @@ start_dnsmasq (NMDnsDnsmasq *self)
 	const char *argv[15];
 	GPid pid = 0;
 	guint idx = 0;
-	NMDBusManager *dbus_mgr;
+	NMBusManager *dbus_mgr;
 	GDBusConnection *connection;
 
 	if (priv->running) {
@@ -460,10 +530,10 @@ start_dnsmasq (NMDnsDnsmasq *self)
 		return;
 	}
 
-	dbus_mgr = nm_dbus_manager_get ();
+	dbus_mgr = nm_bus_manager_get ();
 	g_return_if_fail (dbus_mgr);
 
-	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	connection = nm_bus_manager_get_connection (dbus_mgr);
 	g_return_if_fail (connection);
 
 	priv->dnsmasq_cancellable = g_cancellable_new ();
@@ -480,16 +550,15 @@ start_dnsmasq (NMDnsDnsmasq *self)
 
 static gboolean
 update (NMDnsPlugin *plugin,
+        const GPtrArray *configs,
         const NMGlobalDnsConfig *global_config,
-        const CList *ip_config_lst_head,
         const char *hostname)
 {
 	NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
 	NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self);
 	GVariantBuilder servers;
-	int prio, first_prio = 0;
-	const NMDnsIPConfigData *ip_data;
-	gboolean is_first = TRUE;
+	guint i;
+	int prio, first_prio;
 
 	start_dnsmasq (self);
 
@@ -498,18 +567,15 @@ update (NMDnsPlugin *plugin,
 	if (global_config)
 		add_global_config (self, &servers, global_config);
 	else {
-		c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) {
-			prio = nm_ip_config_get_dns_priority (ip_data->ip_config);
-			if (is_first) {
-				is_first = FALSE;
+		for (i = 0; i < configs->len; i++) {
+			const NMDnsIPConfigData *data = configs->pdata[i];
+
+			prio = nm_ip_config_get_dns_priority (data->config);
+			if (i == 0)
 				first_prio = prio;
-			} else if (first_prio < 0 && first_prio != prio)
+			else if (first_prio < 0 && first_prio != prio)
 				break;
-			add_ip_config (self,
-			               &servers,
-			               ip_data->data->ifindex,
-			               ip_data->ip_config,
-			               ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN);
+			add_ip_config_data (self, &servers, data);
 		}
 	}
 
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index 10a16e57..dc545470 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -46,7 +46,6 @@
 #include "nm-ip6-config.h"
 #include "NetworkManagerUtils.h"
 #include "nm-config.h"
-#include "nm-dbus-object.h"
 #include "devices/nm-device.h"
 #include "nm-manager.h"
 
@@ -55,6 +54,8 @@
 #include "nm-dns-systemd-resolved.h"
 #include "nm-dns-unbound.h"
 
+#include "introspection/org.freedesktop.NetworkManager.DnsManager.h"
+
 #define HASH_LEN 20
 
 #ifndef RESOLVCONF_PATH
@@ -69,24 +70,6 @@
 #define PLUGIN_RATELIMIT_BURST       5
 #define PLUGIN_RATELIMIT_DELAY       300
 
-/*****************************************************************************/
-
-typedef enum {
-	SR_SUCCESS,
-	SR_NOTFOUND,
-	SR_ERROR
-} SpawnResult;
-
-typedef struct {
-	GPtrArray *nameservers;
-	GPtrArray *searches;
-	GPtrArray *options;
-	const char *nis_domain;
-	GPtrArray *nis_servers;
-} NMResolvConfData;
-
-/*****************************************************************************/
-
 enum {
 	CONFIG_CHANGED,
 
@@ -101,16 +84,44 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDnsManager,
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-typedef struct {
-	GHashTable *configs;
-	CList ip_config_lst_head;
-	GVariant *config_variant;
+typedef enum {
+	SR_SUCCESS,
+	SR_NOTFOUND,
+	SR_ERROR
+} SpawnResult;
 
-	NMDnsIPConfigData *best_ip_config_4;
-	NMDnsIPConfigData *best_ip_config_6;
+NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER);
 
-	bool ip_config_lst_need_sort:1;
+/*****************************************************************************/
 
+#define _NMLOG_PREFIX_NAME                "dns-mgr"
+#define _NMLOG_DOMAIN                     LOGD_DNS
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[20]; \
+            const NMDnsManager *const __self = (self); \
+            \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     ((!__self || __self == singleton_instance) \
+                        ? "" \
+                        : nm_sprintf_buf (__prefix, "[%p]", __self)) \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*****************************************************************************/
+
+typedef struct {
+	GPtrArray *configs;
+	GVariant *config_variant;
+	NMDnsIPConfigData *best_conf4, *best_conf6;
+
+	bool need_sort:1;
 	bool dns_touched:1;
 	bool is_stopped:1;
 
@@ -134,52 +145,20 @@ typedef struct {
 } NMDnsManagerPrivate;
 
 struct _NMDnsManager {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMDnsManagerPrivate _priv;
 };
 
 struct _NMDnsManagerClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER)
 
-NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER);
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME                "dns-mgr"
-#define _NMLOG_DOMAIN                     LOGD_DNS
-#define _NMLOG(level, ...) \
-    G_STMT_START { \
-        const NMLogLevel __level = (level); \
-        \
-        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
-            char __prefix[20]; \
-            const NMDnsManager *const __self = (self); \
-            \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
-                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     _NMLOG_PREFIX_NAME, \
-                     ((!__self || __self == singleton_instance) \
-                        ? "" \
-                        : nm_sprintf_buf (__prefix, "[%p]", __self)) \
-                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
-        } \
-    } G_STMT_END
-
-/*****************************************************************************/
-
-static void _ip_config_dns_priority_changed (gpointer config,
-                                             GParamSpec *pspec,
-                                             NMDnsIPConfigData *ip_data);
-
-/*****************************************************************************/
-
 static gboolean
-domain_is_valid (const char *domain, gboolean check_public_suffix)
+domain_is_valid (const gchar *domain, gboolean check_public_suffix)
 {
 	if (*domain == '\0')
 		return FALSE;
@@ -190,14 +169,16 @@ domain_is_valid (const char *domain, gboolean check_public_suffix)
 	return TRUE;
 }
 
-static gboolean
-domain_is_routing (const char *domain)
-{
-	return domain[0] == '~';
-}
-
 /*****************************************************************************/
 
+typedef struct {
+	GPtrArray *nameservers;
+	GPtrArray *searches;
+	GPtrArray *options;
+	const char *nis_domain;
+	GPtrArray *nis_servers;
+} NMResolvConfData;
+
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager,
 	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN,        "unknown"),
@@ -211,109 +192,44 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConf
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_config_type_to_string, NMDnsIPConfigType,
 	NM_UTILS_LOOKUP_DEFAULT_WARN ("<unknown>"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_REMOVED, "removed"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_DEFAULT, "default"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE, "best"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"),
 );
 
-/*****************************************************************************/
-
-static void
-_ASSERT_config_data (const NMDnsConfigData *data)
-{
-	nm_assert (data);
-	nm_assert (NM_IS_DNS_MANAGER (data->self));
-	nm_assert (data->ifindex > 0);
-}
-
-static void
-_ASSERT_ip_config_data (const NMDnsIPConfigData *ip_data)
-{
-	nm_assert (ip_data);
-	_ASSERT_config_data (ip_data->data);
-	nm_assert (NM_IS_IP_CONFIG (ip_data->ip_config));
-	nm_assert (c_list_contains (&ip_data->data->data_lst_head, &ip_data->data_lst));
-	nm_assert (ip_data->data->ifindex == nm_ip_config_get_ifindex (ip_data->ip_config));
-}
-
 static NMDnsIPConfigData *
-_ip_config_data_new (NMDnsConfigData *data,
-                     NMIPConfig *ip_config,
-                     NMDnsIPConfigType ip_config_type)
-{
-	NMDnsIPConfigData *ip_data;
-
-	_ASSERT_config_data (data);
-	nm_assert (NM_IS_IP_CONFIG (ip_config));
-	nm_assert (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED);
-
-	ip_data = g_slice_new0 (NMDnsIPConfigData);
-	ip_data->data = data;
-	ip_data->ip_config = g_object_ref (ip_config);
-	ip_data->ip_config_type = ip_config_type;
-	c_list_link_tail (&data->data_lst_head, &ip_data->data_lst);
-	c_list_link_tail (&NM_DNS_MANAGER_GET_PRIVATE (data->self)->ip_config_lst_head, &ip_data->ip_config_lst);
-
-	g_signal_connect (ip_config,
-	                  NM_IS_IP4_CONFIG (ip_config)
-	                    ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY
-	                    : "notify::" NM_IP6_CONFIG_DNS_PRIORITY,
-	                  (GCallback) _ip_config_dns_priority_changed, ip_data);
-
-	_ASSERT_ip_config_data (ip_data);
-	return ip_data;
-}
-
-static void
-_ip_config_data_free (NMDnsIPConfigData *ip_data)
+ip_config_data_new (gpointer config, NMDnsIPConfigType type, const char *iface)
 {
-	_ASSERT_ip_config_data (ip_data);
+	NMDnsIPConfigData *data;
 
-	c_list_unlink_stale (&ip_data->data_lst);
-	c_list_unlink_stale (&ip_data->ip_config_lst);
+	data = g_slice_new0 (NMDnsIPConfigData);
+	data->config = g_object_ref (config);
+	data->iface = g_strdup (iface);
+	data->type = type;
 
-	g_signal_handlers_disconnect_by_func (ip_data->ip_config,
-	                                      _ip_config_dns_priority_changed,
-	                                      ip_data);
-
-	g_object_unref (ip_data->ip_config);
-	g_slice_free (NMDnsIPConfigData, ip_data);
-}
-
-static NMDnsIPConfigData *
-_config_data_find_ip_config (NMDnsConfigData *data,
-                             NMIPConfig *ip_config)
-{
-	NMDnsIPConfigData *ip_data;
-
-	_ASSERT_config_data (data);
-
-	c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) {
-		_ASSERT_ip_config_data (ip_data);
-
-		if (ip_data->ip_config == ip_config)
-			return ip_data;
-	}
-	return NULL;
+	return data;
 }
 
 static void
-_config_data_free (NMDnsConfigData *data)
+ip_config_data_destroy (gpointer ptr)
 {
-	_ASSERT_config_data (data);
+	NMDnsIPConfigData *data = ptr;
+
+	if (!data)
+		return;
 
-	nm_assert (c_list_is_empty (&data->data_lst_head));
-	g_slice_free (NMDnsConfigData, data);
+	g_object_unref (data->config);
+	g_free (data->iface);
+	g_slice_free (NMDnsIPConfigData, data);
 }
 
 static gint
-_ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b)
+ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b)
 {
 	int a_prio, b_prio;
 
-	a_prio = nm_ip_config_get_dns_priority (a->ip_config);
-	b_prio = nm_ip_config_get_dns_priority (b->ip_config);
+	a_prio = nm_ip_config_get_dns_priority (a->config);
+	b_prio = nm_ip_config_get_dns_priority (b->config);
 
 	/* Configurations with lower priority value first */
 	if (a_prio < b_prio)
@@ -322,40 +238,24 @@ _ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b)
 		return 1;
 
 	/* Sort also according to type */
-	if (a->ip_config_type > b->ip_config_type)
+	if (a->type > b->type)
 		return -1;
-	else if (a->ip_config_type < b->ip_config_type)
+	else if (a->type < b->type)
 		return 1;
 
 	return 0;
 }
 
 static gint
-_ip_config_lst_cmp (const CList *a,
-                    const CList *b,
-                    const void *user_data)
+ip_config_data_ptr_compare (gconstpointer a, gconstpointer b)
 {
-	return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst),
-	                            c_list_entry (b, NMDnsIPConfigData, ip_config_lst));
-}
+	const NMDnsIPConfigData *const *ptr_a = a, *const *ptr_b = b;
 
-static CList *
-_ip_config_lst_head (NMDnsManager *self)
-{
-	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-
-	if (priv->ip_config_lst_need_sort) {
-		priv->ip_config_lst_need_sort = FALSE;
-		c_list_sort (&priv->ip_config_lst_head, _ip_config_lst_cmp, NULL);
-	}
-
-	return &priv->ip_config_lst_head;
+	return ip_config_data_compare (*ptr_a, *ptr_b);
 }
 
-/*****************************************************************************/
-
 static void
-add_string_item (GPtrArray *array, const char *str, gboolean dup)
+add_string_item (GPtrArray *array, const char *str)
 {
 	int i;
 
@@ -371,7 +271,7 @@ add_string_item (GPtrArray *array, const char *str, gboolean dup)
 	}
 
 	/* No dupes, add the new item */
-	g_ptr_array_add (array, dup ? g_strdup (str): (gpointer) str);
+	g_ptr_array_add (array, g_strdup (str));
 }
 
 static void
@@ -382,55 +282,24 @@ add_dns_option_item (GPtrArray *array, const char *str)
 }
 
 static void
-add_dns_domains (GPtrArray *array, const NMIPConfig *ip_config,
-                 gboolean include_routing, gboolean dup)
-{
-	guint num_domains, num_searches, i;
-	const char *str;
-
-	num_domains = nm_ip_config_get_num_domains (ip_config);
-	num_searches = nm_ip_config_get_num_searches (ip_config);
-
-	for (i = 0; i < num_searches; i++) {
-		str = nm_ip_config_get_search (ip_config, i);
-		if (!include_routing && domain_is_routing (str))
-			continue;
-		if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE))
-			continue;
-		add_string_item (array, str, dup);
-	}
-	if (num_domains > 1 || !num_searches) {
-		for (i = 0; i < num_domains; i++) {
-			str = nm_ip_config_get_domain (ip_config, i);
-			if (!include_routing && domain_is_routing (str))
-				continue;
-			if (!domain_is_valid (nm_utils_parse_dns_domain (str, NULL), FALSE))
-				continue;
-			add_string_item (array, str, dup);
-		}
-	}
-}
-
-static void
 merge_one_ip_config (NMResolvConfData *rc,
-                     int ifindex,
-                     const NMIPConfig *ip_config)
+                     const NMIPConfig *config,
+                     const char *iface)
 {
 	int addr_family;
-	guint num, i;
+	guint num, num_domains, num_searches, i;
 	char buf[NM_UTILS_INET_ADDRSTRLEN + 50];
+	const char *str;
 
-	addr_family = nm_ip_config_get_addr_family (ip_config);
+	addr_family = nm_ip_config_get_addr_family (config);
 
 	nm_assert_addr_family (addr_family);
-	nm_assert (ifindex > 0);
-	nm_assert (ifindex == nm_ip_config_get_ifindex (ip_config));
 
-	num = nm_ip_config_get_num_nameservers (ip_config);
+	num = nm_ip_config_get_num_nameservers (config);
 	for (i = 0; i < num; i++) {
 		const NMIPAddr *addr;
 
-		addr = nm_ip_config_get_nameserver (ip_config, i);
+		addr = nm_ip_config_get_nameserver (config, i);
 		if (addr_family == AF_INET)
 			nm_utils_inet_ntop (addr_family, addr, buf);
 		else if (IN6_IS_ADDR_V4MAPPED (addr))
@@ -438,42 +307,49 @@ merge_one_ip_config (NMResolvConfData *rc,
 		else {
 			nm_utils_inet6_ntop (&addr->addr6, buf);
 			if (IN6_IS_ADDR_LINKLOCAL (addr)) {
-				const char *ifname;
-
-				ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex);
-				if (ifname) {
-					g_strlcat (buf, "%", sizeof (buf));
-					g_strlcat (buf, ifname, sizeof (buf));
-				}
+				g_strlcat (buf, "%", sizeof (buf));
+				g_strlcat (buf, iface, sizeof (buf));
 			}
 		}
 
-		add_string_item (rc->nameservers, buf, TRUE);
+		add_string_item (rc->nameservers, buf);
 	}
 
-	add_dns_domains (rc->searches, ip_config, FALSE, TRUE);
+	num_domains = nm_ip_config_get_num_domains (config);
+	num_searches = nm_ip_config_get_num_searches (config);
+	for (i = 0; i < num_searches; i++) {
+		str = nm_ip_config_get_search (config, i);
+		if (domain_is_valid (str, FALSE))
+			add_string_item (rc->searches, str);
+	}
+	if (num_domains > 1 || !num_searches) {
+		for (i = 0; i < num_domains; i++) {
+			str = nm_ip_config_get_domain (config, i);
+			if (domain_is_valid (str, FALSE))
+				add_string_item (rc->searches, str);
+		}
+	}
 
-	num = nm_ip_config_get_num_dns_options (ip_config);
+	num = nm_ip_config_get_num_dns_options (config);
 	for (i = 0; i < num; i++) {
 		add_dns_option_item (rc->options,
-		                     nm_ip_config_get_dns_option (ip_config, i));
+		                     nm_ip_config_get_dns_option (config, i));
 	}
 
 	if (addr_family == AF_INET) {
-		const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config;
+		const NMIP4Config *config4 = (const NMIP4Config *) config;
 
 		/* NIS stuff */
-		num = nm_ip4_config_get_num_nis_servers (ip4_config);
+		num = nm_ip4_config_get_num_nis_servers (config4);
 		for (i = 0; i < num; i++) {
 			add_string_item (rc->nis_servers,
-			                 nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (ip4_config, i), buf),
-			                 TRUE);
+			                 nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (config4, i), buf));
 		}
 
-		if (nm_ip4_config_get_nis_domain (ip4_config)) {
+		if (nm_ip4_config_get_nis_domain (config4)) {
 			/* FIXME: handle multiple domains */
 			if (!rc->nis_domain)
-				rc->nis_domain = nm_ip4_config_get_nis_domain (ip4_config);
+				rc->nis_domain = nm_ip4_config_get_nis_domain (config4);
 		}
 	}
 }
@@ -502,59 +378,58 @@ run_netconfig (NMDnsManager *self, GError **error, gint *stdin_fd)
 }
 
 static void
-netconfig_construct_str (NMDnsManager *self, GString *str, const char *key, const char *value)
+write_to_netconfig (NMDnsManager *self, gint fd, const char *key, const char *value)
 {
-	if (value) {
-		_LOGD ("writing to netconfig: %s='%s'", key, value);
-		g_string_append_printf (str, "%s='%s'\n", key, value);
-	}
-}
+	char *str;
+	int x;
 
-static void
-netconfig_construct_strv (NMDnsManager *self, GString *str, const char *key, const char *const*values)
-{
-	if (values) {
-		gs_free char *value = NULL;
-
-		value = g_strjoinv (" ", (char **) values);
-		netconfig_construct_str (self, str, key, value);
-	}
+	str = g_strdup_printf ("%s='%s'\n", key, value);
+	_LOGD ("writing to netconfig: %s", str);
+	x = write (fd, str, strlen (str));
+	g_free (str);
 }
 
 static SpawnResult
 dispatch_netconfig (NMDnsManager *self,
-                    const char *const*searches,
-                    const char *const*nameservers,
+                    char **searches,
+                    char **nameservers,
                     const char *nis_domain,
-                    const char *const*nis_servers,
+                    char **nis_servers,
                     GError **error)
 {
+	char *str;
 	GPid pid;
 	gint fd;
 	int status;
-	gssize l;
-	nm_auto_free_gstring GString *str = NULL;
 
 	pid = run_netconfig (self, error, &fd);
 	if (pid <= 0)
 		return SR_NOTFOUND;
 
-	str = g_string_new ("");
-
 	/* NM is writing already-merged DNS information to netconfig, so it
 	 * does not apply to a specific network interface.
 	 */
-	netconfig_construct_str (self, str, "INTERFACE", "NetworkManager");
-	netconfig_construct_strv (self, str, "DNSSEARCH", searches);
-	netconfig_construct_strv (self, str, "DNSSERVERS", nameservers);
-	netconfig_construct_str (self, str, "NISDOMAIN", nis_domain);
-	netconfig_construct_strv (self, str, "NISSERVERS", nis_servers);
+	write_to_netconfig (self, fd, "INTERFACE", "NetworkManager");
 
-again:
-	l = write (fd, str->str, str->len);
-	if (l == -1)  {
-		if (errno == EINTR)
-			goto again;
+	if (searches) {
+		str = g_strjoinv (" ", searches);
+		write_to_netconfig (self, fd, "DNSSEARCH", str);
+		g_free (str);
+	}
+
+	if (nameservers) {
+		str = g_strjoinv (" ", nameservers);
+		write_to_netconfig (self, fd, "DNSSERVERS", str);
+		g_free (str);
+	}
+
+	if (nis_domain)
+		write_to_netconfig (self, fd, "NISDOMAIN", nis_domain);
+
+	if (nis_servers) {
+		str = g_strjoinv (" ", nis_servers);
+		write_to_netconfig (self, fd, "NISSERVERS", str);
+		g_free (str);
 	}
 
 	nm_close (fd);
@@ -868,7 +743,7 @@ update_resolv_conf (NMDnsManager *self,
 
 	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
 		_LOGT ("update-resolv-conf: write internal file %s succeeded (rc-manager=%s)",
-		       MY_RESOLV_CONF, _rc_manager_to_string (rc_manager));
+		       rc_path, _rc_manager_to_string (rc_manager));
 		return write_file_result;
 	}
 
@@ -942,21 +817,25 @@ update_resolv_conf (NMDnsManager *self,
 static void
 compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN])
 {
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	GChecksum *sum;
 	gsize len = HASH_LEN;
-	NMDnsIPConfigData *ip_data;
+	guint i;
 
 	sum = g_checksum_new (G_CHECKSUM_SHA1);
-	nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1));
+	g_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1));
 
 	if (global)
 		nm_global_dns_config_update_checksum (global, sum);
 	else {
-		const CList *head;
+		for (i = 0; i < priv->configs->len; i++) {
+			NMDnsIPConfigData *data = priv->configs->pdata[i];
 
-		head = _ip_config_lst_head (self);
-		c_list_for_each_entry (ip_data, head, ip_config_lst)
-			nm_ip_config_hash (ip_data->ip_config, sum, TRUE);
+			if (NM_IS_IP4_CONFIG (data->config))
+				nm_ip4_config_hash ((NMIP4Config *) data->config, sum, TRUE);
+			else if (NM_IS_IP6_CONFIG (data->config))
+				nm_ip6_config_hash ((NMIP6Config *) data->config, sum, TRUE);
+		}
 	}
 
 	g_checksum_get_digest (sum, buffer, &len);
@@ -970,36 +849,27 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 	const char *const *searches;
 	const char *const *options;
 	const char *const *servers;
-	guint i;
+	gint i;
 
 	if (!global_conf)
 		return FALSE;
 
 	searches = nm_global_dns_config_get_searches (global_conf);
-	if (searches) {
-		for (i = 0; searches[i]; i++) {
-			if (domain_is_routing (searches[i]))
-				continue;
-			if (!domain_is_valid (searches[i], FALSE))
-				continue;
-			add_string_item (rc->searches, searches[i], TRUE);
-		}
-	}
-
 	options = nm_global_dns_config_get_options (global_conf);
-	if (options) {
-		for (i = 0; options[i]; i++)
-			add_string_item (rc->options, options[i], TRUE);
+
+	for (i = 0; searches && searches[i]; i++) {
+		if (domain_is_valid (searches[i], FALSE))
+			add_string_item (rc->searches, searches[i]);
 	}
 
-	default_domain = nm_global_dns_config_lookup_domain (global_conf, "*");
-	nm_assert (default_domain);
+	for (i = 0; options && options[i]; i++)
+		add_string_item (rc->options, options[i]);
 
+	default_domain = nm_global_dns_config_lookup_domain (global_conf, "*");
+	g_assert (default_domain);
 	servers = nm_global_dns_domain_get_servers (default_domain);
-	if (servers) {
-		for (i = 0; servers[i]; i++)
-			add_string_item (rc->nameservers, servers[i], TRUE);
-	}
+	for (i = 0; servers && servers[i]; i++)
+		add_string_item (rc->nameservers, servers[i]);
 
 	return TRUE;
 }
@@ -1039,16 +909,17 @@ _ptrarray_to_strv (GPtrArray *parray)
 }
 
 static void
-_collect_resolv_conf_data (NMDnsManager *self,
+_collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no other side-effects */
                            NMGlobalDnsConfig *global_config,
+                           const GPtrArray *configs,
+                           const char *hostname,
                            char ***out_searches,
                            char ***out_options,
                            char ***out_nameservers,
                            char ***out_nis_servers,
                            const char **out_nis_domain)
 {
-	NMDnsManagerPrivate *priv;
-	guint i, num, len;
+	guint i, j, num, len;
 	NMResolvConfData rc = {
 		.nameservers = g_ptr_array_new (),
 		.searches = g_ptr_array_new (),
@@ -1057,44 +928,37 @@ _collect_resolv_conf_data (NMDnsManager *self,
 		.nis_servers = g_ptr_array_new (),
 	};
 
-	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-
 	if (global_config)
 		merge_global_dns_config (&rc, global_config);
 	else {
 		nm_auto_free_gstring GString *tmp_gstring = NULL;
 		int prio, first_prio = 0;
-		const NMDnsIPConfigData *ip_data;
-		const CList *head;
-		gboolean is_first = TRUE;
+		NMDnsIPConfigData *current;
 
-		head = _ip_config_lst_head (self);
-		c_list_for_each_entry (ip_data, head, ip_config_lst) {
+		for (i = 0, j = 0; i < configs->len; i++) {
 			gboolean skip = FALSE;
 
-			_ASSERT_ip_config_data (ip_data);
+			current = configs->pdata[i];
 
-			prio = nm_ip_config_get_dns_priority (ip_data->ip_config);
+			prio = nm_ip_config_get_dns_priority (current->config);
 
-			if (is_first) {
-				is_first = FALSE;
+			if (i == 0)
 				first_prio = prio;
-			} else if (   first_prio < 0
-			           && first_prio != prio)
+			else if (first_prio < 0 && first_prio != prio)
 				skip = TRUE;
 
-			if (nm_ip_config_get_num_nameservers (ip_data->ip_config)) {
-				_LOGT ("config: %8d %-7s v%c %-5d %s: %s",
+			if (nm_ip_config_get_num_nameservers (current->config)) {
+				_LOGT ("config: %8d %-7s v%c %-16s %s: %s",
 				       prio,
-				       _config_type_to_string (ip_data->ip_config_type),
-				       nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (ip_data->ip_config)),
-				       ip_data->data->ifindex,
+				       _config_type_to_string (current->type),
+				       nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (current->config)),
+				       current->iface,
 				       skip ? "<SKIP>" : "",
-				       get_nameserver_list (ip_data->ip_config, &tmp_gstring));
+				       get_nameserver_list (current->config, &tmp_gstring));
 			}
 
 			if (!skip)
-				merge_one_ip_config (&rc, ip_data->data->ifindex, ip_data->ip_config);
+				merge_one_ip_config (&rc, current->config, current->iface);
 		}
 	}
 
@@ -1105,16 +969,16 @@ _collect_resolv_conf_data (NMDnsManager *self,
 	 * (eg, "example.com"), then use the hostname itself as the search (since the user is
 	 * unlikely to want "com" as a search domain).
 	 */
-	if (priv->hostname) {
-		const char *hostdomain = strchr (priv->hostname, '.');
+	if (hostname) {
+		const char *hostdomain = strchr (hostname, '.');
 
 		if (   hostdomain
-		    && !nm_utils_ipaddr_valid (AF_UNSPEC, priv->hostname)) {
+		    && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) {
 			hostdomain++;
 			if (domain_is_valid (hostdomain, TRUE))
-				add_string_item (rc.searches, hostdomain, TRUE);
-			else if (domain_is_valid (priv->hostname, TRUE))
-				add_string_item (rc.searches, priv->hostname, TRUE);
+				add_string_item (rc.searches, hostdomain);
+			else if (domain_is_valid (hostname, TRUE))
+				add_string_item (rc.searches, hostname);
 		}
 	}
 
@@ -1176,12 +1040,16 @@ update_dns (NMDnsManager *self,
 	data = nm_config_get_data (priv->config);
 	global_config = nm_config_data_get_global_dns_config (data);
 
+	if (priv->need_sort) {
+		g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare);
+		priv->need_sort = FALSE;
+	}
+
 	/* Update hash with config we're applying */
 	compute_hash (self, global_config, priv->hash);
 
-	_collect_resolv_conf_data (self, global_config,
-	                           &searches, &options, &nameservers,
-	                           &nis_servers, &nis_domain);
+	_collect_resolv_conf_data (self, global_config, priv->configs, priv->hostname,
+	                           &searches, &options, &nameservers, &nis_servers, &nis_domain);
 
 	/* Let any plugins do their thing first */
 	if (priv->plugin) {
@@ -1199,8 +1067,8 @@ update_dns (NMDnsManager *self,
 
 		_LOGD ("update-dns: updating plugin %s", plugin_name);
 		if (!nm_dns_plugin_update (plugin,
+		                           priv->configs,
 		                           global_config,
-		                           _ip_config_lst_head (self),
 		                           priv->hostname)) {
 			_LOGW ("update-dns: plugin %s update failed", plugin_name);
 
@@ -1246,12 +1114,8 @@ update_dns (NMDnsManager *self,
 			result = dispatch_resolvconf (self, searches, nameservers, options, error);
 			break;
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG:
-			result = dispatch_netconfig (self,
-			                             (const char *const*) searches,
-			                             (const char *const*) nameservers,
-			                             nis_domain,
-			                             (const char *const*) nis_servers,
-			                             error);
+			result = dispatch_netconfig (self, searches, nameservers, nis_domain,
+			                             nis_servers, error);
 			break;
 		default:
 			g_assert_not_reached ();
@@ -1341,96 +1205,82 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data)
 }
 
 static void
-_ip_config_dns_priority_changed (gpointer config,
-                                 GParamSpec *pspec,
-                                 NMDnsIPConfigData *ip_data)
+ip_config_dns_priority_changed (gpointer config,
+                                GParamSpec *pspec,
+                                NMDnsManager *self)
 {
-	_ASSERT_ip_config_data (ip_data);
+	NM_DNS_MANAGER_GET_PRIVATE (self)->need_sort = TRUE;
+}
 
-	NM_DNS_MANAGER_GET_PRIVATE (ip_data->data->self)->ip_config_lst_need_sort = TRUE;
+static void
+forget_data (NMDnsManager *self, NMDnsIPConfigData *data)
+{
+	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+	if (data == priv->best_conf4)
+		priv->best_conf4 = NULL;
+	else if (data == priv->best_conf6)
+		priv->best_conf6 = NULL;
+
+	g_signal_handlers_disconnect_by_func (data->config, ip_config_dns_priority_changed, self);
 }
 
 gboolean
-nm_dns_manager_set_ip_config (NMDnsManager *self,
-                              NMIPConfig *ip_config,
-                              NMDnsIPConfigType ip_config_type)
+nm_dns_manager_add_ip_config (NMDnsManager *self,
+                              const char *iface,
+                              gpointer config,
+                              NMDnsIPConfigType cfg_type)
 {
 	NMDnsManagerPrivate *priv;
 	GError *error = NULL;
-	NMDnsIPConfigData *ip_data;
-	NMDnsConfigData *data;
-	int ifindex;
-	NMDnsIPConfigData **p_best;
+	NMDnsIPConfigData *data;
+	gboolean v4 = NM_IS_IP4_CONFIG (config);
+	guint i;
 
 	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
-	g_return_val_if_fail (NM_IS_IP_CONFIG (ip_config), FALSE);
-
-	ifindex = nm_ip_config_get_ifindex (ip_config);
-	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (config, FALSE);
+	g_return_val_if_fail (iface && iface[0], FALSE);
+	nm_assert (NM_IS_IP_CONFIG (config));
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
-	data = g_hash_table_lookup (priv->configs, GINT_TO_POINTER (ifindex));
-	if (!data)
-		ip_data = NULL;
-	else
-		ip_data = _config_data_find_ip_config (data, ip_config);
-
-	if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) {
-		if (!ip_data)
-			return FALSE;
-		if (priv->best_ip_config_4 == ip_data)
-			priv->best_ip_config_4 = NULL;
-		if (priv->best_ip_config_6 == ip_data)
-			priv->best_ip_config_6 = NULL;
-		/* deleting a config doesn't invalidate the configs' sort order. */
-		_ip_config_data_free (ip_data);
-		if (c_list_is_empty (&data->data_lst_head))
-			g_hash_table_remove (priv->configs, GINT_TO_POINTER (ifindex));
-		goto changed;
-	}
-
-	if (   ip_data
-	    && ip_data->ip_config_type == ip_config_type) {
-		/* nothing to do. */
-		return FALSE;
-	}
-
-	if (!data) {
-		data = g_slice_new0 (NMDnsConfigData);
-		data->ifindex = ifindex;
-		data->self = self;
-		c_list_init (&data->data_lst_head);
-		_ASSERT_config_data (data);
-		g_hash_table_insert (priv->configs, GINT_TO_POINTER (ifindex), data);
+	for (i = 0; i < priv->configs->len; i++) {
+		data = priv->configs->pdata[i];
+		if (data->config == config) {
+			if (   nm_streq (data->iface, iface)
+			    && data->type == cfg_type)
+				return FALSE;
+			else {
+				forget_data (self, data);
+				g_ptr_array_remove_index_fast (priv->configs, i);
+				break;
+			}
+		}
 	}
 
-	if (!ip_data)
-		ip_data = _ip_config_data_new (data, ip_config, ip_config_type);
-	else
-		ip_data->ip_config_type = ip_config_type;
-
-	priv->ip_config_lst_need_sort = TRUE;
-
-	p_best = NM_IS_IP4_CONFIG (ip_config)
-	         ? &priv->best_ip_config_4
-	         : &priv->best_ip_config_6;
+	data = ip_config_data_new (config, cfg_type, iface);
+	g_ptr_array_add (priv->configs, data);
+	g_signal_connect (config,
+	                  v4 ?
+	                    "notify::" NM_IP4_CONFIG_DNS_PRIORITY :
+	                    "notify::" NM_IP6_CONFIG_DNS_PRIORITY,
+	                  (GCallback) ip_config_dns_priority_changed, self);
+	priv->need_sort = TRUE;
 
-	if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) {
+	if (cfg_type == NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE) {
 		/* Only one best-device per IP version is allowed */
-		if (*p_best != ip_data) {
-			if (*p_best)
-				(*p_best)->ip_config_type = NM_DNS_IP_CONFIG_TYPE_DEFAULT;
-			*p_best = ip_data;
+		if (v4) {
+			if (priv->best_conf4)
+				priv->best_conf4->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT;
+			priv->best_conf4 = data;
+		} else {
+			if (priv->best_conf6)
+				priv->best_conf6->type = NM_DNS_IP_CONFIG_TYPE_DEFAULT;
+			priv->best_conf6 = data;
 		}
-	} else {
-		if (*p_best == ip_data)
-			*p_best = NULL;
 	}
 
-changed:
-	if (   !priv->updates_queue
-	    && !update_dns (self, FALSE, &error)) {
+	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
 		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
 	}
@@ -1438,6 +1288,38 @@ changed:
 	return TRUE;
 }
 
+gboolean
+nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config)
+{
+	NMDnsManagerPrivate *priv;
+	GError *error = NULL;
+	NMDnsIPConfigData *data;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
+	g_return_val_if_fail (config, FALSE);
+	nm_assert (NM_IS_IP_CONFIG (config));
+
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+	for (i = 0; i < priv->configs->len; i++) {
+		data = priv->configs->pdata[i];
+
+		if (data->config == config) {
+			forget_data (self, data);
+			g_ptr_array_remove_index (priv->configs, i);
+
+			if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+				_LOGW ("could not commit DNS changes: %s", error->message);
+				g_clear_error (&error);
+			}
+
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
 void
 nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                      const char *hostname)
@@ -1480,6 +1362,23 @@ nm_dns_manager_set_hostname (NMDnsManager *self,
 	}
 }
 
+gboolean
+nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self)
+{
+	NMDnsManagerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
+
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+	if (   NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED,
+	                                    NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE)
+	    || priv->plugin)
+		return FALSE;
+
+	return TRUE;
+}
+
 void
 nm_dns_manager_begin_updates (NMDnsManager *self, const char *func)
 {
@@ -1510,6 +1409,11 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	g_return_if_fail (priv->updates_queue > 0);
 
+	if (priv->need_sort) {
+		g_ptr_array_sort (priv->configs, ip_config_data_ptr_compare);
+		priv->need_sort = FALSE;
+	}
+
 	compute_hash (self, nm_config_data_get_global_dns_config (nm_config_get_data (priv->config)), new);
 	changed = (memcmp (new, priv->prev_hash, sizeof (new)) != 0) ? TRUE : FALSE;
 	_LOGD ("(%s): DNS configuration %s", func, changed ? "changed" : "did not change");
@@ -1906,14 +1810,14 @@ _get_config_variant (NMDnsManager *self)
 	NMGlobalDnsConfig *global_config;
 	gs_free char *str = NULL;
 	GVariantBuilder builder;
-	NMDnsIPConfigData *ip_data;
-	const CList *head;
-	gs_unref_ptrarray GPtrArray *array_domains = NULL;
+	NMConfigData *data;
+	guint i, j;
 
 	if (priv->config_variant)
 		return priv->config_variant;
 
-	global_config = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
+	data = nm_config_get_data (priv->config);
+	global_config = nm_config_data_get_global_dns_config (data);
 	if (global_config) {
 		priv->config_variant = _get_global_config_variant (global_config);
 		_LOGT ("current configuration: %s", (str = g_variant_print (priv->config_variant, TRUE)));
@@ -1922,26 +1826,25 @@ _get_config_variant (NMDnsManager *self)
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
 
-	head = _ip_config_lst_head (self);
-	c_list_for_each_entry (ip_data, head, ip_config_lst) {
-		const NMIPConfig *ip_config = ip_data->ip_config;
+	for (i = 0; i < priv->configs->len; i++) {
+		NMDnsIPConfigData *current = priv->configs->pdata[i];
+		const NMIPConfig *config = current->config;
 		GVariantBuilder entry_builder;
 		GVariantBuilder strv_builder;
-		guint i, num;
-		const int addr_family = nm_ip_config_get_addr_family (ip_config);
+		guint num;
+		const int addr_family = nm_ip_config_get_addr_family (config);
 		char buf[NM_UTILS_INET_ADDRSTRLEN];
 		const NMIPAddr *addr;
-		const char *ifname;
 
-		num = nm_ip_config_get_num_nameservers (ip_config);
+		num = nm_ip_config_get_num_nameservers (config);
 		if (!num)
 			continue;
 
 		g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}"));
 
 		g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
-		for (i = 0; i < num; i++) {
-			addr = nm_ip_config_get_nameserver (ip_config, i);
+		for (j = 0; j < num; j++) {
+			addr = nm_ip_config_get_nameserver (config, j);
 			g_variant_builder_add (&strv_builder,
 			                       "s",
 			                       nm_utils_inet_ntop (addr_family, addr, buf));
@@ -1951,47 +1854,36 @@ _get_config_variant (NMDnsManager *self)
 		                       "nameservers",
 		                       g_variant_builder_end (&strv_builder));
 
-
-		num = nm_ip_config_get_num_domains (ip_config);
-		num += nm_ip_config_get_num_searches (ip_config);
+		num = nm_ip_config_get_num_domains (config);
 		if (num > 0) {
-			if (!array_domains)
-				array_domains = g_ptr_array_sized_new (num);
-			else
-				g_ptr_array_set_size (array_domains, 0);
-
-			add_dns_domains (array_domains, ip_config, TRUE, FALSE);
-			if (array_domains->len) {
-				g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
-				for (i = 0; i < array_domains->len; i++) {
-					g_variant_builder_add (&strv_builder,
-					                       "s",
-					                       array_domains->pdata[i]);
-				}
-				g_variant_builder_add (&entry_builder,
-				                       "{sv}",
-				                       "domains",
-				                       g_variant_builder_end (&strv_builder));
+			g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
+			for (j = 0; j < num; j++) {
+				g_variant_builder_add (&strv_builder,
+				                       "s",
+				                       nm_ip_config_get_domain (config, j));
 			}
+			g_variant_builder_add (&entry_builder,
+			                       "{sv}",
+			                       "domains",
+			                       g_variant_builder_end (&strv_builder));
 		}
 
-		ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ip_data->data->ifindex);
-		if (ifname) {
+		if (current->iface) {
 			g_variant_builder_add (&entry_builder,
 			                       "{sv}",
 			                       "interface",
-			                       g_variant_new_string (ifname));
+			                       g_variant_new_string (current->iface));
 		}
 
 		g_variant_builder_add (&entry_builder,
 		                       "{sv}",
 		                       "priority",
-		                       g_variant_new_int32 (nm_ip_config_get_dns_priority (ip_config)));
+		                       g_variant_new_int32 (nm_ip_config_get_dns_priority (config)));
 
 		g_variant_builder_add (&entry_builder,
 		                       "{sv}",
 		                       "vpn",
-		                       g_variant_new_boolean (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN));
+		                       g_variant_new_boolean (current->type == NM_DNS_IP_CONFIG_TYPE_VPN));
 
 		g_variant_builder_add (&builder, "a{sv}", &entry_builder);
 	}
@@ -2032,12 +1924,8 @@ nm_dns_manager_init (NMDnsManager *self)
 
 	_LOGT ("creating...");
 
-	c_list_init (&priv->ip_config_lst_head);
-
 	priv->config = g_object_ref (nm_config_get ());
-
-	priv->configs = g_hash_table_new_full (nm_direct_hash, NULL,
-	                                       NULL, (GDestroyNotify) _config_data_free);
+	priv->configs = g_ptr_array_new_full (8, ip_config_data_destroy);
 
 	/* Set the initial hash */
 	compute_hash (self, NULL, NM_DNS_MANAGER_GET_PRIVATE (self)->hash);
@@ -2054,33 +1942,33 @@ dispose (GObject *object)
 {
 	NMDnsManager *self = NM_DNS_MANAGER (object);
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	NMDnsIPConfigData *ip_data, *ip_data_safe;
+	NMDnsIPConfigData *data;
+	guint i;
 
 	_LOGT ("disposing");
 
 	if (!priv->is_stopped)
 		nm_dns_manager_stop (self);
 
-	if (priv->config)
-		g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
-
 	_clear_plugin (self);
 
-	priv->best_ip_config_4 = NULL;
-	priv->best_ip_config_6 = NULL;
-
-	c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst)
-		_ip_config_data_free (ip_data);
+	if (priv->config) {
+		g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
+		g_clear_object (&priv->config);
+	}
 
-	g_clear_pointer (&priv->configs, g_hash_table_destroy);
+	if (priv->configs) {
+		for (i = 0; i < priv->configs->len; i++) {
+			data = priv->configs->pdata[i];
+			forget_data (self, data);
+		}
+		g_ptr_array_free (priv->configs, TRUE);
+		priv->configs = NULL;
+	}
 
 	nm_clear_g_source (&priv->plugin_ratelimit.timer);
 
-	g_clear_object (&priv->config);
-
 	G_OBJECT_CLASS (nm_dns_manager_parent_class)->dispose (object);
-
-	g_clear_pointer (&priv->config_variant, g_variant_unref);
 }
 
 static void
@@ -2095,30 +1983,18 @@ finalize (GObject *object)
 	G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_dns_manager = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DNS_MANAGER,
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",          "s",      NM_DNS_MANAGER_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RcManager",     "s",      NM_DNS_MANAGER_RC_MANAGER),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Configuration", "aa{sv}", NM_DNS_MANAGER_CONFIGURATION),
-		),
-	),
-};
-
 static void
 nm_dns_manager_class_init (NMDnsManagerClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass);
 
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	object_class->get_property = get_property;
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH "/DnsManager");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dns_manager);
-	dbus_object_class->export_on_construction = TRUE;
+	exported_object_class->export_path = NM_DBUS_PATH "/DnsManager";
+	exported_object_class->export_on_construction = TRUE;
 
 	obj_properties[PROP_MODE] =
 	    g_param_spec_string (NM_DNS_MANAGER_MODE, "", "",
@@ -2148,4 +2024,9 @@ nm_dns_manager_class_init (NMDnsManagerClass *klass)
 	                  0, NULL, NULL,
 	                  g_cclosure_marshal_VOID__VOID,
 	                  G_TYPE_NONE, 0);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DNS_MANAGER_SKELETON,
+	                                        NULL);
 }
+
diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h
index 56889367..b38ea701 100644
--- a/src/dns/nm-dns-manager.h
+++ b/src/dns/nm-dns-manager.h
@@ -26,14 +26,11 @@
 
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-setting-connection.h"
 
 typedef enum {
-	NM_DNS_IP_CONFIG_TYPE_REMOVED = -1,
-
 	NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0,
 	NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE,
-	NM_DNS_IP_CONFIG_TYPE_VPN,
+	NM_DNS_IP_CONFIG_TYPE_VPN
 } NMDnsIPConfigType;
 
 enum {
@@ -41,23 +38,12 @@ enum {
 	NM_DNS_PRIORITY_DEFAULT_VPN     = 50,
 };
 
-struct _NMDnsConfigData;
-struct _NMDnsManager;
-
 typedef struct {
-	struct _NMDnsConfigData *data;
-	NMIPConfig *ip_config;
-	CList data_lst;
-	CList ip_config_lst;
-	NMDnsIPConfigType ip_config_type;
+	gpointer config;
+	NMDnsIPConfigType type;
+	char *iface;
 } NMDnsIPConfigData;
 
-typedef struct _NMDnsConfigData {
-	struct _NMDnsManager *self;
-	CList data_lst_head;
-	int ifindex;
-} NMDnsConfigData;
-
 #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ())
 #define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager))
 #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
@@ -84,9 +70,12 @@ NMDnsManager * nm_dns_manager_get (void);
 void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func);
 void nm_dns_manager_end_updates (NMDnsManager *self, const char *func);
 
-gboolean nm_dns_manager_set_ip_config (NMDnsManager *self,
-                                       NMIPConfig *ip_config,
-                                       NMDnsIPConfigType ip_config_type);
+gboolean nm_dns_manager_add_ip_config (NMDnsManager *self,
+                                       const char *iface,
+                                       gpointer config,
+                                       NMDnsIPConfigType cfg_type);
+
+gboolean nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config);
 
 void nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                           const char *hostname);
@@ -123,6 +112,8 @@ typedef enum {
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,
 } NMDnsManagerResolvConfManager;
 
+gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self);
+
 void nm_dns_manager_stop (NMDnsManager *self);
 
 #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */
diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c
index d9400e3e..5805b7d8 100644
--- a/src/dns/nm-dns-plugin.c
+++ b/src/dns/nm-dns-plugin.c
@@ -77,15 +77,15 @@ G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_A
 
 gboolean
 nm_dns_plugin_update (NMDnsPlugin *self,
+                      const GPtrArray *configs,
                       const NMGlobalDnsConfig *global_config,
-                      const CList *ip_config_lst_head,
                       const char *hostname)
 {
 	g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
 
 	return NM_DNS_PLUGIN_GET_CLASS (self)->update (self,
+	                                               configs,
 	                                               global_config,
-	                                               ip_config_lst_head,
 	                                               hostname);
 }
 
diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h
index 80b77d95..996695c0 100644
--- a/src/dns/nm-dns-plugin.h
+++ b/src/dns/nm-dns-plugin.h
@@ -50,8 +50,8 @@ typedef struct {
 	 * configuration.
 	 */
 	gboolean (*update) (NMDnsPlugin *self,
+	                    const GPtrArray *configs,
 	                    const NMGlobalDnsConfig *global_config,
-	                    const CList *ip_config_lst_head,
 	                    const char *hostname);
 
 	/* Subclasses should override and return TRUE if they start a local
@@ -80,8 +80,8 @@ gboolean nm_dns_plugin_is_caching (NMDnsPlugin *self);
 const char *nm_dns_plugin_get_name (NMDnsPlugin *self);
 
 gboolean nm_dns_plugin_update (NMDnsPlugin *self,
+                               const GPtrArray *configs,
                                const NMGlobalDnsConfig *global_config,
-                               const CList *ip_config_lst_head,
                                const char *hostname);
 
 void nm_dns_plugin_stop (NMDnsPlugin *self);
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 7da27e5f..6ab2ea18 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -31,15 +31,13 @@
 #include <sys/stat.h>
 #include <linux/if.h>
 
-#include "nm-utils/nm-c-list.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
 #include "nm-utils.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "nm-manager.h"
-#include "nm-setting-connection.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
 
@@ -50,23 +48,17 @@
 
 typedef struct {
 	int ifindex;
-	CList configs_lst_head;
+	GList *configs;
 } InterfaceConfig;
 
-typedef struct {
-	CList request_queue_lst;
-	const char *operation;
-	GVariant *argument;
-} RequestItem;
-
 /*****************************************************************************/
 
 typedef struct {
 	GDBusProxy *resolve;
 	GCancellable *init_cancellable;
 	GCancellable *update_cancellable;
-	GCancellable *mdns_cancellable;
-	CList request_queue_lst_head;
+	GQueue dns_updates;
+	GQueue domain_updates;
 } NMDnsSystemdResolvedPrivate;
 
 struct _NMDnsSystemdResolved {
@@ -90,36 +82,6 @@ G_DEFINE_TYPE (NMDnsSystemdResolved, nm_dns_systemd_resolved, NM_TYPE_DNS_PLUGIN
 /*****************************************************************************/
 
 static void
-_request_item_free (RequestItem *request_item)
-{
-	c_list_unlink_stale (&request_item->request_queue_lst);
-	g_variant_unref (request_item->argument);
-	g_slice_free (RequestItem, request_item);
-}
-
-static void
-_request_item_append (CList *request_queue_lst_head,
-                      const char *operation,
-                      GVariant *argument)
-{
-	RequestItem *request_item;
-
-	request_item = g_slice_new (RequestItem);
-	request_item->operation = operation;
-	request_item->argument = g_variant_ref_sink (argument);
-	c_list_link_tail (request_queue_lst_head, &request_item->request_queue_lst);
-}
-
-/*****************************************************************************/
-
-static void
-_interface_config_free (InterfaceConfig *config)
-{
-	nm_c_list_elem_free_all (&config->configs_lst_head, NULL);
-	g_slice_free (InterfaceConfig, config);
-}
-
-static void
 call_done (GObject *source, GAsyncResult *r, gpointer user_data)
 {
 	GVariant *v;
@@ -127,58 +89,122 @@ call_done (GObject *source, GAsyncResult *r, gpointer user_data)
 	NMDnsSystemdResolved *self = (NMDnsSystemdResolved *) user_data;
 
 	v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), r, &error);
-	if (!v) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	if (error != NULL) {
 		_LOGW ("Failed: %s\n", error->message);
 		g_error_free (error);
 	}
 }
 
 static void
+add_interface_configuration (NMDnsSystemdResolved *self,
+                             GArray *interfaces,
+                             const NMDnsIPConfigData *data,
+                             gboolean skip)
+{
+	int i;
+	InterfaceConfig *ic = NULL;
+	int ifindex;
+
+	if (NM_IS_IP4_CONFIG (data->config))
+		ifindex = nm_ip4_config_get_ifindex (data->config);
+	else if (NM_IS_IP6_CONFIG  (data->config))
+		ifindex = nm_ip6_config_get_ifindex (data->config);
+	else
+		g_return_if_reached ();
+
+	for (i = 0; i < interfaces->len; i++) {
+		InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i);
+		if (ifindex == tic->ifindex) {
+			ic = tic;
+			break;
+		}
+	}
+
+	if (!ic) {
+		g_array_set_size (interfaces, interfaces->len + 1);
+		ic = &g_array_index (interfaces, InterfaceConfig,
+		                     interfaces->len - 1);
+		ic->ifindex = ifindex;
+	}
+
+	if (!skip)
+		ic->configs = g_list_append (ic->configs, data->config);
+}
+
+static void
 update_add_ip_config (NMDnsSystemdResolved *self,
                       GVariantBuilder *dns,
                       GVariantBuilder *domains,
-                      NMIPConfig *config)
+                      gpointer config)
 {
 	int addr_family;
 	gsize addr_size;
 	guint i, n;
-	gboolean is_routing;
-	const char *domain;
+	gboolean route_only;
+
+	if (NM_IS_IP4_CONFIG (config))
+		addr_family = AF_INET;
+	else if (NM_IS_IP6_CONFIG (config))
+		addr_family = AF_INET6;
+	else
+		g_return_if_reached ();
 
-	addr_family = nm_ip_config_get_addr_family (config);
 	addr_size = nm_utils_addr_family_to_size (addr_family);
 
-	n = nm_ip_config_get_num_nameservers (config);
+	n =   addr_family == AF_INET
+	    ? nm_ip4_config_get_num_nameservers (config)
+	    : nm_ip6_config_get_num_nameservers (config);
 	for (i = 0 ; i < n; i++) {
+		in_addr_t ns4;
+		gconstpointer ns;
+
+		if (addr_family == AF_INET) {
+			ns4 = nm_ip4_config_get_nameserver (config, i);
+			ns = &ns4;
+		} else
+			ns = nm_ip6_config_get_nameserver (config, i);
+
 		g_variant_builder_open (dns, G_VARIANT_TYPE ("(iay)"));
 		g_variant_builder_add (dns, "i", addr_family);
 		g_variant_builder_add_value (dns,
 		                             g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                                        nm_ip_config_get_nameserver (config, i),
+		                                                        ns,
 		                                                        addr_size,
 		                                                        1));
 		g_variant_builder_close (dns);
 	}
 
-	n = nm_ip_config_get_num_searches (config);
+	/* If this link is never the default (e.g. only used for resources on this
+	 * network) add a routing domain. */
+	route_only =   addr_family == AF_INET
+	             ? !nm_ip4_config_best_default_route_get (config)
+	             : !nm_ip6_config_best_default_route_get (config);
+
+	n =   addr_family == AF_INET
+	    ? nm_ip4_config_get_num_searches (config)
+	    : nm_ip6_config_get_num_searches (config);
 	if (n  > 0) {
 		for (i = 0; i < n; i++) {
-			domain = nm_utils_parse_dns_domain (nm_ip_config_get_search (config, i),
-			                                    &is_routing);
 			g_variant_builder_add (domains, "(sb)",
-			                       domain,
-			                       is_routing);
+			                       addr_family == AF_INET
+			                         ? nm_ip4_config_get_search (config, i)
+			                         : nm_ip6_config_get_search (config, i),
+			                       route_only);
 		}
 	} else {
-		n = nm_ip_config_get_num_domains (config);
+		n =   addr_family == AF_INET
+		    ? nm_ip4_config_get_num_domains (config)
+		    : nm_ip6_config_get_num_domains (config);
 		for (i = 0; i < n; i++) {
-			domain = nm_utils_parse_dns_domain (nm_ip_config_get_domain (config, i),
-			                                    &is_routing);
 			g_variant_builder_add (domains, "(sb)",
-			                       domain,
-			                       is_routing);
+			                       addr_family == AF_INET
+			                         ? nm_ip4_config_get_domain (config, i)
+			                         : nm_ip6_config_get_domain (config, i),
+			                       route_only);
 		}
 	}
 }
@@ -187,13 +213,13 @@ static void
 free_pending_updates (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	RequestItem *request_item, *request_item_safe;
+	GVariant *v;
 
-	c_list_for_each_entry_safe (request_item,
-	                            request_item_safe,
-	                            &priv->request_queue_lst_head,
-	                            request_queue_lst)
-		_request_item_free (request_item);
+	while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL)
+		g_variant_unref (v);
+
+	while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL)
+		g_variant_unref (v);
 }
 
 static void
@@ -201,9 +227,7 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
 	GVariantBuilder dns, domains;
-	NMCListElem *elem;
-	NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
-	const char *mdns_arg = NULL;
+	GList *l;
 
 	g_variant_builder_init (&dns, G_VARIANT_TYPE ("(ia(iay))"));
 	g_variant_builder_add (&dns, "i", ic->ifindex);
@@ -213,50 +237,23 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
 	g_variant_builder_add (&domains, "i", ic->ifindex);
 	g_variant_builder_open (&domains, G_VARIANT_TYPE ("a(sb)"));
 
-	c_list_for_each_entry (elem, &ic->configs_lst_head, lst) {
-		NMIPConfig *ip_config = elem->data;
-
-		update_add_ip_config (self, &dns, &domains, ip_config);
-
-		if (NM_IS_IP4_CONFIG (ip_config))
-			mdns = NM_MAX (mdns, nm_ip4_config_mdns_get (NM_IP4_CONFIG (ip_config)));
-	}
+	for (l = ic->configs; l; l = l->next)
+		update_add_ip_config (self, &dns, &domains, l->data);
 
 	g_variant_builder_close (&dns);
 	g_variant_builder_close (&domains);
 
-	switch (mdns) {
-	case NM_SETTING_CONNECTION_MDNS_NO:
-		mdns_arg = "no";
-		break;
-	case NM_SETTING_CONNECTION_MDNS_RESOLVE:
-		mdns_arg = "resolve";
-		break;
-	case NM_SETTING_CONNECTION_MDNS_YES:
-		mdns_arg = "yes";
-		break;
-	case NM_SETTING_CONNECTION_MDNS_DEFAULT:
-		mdns_arg = "";
-		break;
-	}
-	nm_assert (mdns_arg);
-
-	_request_item_append (&priv->request_queue_lst_head,
-	                      "SetLinkDNS",
-	                      g_variant_builder_end (&dns));
-	_request_item_append (&priv->request_queue_lst_head,
-	                      "SetLinkDomains",
-	                      g_variant_builder_end (&domains));
-	_request_item_append (&priv->request_queue_lst_head,
-	                      "SetLinkMulticastDNS",
-	                      g_variant_new ("(is)", ic->ifindex, mdns_arg ?: ""));
+	g_queue_push_tail (&priv->dns_updates,
+	                   g_variant_ref_sink (g_variant_builder_end (&dns)));
+	g_queue_push_tail (&priv->domain_updates,
+	                   g_variant_ref_sink (g_variant_builder_end (&domains)));
 }
 
 static void
 send_updates (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	RequestItem *request_item, *request_item_safe;
+	GVariant *v;
 
 	nm_clear_g_cancellable (&priv->update_cancellable);
 
@@ -265,81 +262,55 @@ send_updates (NMDnsSystemdResolved *self)
 
 	priv->update_cancellable = g_cancellable_new ();
 
-	c_list_for_each_entry_safe (request_item,
-	                            request_item_safe,
-	                            &priv->request_queue_lst_head,
-	                            request_queue_lst) {
-		g_dbus_proxy_call (priv->resolve,
-		                   request_item->operation,
-		                   request_item->argument,
+	while ((v = g_queue_pop_head (&priv->dns_updates)) != NULL) {
+		g_dbus_proxy_call (priv->resolve, "SetLinkDNS", v,
 		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->update_cancellable,
-		                   call_done,
-		                   self);
-		_request_item_free (request_item);
+		                   -1, priv->update_cancellable, call_done, self);
+		g_variant_unref (v);
+	}
+
+	while ((v = g_queue_pop_head (&priv->domain_updates)) != NULL) {
+		g_dbus_proxy_call (priv->resolve, "SetLinkDomains", v,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1, priv->update_cancellable, call_done, self);
+		g_variant_unref (v);
 	}
 }
 
 static gboolean
 update (NMDnsPlugin *plugin,
+        const GPtrArray *configs,
         const NMGlobalDnsConfig *global_config,
-        const CList *ip_config_lst_head,
         const char *hostname)
 {
 	NMDnsSystemdResolved *self = NM_DNS_SYSTEMD_RESOLVED (plugin);
-	gs_unref_hashtable GHashTable *interfaces = NULL;
-	gs_free gpointer *interfaces_keys = NULL;
-	guint interfaces_len;
+	GArray *interfaces = g_array_new (TRUE, TRUE, sizeof (InterfaceConfig));
 	guint i;
 	int prio, first_prio = 0;
-	NMDnsIPConfigData *ip_data;
-	gboolean is_first = TRUE;
-
-	interfaces = g_hash_table_new_full (nm_direct_hash, NULL,
-	                                    NULL, (GDestroyNotify) _interface_config_free);
 
-	c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) {
+	for (i = 0; i < configs->len; i++) {
+		const NMDnsIPConfigData *data = configs->pdata[i];
 		gboolean skip = FALSE;
-		InterfaceConfig *ic = NULL;
-		int ifindex;
 
-		prio = nm_ip_config_get_dns_priority (ip_data->ip_config);
-		if (is_first) {
-			is_first = FALSE;
+		prio = nm_ip_config_get_dns_priority (data->config);
+		if (i == 0)
 			first_prio = prio;
-		} else if (first_prio < 0 && first_prio != prio)
+		else if (first_prio < 0 && first_prio != prio)
 			skip = TRUE;
-
-		ifindex = ip_data->data->ifindex;
-		nm_assert (ifindex == nm_ip_config_get_ifindex (ip_data->ip_config));
-
-		ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (ifindex));
-		if (!ic) {
-			ic = g_slice_new (InterfaceConfig);
-			ic->ifindex = ifindex;
-			c_list_init (&ic->configs_lst_head);
-			g_hash_table_insert (interfaces, GINT_TO_POINTER (ifindex), ic);
-		}
-
-		if (!skip) {
-			c_list_link_tail (&ic->configs_lst_head,
-			                  &nm_c_list_elem_new_stale (ip_data->ip_config)->lst);
-		}
+		add_interface_configuration (self, interfaces, data, skip);
 	}
 
 	free_pending_updates (self);
 
-	interfaces_keys = nm_utils_hash_keys_to_array (interfaces,
-	                                               nm_cmp_int2ptr_p_with_data,
-	                                               NULL,
-	                                               &interfaces_len);
-	for (i = 0; i < interfaces_len; i++) {
-		InterfaceConfig *ic = g_hash_table_lookup (interfaces, GINT_TO_POINTER (interfaces_keys[i]));
+	for (i = 0; i < interfaces->len; i++) {
+		InterfaceConfig *ic = &g_array_index (interfaces, InterfaceConfig, i);
 
 		prepare_one_interface (self, ic);
+		g_list_free (ic->configs);
 	}
 
+	g_array_free (interfaces, TRUE);
+
 	send_updates (self);
 
 	return TRUE;
@@ -392,15 +363,16 @@ static void
 nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self)
 {
 	NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self);
-	NMDBusManager *dbus_mgr;
+	NMBusManager *dbus_mgr;
 	GDBusConnection *connection;
 
-	c_list_init (&priv->request_queue_lst_head);
+	g_queue_init (&priv->dns_updates);
+	g_queue_init (&priv->domain_updates);
 
-	dbus_mgr = nm_dbus_manager_get ();
+	dbus_mgr = nm_bus_manager_get ();
 	g_return_if_fail (dbus_mgr);
 
-	connection = nm_dbus_manager_get_connection (dbus_mgr);
+	connection = nm_bus_manager_get_connection (dbus_mgr);
 	g_return_if_fail (connection);
 
 	priv->init_cancellable = g_cancellable_new ();
@@ -432,7 +404,6 @@ dispose (GObject *object)
 	g_clear_object (&priv->resolve);
 	nm_clear_g_cancellable (&priv->init_cancellable);
 	nm_clear_g_cancellable (&priv->update_cancellable);
-	nm_clear_g_cancellable (&priv->mdns_cancellable);
 
 	G_OBJECT_CLASS (nm_dns_systemd_resolved_parent_class)->dispose (object);
 }
diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c
index e06128aa..0b80055f 100644
--- a/src/dns/nm-dns-unbound.c
+++ b/src/dns/nm-dns-unbound.c
@@ -39,8 +39,8 @@ G_DEFINE_TYPE (NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN)
 
 static gboolean
 update (NMDnsPlugin *plugin,
+        const GPtrArray *configs,
         const NMGlobalDnsConfig *global_config,
-        const CList *ip_config_lst_head,
         const char *hostname)
 {
 	char *argv[] = { DNSSEC_TRIGGER_SCRIPT, "--async", "--update", NULL };
diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c
index 041e75ea..323ef781 100644
--- a/src/dnsmasq/nm-dnsmasq-manager.c
+++ b/src/dnsmasq/nm-dnsmasq-manager.c
@@ -181,7 +181,7 @@ create_dm_cmd_line (const char *iface,
 		nm_cmd_line_add_string (cmd, "--log-queries");
 	}
 
-	/* dnsmasq may read from its default config file location, which if that
+	/* dnsmasq may read from it's default config file location, which if that
 	 * location is a valid config file, it will combine with the options here
 	 * and cause undesirable side-effects.  Like sending bogus IP addresses
 	 * as the gateway or whatever.  So tell dnsmasq not to use any config file
diff --git a/src/dnsmasq/tests/meson.build b/src/dnsmasq/tests/meson.build
deleted file mode 100644
index 40f42f6f..00000000
--- a/src/dnsmasq/tests/meson.build
+++ /dev/null
@@ -1,14 +0,0 @@
-test_unit = 'test-dnsmasq-utils'
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: '-DTESTDIR="@0@"'.format(meson.source_root())
-)
-
-test(
-  'dnsmasq/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/main.c b/src/main.c
index bd8e26a5..d59da052 100644
--- a/src/main.c
+++ b/src/main.c
@@ -39,7 +39,7 @@
 #include "NetworkManagerUtils.h"
 #include "nm-manager.h"
 #include "platform/nm-linux-platform.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "devices/nm-device.h"
 #include "dhcp/nm-dhcp-manager.h"
 #include "nm-config.h"
@@ -48,7 +48,7 @@
 #include "settings/nm-settings.h"
 #include "nm-auth-manager.h"
 #include "nm-core-internal.h"
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-connectivity.h"
 #include "dns/nm-dns-manager.h"
 #include "systemd/nm-sd.h"
@@ -93,10 +93,8 @@ static void
 _init_nm_debug (NMConfig *config)
 {
 	gs_free char *debug = NULL;
-	enum {
-		D_RLIMIT_CORE =    (1 << 0),
-		D_FATAL_WARNINGS = (1 << 1),
-	};
+	const guint D_RLIMIT_CORE = 1;
+	const guint D_FATAL_WARNINGS = 2;
 	GDebugKey keys[] = {
 		{ "RLIMIT_CORE", D_RLIMIT_CORE },
 		{ "fatal-warnings", D_FATAL_WARNINGS },
@@ -225,7 +223,6 @@ int
 main (int argc, char *argv[])
 {
 	gboolean success = FALSE;
-	NMManager *manager;
 	NMConfig *config;
 	GError *error = NULL;
 	gboolean wrote_pidfile = FALSE;
@@ -233,11 +230,13 @@ main (int argc, char *argv[])
 	NMConfigCmdLineOptions *config_cli;
 	guint sd_id = 0;
 
+	nm_g_type_init ();
+
 	/* Known to cause a possible deadlock upon GDBus initialization:
 	 * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */
 	g_type_ensure (G_TYPE_SOCKET);
 	g_type_ensure (G_TYPE_DBUS_CONNECTION);
-	g_type_ensure (NM_TYPE_DBUS_MANAGER);
+	g_type_ensure (NM_TYPE_BUS_MANAGER);
 
 	_nm_utils_is_manager_process = TRUE;
 
@@ -395,18 +394,27 @@ main (int argc, char *argv[])
 	                                                         NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
 	                                                         NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL));
 
-	manager = nm_manager_setup ();
+	nm_manager_setup ();
 
-	if (!nm_dbus_manager_start (nm_dbus_manager_get (),
-	                            nm_manager_dbus_set_property_handle,
-	                            manager))
-		goto done;
+	if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) {
+		nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
+	} else {
+		/* Start our DBus service */
+		if (!nm_bus_manager_start_service (nm_bus_manager_get ())) {
+			nm_log_err (LOGD_CORE, "failed to start the dbus service.");
+			goto done;
+		}
+	}
+
+#if WITH_CONCHECK
+	NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
+#endif
 
 	nm_dispatcher_init ();
 
-	g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
+	g_signal_connect (nm_manager_get (), NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
 
-	if (!nm_manager_start (manager, &error)) {
+	if (!nm_manager_start (nm_manager_get (), &error)) {
 		nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message);
 		goto done;
 	}
@@ -440,14 +448,11 @@ done:
 	 * state here. We don't bother updating the state as devices
 	 * change during regular operation. If NM is killed with SIGKILL,
 	 * it misses to update the state. */
-	nm_manager_write_device_state (manager);
+	nm_manager_write_device_state (nm_manager_get ());
 
-	/* FIXME: we don't properly shut down on exit. That is a bug.
-	 * NMDBusObject have an assertion that they get unexported before disposing.
-	 * We need this workaround and disable the assertion during our leaky shutdown. */
-	nm_dbus_object_set_quitting ();
+	nm_exported_object_class_set_quitting ();
 
-	nm_manager_stop (manager);
+	nm_manager_stop (nm_manager_get ());
 
 	nm_config_state_set (config, TRUE, TRUE);
 
diff --git a/src/meson.build b/src/meson.build
deleted file mode 100644
index 591c9c79..00000000
--- a/src/meson.build
+++ /dev/null
@@ -1,319 +0,0 @@
-src_inc = include_directories('.')
-
-install_data(
-  'org.freedesktop.NetworkManager.conf',
-  install_dir: dbus_conf_dir
-)
-
-subdir('systemd')
-
-core_plugins = []
-
-nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON']
-
-nm_dep = declare_dependency(
-  include_directories: src_inc,
-  dependencies: nm_core_dep,
-  compile_args: nm_cflags
-)
-
-cflags = nm_cflags + [
-  '-DPREFIX="@0@"'.format(nm_prefix),
-  '-DBINDIR="@0@"'.format(nm_bindir),
-  '-DDATADIR="@0@"'.format(nm_datadir),
-  '-DLIBEXECDIR="@0@"'.format(nm_libexecdir),
-  '-DLOCALSTATEDIR="@0@"'.format(nm_localstatedir),
-  '-DRUNSTATEDIR="@0@"'.format(nm_runstatedir),
-  '-DSBINDIR="@0@"'.format(nm_sbindir),
-  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir),
-  '-DRUNDIR="@0@"'.format(nm_pkgrundir),
-  '-DNMCONFDIR="@0@"'.format(nm_pkgconfdir),
-  '-DNMLOCALEDIR="@0@"'.format(nm_localedir),
-  '-DNMPLUGINDIR="@0@"'.format(nm_pkglibdir),
-  '-DNMRUNDIR="@0@"'.format(nm_pkgrundir),
-  '-DNMSTATEDIR="@0@"'.format(nm_pkgstatedir),
-  '-DNMLIBDIR="@0@"'.format(nm_pkglibdir)
-]
-
-if enable_dhcpcanon
-  cflags += '-DDHCPCANON_PATH="@0@"'.format(dhcpcanon.path())
-endif
-
-if enable_dhclient
-  cflags += '-DDHCLIENT_PATH="@0@"'.format(dhclient.path())
-endif
-
-if enable_dhcpcd
-  cflags += '-DDHCPCD_PATH="@0@"'.format(dhcpcd.path())
-endif
-
-sources = files(
-  'dhcp/nm-dhcp-client.c',
-  'dhcp/nm-dhcp-manager.c',
-  'dhcp/nm-dhcp-systemd.c',
-  'dhcp/nm-dhcp-utils.c',
-  'ndisc/nm-lndp-ndisc.c',
-  'ndisc/nm-ndisc.c',
-  'platform/nm-netlink.c',
-  'platform/wifi/wifi-utils-nl80211.c',
-  'platform/wifi/wifi-utils.c',
-  'platform/nm-linux-platform.c',
-  'platform/nm-platform.c',
-  'platform/nm-platform-utils.c',
-  'platform/nmp-netns.c',
-  'platform/nmp-object.c',
-  'main-utils.c',
-  'NetworkManagerUtils.c',
-  'nm-core-utils.c',
-  'nm-dbus-object.c',
-  'nm-dbus-utils.c',
-  'nm-ip4-config.c',
-  'nm-ip6-config.c',
-  'nm-logging.c'
-)
-
-deps = [
-  libsystemd_dep,
-  libudev_dep,
-  nm_core_dep
-]
-
-if enable_wext
-  sources += files('platform/wifi/wifi-utils-wext.c')
-endif
-
-libnetwork_manager_base = static_library(
-  nm_name + 'Base',
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags,
-  link_with: libnm_core
-)
-
-sources = files(
-  'devices/nm-acd-manager.c',
-  'devices/nm-device-bond.c',
-  'devices/nm-device-bridge.c',
-  'devices/nm-device.c',
-  'devices/nm-device-dummy.c',
-  'devices/nm-device-ethernet.c',
-  'devices/nm-device-ethernet-utils.c',
-  'devices/nm-device-factory.c',
-  'devices/nm-device-generic.c',
-  'devices/nm-device-infiniband.c',
-  'devices/nm-device-ip-tunnel.c',
-  'devices/nm-device-macsec.c',
-  'devices/nm-device-macvlan.c',
-  'devices/nm-device-ppp.c',
-  'devices/nm-device-tun.c',
-  'devices/nm-device-veth.c',
-  'devices/nm-device-vlan.c',
-  'devices/nm-device-vxlan.c',
-  'devices/nm-lldp-listener.c',
-  'dhcp/nm-dhcp-dhclient.c',
-  'dhcp/nm-dhcp-dhclient-utils.c',
-  'dhcp/nm-dhcp-dhcpcanon.c',
-  'dhcp/nm-dhcp-dhcpcd.c',
-  'dhcp/nm-dhcp-listener.c',
-  'dns/nm-dns-dnsmasq.c',
-  'dns/nm-dns-manager.c',
-  'dns/nm-dns-plugin.c',
-  'dns/nm-dns-systemd-resolved.c',
-  'dns/nm-dns-unbound.c',
-  'dnsmasq/nm-dnsmasq-manager.c',
-  'dnsmasq/nm-dnsmasq-utils.c',
-  'ppp/nm-ppp-manager-call.c',
-  'settings/plugins/keyfile/nms-keyfile-connection.c',
-  'settings/plugins/keyfile/nms-keyfile-plugin.c',
-  'settings/plugins/keyfile/nms-keyfile-reader.c',
-  'settings/plugins/keyfile/nms-keyfile-utils.c',
-  'settings/plugins/keyfile/nms-keyfile-writer.c',
-  'settings/nm-agent-manager.c',
-  'settings/nm-secret-agent.c',
-  'settings/nm-settings.c',
-  'settings/nm-settings-connection.c',
-  'settings/nm-settings-plugin.c',
-  'supplicant/nm-supplicant-config.c',
-  'supplicant/nm-supplicant-interface.c',
-  'supplicant/nm-supplicant-manager.c',
-  'supplicant/nm-supplicant-settings-verify.c',
-  'vpn/nm-vpn-connection.c',
-  'vpn/nm-vpn-manager.c',
-  'nm-active-connection.c',
-  'nm-act-request.c',
-  'nm-audit-manager.c',
-  'nm-auth-manager.c',
-  'nm-auth-subject.c',
-  'nm-auth-utils.c',
-  'nm-dbus-manager.c',
-  'nm-checkpoint.c',
-  'nm-checkpoint-manager.c',
-  'nm-config.c',
-  'nm-config-data.c',
-  'nm-connectivity.c',
-  'nm-dcb.c',
-  'nm-dhcp4-config.c',
-  'nm-dhcp6-config.c',
-  'nm-dispatcher.c',
-  'nm-firewall-manager.c',
-  'nm-hostname-manager.c',
-  'nm-manager.c',
-  'nm-netns.c',
-  'nm-pacrunner-manager.c',
-  'nm-policy.c',
-  'nm-proxy-config.c',
-  'nm-rfkill-manager.c',
-  'nm-session-monitor.c',
-  'nm-sleep-monitor.c'
-)
-
-deps = [
-  dl_dep,
-  libndp_dep,
-  # FIXME: Some files use introspection/dbus* headers, so
-  #        this dependency might be needed
-  #libnmdbus_dep,
-  libudev_dep,
-  nm_core_dep,
-  shared_n_acd_dep
-]
-
-if enable_concheck
-  deps += libcurl_dep
-endif
-
-if enable_libaudit
-  deps += libaudit_dep
-endif
-
-if enable_libpsl
-  deps += libpsl_dep
-endif
-
-if enable_selinux
-  deps += selinux_dep
-endif
-
-if enable_session_tracking
-  deps += logind_dep
-endif
-
-libnetwork_manager = static_library(
-  nm_name,
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags,
-  link_with: [libnetwork_manager_base, libsystemd_nm]
-)
-
-ldflags = ['-rdynamic']
-
-# FIXME: this doesn't work and it depends on libtool
-'''
-src/NetworkManager.ver: src/libNetworkManager.la $(core_plugins)
-  $(AM_V_GEN) NM="$(NM)" "$(srcdir)/tools/create-exports-NetworkManager.sh" --called-from-make "$(srcdir)"
-
-src_NetworkManager_LDFLAGS = \
-  -rdynamic \
-  -Wl,--version-script="src/NetworkManager.ver"
-
-nm = find_program('gcc-nm', 'nm')
-create_exports_networkmanager = join_paths(meson.source_root(), 'tools', 'create-exports-NetworkManager.sh')
-
-symbol_map_name = 'NetworkManager.ver'
-
-linker_script = custom_target(
-  symbol_map_name,
-  input: meson.source_root(),
-  output: symbol_map_name,
-  capture: true,
-  #command: ['NM=' + nm.path(), create_exports_networkmanager, '--called-from-make', '@INPUT@']
-  command: [create_exports_networkmanager, '--called-from-make', '@INPUT@']
-)
-
-ldflags += '-Wl,--version-script,@0@'.format(linker_script)
-'''
-
-network_manager = executable(
-  nm_name,
-  'main.c',
-  dependencies: deps,
-  c_args: cflags,
-  link_with: libnetwork_manager,
-  link_args: ldflags,
-  #FIXME
-  #link_depends: linker_script,
-  install: true,
-  install_dir: nm_sbindir
-)
-
-deps = [
-  dl_dep,
-  libndp_dep,
-  libudev_dep,
-  nm_core_dep
-]
-
-name = 'nm-iface-helper'
-
-executable(
-  name,
-  name + '.c',
-  dependencies: deps,
-  c_args: cflags,
-  link_with: [libnetwork_manager_base, libsystemd_nm],
-  link_args: ldflags_linker_script_binary,
-  link_depends: linker_script_binary,
-  install: true,
-  install_dir: nm_libexecdir
-)
-
-if enable_tests
-  sources = files(
-    'ndisc/nm-fake-ndisc.c',
-    'platform/tests/test-common.c',
-    'platform/nm-fake-platform.c'
-  )
-
-  deps = [
-    libudev_dep,
-    nm_core_dep
-  ]
-
-  test_cflags = ['-DNETWORKMANAGER_COMPILATION_TEST']
-  if require_root_tests
-    test_cflags += ['-DREQUIRE_ROOT_TESTS=1']
-  endif
-
-  platform = (host_machine.system().contains('linux') ? 'linux' : 'fake')
-  test_cflags_platform = '-DSETUP=nm_' + platform + '_platform_setup'
-
-  libnetwork_manager_test = static_library(
-    nm_name + 'Test',
-    sources: sources,
-    dependencies: deps,
-    c_args: cflags + test_cflags,
-    link_with: libnetwork_manager
-  )
-
-  test_nm_dep = declare_dependency(
-    dependencies: nm_dep,
-    compile_args: test_cflags,
-    link_with: libnetwork_manager_test
-  )
-
-  subdir('dnsmasq/tests')
-  subdir('ndisc/tests')
-  subdir('platform/tests')
-  subdir('supplicant/tests')
-  subdir('tests')
-endif
-
-subdir('dhcp')
-
-if enable_ppp
-  subdir('ppp')
-endif
-
-subdir('devices')
-subdir('settings/plugins')
diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c
index c0a0cd40..70200ed3 100644
--- a/src/ndisc/nm-lndp-ndisc.c
+++ b/src/ndisc/nm-lndp-ndisc.c
@@ -173,10 +173,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 	 */
 	{
 		const NMNDiscGateway gateway = {
-			.address = gateway_addr,
-			.timestamp = now,
-			.lifetime = ndp_msgra_router_lifetime (msgra),
-			.preference = _route_preference_coerce (ndp_msgra_route_preference (msgra)),
+		    .address = gateway_addr,
+		    .timestamp = now,
+		    .lifetime = ndp_msgra_router_lifetime (msgra),
+		    .preference = _route_preference_coerce (ndp_msgra_route_preference (msgra)),
 		};
 
 		if (nm_ndisc_add_gateway (ndisc, &gateway))
@@ -195,16 +195,12 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 			continue;
 		nm_utils_ip6_address_clear_host_address (&r_network, ndp_msg_opt_prefix (msg, offset), r_plen);
 
-		if (   IN6_IS_ADDR_UNSPECIFIED (&r_network)
-		    || IN6_IS_ADDR_LINKLOCAL (&r_network))
-			continue;
-
 		if (ndp_msg_opt_prefix_flag_on_link (msg, offset)) {
-			const NMNDiscRoute route = {
-				.network = r_network,
-				.plen = r_plen,
-				.timestamp = now,
-				.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset),
+			NMNDiscRoute route = {
+			    .network = r_network,
+			    .plen = r_plen,
+			    .timestamp = now,
+			    .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset),
 			};
 
 			if (nm_ndisc_add_route (ndisc, &route))
@@ -215,10 +211,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 		if (   r_plen == 64
 		    && ndp_msg_opt_prefix_flag_auto_addr_conf (msg, offset)) {
 			NMNDiscAddress address = {
-				.address = r_network,
-				.timestamp = now,
-				.lifetime = ndp_msg_opt_prefix_valid_time (msg, offset),
-				.preferred = ndp_msg_opt_prefix_preferred_time (msg, offset),
+			    .address = r_network,
+			    .timestamp = now,
+			    .lifetime = ndp_msg_opt_prefix_valid_time (msg, offset),
+			    .preferred = ndp_msg_opt_prefix_preferred_time (msg, offset),
 			};
 
 			if (address.preferred > address.lifetime)
@@ -229,11 +225,11 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 	}
 	ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) {
 		NMNDiscRoute route = {
-			.gateway = gateway_addr,
-			.plen = ndp_msg_opt_route_prefix_len (msg, offset),
-			.timestamp = now,
-			.lifetime = ndp_msg_opt_route_lifetime (msg, offset),
-			.preference = _route_preference_coerce (ndp_msg_opt_route_preference (msg, offset)),
+		    .gateway = gateway_addr,
+		    .plen = ndp_msg_opt_route_prefix_len (msg, offset),
+		    .timestamp = now,
+		    .lifetime = ndp_msg_opt_route_lifetime (msg, offset),
+		    .preference = _route_preference_coerce (ndp_msg_opt_route_preference (msg, offset)),
 		};
 
 		if (route.plen == 0 || route.plen > 128)
@@ -252,9 +248,9 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 
 		ndp_msg_opt_rdnss_for_each_addr (addr, addr_index, msg, offset) {
 			NMNDiscDNSServer dns_server = {
-				.address = *addr,
-				.timestamp = now,
-				.lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset),
+			    .address = *addr,
+			    .timestamp = now,
+			    .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset),
 			};
 
 			/* Pad the lifetime somewhat to give a bit of slack in cases
@@ -274,9 +270,9 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 
 		ndp_msg_opt_dnssl_for_each_domain (domain, domain_index, msg, offset) {
 			NMNDiscDNSDomain dns_domain = {
-				.domain = domain,
-				.timestamp = now,
-				.lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset),
+			    .domain = domain,
+			    .timestamp = now,
+			    .lifetime = ndp_msg_opt_rdnss_lifetime (msg, offset),
 			};
 
 			/* Pad the lifetime somewhat to give a bit of slack in cases
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index ba61cb11..6b44a96c 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -89,7 +89,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 );
 
 enum {
-	CONFIG_RECEIVED,
+	CONFIG_CHANGED,
 	RA_TIMEOUT,
 	LAST_SIGNAL
 };
@@ -235,7 +235,7 @@ static void
 _emit_config_change (NMNDisc *self, NMNDiscConfigMap changed)
 {
 	_config_changed_log (self, changed);
-	g_signal_emit (self, signals[CONFIG_RECEIVED], 0,
+	g_signal_emit (self, signals[CONFIG_CHANGED], 0,
 	               _data_complete (&NM_NDISC_GET_PRIVATE (self)->rdata),
 	               (guint) changed);
 }
@@ -349,11 +349,6 @@ nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new)
 	NMNDiscDataInternal *rdata = &priv->rdata;
 	guint i;
 
-	nm_assert (new);
-	nm_assert (new->timestamp > 0 && new->timestamp < G_MAXINT32);
-	nm_assert (!IN6_IS_ADDR_UNSPECIFIED (&new->address));
-	nm_assert (!IN6_IS_ADDR_LINKLOCAL (&new->address));
-
 	for (i = 0; i < rdata->addresses->len; i++) {
 		NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
 
@@ -797,7 +792,7 @@ nm_ndisc_start (NMNDisc *ndisc)
 }
 
 void
-nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address)
+nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address)
 {
 	NMNDiscDataInternal *rdata;
 	guint i;
@@ -892,23 +887,6 @@ get_expiry_time (guint32 timestamp, guint32 lifetime)
 		                   : (_item->lifetime) / 2); \
 	})
 
-static const char *
-_get_exp (char *buf, gsize buf_size, gint64 now_ns, gint32 expiry_time)
-{
-	int l;
-
-	if (expiry_time == G_MAXINT32)
-		return "permanent";
-	l = g_snprintf (buf, buf_size,
-	                "%.4f",
-	                ((double) ((expiry_time * NM_UTILS_NS_PER_SECOND) - now_ns)) / ((double) NM_UTILS_NS_PER_SECOND));
-	nm_assert (l < buf_size);
-	return buf;
-}
-
-#define get_exp(buf, now_ns, item) \
-	_get_exp ((buf), G_N_ELEMENTS (buf), (now_ns), (get_expiry (item)))
-
 static void
 _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
 {
@@ -918,14 +896,10 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
 	char changedstr[CONFIG_MAP_MAX_STR];
 	char addrstr[INET6_ADDRSTRLEN];
 	char str_pref[35];
-	char str_exp[100];
-	gint64 now_ns;
 
 	if (!_LOGD_ENABLED ())
 		return;
 
-	now_ns = nm_utils_get_monotonic_timestamp_ns ();
-
 	priv = NM_NDISC_GET_PRIVATE (ndisc);
 	rdata = &priv->rdata;
 
@@ -936,38 +910,35 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
 		NMNDiscGateway *gateway = &g_array_index (rdata->gateways, NMNDiscGateway, i);
 
 		inet_ntop (AF_INET6, &gateway->address, addrstr, sizeof (addrstr));
-		_LOGD ("  gateway %s pref %s exp %s", addrstr,
+		_LOGD ("  gateway %s pref %s exp %d", addrstr,
 		       nm_icmpv6_router_pref_to_string (gateway->preference, str_pref, sizeof (str_pref)),
-		       get_exp (str_exp, now_ns, gateway));
+		       get_expiry (gateway));
 	}
 	for (i = 0; i < rdata->addresses->len; i++) {
-		const NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i);
+		NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i);
 
 		inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
-		_LOGD ("  address %s exp %s", addrstr,
-		       get_exp (str_exp, now_ns, address));
+		_LOGD ("  address %s exp %d", addrstr, get_expiry (address));
 	}
 	for (i = 0; i < rdata->routes->len; i++) {
 		NMNDiscRoute *route = &g_array_index (rdata->routes, NMNDiscRoute, i);
 
 		inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr));
-		_LOGD ("  route %s/%u via %s pref %s exp %s", addrstr, (guint) route->plen,
+		_LOGD ("  route %s/%u via %s pref %s exp %d", addrstr, (guint) route->plen,
 		       nm_utils_inet6_ntop (&route->gateway, NULL),
 		       nm_icmpv6_router_pref_to_string (route->preference, str_pref, sizeof (str_pref)),
-		       get_exp (str_exp, now_ns, route));
+		       get_expiry (route));
 	}
 	for (i = 0; i < rdata->dns_servers->len; i++) {
 		NMNDiscDNSServer *dns_server = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i);
 
 		inet_ntop (AF_INET6, &dns_server->address, addrstr, sizeof (addrstr));
-		_LOGD ("  dns_server %s exp %s", addrstr,
-		       get_exp (str_exp, now_ns, dns_server));
+		_LOGD ("  dns_server %s exp %d", addrstr, get_expiry (dns_server));
 	}
 	for (i = 0; i < rdata->dns_domains->len; i++) {
 		NMNDiscDNSDomain *dns_domain = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i);
 
-		_LOGD ("  dns_domain %s exp %s", dns_domain->domain,
-		       get_exp (str_exp, now_ns, dns_domain));
+		_LOGD ("  dns_domain %s exp %d", dns_domain->domain, get_expiry (dns_domain));
 	}
 }
 
@@ -1008,7 +979,7 @@ clean_addresses (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap *changed, gint32 *
 	rdata = &NM_NDISC_GET_PRIVATE (ndisc)->rdata;
 
 	for (i = 0; i < rdata->addresses->len; ) {
-		const NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
+		NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
 
 		if (item->lifetime != NM_NDISC_INFINITY) {
 			gint32 expiry = get_expiry (item);
@@ -1383,7 +1354,7 @@ nm_ndisc_class_init (NMNDiscClass *klass)
 	                      G_PARAM_STATIC_STRINGS);
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	signals[CONFIG_RECEIVED] =
+	signals[CONFIG_CHANGED] =
 	    g_signal_new (NM_NDISC_CONFIG_RECEIVED,
 	                  G_OBJECT_CLASS_TYPE (klass),
 	                  G_SIGNAL_RUN_FIRST,
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index 9a8a27d7..b66c2289 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -27,9 +27,6 @@
 #include "nm-setting-ip6-config.h"
 #include "NetworkManagerUtils.h"
 
-#include "platform/nm-platform.h"
-#include "platform/nmp-object.h"
-
 #define NM_TYPE_NDISC            (nm_ndisc_get_type ())
 #define NM_NDISC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NDISC, NMNDisc))
 #define NM_NDISC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NDISC, NMNDiscClass))
@@ -177,7 +174,7 @@ NMNDiscNodeType nm_ndisc_get_node_type (NMNDisc *self);
 
 gboolean nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid);
 void nm_ndisc_start (NMNDisc *ndisc);
-void nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address);
+void nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address);
 void nm_ndisc_set_config (NMNDisc *ndisc,
                           const GArray *addresses,
                           const GArray *dns_servers,
@@ -187,32 +184,4 @@ NMPlatform *nm_ndisc_get_platform (NMNDisc *self);
 NMPNetns *nm_ndisc_netns_get (NMNDisc *self);
 gboolean nm_ndisc_netns_push (NMNDisc *self, NMPNetns **netns);
 
-static inline gboolean
-nm_ndisc_dad_addr_is_fail_candidate_event (NMPlatformSignalChangeType change_type,
-                                           const NMPlatformIP6Address *addr)
-{
-	return    !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TEMPORARY)
-	       && (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
-	           || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE));
-}
-
-static inline gboolean
-nm_ndisc_dad_addr_is_fail_candidate (NMPlatform *platform,
-                                     const NMPObject *obj)
-{
-	const NMPlatformIP6Address *addr;
-
-	addr = NMP_OBJECT_CAST_IP6_ADDRESS (nm_platform_lookup_obj (platform,
-	                                                            NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-	                                                            obj));
-	if (   addr
-	    && (   NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TEMPORARY)
-	        || !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED))) {
-		/* the address still/again exists and is not in DADFAILED state. Skip it. */
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
 #endif /* __NETWORKMANAGER_NDISC_H__ */
diff --git a/src/ndisc/tests/meson.build b/src/ndisc/tests/meson.build
deleted file mode 100644
index 2f479c2d..00000000
--- a/src/ndisc/tests/meson.build
+++ /dev/null
@@ -1,23 +0,0 @@
-test_unit = 'test-ndisc-fake'
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: test_cflags_platform
-)
-
-test(
-  'ndisc/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
-
-test = 'test-ndisc-linux'
-
-exe = executable(
-  test,
-  test + '.c',
-  dependencies: test_nm_dep,
-  c_args: test_cflags_platform
-)
diff --git a/src/nm-act-request.c b/src/nm-act-request.c
index 0ac20b85..dd73947d 100644
--- a/src/nm-act-request.c
+++ b/src/nm-act-request.c
@@ -28,7 +28,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
@@ -242,6 +242,8 @@ _do_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id, g
 void
 nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id)
 {
+	NMActRequestPrivate *priv;
+
 	g_return_if_fail (call_id);
 
 	if (self) {
@@ -253,6 +255,8 @@ nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId
 		self = call_id->self;
 	}
 
+	priv = NM_ACT_REQUEST_GET_PRIVATE (self);
+
 	if (!c_list_is_linked (&call_id->call_ids_lst))
 		g_return_if_reached ();
 
@@ -514,7 +518,7 @@ get_property (GObject *object, guint prop_id,
 	    || !NM_IN_SET (nm_active_connection_get_state (active),
 	                   NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
 	                   NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) {
-		g_value_set_string (value, NULL);
+		g_value_set_string (value, "/");
 		return;
 	}
 
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index c6d6645d..c3f92464 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -28,15 +28,14 @@
 #include "settings/nm-settings-connection.h"
 #include "nm-simple-connection.h"
 #include "nm-auth-utils.h"
-#include "nm-auth-manager.h"
 #include "nm-auth-subject.h"
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
-#define AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED  ((NMAuthManagerCallId *) GINT_TO_POINTER (1))
+#include "introspection/org.freedesktop.NetworkManager.Connection.Active.h"
 
 typedef struct _NMActiveConnectionPrivate {
-	NMDBusTrackObjPath settings_connection;
+	NMSettingsConnection *settings_connection;
 	NMConnection *applied_connection;
 	char *specific_object;
 	NMDevice *device;
@@ -62,15 +61,11 @@ typedef struct _NMActiveConnectionPrivate {
 
 	NMActiveConnection *parent;
 
-	struct {
-		NMAuthManagerCallId *call_id_network_control;
-		NMAuthManagerCallId *call_id_wifi_shared_permission;
-
-		NMActiveConnectionAuthResultFunc result_func;
-		gpointer user_data1;
-		gpointer user_data2;
-	} auth;
-
+	NMAuthChain *chain;
+	const char *wifi_shared_permission;
+	NMActiveConnectionAuthResultFunc result_func;
+	gpointer user_data1;
+	gpointer user_data2;
 } NMActiveConnectionPrivate;
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection,
@@ -110,19 +105,17 @@ enum {
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_ACTIVE_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMActiveConnection, NM_IS_ACTIVE_CONNECTION)
 
 /*****************************************************************************/
 
-static const NMDBusInterfaceInfoExtended interface_info_active_connection;
-static const GDBusSignalInfo signal_info_state_changed;
-
 static void check_master_ready (NMActiveConnection *self);
 static void _device_cleanup (NMActiveConnection *self);
-static void _settings_connection_flags_changed (NMSettingsConnection *settings_connection,
-                                                NMActiveConnection *self);
+static void _settings_connection_notify_flags (NMSettingsConnection *settings_connection,
+                                               GParamSpec *param,
+                                               NMActiveConnection *self);
 static void _set_activation_type_managed (NMActiveConnection *self);
 
 /*****************************************************************************/
@@ -190,24 +183,40 @@ _settings_connection_updated (NMSettingsConnection *connection,
 }
 
 static void
+_settings_connection_removed (NMSettingsConnection *connection,
+                              gpointer user_data)
+{
+	NMActiveConnection *self = user_data;
+
+	/* Our settings connection is about to drop off. The next active connection
+	 * cleanup is going to tear us down (at least until we grow the capability to
+	 * re-link; in that case we'd just clean the references to the old connection here).
+	 * Let's remove ourselves from the bus so that we're not exposed with a dangling
+	 * reference to the setting connection once it's gone. */
+	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self)))
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (self));
+}
+
+static void
 _set_settings_connection (NMActiveConnection *self, NMSettingsConnection *connection)
 {
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
-	if (priv->settings_connection.obj == connection)
+	if (priv->settings_connection == connection)
 		return;
-
-	if (priv->settings_connection.obj) {
-		g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_updated, self);
-		g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_flags_changed, self);
+	if (priv->settings_connection) {
+		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_updated, self);
+		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_removed, self);
+		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_notify_flags, self);
+		g_clear_object (&priv->settings_connection);
 	}
 	if (connection) {
+		priv->settings_connection = g_object_ref (connection);
 		g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, (GCallback) _settings_connection_updated, self);
+		g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, (GCallback) _settings_connection_removed, self);
 		if (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL)
-			g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, (GCallback) _settings_connection_flags_changed, self);
+			g_signal_connect (connection, "notify::"NM_SETTINGS_CONNECTION_FLAGS, (GCallback) _settings_connection_notify_flags, self);
 	}
-
-	nm_dbus_track_obj_path_set (&priv->settings_connection, connection, TRUE);
 }
 
 NMActiveConnectionState
@@ -216,18 +225,6 @@ nm_active_connection_get_state (NMActiveConnection *self)
 	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->state;
 }
 
-static void
-emit_state_changed (NMActiveConnection *self, guint state, guint reason)
-{
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_active_connection,
-	                            &signal_info_state_changed,
-	                            "(uu)",
-	                            (guint32) state,
-	                            (guint32) reason);
-	g_signal_emit (self, signals[STATE_CHANGED], 0, state, reason);
-}
-
 void
 nm_active_connection_set_state (NMActiveConnection *self,
                                 NMActiveConnectionState new_state,
@@ -258,14 +255,14 @@ nm_active_connection_set_state (NMActiveConnection *self,
 	old_state = priv->state;
 	priv->state = new_state;
 	priv->state_set = TRUE;
-	emit_state_changed (self, new_state, reason);
+	g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) new_state, (guint) reason);
 	_notify (self, PROP_STATE);
 
 	check_master_ready (self);
 
 	if (   new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
 	    || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		nm_settings_connection_update_timestamp (priv->settings_connection.obj,
+		nm_settings_connection_update_timestamp (priv->settings_connection,
 		                                         (guint64) time (NULL), TRUE);
 	}
 
@@ -361,7 +358,7 @@ nm_active_connection_get_settings_connection_id (NMActiveConnection *self)
 
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL);
 
-	con = NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj;
+	con = NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection;
 	return con
 	       ? nm_connection_get_id (NM_CONNECTION (con))
 	       : NULL;
@@ -372,7 +369,7 @@ _nm_active_connection_get_settings_connection (NMActiveConnection *self)
 {
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL);
 
-	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj;
+	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection;
 }
 
 NMSettingsConnection *
@@ -426,7 +423,10 @@ _set_applied_connection_take (NMActiveConnection *self,
 	if (nm_setting_connection_get_master (s_con))
 		flags_val |= NM_ACTIVATION_STATE_FLAG_IS_SLAVE;
 
-	if (+_nm_connection_type_is_master (nm_setting_connection_get_connection_type (s_con)))
+	if (NM_IN_STRSET (nm_setting_connection_get_connection_type (s_con),
+	                  NM_SETTING_BOND_SETTING_NAME,
+	                  NM_SETTING_BRIDGE_SETTING_NAME,
+	                  NM_SETTING_TEAM_SETTING_NAME))
 		flags_val |= NM_ACTIVATION_STATE_FLAG_IS_MASTER;
 
 	nm_active_connection_set_state_flags_full (self,
@@ -446,7 +446,7 @@ nm_active_connection_set_settings_connection (NMActiveConnection *self,
 	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
-	g_return_if_fail (!priv->settings_connection.obj);
+	g_return_if_fail (!priv->settings_connection);
 	g_return_if_fail (!priv->applied_connection);
 
 	/* Can't change connection after the ActiveConnection is exported over D-Bus.
@@ -456,12 +456,12 @@ nm_active_connection_set_settings_connection (NMActiveConnection *self,
 	 * never changes (once it's set). That has effects for NMVpnConnection and
 	 * NMActivationRequest.
 	 * For example, we'd have to cancel all pending seret requests. */
-	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self)));
+	g_return_if_fail (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self)));
 
 	_set_settings_connection (self, connection);
 
 	_set_applied_connection_take (self,
-	                              nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection.obj)));
+	                              nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection)));
 }
 
 gboolean
@@ -473,9 +473,9 @@ nm_active_connection_has_unmodified_applied_connection (NMActiveConnection *self
 
 	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
-	g_return_val_if_fail (priv->settings_connection.obj, FALSE);
+	g_return_val_if_fail (priv->settings_connection, FALSE);
 
-	return nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj,
+	return nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection,
 	                                                                 priv->applied_connection,
 	                                                                 compare_flags);
 }
@@ -491,10 +491,10 @@ nm_active_connection_clear_secrets (NMActiveConnection *self)
 
 	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
-	if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj,
+	if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection,
 	                                                              priv->applied_connection,
 	                                                              NM_SETTING_COMPARE_FLAG_NONE))
-		nm_connection_clear_secrets ((NMConnection *) priv->settings_connection.obj);
+		nm_connection_clear_secrets ((NMConnection *) priv->settings_connection);
 	nm_connection_clear_secrets (priv->applied_connection);
 }
 
@@ -515,9 +515,9 @@ nm_active_connection_set_specific_object (NMActiveConnection *self,
 	/* Nothing that calls this function should be using paths from D-Bus,
 	 * where NM uses "/" to mean NULL.
 	 */
-	nm_assert (!nm_streq0 (specific_object, "/"));
+	g_assert (g_strcmp0 (specific_object, "/") != 0);
 
-	if (nm_streq0 (priv->specific_object, specific_object))
+	if (g_strcmp0 (priv->specific_object, specific_object) == 0)
 		return;
 
 	g_free (priv->specific_object);
@@ -815,7 +815,7 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m
 
 	/* Master is write-once, and must be set before exporting the object */
 	g_return_if_fail (priv->master == NULL);
-	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self)));
+	g_return_if_fail (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (self)));
 	if (priv->device) {
 		/* Note, the master ActiveConnection may not yet have a device */
 		g_return_if_fail (priv->device != nm_active_connection_get_device (master));
@@ -854,11 +854,11 @@ _set_activation_type (NMActiveConnection *self,
 
 	priv->activation_type = activation_type;
 
-	if (priv->settings_connection.obj) {
+	if (priv->settings_connection) {
 		if (activation_type == NM_ACTIVATION_TYPE_EXTERNAL)
-			g_signal_connect (priv->settings_connection.obj, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, (GCallback) _settings_connection_flags_changed, self);
+			g_signal_connect (priv->settings_connection, "notify::"NM_SETTINGS_CONNECTION_FLAGS, (GCallback) _settings_connection_notify_flags, self);
 		else
-			g_signal_handlers_disconnect_by_func (priv->settings_connection.obj, _settings_connection_flags_changed, self);
+			g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_notify_flags, self);
 	}
 }
 
@@ -895,18 +895,19 @@ nm_active_connection_get_activation_reason (NMActiveConnection *self)
 /*****************************************************************************/
 
 static void
-_settings_connection_flags_changed (NMSettingsConnection *settings_connection,
-                                    NMActiveConnection *self)
+_settings_connection_notify_flags (NMSettingsConnection *settings_connection,
+                                   GParamSpec *param,
+                                   NMActiveConnection *self)
 {
 	GError *error = NULL;
 
 	nm_assert (NM_IS_ACTIVE_CONNECTION (self));
 	nm_assert (NM_IS_SETTINGS_CONNECTION (settings_connection));
 	nm_assert (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL);
-	nm_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection.obj == settings_connection);
+	nm_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection == settings_connection);
 
 	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
-	                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
+	                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED))
 		return;
 
 	_set_activation_type_managed (self);
@@ -986,93 +987,62 @@ nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *p
 /*****************************************************************************/
 
 static void
-auth_cancel (NMActiveConnection *self)
-{
-	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
-
-	if (priv->auth.call_id_network_control)
-		nm_auth_manager_check_authorization_cancel (priv->auth.call_id_network_control);
-	if (priv->auth.call_id_wifi_shared_permission) {
-		if (priv->auth.call_id_wifi_shared_permission == AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED)
-			priv->auth.call_id_wifi_shared_permission = NULL;
-		else
-			nm_auth_manager_check_authorization_cancel (priv->auth.call_id_wifi_shared_permission);
-	}
-	priv->auth.result_func = NULL;
-	priv->auth.user_data1 = NULL;
-	priv->auth.user_data2 = NULL;
-}
-
-static void
-auth_complete (NMActiveConnection *self, gboolean result, const char *message)
-{
-	_nm_unused gs_unref_object NMActiveConnection *self_keep_alive = g_object_ref (self);
-	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
-
-	priv->auth.result_func (self,
-	                        result,
-	                        message,
-	                        priv->auth.user_data1,
-	                        priv->auth.user_data2);
-	auth_cancel (self);
-}
-
-static void
-auth_done (NMAuthManager *auth_mgr,
-           NMAuthManagerCallId *auth_call_id,
-           gboolean is_authorized,
-           gboolean is_challenge,
+auth_done (NMAuthChain *chain,
            GError *error,
+           GDBusMethodInvocation *unused,
            gpointer user_data)
-
 {
 	NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data);
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 	NMAuthCallResult result;
 
-	nm_assert (auth_call_id);
-	nm_assert (priv->auth.result_func);
+	g_assert (priv->chain == chain);
+	g_assert (priv->result_func != NULL);
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		if (auth_call_id == priv->auth.call_id_network_control)
-			priv->auth.call_id_network_control = NULL;
-		else {
-			nm_assert (auth_call_id == priv->auth.call_id_wifi_shared_permission);
-			priv->auth.call_id_wifi_shared_permission = NULL;
-		}
-		return;
+	/* Must stay alive over the callback */
+	g_object_ref (self);
+
+	if (error) {
+		priv->result_func (self, FALSE, error->message, priv->user_data1, priv->user_data2);
+		goto done;
 	}
 
-	result = nm_auth_call_result_eval (is_authorized, is_challenge, error);
+	/* Caller has had a chance to obtain authorization, so we only need to
+	 * check for 'yes' here.
+	 */
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+	if (result != NM_AUTH_CALL_RESULT_YES) {
+		priv->result_func (self,
+		                   FALSE,
+		                   "Not authorized to control networking.",
+		                   priv->user_data1,
+		                   priv->user_data2);
+		goto done;
+	}
 
-	if (auth_call_id == priv->auth.call_id_network_control) {
-		priv->auth.call_id_network_control = NULL;
+	if (priv->wifi_shared_permission) {
+		result = nm_auth_chain_get_result (chain, priv->wifi_shared_permission);
 		if (result != NM_AUTH_CALL_RESULT_YES) {
-			auth_complete (self, FALSE, "Not authorized to control networking.");
-			return;
+			priv->result_func (self,
+			                   FALSE,
+			                   "Not authorized to share connections via wifi.",
+			                   priv->user_data1,
+			                   priv->user_data2);
+			goto done;
 		}
-	} else {
-		nm_assert (auth_call_id == priv->auth.call_id_wifi_shared_permission);
-		if (result != NM_AUTH_CALL_RESULT_YES) {
-			/* we don't fail right away. Instead, we mark that wifi-shared-permissions
-			 * are missing. We prefer to report the failure about network-control.
-			 * Below, we will wait longer for call_id_network_control (if it's still
-			 * pending). */
-			priv->auth.call_id_wifi_shared_permission = AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED;
-		} else
-			priv->auth.call_id_wifi_shared_permission = NULL;
 	}
 
-	if (priv->auth.call_id_network_control)
-		return;
+	/* Otherwise authorized and available to activate */
+	priv->result_func (self, TRUE, NULL, priv->user_data1, priv->user_data2);
 
-	if (priv->auth.call_id_wifi_shared_permission) {
-		if (priv->auth.call_id_wifi_shared_permission == AUTH_CALL_ID_SHARED_WIFI_PERMISSION_FAILED)
-			auth_complete (self, FALSE, "Not authorized to share connections via wifi.");
-		return;
-	}
+done:
+	nm_auth_chain_unref (chain);
+	priv->chain = NULL;
+	priv->result_func = NULL;
+	priv->user_data1 = NULL;
+	priv->user_data2 = NULL;
 
-	auth_complete (self, TRUE, NULL);
+	g_object_unref (self);
 }
 
 /**
@@ -1100,43 +1070,37 @@ nm_active_connection_authorize (NMActiveConnection *self,
 	const char *wifi_permission = NULL;
 	NMConnection *con;
 
-	g_return_if_fail (result_func);
-	g_return_if_fail (!priv->auth.call_id_network_control);
-	nm_assert (!priv->auth.call_id_wifi_shared_permission);
+	g_return_if_fail (result_func != NULL);
+	g_return_if_fail (priv->chain == NULL);
 
 	if (initial_connection) {
 		g_return_if_fail (NM_IS_CONNECTION (initial_connection));
-		g_return_if_fail (!priv->settings_connection.obj);
+		g_return_if_fail (!priv->settings_connection);
 		g_return_if_fail (!priv->applied_connection);
 		con = initial_connection;
 	} else {
-		g_return_if_fail (NM_IS_SETTINGS_CONNECTION (priv->settings_connection.obj));
+		g_return_if_fail (NM_IS_SETTINGS_CONNECTION (priv->settings_connection));
 		g_return_if_fail (NM_IS_CONNECTION (priv->applied_connection));
 		con = priv->applied_connection;
 	}
 
-	priv->auth.call_id_network_control = nm_auth_manager_check_authorization (nm_auth_manager_get (),
-	                                                                          priv->subject,
-	                                                                          NM_AUTH_PERMISSION_NETWORK_CONTROL,
-	                                                                          TRUE,
-	                                                                          auth_done,
-	                                                                          self);
+	priv->chain = nm_auth_chain_new_subject (priv->subject, NULL, auth_done, self);
+	g_assert (priv->chain);
+
+	/* Check that the subject is allowed to use networking at all */
+	nm_auth_chain_add_call (priv->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
 
 	/* Shared wifi connections require special permissions too */
 	wifi_permission = nm_utils_get_shared_wifi_permission (con);
 	if (wifi_permission) {
-		priv->auth.call_id_wifi_shared_permission = nm_auth_manager_check_authorization (nm_auth_manager_get (),
-		                                                                                 priv->subject,
-		                                                                                 wifi_permission,
-		                                                                                 TRUE,
-		                                                                                 auth_done,
-		                                                                                 self);
+		priv->wifi_shared_permission = wifi_permission;
+		nm_auth_chain_add_call (priv->chain, wifi_permission, TRUE);
 	}
 
 	/* Wait for authorization */
-	priv->auth.result_func = result_func;
-	priv->auth.user_data1 = user_data1;
-	priv->auth.user_data2 = user_data2;
+	priv->result_func = result_func;
+	priv->user_data1 = user_data1;
+	priv->user_data2 = user_data2;
 }
 
 /*****************************************************************************/
@@ -1198,40 +1162,31 @@ get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE ((NMActiveConnection *) object);
-	char **strv;
+	GPtrArray *devices;
 	NMDevice *master_device = NULL;
 
 	switch (prop_id) {
-
-	/* note that while priv->settings_connection.obj might not be set initially,
-	 * it will be set before the object is exported on D-Bus. Hence,
-	 * nobody is calling these property getters before the object is
-	 * exported, at which point we will have a valid settings-connection.
-	 *
-	 * Therefore, intentionally not check whether priv->settings_connection.obj
-	 * is set, to get an assertion failure if somebody tries to access the
-	 * getters at the wrong time. */
 	case PROP_CONNECTION:
-		g_value_set_string (value, nm_dbus_track_obj_path_get (&priv->settings_connection));
+		g_value_set_string (value, nm_connection_get_path (NM_CONNECTION (priv->settings_connection)));
 		break;
 	case PROP_ID:
-		g_value_set_string (value, nm_connection_get_id (NM_CONNECTION (priv->settings_connection.obj)));
+		g_value_set_string (value, nm_connection_get_id (NM_CONNECTION (priv->settings_connection)));
 		break;
 	case PROP_UUID:
-		g_value_set_string (value, nm_connection_get_uuid (NM_CONNECTION (priv->settings_connection.obj)));
+		g_value_set_string (value, nm_connection_get_uuid (NM_CONNECTION (priv->settings_connection)));
 		break;
 	case PROP_TYPE:
-		g_value_set_string (value, nm_connection_get_connection_type (NM_CONNECTION (priv->settings_connection.obj)));
+		g_value_set_string (value, nm_connection_get_connection_type (NM_CONNECTION (priv->settings_connection)));
 		break;
-
 	case PROP_SPECIFIC_OBJECT:
-		g_value_set_string (value, priv->specific_object);
+		g_value_set_string (value, priv->specific_object ? priv->specific_object : "/");
 		break;
 	case PROP_DEVICES:
-		strv = g_new0 (char *, 2);
+		devices = g_ptr_array_sized_new (2);
 		if (priv->device && priv->state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
-			strv[0] = g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (priv->device)));
-		g_value_take_boxed (value, strv);
+			g_ptr_array_add (devices, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (priv->device))));
+		g_ptr_array_add (devices, NULL);
+		g_value_take_boxed (value, (char **) g_ptr_array_free (devices, FALSE));
 		break;
 	case PROP_STATE:
 		if (priv->state_set)
@@ -1251,19 +1206,19 @@ get_property (GObject *object, guint prop_id,
 		break;
 	case PROP_IP4_CONFIG:
 		/* The IP and DHCP config properties may be overridden by a subclass */
-		g_value_set_string (value, NULL);
+		g_value_set_string (value, "/");
 		break;
 	case PROP_DHCP4_CONFIG:
-		g_value_set_string (value, NULL);
+		g_value_set_string (value, "/");
 		break;
 	case PROP_DEFAULT6:
 		g_value_set_boolean (value, priv->is_default6);
 		break;
 	case PROP_IP6_CONFIG:
-		g_value_set_string (value, NULL);
+		g_value_set_string (value, "/");
 		break;
 	case PROP_DHCP6_CONFIG:
-		g_value_set_string (value, NULL);
+		g_value_set_string (value, "/");
 		break;
 	case PROP_VPN:
 		g_value_set_boolean (value, priv->vpn);
@@ -1271,7 +1226,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_MASTER:
 		if (priv->master)
 			master_device = nm_active_connection_get_device (priv->master);
-		nm_dbus_utils_g_value_set_object_path (value, master_device);
+		nm_utils_g_value_set_object_path (value, master_device);
 		break;
 	case PROP_INT_SUBJECT:
 		g_value_set_object (value, priv->subject);
@@ -1345,8 +1300,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_SPECIFIC_OBJECT:
 		/* construct-only */
 		tmp = g_value_get_string (value);
-		tmp = nm_utils_dbus_normalize_object_path (tmp);
-		priv->specific_object = g_strdup (tmp);
+		/* NM uses "/" to mean NULL */
+		if (g_strcmp0 (tmp, "/") != 0)
+			priv->specific_object = g_strdup (tmp);
 		break;
 	case PROP_DEFAULT:
 		priv->is_default = g_value_get_boolean (value);
@@ -1376,10 +1332,6 @@ nm_active_connection_init (NMActiveConnection *self)
 	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate);
 	self->_priv = priv;
 
-	nm_dbus_track_obj_path_init (&priv->settings_connection,
-	                             G_OBJECT (self),
-	                             obj_properties[PROP_CONNECTION]);
-
 	c_list_init (&self->active_connections_lst);
 
 	_LOGT ("creating");
@@ -1397,8 +1349,8 @@ constructed (GObject *object)
 	G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object);
 
 	if (   !priv->applied_connection
-	    && priv->settings_connection.obj)
-		priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection.obj));
+	    && priv->settings_connection)
+		priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection));
 
 	_LOGD ("constructed (%s, version-id %llu, type %s)",
 	       G_OBJECT_TYPE_NAME (self),
@@ -1428,9 +1380,13 @@ dispose (GObject *object)
 
 	_LOGD ("disposing");
 
-	auth_cancel (self);
+	if (priv->chain) {
+		nm_auth_chain_unref (priv->chain);
+		priv->chain = NULL;
+	}
 
-	nm_clear_g_free (&priv->specific_object);
+	g_free (priv->specific_object);
+	priv->specific_object = NULL;
 
 	_set_settings_connection (self, NULL);
 	g_clear_object (&priv->applied_connection);
@@ -1453,70 +1409,21 @@ dispose (GObject *object)
 }
 
 static void
-finalize (GObject *object)
-{
-	NMActiveConnection *self = NM_ACTIVE_CONNECTION (object);
-	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
-
-	nm_dbus_track_obj_path_set (&priv->settings_connection, NULL, FALSE);
-
-	G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object);
-}
-
-static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"StateChanged",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("state",  "u"),
-		NM_DEFINE_GDBUS_ARG_INFO ("reason", "u"),
-	),
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_active_connection = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_ACTIVE_CONNECTION,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&signal_info_state_changed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connection",      "o",  NM_ACTIVE_CONNECTION_CONNECTION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("SpecificObject",  "o",  NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Id",              "s",  NM_ACTIVE_CONNECTION_ID),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Uuid",            "s",  NM_ACTIVE_CONNECTION_UUID),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Type",            "s",  NM_ACTIVE_CONNECTION_TYPE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Devices",         "ao", NM_ACTIVE_CONNECTION_DEVICES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("State",           "u",  NM_ACTIVE_CONNECTION_STATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("StateFlags",      "u",  NM_ACTIVE_CONNECTION_STATE_FLAGS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Default",         "b",  NM_ACTIVE_CONNECTION_DEFAULT),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip4Config",       "o",  NM_ACTIVE_CONNECTION_IP4_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp4Config",     "o",  NM_ACTIVE_CONNECTION_DHCP4_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Default6",        "b",  NM_ACTIVE_CONNECTION_DEFAULT6),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Ip6Config",       "o",  NM_ACTIVE_CONNECTION_IP6_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Dhcp6Config",     "o",  NM_ACTIVE_CONNECTION_DHCP6_CONFIG),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Vpn",             "b",  NM_ACTIVE_CONNECTION_VPN),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Master",          "o",  NM_ACTIVE_CONNECTION_MASTER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
-static void
 nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (ac_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ac_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (ac_class);
 
 	g_type_class_add_private (ac_class, sizeof (NMActiveConnectionPrivate));
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/ActiveConnection");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_active_connection);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/ActiveConnection");
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
-	object_class->finalize = finalize;
 
+	/* D-Bus exported properties */
 	obj_properties[PROP_CONNECTION] =
 	     g_param_spec_string (NM_ACTIVE_CONNECTION_CONNECTION, "", "",
 	                          NULL,
@@ -1701,4 +1608,9 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ac_class),
+	                                        NMDBUS_TYPE_ACTIVE_CONNECTION_SKELETON,
+	                                        NULL);
 }
+
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index c3d08b64..9b6a49ef 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -21,9 +21,10 @@
 #ifndef __NETWORKMANAGER_ACTIVE_CONNECTION_H__
 #define __NETWORKMANAGER_ACTIVE_CONNECTION_H__
 
-#include "c-list/src/c-list.h"
+#include "nm-exported-object.h"
 #include "nm-connection.h"
-#include "nm-dbus-object.h"
+
+#include "nm-utils/c-list.h"
 
 #define NM_TYPE_ACTIVE_CONNECTION            (nm_active_connection_get_type ())
 #define NM_ACTIVE_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection))
@@ -71,7 +72,7 @@
 struct _NMActiveConnectionPrivate;
 
 struct _NMActiveConnection {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	struct _NMActiveConnectionPrivate *_priv;
 
 	/* active connection can be tracked in a list by NMManager. This is
@@ -80,7 +81,7 @@ struct _NMActiveConnection {
 };
 
 typedef struct {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 
 	/* re-emits device state changes as a convenience for subclasses for
 	 * device states >= DISCONNECTED.
diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c
index 45d068f9..2d75c0e7 100644
--- a/src/nm-audit-manager.c
+++ b/src/nm-audit-manager.c
@@ -123,7 +123,7 @@ build_message (GPtrArray *fields, AuditBackend backend)
 	for (i = 0; i < fields->len; i++) {
 		field = fields->pdata[i];
 
-		if (!NM_FLAGS_ANY (field->backends, backend))
+		if (!NM_FLAGS_HAS (field->backends, backend))
 			continue;
 
 		if (first)
diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h
index 59b8048f..56e26584 100644
--- a/src/nm-audit-manager.h
+++ b/src/nm-audit-manager.h
@@ -57,7 +57,6 @@ typedef struct _NMAuditManagerClass NMAuditManagerClass;
 #define NM_AUDIT_OP_CHECKPOINT_CREATE       "checkpoint-create"
 #define NM_AUDIT_OP_CHECKPOINT_ROLLBACK     "checkpoint-rollback"
 #define NM_AUDIT_OP_CHECKPOINT_DESTROY      "checkpoint-destroy"
-#define NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT "checkpoint-adjust-rollback-timeout"
 
 GType nm_audit_manager_get_type (void);
 NMAuditManager *nm_audit_manager_get (void);
diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c
index 199a2e40..003d9975 100644
--- a/src/nm-auth-manager.c
+++ b/src/nm-auth-manager.c
@@ -22,7 +22,6 @@
 
 #include "nm-auth-manager.h"
 
-#include "c-list/src/c-list.h"
 #include "nm-errors.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
@@ -31,9 +30,6 @@
 #define POLKIT_OBJECT_PATH                  "/org/freedesktop/PolicyKit1/Authority"
 #define POLKIT_INTERFACE                    "org.freedesktop.PolicyKit1.Authority"
 
-#define CANCELLATION_ID_PREFIX "cancellation-id-"
-#define CANCELLATION_TIMEOUT_MS 5000
-
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
@@ -48,14 +44,13 @@ enum {
 static guint signals[LAST_SIGNAL] = {0};
 
 typedef struct {
-	CList calls_lst_head;
-	GDBusProxy *proxy;
+	gboolean polkit_enabled;
+#if WITH_POLKIT
+	guint call_id_counter;
 	GCancellable *new_proxy_cancellable;
-	GCancellable *cancel_cancellable;
-	guint64 call_numid_counter;
-	bool polkit_enabled:1;
-	bool disposing:1;
-	bool shutting_down:1;
+	GSList *queued_calls;
+	GDBusProxy *proxy;
+#endif
 } NMAuthManagerPrivate;
 
 struct _NMAuthManager {
@@ -90,22 +85,6 @@ NM_DEFINE_SINGLETON_REGISTER (NMAuthManager);
         } \
     } G_STMT_END
 
-#define _NMLOG2(level, call_id, ...) \
-    G_STMT_START { \
-        if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
-            NMAuthManagerCallId *_call_id = (call_id); \
-            char __prefix[30] = _NMLOG_PREFIX_NAME; \
-            \
-            if (_call_id->self != singleton_instance) \
-                g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"[%p]", _call_id->self); \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
-                     "%s: call[%"G_GUINT64_FORMAT"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                     __prefix, \
-                     _call_id->call_numid \
-                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-        } \
-    } G_STMT_END
-
 /*****************************************************************************/
 
 gboolean
@@ -118,323 +97,251 @@ nm_auth_manager_get_polkit_enabled (NMAuthManager *self)
 
 /*****************************************************************************/
 
+#if WITH_POLKIT
+
 typedef enum {
 	POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE                   = 0,
 	POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION = (1<<0),
 } PolkitCheckAuthorizationFlags;
 
-typedef enum {
-	IDLE_REASON_AUTHORIZED,
-	IDLE_REASON_NO_DBUS,
-} IdleReason;
-
-struct _NMAuthManagerCallId {
-	CList calls_lst;
+typedef struct {
+	guint call_id;
 	NMAuthManager *self;
+	GSimpleAsyncResult *simple;
+	gchar *cancellation_id;
 	GVariant *dbus_parameters;
-	GCancellable *dbus_cancellable;
-	NMAuthManagerCheckAuthorizationCallback callback;
-	gpointer user_data;
-	guint64 call_numid;
-	guint idle_id;
-	IdleReason idle_reason:8;
-};
-
-#define cancellation_id_to_str_a(call_numid) \
-	nm_sprintf_bufa (NM_STRLEN (CANCELLATION_ID_PREFIX) + 20, \
-	                 CANCELLATION_ID_PREFIX"%"G_GUINT64_FORMAT, \
-	                 (call_numid))
+	GCancellable *cancellable;
+} CheckAuthData;
 
 static void
-_call_id_free (NMAuthManagerCallId *call_id)
+_check_auth_data_free (CheckAuthData *data)
 {
-	c_list_unlink (&call_id->calls_lst);
-	nm_clear_g_source (&call_id->idle_id);
-	if (call_id->dbus_parameters)
-		g_variant_unref (g_steal_pointer (&call_id->dbus_parameters));
-
-	if (call_id->dbus_cancellable) {
-		/* we have a pending D-Bus call. We keep the call-id instance alive
-		 * for _call_check_authorize_cb() */
-		g_cancellable_cancel (call_id->dbus_cancellable);
-		return;
-	}
-
-	g_object_unref (call_id->self);
-	g_slice_free (NMAuthManagerCallId, call_id);
+	if (data->dbus_parameters)
+		g_variant_unref (data->dbus_parameters);
+	g_object_unref (data->self);
+	g_object_unref (data->simple);
+	g_clear_object (&data->cancellable);
+	g_free (data->cancellation_id);
+	g_free (data);
 }
 
 static void
-_call_id_invoke_callback (NMAuthManagerCallId *call_id,
-                          gboolean is_authorized,
-                          gboolean is_challenge,
-                          GError *error)
+_call_check_authorization_complete_with_error (CheckAuthData *data,
+                                               const char *error_message)
 {
-	c_list_unlink (&call_id->calls_lst);
-
-	call_id->callback (call_id->self,
-	                   call_id,
-	                   is_authorized,
-	                   is_challenge,
-	                   error,
-	                   call_id->user_data);
-	_call_id_free (call_id);
+	NMAuthManager *self = data->self;
+
+	_LOGD ("call[%u]: CheckAuthorization failed due to internal error: %s", data->call_id, error_message);
+	g_simple_async_result_set_error (data->simple,
+	                                 NM_MANAGER_ERROR,
+	                                 NM_MANAGER_ERROR_FAILED,
+	                                 "Authorization check failed: %s",
+	                                 error_message);
+
+	g_simple_async_result_complete_in_idle (data->simple);
+
+	_check_auth_data_free (data);
 }
 
 static void
-cancel_check_authorization_cb (GObject *proxy,
+cancel_check_authorization_cb (GDBusProxy *proxy,
                                GAsyncResult *res,
                                gpointer user_data)
 {
-	NMAuthManagerCallId *call_id = user_data;
-	gs_unref_variant GVariant *value = NULL;
-	gs_free_error GError *error= NULL;
-
-	value = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		_LOG2T (call_id, "cancel request was cancelled");
-	else if (error)
-		_LOG2T (call_id, "cancel request failed: %s", error->message);
-	else
-		_LOG2T (call_id, "cancel request succeeded");
+	NMAuthManager *self = user_data;
+	GVariant *value;
+	GError *error= NULL;
+
+	value = g_dbus_proxy_call_finish (proxy, res, &error);
+	if (value == NULL) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGD ("Error cancelling authorization check: %s", error->message);
+		g_error_free (error);
+	} else
+		g_variant_unref (value);
 
-	_call_id_free (call_id);
+	g_object_unref (self);
 }
 
+typedef struct {
+	gboolean is_authorized;
+	gboolean is_challenge;
+} CheckAuthorizationResult;
+
 static void
-_call_check_authorize_cb (GObject *proxy,
-                          GAsyncResult *res,
-                          gpointer user_data)
+check_authorization_cb (GDBusProxy *proxy,
+                        GAsyncResult *res,
+                        gpointer user_data)
 {
-	NMAuthManagerCallId *call_id = user_data;
-	NMAuthManager *self;
-	NMAuthManagerPrivate *priv;
-	gs_unref_variant GVariant *value = NULL;
-	gs_free_error GError *error = NULL;
-	gboolean is_authorized = FALSE;
-	gboolean is_challenge = FALSE;
-
-	/* we need to clear the cancelable, to signal for _call_id_free() that we
-	 * are not in a pending call.
-	 *
-	 * Note how _call_id_free() kept call-id alive, even if the request was
-	 * already cancelled. */
-	g_clear_object (&call_id->dbus_cancellable);
-
-	self = call_id->self;
-	priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
-
-	value = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, G_VARIANT_TYPE ("((bba{ss}))"), &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		/* call_id was cancelled externally, but _call_id_free() kept call_id
-		 * alive (and it has still the reference on @self. */
-
-		if (!priv->cancel_cancellable) {
-			/* we do a forced shutdown. There is no more time for cancelling... */
-			_call_id_free (call_id);
-
-			/* this shouldn't really happen, because:
-			 * _call_check_authorize() only scheduled the D-Bus request at a time when
-			 * cancel_cancellable was still set. It means, somebody called force-shutdown
-			 * after call-id was schedule.
-			 * force-shutdown should only be called after:
-			 *   - cancel all pending requests
-			 *   - give enough time to cancel the request and schedule a D-Bus call
-			 *     to CancelCheckAuthorization (below), before issuing force-shutdown. */
-			g_return_if_reached ();
-		}
+	CheckAuthData *data = user_data;
+	NMAuthManager *self = data->self;
+	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
+	GVariant *value;
+	GError *error = NULL;
+
+	value = _nm_dbus_proxy_call_finish (proxy, res, G_VARIANT_TYPE ("((bba{ss}))"), &error);
+	if (value == NULL) {
+		if (data->cancellation_id != NULL &&
+		    (   g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
+		     && !g_dbus_error_is_remote_error (error))) {
+			_LOGD ("call[%u]: CheckAuthorization cancelled", data->call_id);
+			g_dbus_proxy_call (priv->proxy,
+			                   "CancelCheckAuthorization",
+			                   g_variant_new ("(s)", data->cancellation_id),
+			                   G_DBUS_CALL_FLAGS_NONE,
+			                   -1,
+			                   NULL, /* GCancellable */
+			                   (GAsyncReadyCallback) cancel_check_authorization_cb,
+			                   g_object_ref (self));
+		} else
+			_LOGD ("call[%u]: CheckAuthorization failed: %s", data->call_id, error->message);
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_set_error (data->simple,
+		                                 NM_MANAGER_ERROR,
+		                                 NM_MANAGER_ERROR_FAILED,
+		                                 "Authorization check failed: %s",
+		                                 error->message);
+		g_error_free (error);
+	} else {
+		CheckAuthorizationResult *result;
 
-		g_dbus_proxy_call (priv->proxy,
-		                   "CancelCheckAuthorization",
-		                   g_variant_new ("(s)",
-		                                  cancellation_id_to_str_a (call_id->call_numid)),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   CANCELLATION_TIMEOUT_MS,
-		                   priv->cancel_cancellable,
-		                   cancel_check_authorization_cb,
-		                   call_id);
-		return;
-	}
+		result = g_new0 (CheckAuthorizationResult, 1);
 
-	if (!error) {
 		g_variant_get (value,
 		               "((bb@a{ss}))",
-		               &is_authorized,
-		               &is_challenge,
+		               &result->is_authorized,
+		               &result->is_challenge,
 		               NULL);
-		_LOG2T (call_id, "completed: authorized=%d, challenge=%d",
-		        is_authorized, is_challenge);
-	} else
-		_LOG2T (call_id, "completed: failed: %s", error->message);
+		g_variant_unref (value);
+
+		_LOGD ("call[%u]: CheckAuthorization succeeded: (is_authorized=%d, is_challenge=%d)", data->call_id, result->is_authorized, result->is_challenge);
+		g_simple_async_result_set_op_res_gpointer (data->simple, result, g_free);
+	}
+
+	g_simple_async_result_complete (data->simple);
 
-	_call_id_invoke_callback (call_id, is_authorized, is_challenge, error);
+	_check_auth_data_free (data);
 }
 
 static void
-_call_check_authorize (NMAuthManagerCallId *call_id)
+_call_check_authorization (CheckAuthData *data)
 {
-	NMAuthManager *self = call_id->self;
-	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
-
-	nm_assert (call_id->dbus_parameters);
-	nm_assert (g_variant_is_floating (call_id->dbus_parameters));
-	nm_assert (!call_id->dbus_cancellable);
-
-	call_id->dbus_cancellable = g_cancellable_new ();
-
-	nm_assert (priv->cancel_cancellable);
+	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (data->self);
 
 	g_dbus_proxy_call (priv->proxy,
 	                   "CheckAuthorization",
-	                   g_steal_pointer (&call_id->dbus_parameters),
+	                   data->dbus_parameters,
 	                   G_DBUS_CALL_FLAGS_NONE,
 	                   G_MAXINT, /* no timeout */
-	                   call_id->dbus_cancellable,
-	                   _call_check_authorize_cb,
-	                   call_id);
+	                   data->cancellable,
+	                   (GAsyncReadyCallback) check_authorization_cb,
+	                   data);
+	g_clear_object (&data->cancellable);
+	data->dbus_parameters = NULL;
 }
 
-static gboolean
-_call_on_idle (gpointer user_data)
-{
-	NMAuthManagerCallId *call_id = user_data;
-	gs_free_error GError *error = NULL;
-	gboolean is_authorized = FALSE;
-	gboolean is_challenge = FALSE;
-	const char *error_msg = NULL;
-
-	call_id->idle_id = 0;
-	if (call_id->idle_reason == IDLE_REASON_AUTHORIZED) {
-		is_authorized = TRUE;
-		_LOG2T (call_id, "completed: authorized=%d, challenge=%d (simulated)",
-		        is_authorized, is_challenge);
-	} else {
-		nm_assert (call_id->idle_reason == IDLE_REASON_NO_DBUS);
-		error_msg = "failure creating GDBusProxy for authorization request";
-		_LOG2T (call_id, "completed: failed due to no D-Bus proxy");
-	}
-
-	if (error_msg)
-		g_set_error_literal (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, error_msg);
-	_call_id_invoke_callback (call_id, is_authorized, is_challenge, error);
-	return G_SOURCE_REMOVE;
-}
-
-/*
- * @callback must never be invoked synchronously.
- *
- * @callback is always invoked exactly once, and never synchronously.
- * You may cancel the invocation with nm_auth_manager_check_authorization_cancel(),
- * but: you may only do so exactly once, and only before @callback is
- * invoked. Even if you cancel the request, @callback will still be invoked
- * (synchronously, during the _cancel() callback).
- *
- * The request keeps @self alive (it needs to do so, because when cancelling a
- * request we might need to do an additional CancelCheckAuthorization call, for
- * which @self must be live long enough).
- */
-NMAuthManagerCallId *
-nm_auth_manager_check_authorization (NMAuthManager *self,
-                                     NMAuthSubject *subject,
-                                     const char *action_id,
-                                     gboolean allow_user_interaction,
-                                     NMAuthManagerCheckAuthorizationCallback callback,
-                                     gpointer user_data)
+void
+nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self,
+                                                      NMAuthSubject *subject,
+                                                      const char *action_id,
+                                                      gboolean allow_user_interaction,
+                                                      GCancellable *cancellable,
+                                                      GAsyncReadyCallback callback,
+                                                      gpointer user_data)
 {
 	NMAuthManagerPrivate *priv;
-	PolkitCheckAuthorizationFlags flags;
 	char subject_buf[64];
 	GVariantBuilder builder;
+	PolkitCheckAuthorizationFlags flags;
 	GVariant *subject_value;
 	GVariant *details_value;
-	NMAuthManagerCallId *call_id;
+	CheckAuthData *data;
 
-	g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), NULL);
-	g_return_val_if_fail (NM_IN_SET (nm_auth_subject_get_subject_type (subject),
-	                                 NM_AUTH_SUBJECT_TYPE_INTERNAL,
-	                                 NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS),
-	                      NULL);
-	g_return_val_if_fail (action_id, NULL);
+	g_return_if_fail (NM_IS_AUTH_MANAGER (self));
+	g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
+	g_return_if_fail (nm_auth_subject_is_unix_process (subject));
+	g_return_if_fail (action_id != NULL);
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
 	priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
 
-	g_return_val_if_fail (!priv->disposing, NULL);
-	g_return_val_if_fail (!priv->shutting_down, NULL);
+	g_return_if_fail (priv->polkit_enabled);
 
 	flags = allow_user_interaction
 	    ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION
 	    : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
 
-	call_id = g_slice_new0 (NMAuthManagerCallId);
-	call_id->self = g_object_ref (self);
-	call_id->callback = callback;
-	call_id->user_data = user_data;
-	call_id->call_numid = ++priv->call_numid_counter;
-	c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst);
-
-	if (!priv->polkit_enabled) {
-		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding due to polkit authorization disabled)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
-		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
-	} else if (nm_auth_subject_is_internal (subject)) {
-		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for internal request)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
-		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
-	} else if (nm_auth_subject_get_unix_process_uid (subject) == 0) {
-		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for root)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_AUTHORIZED;
-		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
-	} else if (   !priv->proxy
-	           && !priv->new_proxy_cancellable) {
-		_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (failing due to invalid DBUS proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		call_id->idle_reason = IDLE_REASON_NO_DBUS;
-		call_id->idle_id = g_idle_add (_call_on_idle, call_id);
-	} else {
-		subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject);
-		nm_assert (g_variant_is_floating (subject_value));
-
-		/* ((PolkitDetails *)NULL) */
-		g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
-		details_value = g_variant_builder_end (&builder);
-
-		call_id->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)",
-		                                          subject_value,
-		                                          action_id,
-		                                          details_value,
-		                                          (guint32) flags,
-		                                          cancellation_id_to_str_a (call_id->call_numid));
-		if (!priv->proxy) {
-			_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (wait for proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-		} else {
-			_LOG2T (call_id, "CheckAuthorization(%s), subject=%s", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
-			_call_check_authorize (call_id);
-		}
+	subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject);
+	nm_assert (g_variant_is_floating (subject_value));
+
+	/* ((PolkitDetails *)NULL) */
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+	details_value = g_variant_builder_end (&builder);
+
+	data = g_new0 (CheckAuthData, 1);
+	data->call_id = ++priv->call_id_counter;
+	data->self = g_object_ref (self);
+	data->simple = g_simple_async_result_new (G_OBJECT (self),
+	                                          callback,
+	                                          user_data,
+	                                          nm_auth_manager_polkit_authority_check_authorization);
+	if (cancellable != NULL) {
+		data->cancellation_id = g_strdup_printf ("cancellation-id-%u", data->call_id);
+		data->cancellable = g_object_ref (cancellable);
 	}
 
-	return call_id;
+	data->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)",
+	                                       subject_value,
+	                                       action_id,
+	                                       details_value,
+	                                       (guint32) flags,
+	                                       data->cancellation_id != NULL ? data->cancellation_id : "");
+
+	if (priv->new_proxy_cancellable) {
+		_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (wait for proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
+
+		priv->queued_calls = g_slist_prepend (priv->queued_calls, data);
+	} else if (!priv->proxy) {
+		_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s (fails due to invalid DBUS proxy)", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
+
+		_call_check_authorization_complete_with_error (data, "invalid DBUS proxy");
+	} else {
+		_LOGD ("call[%u]: CheckAuthorization(%s), subject=%s", data->call_id, action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
+
+		_call_check_authorization (data);
+	}
 }
 
-void
-nm_auth_manager_check_authorization_cancel (NMAuthManagerCallId *call_id)
+gboolean
+nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self,
+                                                             GAsyncResult *res,
+                                                             gboolean *out_is_authorized,
+                                                             gboolean *out_is_challenge,
+                                                             GError **error)
 {
-	NMAuthManager *self;
-	gs_free_error GError *error = NULL;
-
-	g_return_if_fail (call_id);
+	gboolean success = FALSE;
+	gboolean is_authorized = FALSE;
+	gboolean is_challenge = FALSE;
 
-	self = call_id->self;
+	g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	g_return_if_fail (NM_IS_AUTH_MANAGER (self));
-	g_return_if_fail (!c_list_is_empty (&call_id->calls_lst));
+	if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) {
+		CheckAuthorizationResult *result;
 
-	nm_assert (c_list_contains (&NM_AUTH_MANAGER_GET_PRIVATE (self)->calls_lst_head, &call_id->calls_lst));
+		result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+		is_authorized = !!result->is_authorized;
+		is_challenge = !!result->is_challenge;
+		success = TRUE;
+	}
+	g_assert ((success && !error) || (!success || error));
 
-	nm_utils_error_set_cancelled (&error, FALSE, "NMAuthManager");
-	_LOG2T (call_id, "completed: failed due to call cancelled");
-	_call_id_invoke_callback (call_id,
-	                          FALSE,
-	                          FALSE,
-	                          error);
+	if (out_is_authorized)
+		*out_is_authorized = is_authorized;
+	if (out_is_challenge)
+		*out_is_challenge = is_challenge;
+	return success;
 }
 
 /*****************************************************************************/
@@ -443,14 +350,14 @@ static void
 _emit_changed_signal (NMAuthManager *self)
 {
 	_LOGD ("emit changed signal");
-	g_signal_emit (self, signals[CHANGED_SIGNAL], 0);
+	g_signal_emit_by_name (self, NM_AUTH_MANAGER_SIGNAL_CHANGED);
 }
 
 static void
 _log_name_owner (NMAuthManager *self, char **out_name_owner)
 {
 	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
-	gs_free char *name_owner = NULL;
+	char *name_owner;
 
 	name_owner = g_dbus_proxy_get_name_owner (priv->proxy);
 	if (name_owner)
@@ -458,7 +365,10 @@ _log_name_owner (NMAuthManager *self, char **out_name_owner)
 	else
 		_LOGD ("dbus name owner: none");
 
-	NM_SET_OUT (out_name_owner, g_steal_pointer (&name_owner));
+	if (out_name_owner)
+		*out_name_owner = name_owner;
+	else
+		g_free (name_owner);
 }
 
 static void
@@ -467,16 +377,20 @@ _dbus_on_name_owner_notify_cb (GObject    *object,
                                gpointer    user_data)
 {
 	NMAuthManager *self = user_data;
-	gs_free char *name_owner = NULL;
+	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
+	char *name_owner;
 
-	nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == (GDBusProxy *) object);
+	g_return_if_fail (priv->proxy == (void *) object);
 
 	_log_name_owner (self, &name_owner);
+
 	if (!name_owner) {
 		/* when the name disappears, we also want to raise a emit signal.
 		 * When it appears, we raise one already. */
 		_emit_changed_signal (self);
 	}
+
+	g_free (name_owner);
 }
 
 static void
@@ -484,8 +398,9 @@ _dbus_on_changed_signal_cb (GDBusProxy *proxy,
                             gpointer    user_data)
 {
 	NMAuthManager *self = user_data;
+	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
 
-	nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == proxy);
+	g_return_if_fail (priv->proxy == proxy);
 
 	_LOGD ("dbus signal: \"Changed\"");
 	_emit_changed_signal (self);
@@ -496,39 +411,49 @@ _dbus_new_proxy_cb (GObject *source_object,
                     GAsyncResult *res,
                     gpointer user_data)
 {
-	NMAuthManager *self;
+	NMAuthManager **p_self = user_data;
+	NMAuthManager *self = NULL;
 	NMAuthManagerPrivate *priv;
-	gs_free GError *error = NULL;
+	GError *error = NULL;
 	GDBusProxy *proxy;
-	NMAuthManagerCallId *call_id;
+	CheckAuthData *data;
 
 	proxy = g_dbus_proxy_new_for_bus_finish  (res, &error);
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+	if (!*p_self) {
+		_LOGD ("_dbus_new_proxy_cb(): manager destroyed before callback finished. Abort");
+		g_clear_object (&proxy);
+		g_clear_error (&error);
+		g_free (p_self);
 		return;
+	}
+	self = *p_self;
+	g_object_remove_weak_pointer (G_OBJECT (self), (void **)p_self);
+	g_free (p_self);
 
-	self = user_data;
 	priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
 
-	priv->proxy = proxy;
+	g_return_if_fail (priv->new_proxy_cancellable);
+	g_return_if_fail (!priv->proxy);
+
 	g_clear_object (&priv->new_proxy_cancellable);
 
+	priv->queued_calls = g_slist_reverse (priv->queued_calls);
+
+	priv->proxy = proxy;
 	if (!priv->proxy) {
-		_LOGE ("could not create polkit proxy: %s", error->message);
-
-again:
-		c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) {
-			if (call_id->dbus_parameters) {
-				_LOG2T (call_id, "completed: failed due to no D-Bus proxy after startup");
-				_call_id_invoke_callback (call_id, FALSE, FALSE, error);
-				goto again;
-			}
+		_LOGE ("could not get polkit proxy: %s", error->message);
+		g_clear_error (&error);
+
+		while (priv->queued_calls) {
+			data = priv->queued_calls->data;
+			priv->queued_calls = g_slist_remove (priv->queued_calls, data);
+
+			_call_check_authorization_complete_with_error (data, "error creating DBUS proxy");
 		}
 		return;
 	}
 
-	priv->cancel_cancellable = g_cancellable_new ();
-
 	g_signal_connect (priv->proxy,
 	                  "notify::g-name-owner",
 	                  G_CALLBACK (_dbus_on_name_owner_notify_cb),
@@ -539,16 +464,17 @@ again:
 
 	_log_name_owner (self, NULL);
 
-	c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) {
-		if (call_id->dbus_parameters) {
-			_LOG2T (call_id, "CheckAuthorization invoke now");
-			_call_check_authorize (call_id);
-		}
+	while (priv->queued_calls) {
+		data = priv->queued_calls->data;
+		priv->queued_calls = g_slist_remove (priv->queued_calls, data);
+		_LOGD ("call[%u]: CheckAuthorization invoke now", data->call_id);
+		_call_check_authorization (data);
 	}
-
 	_emit_changed_signal (self);
 }
 
+#endif
+
 /*****************************************************************************/
 
 NMAuthManager *
@@ -559,42 +485,23 @@ nm_auth_manager_get ()
 	return singleton_instance;
 }
 
-void
-nm_auth_manager_force_shutdown (NMAuthManager *self)
-{
-	NMAuthManagerPrivate *priv;
-
-	g_return_if_fail (NM_IS_AUTH_MANAGER (self));
+/*****************************************************************************/
 
-	priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE ((NMAuthManager *) object);
 
-	/* while we have pending requests (NMAuthManagerCallId), the instance
-	 * is kept alive.
-	 *
-	 * Even if the caller cancells all pending call-ids, we still need to keep
-	 * a reference to self, in order to handle pending CancelCheckAuthorization
-	 * requests.
-	 *
-	 * To do a corrdinated shutdown, do the following:
-	 * - cancel all pending NMAuthManagerCallId requests.
-	 * - ensure everybody unrefs the NMAuthManager instance. If by that, the instance
-	 *   gets destroyed, the shutdown already completed successfully.
-	 * - Otherwise, the object is kept alive by pending CancelCheckAuthorization requests.
-	 *   wait a certain timeout (1 second) for all requests to complete (by watching
-	 *   for destruction of NMAuthManager).
-	 * - if that doesn't happen within timeout, issue nm_auth_manager_force_shutdown() and
-	 *   wait longer. After that, soon the instance should be destroyed and you
-	 *   did a successful shutdown.
-	 * - if the instance was still not destroyed within a short timeout, you leaked
-	 *   resources. You cannot properly shutdown.
-	 */
-
-	priv->shutting_down = TRUE;
-	nm_clear_g_cancellable (&priv->cancel_cancellable);
+	switch (prop_id) {
+	case PROP_POLKIT_ENABLED:
+		g_value_set_boolean (value, priv->polkit_enabled);
+		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)
 {
@@ -616,9 +523,6 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 static void
 nm_auth_manager_init (NMAuthManager *self)
 {
-	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
-
-	c_list_init (&priv->calls_lst_head);
 }
 
 static void
@@ -629,10 +533,16 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object);
 
+#if WITH_POLKIT
 	_LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled");
 
 	if (priv->polkit_enabled) {
+		NMAuthManager **p_self;
+
 		priv->new_proxy_cancellable = g_cancellable_new ();
+		p_self = g_new (NMAuthManager *, 1);
+		*p_self = self;
+		g_object_add_weak_pointer (G_OBJECT (self), (void **) p_self);
 		g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 		                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
 		                          NULL,
@@ -641,8 +551,14 @@ constructed (GObject *object)
 		                          POLKIT_INTERFACE,
 		                          priv->new_proxy_cancellable,
 		                          _dbus_new_proxy_cb,
-		                          self);
+		                          p_self);
 	}
+#else
+	if (priv->polkit_enabled)
+		_LOGW ("create auth-manager: polkit disabled at compile time. All authentication requests will fail");
+	else
+		_LOGD ("create auth-manager: polkit disabled at compile time");
+#endif
 }
 
 NMAuthManager *
@@ -669,21 +585,23 @@ static void
 dispose (GObject *object)
 {
 	NMAuthManager* self = NM_AUTH_MANAGER (object);
+#if WITH_POLKIT
 	NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
+#endif
 
 	_LOGD ("dispose");
 
-	nm_assert (c_list_is_empty (&priv->calls_lst_head));
-
-	priv->disposing = TRUE;
+#if WITH_POLKIT
+	/* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */
+	g_assert (!priv->queued_calls);
 
 	nm_clear_g_cancellable (&priv->new_proxy_cancellable);
-	nm_clear_g_cancellable (&priv->cancel_cancellable);
 
 	if (priv->proxy) {
 		g_signal_handlers_disconnect_by_data (priv->proxy, self);
 		g_clear_object (&priv->proxy);
 	}
+#endif
 
 	G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object);
 }
@@ -693,6 +611,7 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
@@ -700,7 +619,7 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass)
 	obj_properties[PROP_POLKIT_ENABLED] =
 	     g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
 	                           FALSE,
-	                           G_PARAM_WRITABLE |
+	                           G_PARAM_READWRITE |
 	                           G_PARAM_CONSTRUCT_ONLY |
 	                           G_PARAM_STATIC_STRINGS);
 
@@ -709,7 +628,11 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass)
 	signals[CHANGED_SIGNAL] = g_signal_new (NM_AUTH_MANAGER_SIGNAL_CHANGED,
 	                                        NM_TYPE_AUTH_MANAGER,
 	                                        G_SIGNAL_RUN_LAST,
-	                                        0, NULL, NULL,
+	                                        0,                      /* class offset     */
+	                                        NULL,                   /* accumulator      */
+	                                        NULL,                   /* accumulator data */
 	                                        g_cclosure_marshal_VOID__VOID,
-	                                        G_TYPE_NONE, 0);
+	                                        G_TYPE_NONE,
+	                                        0);
 }
+
diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h
index fe7ee787..e66ef78c 100644
--- a/src/nm-auth-manager.h
+++ b/src/nm-auth-manager.h
@@ -23,31 +23,6 @@
 
 #include "nm-auth-subject.h"
 
-/*****************************************************************************/
-
-typedef enum {
-	NM_AUTH_CALL_RESULT_UNKNOWN,
-	NM_AUTH_CALL_RESULT_YES,
-	NM_AUTH_CALL_RESULT_AUTH,
-	NM_AUTH_CALL_RESULT_NO,
-} NMAuthCallResult;
-
-static inline NMAuthCallResult
-nm_auth_call_result_eval (gboolean is_authorized,
-                          gboolean is_challenge,
-                          GError *error)
-{
-	if (error)
-		return NM_AUTH_CALL_RESULT_UNKNOWN;
-	if (is_authorized)
-		return NM_AUTH_CALL_RESULT_YES;
-	if (is_challenge)
-		return NM_AUTH_CALL_RESULT_AUTH;
-	return NM_AUTH_CALL_RESULT_NO;
-}
-
-/*****************************************************************************/
-
 #define NM_TYPE_AUTH_MANAGER            (nm_auth_manager_get_type ())
 #define NM_AUTH_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUTH_MANAGER, NMAuthManager))
 #define NM_AUTH_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_AUTH_MANAGER, NMAuthManagerClass))
@@ -67,28 +42,23 @@ GType nm_auth_manager_get_type (void);
 NMAuthManager *nm_auth_manager_setup (gboolean polkit_enabled);
 NMAuthManager *nm_auth_manager_get (void);
 
-void nm_auth_manager_force_shutdown (NMAuthManager *self);
-
 gboolean nm_auth_manager_get_polkit_enabled (NMAuthManager *self);
 
-/*****************************************************************************/
-
-typedef struct _NMAuthManagerCallId NMAuthManagerCallId;
-
-typedef void (*NMAuthManagerCheckAuthorizationCallback) (NMAuthManager *self,
-                                                         NMAuthManagerCallId *call_id,
-                                                         gboolean is_authorized,
-                                                         gboolean is_challenge,
-                                                         GError *error,
-                                                         gpointer user_data);
-
-NMAuthManagerCallId *nm_auth_manager_check_authorization (NMAuthManager *self,
-                                                          NMAuthSubject *subject,
-                                                          const char *action_id,
-                                                          gboolean allow_user_interaction,
-                                                          NMAuthManagerCheckAuthorizationCallback callback,
-                                                          gpointer user_data);
-
-void nm_auth_manager_check_authorization_cancel (NMAuthManagerCallId *call_id);
+#if WITH_POLKIT
+
+void nm_auth_manager_polkit_authority_check_authorization (NMAuthManager *self,
+                                                           NMAuthSubject *subject,
+                                                           const char *action_id,
+                                                           gboolean allow_user_interaction,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+gboolean nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthManager *self,
+                                                                      GAsyncResult *res,
+                                                                      gboolean *out_is_authorized,
+                                                                      gboolean *out_is_challenge,
+                                                                      GError **error);
+
+#endif
 
 #endif /* NM_AUTH_MANAGER_H */
diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c
index 117a3815..0f40ff7c 100644
--- a/src/nm-auth-subject.c
+++ b/src/nm-auth-subject.c
@@ -33,7 +33,8 @@
 #include <string.h>
 #include <stdlib.h>
 
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
+#include "NetworkManagerUtils.h"
 
 enum {
 	PROP_0,
@@ -92,15 +93,17 @@ nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len)
 		            (unsigned long long) priv->unix_process.start_time);
 		break;
 	case NM_AUTH_SUBJECT_TYPE_INTERNAL:
-		g_strlcpy (buf, "internal", buf_len);
+		g_strlcat (buf, "internal", buf_len);
 		break;
 	default:
-		g_strlcpy (buf, "invalid", buf_len);
+		g_strlcat (buf, "invalid", buf_len);
 		break;
 	}
 	return buf;
 }
 
+#if WITH_POLKIT
+
 /* returns a floating variant */
 GVariant *
 nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self)
@@ -122,6 +125,8 @@ nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self)
 	return ret;
 }
 
+#endif
+
 NMAuthSubjectType
 nm_auth_subject_get_subject_type (NMAuthSubject *subject)
 {
@@ -181,20 +186,20 @@ _new_unix_process (GDBusMethodInvocation *context,
 	g_return_val_if_fail (context || (connection && message), NULL);
 
 	if (context) {
-		success = nm_dbus_manager_get_caller_info (nm_dbus_manager_get (),
-		                                           context,
-		                                           &dbus_sender,
-		                                           &uid,
-		                                           &pid);
-	} else {
-		nm_assert (message);
-		success = nm_dbus_manager_get_caller_info_from_message (nm_dbus_manager_get (),
-		                                                        connection,
-		                                                        message,
-		                                                        &dbus_sender,
-		                                                        &uid,
-		                                                        &pid);
-	}
+		success = nm_bus_manager_get_caller_info (nm_bus_manager_get (),
+		                                          context,
+		                                          &dbus_sender,
+		                                          &uid,
+		                                          &pid);
+	} else if (message) {
+		success = nm_bus_manager_get_caller_info_from_message (nm_bus_manager_get (),
+		                                                       connection,
+		                                                       message,
+		                                                       &dbus_sender,
+		                                                       &uid,
+		                                                       &pid);
+	} else
+		g_assert_not_reached ();
 
 	if (!success)
 		return NULL;
diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h
index a9921dbd..a0b6d14b 100644
--- a/src/nm-auth-subject.h
+++ b/src/nm-auth-subject.h
@@ -68,6 +68,10 @@ gulong nm_auth_subject_get_unix_process_uid (NMAuthSubject *subject);
 
 const char *nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len);
 
+#if WITH_POLKIT
+
 GVariant *  nm_auth_subject_unix_process_to_polkit_gvariant (NMAuthSubject *self);
 
+#endif
+
 #endif /* __NETWORKMANAGER_AUTH_SUBJECT_H__ */
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c
index b41f6efa..f1aff430 100644
--- a/src/nm-auth-utils.c
+++ b/src/nm-auth-utils.c
@@ -24,83 +24,48 @@
 
 #include <string.h>
 
-#include "nm-utils/nm-c-list.h"
-
+#include "nm-utils/nm-hash-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-auth-subject.h"
 #include "nm-auth-manager.h"
 #include "nm-session-monitor.h"
 
-/*****************************************************************************/
-
 struct NMAuthChain {
-	GHashTable *data_hash;
-
-	CList auth_call_lst_head;
+	guint32 refcount;
+	GSList *calls;
+	GHashTable *data;
 
 	GDBusMethodInvocation *context;
 	NMAuthSubject *subject;
+	GError *error;
+
+	guint idle_id;
+	gboolean done;
 
 	NMAuthChainResultFunc done_func;
 	gpointer user_data;
-
-	guint32 refcount;
-
-	bool done:1;
 };
 
 typedef struct {
-	CList auth_call_lst;
 	NMAuthChain *chain;
-	NMAuthManagerCallId *call_id;
+	GCancellable *cancellable;
 	char *permission;
+	guint call_idle_id;
 } AuthCall;
 
-/*****************************************************************************/
-
-static void
-_ASSERT_call (AuthCall *call)
-{
-	nm_assert (call);
-	nm_assert (call->chain);
-	nm_assert (nm_c_list_contains_entry (&call->chain->auth_call_lst_head, call, auth_call_lst));
-}
-
-/*****************************************************************************/
-
-static void
-auth_call_free (AuthCall *call)
-{
-	if (call->call_id)
-		nm_auth_manager_check_authorization_cancel (call->call_id);
-	c_list_unlink_stale (&call->auth_call_lst);
-	g_free (call->permission);
-	g_slice_free (AuthCall, call);
-}
-
-/*****************************************************************************/
-
 typedef struct {
-
-	/* must be the first field. */
-	const char *tag;
-
 	gpointer data;
 	GDestroyNotify destroy;
-	char tag_data[];
 } ChainData;
 
 static ChainData *
-chain_data_new (const char *tag, gpointer data, GDestroyNotify destroy)
+chain_data_new (gpointer data, GDestroyNotify destroy)
 {
 	ChainData *tmp;
-	gsize l = strlen (tag);
 
-	tmp = g_malloc (sizeof (ChainData) + l + 1);
-	tmp->tag = &tmp->tag_data[0];
+	tmp = g_slice_new (ChainData);
 	tmp->data = data;
 	tmp->destroy = destroy;
-	memcpy (&tmp->tag_data[0], tag, l + 1);
 	return tmp;
 }
 
@@ -111,7 +76,69 @@ chain_data_free (gpointer data)
 
 	if (tmp->destroy)
 		tmp->destroy (tmp->data);
-	g_free (tmp);
+	memset (tmp, 0, sizeof (ChainData));
+	g_slice_free (ChainData, tmp);
+}
+
+static gboolean
+auth_chain_finish (gpointer user_data)
+{
+	NMAuthChain *self = user_data;
+
+	self->idle_id = 0;
+	self->done = TRUE;
+
+	/* Ensure we stay alive across the callback */
+	self->refcount++;
+	self->done_func (self, self->error, self->context, self->user_data);
+	nm_auth_chain_unref (self);
+	return FALSE;
+}
+
+/* Creates the NMAuthSubject automatically */
+NMAuthChain *
+nm_auth_chain_new_context (GDBusMethodInvocation *context,
+                           NMAuthChainResultFunc done_func,
+                           gpointer user_data)
+{
+	NMAuthSubject *subject;
+	NMAuthChain *chain;
+
+	g_return_val_if_fail (context != NULL, NULL);
+
+	subject = nm_auth_subject_new_unix_process_from_context (context);
+	if (!subject)
+		return NULL;
+
+	chain = nm_auth_chain_new_subject (subject,
+	                                   context,
+	                                   done_func,
+	                                   user_data);
+	g_object_unref (subject);
+	return chain;
+}
+
+/* Requires an NMAuthSubject */
+NMAuthChain *
+nm_auth_chain_new_subject (NMAuthSubject *subject,
+                           GDBusMethodInvocation *context,
+                           NMAuthChainResultFunc done_func,
+                           gpointer user_data)
+{
+	NMAuthChain *self;
+
+	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
+	g_return_val_if_fail (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject), NULL);
+
+	self = g_slice_new0 (NMAuthChain);
+	self->refcount = 1;
+	self->data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, chain_data_free);
+	self->done_func = done_func;
+	self->user_data = user_data;
+	self->context = context ? g_object_ref (context) : NULL;
+	self->subject = g_object_ref (subject);
+
+	return self;
 }
 
 static gpointer
@@ -119,17 +146,15 @@ _get_data (NMAuthChain *self, const char *tag)
 {
 	ChainData *tmp;
 
-	if (!self->data_hash)
-		return NULL;
-	tmp = g_hash_table_lookup (self->data_hash, &tag);
+	tmp = g_hash_table_lookup (self->data, tag);
 	return tmp ? tmp->data : NULL;
 }
 
 gpointer
 nm_auth_chain_get_data (NMAuthChain *self, const char *tag)
 {
-	g_return_val_if_fail (self, NULL);
-	g_return_val_if_fail (tag, NULL);
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (tag != NULL, NULL);
 
 	return _get_data (self, tag);
 }
@@ -150,22 +175,19 @@ nm_auth_chain_steal_data (NMAuthChain *self, const char *tag)
 {
 	ChainData *tmp;
 	gpointer value = NULL;
-
-	g_return_val_if_fail (self, NULL);
-	g_return_val_if_fail (tag, NULL);
-
-	if (!self->data_hash)
-		return NULL;
-
-	tmp = g_hash_table_lookup (self->data_hash, &tag);
-	if (!tmp)
-		return NULL;
-
-	value = tmp->data;
-
-	/* Make sure the destroy handler isn't called when freeing */
-	tmp->destroy = NULL;
-	g_hash_table_remove (self->data_hash, tmp);
+	void *orig_key;
+
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (tag != NULL, NULL);
+
+	if (g_hash_table_lookup_extended (self->data, tag, &orig_key, (gpointer)&tmp)) {
+		g_hash_table_steal (self->data, tag);
+		value = tmp->data;
+		/* Make sure the destroy handler isn't called when freeing */
+		tmp->destroy = NULL;
+		chain_data_free (tmp);
+		g_free (orig_key);
+	}
 	return value;
 }
 
@@ -175,105 +197,165 @@ nm_auth_chain_set_data (NMAuthChain *self,
                         gpointer data,
                         GDestroyNotify data_destroy)
 {
-	g_return_if_fail (self);
-	g_return_if_fail (tag);
-
-	if (data == NULL) {
-		if (self->data_hash)
-			g_hash_table_remove (self->data_hash, &tag);
-	} else {
-		if (!self->data_hash) {
-			self->data_hash = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal,
-			                                         NULL, chain_data_free);
-		}
-		g_hash_table_add (self->data_hash,
-		                  chain_data_new (tag, data, data_destroy));
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (tag != NULL);
+
+	if (data == NULL)
+		g_hash_table_remove (self->data, tag);
+	else {
+		g_hash_table_insert (self->data,
+		                     g_strdup (tag),
+		                     chain_data_new (data, data_destroy));
 	}
 }
 
-/*****************************************************************************/
+gulong
+nm_auth_chain_get_data_ulong (NMAuthChain *self, const char *tag)
+{
+	gulong *data;
 
-NMAuthCallResult
-nm_auth_chain_get_result (NMAuthChain *self, const char *permission)
+	g_return_val_if_fail (self != NULL, 0);
+	g_return_val_if_fail (tag != NULL, 0);
+
+	data = _get_data (self, tag);
+	return data ? *data : 0ul;
+}
+
+void
+nm_auth_chain_set_data_ulong (NMAuthChain *self,
+                              const char *tag,
+                              gulong data)
 {
-	gpointer data;
+	gulong *ptr;
 
-	g_return_val_if_fail (self, NM_AUTH_CALL_RESULT_UNKNOWN);
-	g_return_val_if_fail (permission, NM_AUTH_CALL_RESULT_UNKNOWN);
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (tag != NULL);
 
-	data = _get_data (self, permission);
-	return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN;
+	ptr = g_malloc (sizeof (*ptr));
+	*ptr = data;
+	nm_auth_chain_set_data (self, tag, ptr, g_free);
 }
 
 NMAuthSubject *
 nm_auth_chain_get_subject (NMAuthChain *self)
 {
-	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (self != NULL, NULL);
 
 	return self->subject;
 }
 
-/*****************************************************************************/
+NMAuthCallResult
+nm_auth_chain_get_result (NMAuthChain *self, const char *permission)
+{
+	gpointer data;
 
-static gboolean
-auth_chain_finish (NMAuthChain *self)
+	g_return_val_if_fail (self != NULL, NM_AUTH_CALL_RESULT_UNKNOWN);
+	g_return_val_if_fail (permission != NULL, NM_AUTH_CALL_RESULT_UNKNOWN);
+
+	data = _get_data (self, permission);
+	return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN;
+}
+
+static AuthCall *
+auth_call_new (NMAuthChain *chain, const char *permission)
 {
-	self->done = TRUE;
+	AuthCall *call;
 
-	/* Ensure we stay alive across the callback */
-	nm_assert (self->refcount == 1);
-	self->refcount++;
-	self->done_func (self, NULL, self->context, self->user_data);
-	nm_assert (NM_IN_SET (self->refcount, 1, 2));
-	nm_auth_chain_destroy (self);
-	return FALSE;
+	call = g_slice_new0 (AuthCall);
+	call->chain = chain;
+	call->permission = g_strdup (permission);
+	return call;
 }
 
 static void
+auth_call_free (AuthCall *call)
+{
+	g_free (call->permission);
+	g_clear_object (&call->cancellable);
+	g_slice_free (AuthCall, call);
+}
+
+static gboolean
 auth_call_complete (AuthCall *call)
 {
 	NMAuthChain *self;
 
-	_ASSERT_call (call);
+	g_return_val_if_fail (call, G_SOURCE_REMOVE);
 
 	self = call->chain;
 
-	nm_assert (!self->done);
+	g_return_val_if_fail (self, G_SOURCE_REMOVE);
+	g_return_val_if_fail (g_slist_find (self->calls, call), G_SOURCE_REMOVE);
 
-	auth_call_free (call);
+	self->calls = g_slist_remove (self->calls, call);
 
-	if (c_list_is_empty (&self->auth_call_lst_head)) {
-		/* we are on an idle-handler or a clean call-stack (non-reentrant). */
-		auth_chain_finish (self);
+	if (!self->calls) {
+		g_assert (!self->idle_id && !self->done);
+		self->idle_id = g_idle_add (auth_chain_finish, self);
 	}
+	auth_call_free (call);
+	return FALSE;
 }
 
 static void
-pk_call_cb (NMAuthManager *auth_manager,
-            NMAuthManagerCallId *call_id,
-            gboolean is_authorized,
-            gboolean is_challenge,
-            GError *error,
-            gpointer user_data)
+auth_call_cancel (gpointer user_data)
 {
-	AuthCall *call;
-	NMAuthCallResult call_result;
+	AuthCall *call = user_data;
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
-
-	call = user_data;
-
-	nm_assert (call->call_id == call_id);
+	if (nm_clear_g_cancellable (&call->cancellable)) {
+		/* we don't free call immediately. Instead we cancel the async operation
+		 * and set cancellable to NULL. pk_call_cb() will check for this and
+		 * do the final cleanup. */
+	} else {
+		g_source_remove (call->call_idle_id);
+		auth_call_free (call);
+	}
+}
 
-	call->call_id = NULL;
+#if WITH_POLKIT
+static void
+pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	AuthCall *call = user_data;
+	GError *error = NULL;
+	gboolean is_authorized = FALSE, is_challenge = FALSE;
+	guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN;
+
+	nm_auth_manager_polkit_authority_check_authorization_finish (NM_AUTH_MANAGER (object),
+	                                                             result,
+	                                                             &is_authorized,
+	                                                             &is_challenge,
+	                                                             &error);
+
+	/* If the call is already canceled do nothing */
+	if (!call->cancellable) {
+		nm_log_dbg (LOGD_CORE, "callback already cancelled");
+		g_clear_error (&error);
+		auth_call_free (call);
+		return;
+	}
 
-	call_result = nm_auth_call_result_eval (is_authorized, is_challenge, error);
+	if (error) {
+		/* Don't ruin the chain. Just leave the result unknown. */
+		nm_log_warn (LOGD_CORE, "error requesting auth for %s: %s",
+		             call->permission, error->message);
+		g_clear_error (&error);
+	} else {
+		if (is_authorized) {
+			/* Caller has the permission */
+			call_result = NM_AUTH_CALL_RESULT_YES;
+		} else if (is_challenge) {
+			/* Caller could authenticate to get the permission */
+			call_result = NM_AUTH_CALL_RESULT_AUTH;
+		} else
+			call_result = NM_AUTH_CALL_RESULT_NO;
+	}
 
 	nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL);
 
 	auth_call_complete (call);
 }
+#endif
 
 void
 nm_auth_chain_add_call (NMAuthChain *self,
@@ -283,109 +365,81 @@ nm_auth_chain_add_call (NMAuthChain *self,
 	AuthCall *call;
 	NMAuthManager *auth_manager = nm_auth_manager_get ();
 
-	g_return_if_fail (self);
-	g_return_if_fail (self->subject);
-	g_return_if_fail (!self->done);
+	g_return_if_fail (self != NULL);
 	g_return_if_fail (permission && *permission);
+	g_return_if_fail (self->subject);
 	g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject));
+	g_return_if_fail (!self->idle_id && !self->done);
 
-	call = g_slice_new0 (AuthCall);
-	call->chain = self;
-	call->permission = g_strdup (permission);
-	c_list_link_tail (&self->auth_call_lst_head, &call->auth_call_lst);
-	call->call_id = nm_auth_manager_check_authorization (auth_manager,
-	                                                     self->subject,
-	                                                     permission,
-	                                                     allow_interaction,
-	                                                     pk_call_cb,
-	                                                     call);
-}
-
-/*****************************************************************************/
+	call = auth_call_new (self, permission);
+	self->calls = g_slist_append (self->calls, call);
 
-/* Creates the NMAuthSubject automatically */
-NMAuthChain *
-nm_auth_chain_new_context (GDBusMethodInvocation *context,
-                           NMAuthChainResultFunc done_func,
-                           gpointer user_data)
-{
-	NMAuthSubject *subject;
-	NMAuthChain *chain;
-
-	g_return_val_if_fail (context, NULL);
-
-	subject = nm_auth_subject_new_unix_process_from_context (context);
-	if (!subject)
-		return NULL;
-
-	chain = nm_auth_chain_new_subject (subject,
-	                                   context,
-	                                   done_func,
-	                                   user_data);
-	g_object_unref (subject);
-	return chain;
-}
-
-/* Requires an NMAuthSubject */
-NMAuthChain *
-nm_auth_chain_new_subject (NMAuthSubject *subject,
-                           GDBusMethodInvocation *context,
-                           NMAuthChainResultFunc done_func,
-                           gpointer user_data)
-{
-	NMAuthChain *self;
-
-	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
-	nm_assert (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject));
-
-	self = g_slice_new0 (NMAuthChain);
-	c_list_init (&self->auth_call_lst_head);
-	self->refcount = 1;
-	self->done_func = done_func;
-	self->user_data = user_data;
-	self->context = context ? g_object_ref (context) : NULL;
-	self->subject = g_object_ref (subject);
-	return self;
+	if (   nm_auth_subject_is_internal (self->subject)
+	    || nm_auth_subject_get_unix_process_uid (self->subject) == 0
+	    || !nm_auth_manager_get_polkit_enabled (auth_manager)) {
+		/* Root user or non-polkit always gets the permission */
+		nm_auth_chain_set_data (self, permission, GUINT_TO_POINTER (NM_AUTH_CALL_RESULT_YES), NULL);
+		call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
+	} else {
+		/* Non-root always gets authenticated when using polkit */
+#if WITH_POLKIT
+		call->cancellable = g_cancellable_new ();
+		nm_auth_manager_polkit_authority_check_authorization (auth_manager,
+		                                                      self->subject,
+		                                                      permission,
+		                                                      allow_interaction,
+		                                                      call->cancellable,
+		                                                      pk_call_cb,
+		                                                      call);
+#else
+		if (!call->chain->error) {
+			call->chain->error = g_error_new_literal (NM_MANAGER_ERROR,
+			                                          NM_MANAGER_ERROR_FAILED,
+			                                          "Polkit support is disabled at compile time");
+		}
+		call->call_idle_id = g_idle_add ((GSourceFunc) auth_call_complete, call);
+#endif
+	}
 }
 
 /**
- * nm_auth_chain_destroy:
+ * nm_auth_chain_unref:
  * @self: the auth-chain
  *
- * Destroys the auth-chain. By destroying the auth-chain, you also cancel
+ * Unrefs the auth-chain. By unrefing the auth-chain, you also cancel
  * the receipt of the done-callback. IOW, the callback will not be invoked.
  *
- * The only exception is, if may call nm_auth_chain_destroy() from inside
+ * The only exception is, if you call nm_auth_chain_unref() from inside
  * the callback. In this case, @self stays alive until the callback returns.
- *
- * Note that you might only destroy an auth-chain exactly once, and never
- * after the callback was handled.
  */
 void
-nm_auth_chain_destroy (NMAuthChain *self)
+nm_auth_chain_unref (NMAuthChain *self)
 {
-	AuthCall *call;
-
-	g_return_if_fail (self);
-	g_return_if_fail (NM_IN_SET (self->refcount, 1, 2));
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (self->refcount > 0);
 
-	if (--self->refcount > 0)
+	self->refcount--;
+	if (self->refcount > 0)
 		return;
 
-	nm_clear_g_object (&self->subject);
-	nm_clear_g_object (&self->context);
+	if (self->idle_id)
+		g_source_remove (self->idle_id);
 
-	while ((call = c_list_first_entry (&self->auth_call_lst_head, AuthCall, auth_call_lst)))
-		auth_call_free (call);
+	g_object_unref (self->subject);
 
-	nm_clear_pointer (&self->data_hash, g_hash_table_destroy);
+	if (self->context)
+		g_object_unref (self->context);
 
+	g_slist_free_full (self->calls, auth_call_cancel);
+
+	g_clear_error (&self->error);
+	g_hash_table_destroy (self->data);
+
+	memset (self, 0, sizeof (NMAuthChain));
 	g_slice_free (NMAuthChain, self);
 }
 
-/******************************************************************************
- * utils
- *****************************************************************************/
+/************ utils **************/
 
 gboolean
 nm_auth_is_subject_in_acl (NMConnection *connection,
@@ -396,7 +450,7 @@ nm_auth_is_subject_in_acl (NMConnection *connection,
 	const char *user = NULL;
 	gulong uid;
 
-	g_return_val_if_fail (connection, FALSE);
+	g_return_val_if_fail (connection != NULL, FALSE);
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE);
 	g_return_val_if_fail (nm_auth_subject_is_internal (subject) || nm_auth_subject_is_unix_process (subject), FALSE);
 
@@ -410,8 +464,8 @@ nm_auth_is_subject_in_acl (NMConnection *connection,
 		return TRUE;
 
 	if (!nm_session_monitor_uid_to_user (uid, &user)) {
-		NM_SET_OUT (out_error_desc,
-		            g_strdup_printf ("Could not determine username for uid %lu", uid));
+		if (out_error_desc)
+			*out_error_desc = g_strdup_printf ("Could not determine username for uid %lu", uid);
 		return FALSE;
 	}
 
@@ -425,31 +479,12 @@ nm_auth_is_subject_in_acl (NMConnection *connection,
 
 	/* Match the username returned by the session check to a user in the ACL */
 	if (!nm_setting_connection_permissions_user_allowed (s_con, user)) {
-		NM_SET_OUT (out_error_desc,
-		            g_strdup_printf ("uid %lu has no permission to perform this operation", uid));
+		if (out_error_desc)
+			*out_error_desc = g_strdup_printf ("uid %lu has no permission to perform this operation", uid);
 		return FALSE;
 	}
 
 	return TRUE;
 }
 
-gboolean
-nm_auth_is_subject_in_acl_set_error (NMConnection *connection,
-                                     NMAuthSubject *subject,
-                                     GQuark err_domain,
-                                     int err_code,
-                                     GError **error)
-{
-	char *error_desc = NULL;
-
-	nm_assert (!error || !*error);
-
-	if (nm_auth_is_subject_in_acl (connection,
-	                               subject,
-	                               error ? &error_desc : NULL))
-		return TRUE;
 
-	g_set_error_literal (error, err_domain, err_code, error_desc);
-	g_free (error_desc);
-	return FALSE;
-}
diff --git a/src/nm-auth-utils.h b/src/nm-auth-utils.h
index 5f9823b6..89ed79cc 100644
--- a/src/nm-auth-utils.h
+++ b/src/nm-auth-utils.h
@@ -23,10 +23,15 @@
 
 #include "nm-connection.h"
 
-#include "nm-auth-manager.h"
-
 typedef struct NMAuthChain NMAuthChain;
 
+typedef enum {
+	NM_AUTH_CALL_RESULT_UNKNOWN,
+	NM_AUTH_CALL_RESULT_YES,
+	NM_AUTH_CALL_RESULT_AUTH,
+	NM_AUTH_CALL_RESULT_NO,
+} NMAuthCallResult;
+
 typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain,
                                        GError *error,
                                        GDBusMethodInvocation *context,
@@ -50,6 +55,12 @@ void nm_auth_chain_set_data (NMAuthChain *chain,
                              gpointer data,
                              GDestroyNotify data_destroy);
 
+void nm_auth_chain_set_data_ulong (NMAuthChain *chain,
+                                   const char *tag,
+                                   gulong data);
+
+gulong nm_auth_chain_get_data_ulong (NMAuthChain *chain, const char *tag);
+
 NMAuthCallResult nm_auth_chain_get_result (NMAuthChain *chain,
                                            const char *permission);
 
@@ -57,20 +68,14 @@ void nm_auth_chain_add_call (NMAuthChain *chain,
                              const char *permission,
                              gboolean allow_interaction);
 
-void nm_auth_chain_destroy (NMAuthChain *chain);
-
-NMAuthSubject *nm_auth_chain_get_subject (NMAuthChain *self);
+void nm_auth_chain_unref (NMAuthChain *chain);
 
 /* Caller must free returned error description */
 gboolean nm_auth_is_subject_in_acl (NMConnection *connection,
                                     NMAuthSubject *subect,
                                     char **out_error_desc);
 
-gboolean nm_auth_is_subject_in_acl_set_error (NMConnection *connection,
-                                              NMAuthSubject *subject,
-                                              GQuark err_domain,
-                                              int err_code,
-                                              GError **error);
+NMAuthSubject *nm_auth_chain_get_subject (NMAuthChain *self);
 
 #endif /* __NETWORKMANAGER_MANAGER_AUTH_H__ */
 
diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c
new file mode 100644
index 00000000..f6b86e90
--- /dev/null
+++ b/src/nm-bus-manager.c
@@ -0,0 +1,1024 @@
+/* -*- 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) 2006 - 2013 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-bus-manager.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include "nm-dbus-interface.h"
+#include "nm-core-internal.h"
+#include "nm-dbus-compat.h"
+#include "nm-exported-object.h"
+#include "NetworkManagerUtils.h"
+
+/* The base path for our GDBusObjectManagerServers.  They do not contain
+ * "NetworkManager" because GDBusObjectManagerServer requires that all
+ * exported objects be *below* the base path, and eg the Manager object
+ * is the base path already.
+ */
+#define OBJECT_MANAGER_SERVER_BASE_PATH "/org/freedesktop"
+
+/*****************************************************************************/
+
+enum {
+	DBUS_CONNECTION_CHANGED = 0,
+	PRIVATE_CONNECTION_NEW,
+	PRIVATE_CONNECTION_DISCONNECTED,
+	NUMBER_OF_SIGNALS,
+};
+
+static guint signals[NUMBER_OF_SIGNALS];
+
+typedef struct {
+	GDBusConnection *connection;
+	GDBusObjectManagerServer *obj_manager;
+	gboolean started;
+
+	GSList *private_servers;
+
+	GDBusProxy *proxy;
+
+	gulong bus_closed_id;
+	guint reconnect_id;
+} NMBusManagerPrivate;
+
+struct _NMBusManager {
+	GObject parent;
+	NMBusManagerPrivate _priv;
+};
+
+struct _NMBusManagerClass {
+	GObjectClass parent;
+};
+
+G_DEFINE_TYPE(NMBusManager, nm_bus_manager, G_TYPE_OBJECT)
+
+#define NM_BUS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBusManager, NM_IS_BUS_MANAGER)
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_CORE
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bus-manager", __VA_ARGS__)
+
+/*****************************************************************************/
+
+static gboolean nm_bus_manager_init_bus (NMBusManager *self);
+static void nm_bus_manager_cleanup (NMBusManager *self);
+static void start_reconnection_timeout (NMBusManager *self);
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_REGISTER (NMBusManager);
+
+NMBusManager *
+nm_bus_manager_get (void)
+{
+	if (G_UNLIKELY (!singleton_instance)) {
+		nm_bus_manager_setup (g_object_new (NM_TYPE_BUS_MANAGER, NULL));
+		if (!nm_bus_manager_init_bus (singleton_instance))
+			start_reconnection_timeout (singleton_instance);
+	}
+	return singleton_instance;
+}
+
+void
+nm_bus_manager_setup (NMBusManager *instance)
+{
+	static char already_setup = FALSE;
+
+	g_assert (NM_IS_BUS_MANAGER (instance));
+	g_assert (!already_setup);
+	g_assert (!singleton_instance);
+
+	already_setup = TRUE;
+	singleton_instance = instance;
+	nm_singleton_instance_register ();
+	_LOGD ("setup %s singleton (%p)", "NMBusManager", singleton_instance);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	const char *tag;
+	GQuark detail;
+	char *address;
+	GDBusServer *server;
+
+	/* With peer bus connections, we'll get a new connection for each
+	 * client.  For each connection we create an ObjectManager for
+	 * that connection to handle exporting our objects.  This table
+	 * maps GDBusObjectManager :: 'fake sender'.
+	 *
+	 * Note that even for connections that don't export any objects
+	 * we'll still create GDBusObjectManager since that's where we store
+	 * the pointer to the GDBusConnection.
+	 */
+	GHashTable *obj_managers;
+
+	NMBusManager *manager;
+} PrivateServer;
+
+typedef struct {
+	GDBusConnection *connection;
+	PrivateServer *server;
+	gboolean remote_peer_vanished;
+} CloseConnectionInfo;
+
+static gboolean
+close_connection_in_idle (gpointer user_data)
+{
+	CloseConnectionInfo *info = user_data;
+	PrivateServer *server = info->server;
+	GHashTableIter iter;
+	GDBusObjectManagerServer *manager;
+
+	/* Emit this for the manager */
+	g_signal_emit (server->manager,
+	               signals[PRIVATE_CONNECTION_DISCONNECTED],
+	               server->detail,
+	               info->connection);
+
+	/* FIXME: there's a bug (754730) in GLib for which the connection
+	 * is marked as closed when the remote peer vanishes but its
+	 * resources are not cleaned up.  Work around it by explicitly
+	 * closing the connection in that case. */
+	if (info->remote_peer_vanished)
+		g_dbus_connection_close (info->connection, NULL, NULL, NULL);
+
+	g_hash_table_iter_init (&iter, server->obj_managers);
+	while (g_hash_table_iter_next (&iter, (gpointer) &manager, NULL)) {
+		gs_unref_object GDBusConnection *connection = NULL;
+
+		connection = g_dbus_object_manager_server_get_connection (manager);
+		if (connection == info->connection) {
+			g_hash_table_iter_remove (&iter);
+			break;
+		}
+	}
+
+	g_object_unref (server->manager);
+	g_slice_free (CloseConnectionInfo, info);
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+private_server_closed_connection (GDBusConnection *conn,
+                                  gboolean remote_peer_vanished,
+                                  GError *error,
+                                  gpointer user_data)
+{
+	PrivateServer *s = user_data;
+	CloseConnectionInfo *info;
+
+	/* Clean up after the connection */
+	_LOGD ("(%s) closed connection %p on private socket", s->tag, conn);
+
+	info = g_slice_new0 (CloseConnectionInfo);
+	info->connection = conn;
+	info->server = s;
+	info->remote_peer_vanished = remote_peer_vanished;
+
+	g_object_ref (s->manager);
+
+	/* Delay the close of connection to ensure that D-Bus signals
+	 * are handled */
+	g_idle_add (close_connection_in_idle, info);
+}
+
+static gboolean
+private_server_new_connection (GDBusServer *server,
+                               GDBusConnection *conn,
+                               gpointer user_data)
+{
+	PrivateServer *s = user_data;
+	static guint32 counter = 0;
+	GDBusObjectManagerServer *manager;
+	char *sender;
+
+	g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed_connection), s);
+
+	/* Fake a sender since private connections don't have one */
+	sender = g_strdup_printf ("x:y:%d", counter++);
+
+	manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH);
+	g_dbus_object_manager_server_set_connection (manager, conn);
+	g_hash_table_insert (s->obj_managers, manager, sender);
+
+	_LOGD ("(%s) accepted connection %p on private socket", s->tag, conn);
+
+	/* Emit this for the manager.
+	 *
+	 * It is essential to do this from the "new-connection" signal handler, as
+	 * at that point no messages from the connection are yet processed
+	 * (which avoids races with registering objects). */
+	g_signal_emit (s->manager,
+	               signals[PRIVATE_CONNECTION_NEW],
+	               s->detail,
+	               conn,
+	               manager);
+	return TRUE;
+}
+
+static void
+private_server_manager_destroy (GDBusObjectManagerServer *manager)
+{
+	GDBusConnection *connection = g_dbus_object_manager_server_get_connection (manager);
+
+	if (!g_dbus_connection_is_closed (connection))
+		g_dbus_connection_close (connection, NULL, NULL, NULL);
+	g_dbus_object_manager_server_set_connection (manager, NULL);
+	g_object_unref (manager);
+	g_object_unref (connection);
+}
+
+static gboolean
+private_server_authorize (GDBusAuthObserver *observer,
+                          GIOStream         *stream,
+                          GCredentials      *credentials,
+                          gpointer           user_data)
+{
+	return g_credentials_get_unix_user (credentials, NULL) == 0;
+}
+
+static PrivateServer *
+private_server_new (const char *path,
+                    const char *tag,
+                    NMBusManager *manager)
+{
+	PrivateServer *s;
+	GDBusAuthObserver *auth_observer;
+	GDBusServer *server;
+	GError *error = NULL;
+	char *address, *guid;
+
+	unlink (path);
+	address = g_strdup_printf ("unix:path=%s", path);
+
+	_LOGD ("(%s) creating private socket %s", tag, address);
+
+	guid = g_dbus_generate_guid ();
+	auth_observer = g_dbus_auth_observer_new ();
+	g_signal_connect (auth_observer, "authorize-authenticated-peer",
+	                  G_CALLBACK (private_server_authorize), NULL);
+	server = g_dbus_server_new_sync (address,
+	                                 G_DBUS_SERVER_FLAGS_NONE,
+	                                 guid,
+	                                 auth_observer,
+	                                 NULL, &error);
+	g_free (guid);
+	g_object_unref (auth_observer);
+
+	if (!server) {
+		_LOGW ("(%s) failed to set up private socket %s: %s",
+		       tag, address, error->message);
+		g_error_free (error);
+		g_free (address);
+		return NULL;
+	}
+
+	s = g_malloc0 (sizeof (*s));
+	s->address = address;
+	s->server = server;
+	g_signal_connect (server, "new-connection",
+	                  G_CALLBACK (private_server_new_connection), s);
+
+	s->obj_managers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                         (GDestroyNotify) private_server_manager_destroy,
+	                                         g_free);
+	s->manager = manager;
+	s->detail = g_quark_from_string (tag);
+	s->tag = g_quark_to_string (s->detail);
+
+	g_dbus_server_start (server);
+
+	return s;
+}
+
+static void
+private_server_free (gpointer ptr)
+{
+	PrivateServer *s = ptr;
+
+	unlink (s->address);
+	g_free (s->address);
+	g_hash_table_destroy (s->obj_managers);
+
+	g_dbus_server_stop (s->server);
+	g_object_unref (s->server);
+
+	memset (s, 0, sizeof (*s));
+	g_free (s);
+}
+
+void
+nm_bus_manager_private_server_register (NMBusManager *self,
+                                        const char *path,
+                                        const char *tag)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	PrivateServer *s;
+	GSList *iter;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (tag != NULL);
+
+	/* Only one instance per tag; but don't warn */
+	for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
+		s = iter->data;
+		if (g_strcmp0 (tag, s->tag) == 0)
+			return;
+	}
+
+	s = private_server_new (path, tag, self);
+	if (s)
+		priv->private_servers = g_slist_append (priv->private_servers, s);
+}
+
+static const char *
+private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connection)
+{
+	GHashTableIter iter;
+	GDBusObjectManagerServer *manager;
+	const char *owner;
+
+	g_return_val_if_fail (s != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	g_hash_table_iter_init (&iter, s->obj_managers);
+	while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &owner)) {
+		gs_unref_object GDBusConnection *c = NULL;
+
+		c = g_dbus_object_manager_server_get_connection (manager);
+		if (c == connection)
+			return owner;
+	}
+	return NULL;
+}
+
+static GDBusConnection *
+private_server_get_connection_by_owner (PrivateServer *s, const char *owner)
+{
+	GHashTableIter iter;
+	GDBusObjectManagerServer *manager;
+	const char *priv_sender;
+
+	g_hash_table_iter_init (&iter, s->obj_managers);
+	while (g_hash_table_iter_next (&iter, (gpointer) &manager, (gpointer) &priv_sender)) {
+		if (g_strcmp0 (owner, priv_sender) == 0)
+			return g_dbus_object_manager_server_get_connection (manager);
+	}
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static gboolean
+_bus_get_unix_pid (NMBusManager *self,
+                   const char *sender,
+                   gulong *out_pid,
+                   GError **error)
+{
+	guint32 unix_pid = G_MAXUINT32;
+	gs_unref_variant GVariant *ret = NULL;
+
+	ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy,
+	                                "GetConnectionUnixProcessID",
+	                                g_variant_new ("(s)", sender),
+	                                G_VARIANT_TYPE ("(u)"),
+	                                G_DBUS_CALL_FLAGS_NONE, 2000,
+	                                NULL, error);
+	if (!ret)
+		return FALSE;
+
+	g_variant_get (ret, "(u)", &unix_pid);
+
+	*out_pid = (gulong) unix_pid;
+	return TRUE;
+}
+
+static gboolean
+_bus_get_unix_user (NMBusManager *self,
+                    const char *sender,
+                    gulong *out_user,
+                    GError **error)
+{
+	guint32 unix_uid = G_MAXUINT32;
+	gs_unref_variant GVariant *ret = NULL;
+
+	ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy,
+	                                "GetConnectionUnixUser",
+	                                g_variant_new ("(s)", sender),
+	                                G_VARIANT_TYPE ("(u)"),
+	                                G_DBUS_CALL_FLAGS_NONE, 2000,
+	                                NULL, error);
+	if (!ret)
+		return FALSE;
+
+	g_variant_get (ret, "(u)", &unix_uid);
+
+	*out_user = (gulong) unix_uid;
+	return TRUE;
+}
+
+/**
+ * _get_caller_info():
+ *
+ * Given a GDBus method invocation, or a GDBusConnection + GDBusMessage,
+ * return the sender and the UID of the sender.
+ */
+static gboolean
+_get_caller_info (NMBusManager *self,
+                  GDBusMethodInvocation *context,
+                  GDBusConnection *connection,
+                  GDBusMessage *message,
+                  char **out_sender,
+                  gulong *out_uid,
+                  gulong *out_pid)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	const char *sender;
+	GSList *iter;
+
+	if (context) {
+		connection = g_dbus_method_invocation_get_connection (context);
+
+		/* only bus connections will have a sender */
+		sender = g_dbus_method_invocation_get_sender (context);
+	} else {
+		g_assert (message);
+		sender = g_dbus_message_get_sender (message);
+	}
+	g_assert (connection);
+
+	if (!sender) {
+		/* Might be a private connection, for which we fake a sender */
+		for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
+			PrivateServer *s = iter->data;
+
+			sender = private_server_get_connection_owner (s, connection);
+			if (sender) {
+				if (out_uid)
+					*out_uid = 0;
+				if (out_sender)
+					*out_sender = g_strdup (sender);
+				if (out_pid) {
+					GCredentials *creds;
+
+					creds = g_dbus_connection_get_peer_credentials (connection);
+					if (creds) {
+						pid_t pid;
+
+						pid = g_credentials_get_unix_pid (creds, NULL);
+						if (pid == -1)
+							*out_pid = G_MAXULONG;
+						else
+							*out_pid = pid;
+					} else
+						*out_pid = G_MAXULONG;
+				}
+				return TRUE;
+			}
+		}
+		return FALSE;
+	}
+
+	/* Bus connections always have a sender */
+	g_assert (sender);
+	if (out_uid) {
+		if (!_bus_get_unix_user (self, sender, out_uid, NULL)) {
+			*out_uid = G_MAXULONG;
+			return FALSE;
+		}
+	}
+
+	if (out_pid) {
+		if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) {
+			*out_pid = G_MAXULONG;
+			return FALSE;
+		}
+	}
+
+	if (out_sender)
+		*out_sender = g_strdup (sender);
+
+	return TRUE;
+}
+
+gboolean
+nm_bus_manager_get_caller_info (NMBusManager *self,
+                                GDBusMethodInvocation *context,
+                                char **out_sender,
+                                gulong *out_uid,
+                                gulong *out_pid)
+{
+	return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid, out_pid);
+}
+
+gboolean
+nm_bus_manager_get_caller_info_from_message (NMBusManager *self,
+                                             GDBusConnection *connection,
+                                             GDBusMessage *message,
+                                             char **out_sender,
+                                             gulong *out_uid,
+                                             gulong *out_pid)
+{
+	return _get_caller_info (self, NULL, connection, message, out_sender, out_uid, out_pid);
+}
+
+/**
+ * nm_bus_manager_ensure_uid:
+ *
+ * @self: bus manager instance
+ * @context: D-Bus method invocation
+ * @uid: a user-id
+ * @error_domain: error domain to return on failure
+ * @error_code: error code to return on failure
+ *
+ * Retrieves the uid of the D-Bus method caller and
+ * checks that it matches @uid, unless @uid is G_MAXULONG.
+ * In case of failure the function returns FALSE and finishes
+ * handling the D-Bus method with an error.
+ *
+ * Returns: %TRUE if the check succeeded, %FALSE otherwise
+ */
+gboolean
+nm_bus_manager_ensure_uid (NMBusManager          *self,
+                           GDBusMethodInvocation *context,
+                           gulong uid,
+                           GQuark error_domain,
+                           int error_code)
+{
+	gulong caller_uid;
+	GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE);
+	g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (context), FALSE);
+
+	if (!nm_bus_manager_get_caller_info (self, context, NULL, &caller_uid, NULL)) {
+		error = g_error_new_literal (error_domain,
+		                             error_code,
+		                             "Unable to determine request UID.");
+		g_dbus_method_invocation_take_error (context, error);
+		return FALSE;
+	}
+
+	if (uid != G_MAXULONG && caller_uid != uid) {
+		error = g_error_new_literal (error_domain,
+		                             error_code,
+		                             "Permission denied");
+		g_dbus_method_invocation_take_error (context, error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_bus_manager_get_unix_user (NMBusManager *self,
+                              const char *sender,
+                              gulong *out_uid)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	GSList *iter;
+	GError *error = NULL;
+
+	g_return_val_if_fail (sender != NULL, FALSE);
+	g_return_val_if_fail (out_uid != NULL, FALSE);
+
+	/* Check if it's a private connection sender, which we fake */
+	for (iter = priv->private_servers; iter; iter = iter->next) {
+		gs_unref_object GDBusConnection *connection = NULL;
+
+		connection = private_server_get_connection_by_owner (iter->data, sender);
+		if (connection) {
+			*out_uid = 0;
+			return TRUE;
+		}
+	}
+
+	/* Otherwise, a bus connection */
+	if (!_bus_get_unix_user (self, sender, out_uid, &error)) {
+		_LOGW ("failed to get unix user for dbus sender '%s': %s",
+		       sender, error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+/* Only cleanup a specific dbus connection, not all our private data */
+static void
+nm_bus_manager_cleanup (NMBusManager *self)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->proxy);
+
+	if (priv->connection) {
+		g_signal_handler_disconnect (priv->connection, priv->bus_closed_id);
+		priv->bus_closed_id = 0;
+		g_clear_object (&priv->connection);
+	}
+
+	g_dbus_object_manager_server_set_connection (priv->obj_manager, NULL);
+	priv->started = FALSE;
+}
+
+static gboolean
+nm_bus_manager_reconnect (gpointer user_data)
+{
+	NMBusManager *self = NM_BUS_MANAGER (user_data);
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	g_assert (self != NULL);
+
+	if (nm_bus_manager_init_bus (self)) {
+		if (nm_bus_manager_start_service (self)) {
+			_LOGI ("reconnected to the system bus");
+			g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED],
+			               0, priv->connection);
+			priv->reconnect_id = 0;
+			return FALSE;
+		}
+	}
+
+	/* Try again */
+	nm_bus_manager_cleanup (self);
+	return TRUE;
+}
+
+static void
+start_reconnection_timeout (NMBusManager *self)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->reconnect_id)
+		g_source_remove (priv->reconnect_id);
+
+	/* Schedule timeout for reconnection attempts */
+	priv->reconnect_id = g_timeout_add_seconds (3, nm_bus_manager_reconnect, self);
+}
+
+static void
+closed_cb (GDBusConnection *connection,
+           gboolean remote_peer_vanished,
+           GError *error,
+           gpointer user_data)
+{
+	NMBusManager *self = NM_BUS_MANAGER (user_data);
+
+	/* Clean up existing connection */
+	_LOGW ("disconnected by the system bus");
+
+	nm_bus_manager_cleanup (self);
+
+	g_signal_emit (G_OBJECT (self), signals[DBUS_CONNECTION_CHANGED], 0, NULL);
+
+	start_reconnection_timeout (self);
+}
+
+static gboolean
+nm_bus_manager_init_bus (NMBusManager *self)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	if (priv->connection) {
+		_LOGW ("DBus Manager already has a valid connection");
+		return FALSE;
+	}
+
+	priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+	if (!priv->connection) {
+		/* Log with 'info' severity; there won't be a bus daemon in minimal
+		 * environments (eg, initrd) where we only want to use the private
+		 * socket.
+		 */
+		_LOGI ("could not connect to the system bus (%s); only the "
+		       "private D-Bus socket will be available",
+		       error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_dbus_connection_set_exit_on_close (priv->connection, FALSE);
+	priv->bus_closed_id = g_signal_connect (priv->connection, "closed",
+	                                        G_CALLBACK (closed_cb), self);
+
+	priv->proxy = g_dbus_proxy_new_sync (priv->connection,
+	                                     G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+	                                         G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+	                                     NULL,
+	                                     DBUS_SERVICE_DBUS,
+	                                     DBUS_PATH_DBUS,
+	                                     DBUS_INTERFACE_DBUS,
+	                                     NULL, &error);
+	if (!priv->proxy) {
+		g_clear_object (&priv->connection);
+		_LOGW ("could not create org.freedesktop.DBus proxy (%s); only the "
+		       "private D-Bus socket will be available",
+		       error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_dbus_object_manager_server_set_connection (priv->obj_manager, priv->connection);
+	return TRUE;
+}
+
+/* Register our service on the bus; shouldn't be called until
+ * all necessary message handlers have been registered, because
+ * when we register on the bus, clients may start to call.
+ */
+gboolean
+nm_bus_manager_start_service (NMBusManager *self)
+{
+	NMBusManagerPrivate *priv;
+	gs_unref_variant GVariant *ret = NULL;
+	int result;
+	GError *err = NULL;
+
+	g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE);
+
+	priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->started) {
+		_LOGE ("service has already started");
+		return FALSE;
+	}
+
+	/* Pointless to request a name when we aren't connected to the bus */
+	if (!priv->proxy)
+		return FALSE;
+
+	ret = _nm_dbus_proxy_call_sync (priv->proxy,
+	                                "RequestName",
+	                                g_variant_new ("(su)",
+	                                               NM_DBUS_SERVICE,
+	                                               DBUS_NAME_FLAG_DO_NOT_QUEUE),
+	                                G_VARIANT_TYPE ("(u)"),
+	                                G_DBUS_CALL_FLAGS_NONE, -1,
+	                                NULL, &err);
+	if (!ret) {
+		_LOGE ("could not acquire the NetworkManager service: '%s'", err->message);
+		g_error_free (err);
+		return FALSE;
+	}
+
+	g_variant_get (ret, "(u)", &result);
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		_LOGE ("could not acquire the NetworkManager service as it is already taken");
+		return FALSE;
+	}
+
+	priv->started = TRUE;
+	return priv->started;
+}
+
+GDBusConnection *
+nm_bus_manager_get_connection (NMBusManager *self)
+{
+	g_return_val_if_fail (NM_IS_BUS_MANAGER (self), NULL);
+
+	return NM_BUS_MANAGER_GET_PRIVATE (self)->connection;
+}
+
+void
+nm_bus_manager_register_object (NMBusManager *self,
+                                GDBusObjectSkeleton *object)
+{
+	NMBusManagerPrivate *priv;
+
+	g_return_if_fail (NM_IS_BUS_MANAGER (self));
+	g_return_if_fail (NM_IS_EXPORTED_OBJECT (object));
+
+	priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+#if NM_MORE_ASSERTS >= 1
+#if GLIB_CHECK_VERSION(2,34,0)
+	if (g_dbus_object_manager_server_is_exported (priv->obj_manager, object))
+		g_return_if_reached ();
+#endif
+#endif
+
+	g_dbus_object_manager_server_export (priv->obj_manager, object);
+}
+
+GDBusObjectSkeleton *
+nm_bus_manager_get_registered_object (NMBusManager *self,
+                                      const char *path)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	return G_DBUS_OBJECT_SKELETON (g_dbus_object_manager_get_object ((GDBusObjectManager *) priv->obj_manager, path));
+}
+
+void
+nm_bus_manager_unregister_object (NMBusManager *self,
+                                  GDBusObjectSkeleton *object)
+{
+	NMBusManagerPrivate *priv;
+	gs_free char *path = NULL;
+
+	g_return_if_fail (NM_IS_BUS_MANAGER (self));
+	g_return_if_fail (NM_IS_EXPORTED_OBJECT (object));
+
+	priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+#if NM_MORE_ASSERTS >= 1
+#if GLIB_CHECK_VERSION(2,34,0)
+	if (!g_dbus_object_manager_server_is_exported (priv->obj_manager, object))
+		g_return_if_reached ();
+#endif
+#endif
+
+	g_object_get (G_OBJECT (object), "g-object-path", &path, NULL);
+	g_return_if_fail (path != NULL);
+
+	g_dbus_object_manager_server_unexport (priv->obj_manager, path);
+}
+
+const char *
+nm_bus_manager_connection_get_private_name (NMBusManager *self,
+                                            GDBusConnection *connection)
+{
+	NMBusManagerPrivate *priv;
+	GSList *iter;
+	const char *owner;
+
+	g_return_val_if_fail (NM_IS_BUS_MANAGER (self), FALSE);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+
+	if (g_dbus_connection_get_unique_name (connection)) {
+		/* Shortcut. The connection is not a private connection. */
+		return NULL;
+	}
+
+	priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	for (iter = priv->private_servers; iter; iter = g_slist_next (iter)) {
+		PrivateServer *s = iter->data;
+
+		if ((owner = private_server_get_connection_owner (s, connection)))
+			return owner;
+	}
+	g_return_val_if_reached (NULL);
+}
+
+/**
+ * nm_bus_manager_new_proxy:
+ * @self: the #NMBusManager
+ * @connection: the GDBusConnection for which this connection should be created
+ * @proxy_type: the type of #GDBusProxy to create
+ * @name: any name on the message bus
+ * @path: name of the object instance to call methods on
+ * @iface: name of the interface to call methods on
+ *
+ * Creates a new proxy (of type @proxy_type) for a name on a given bus.  Since
+ * the process which called the D-Bus method could be coming from a private
+ * connection or the system bus connection, different proxies must be created
+ * for each case.  This function abstracts that.
+ *
+ * Returns: a #GDBusProxy capable of calling D-Bus methods of the calling process
+ */
+GDBusProxy *
+nm_bus_manager_new_proxy (NMBusManager *self,
+                          GDBusConnection *connection,
+                          GType proxy_type,
+                          const char *name,
+                          const char *path,
+                          const char *iface)
+{
+	const char *owner;
+	GDBusProxy *proxy;
+	GError *error = NULL;
+
+	g_return_val_if_fail (g_type_is_a (proxy_type, G_TYPE_DBUS_PROXY), NULL);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+	/* Might be a private connection, for which @name is fake */
+	owner = nm_bus_manager_connection_get_private_name (self, connection);
+	if (owner) {
+		g_return_val_if_fail (!g_strcmp0 (owner, name), NULL);
+		name = NULL;
+	}
+
+	proxy = g_initable_new (proxy_type, NULL, &error,
+	                        "g-connection", connection,
+	                        "g-flags", (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+	                                    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
+	                        "g-name", name,
+	                        "g-object-path", path,
+	                        "g-interface-name", iface,
+	                        NULL);
+	if (!proxy) {
+		_LOGW ("could not create proxy for %s on connection %s: %s",
+		       iface, name, error->message);
+		g_error_free (error);
+	}
+	return proxy;
+}
+
+/*****************************************************************************/
+
+static void
+nm_bus_manager_init (NMBusManager *self)
+{
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+
+	priv->obj_manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMBusManager *self = NM_BUS_MANAGER (object);
+	NMBusManagerPrivate *priv = NM_BUS_MANAGER_GET_PRIVATE (self);
+	GList *exported, *iter;
+
+	g_slist_free_full (priv->private_servers, private_server_free);
+	priv->private_servers = NULL;
+
+	nm_bus_manager_cleanup (self);
+
+	if (priv->obj_manager) {
+		/* The ObjectManager owns the last reference to many exported
+		 * objects, and when that reference is dropped the objects unregister
+		 * themselves via nm_bus_manager_unregister_object().  By that time
+		 * priv->obj_manager is already NULL and that prints warnings.  Unregister
+		 * them before clearing the ObjectManager instead.
+		 */
+		exported = g_dbus_object_manager_get_objects ((GDBusObjectManager *) priv->obj_manager);
+		for (iter = exported; iter; iter = iter->next) {
+			nm_bus_manager_unregister_object (self, iter->data);
+			g_object_unref (iter->data);
+		}
+		g_list_free (exported);
+		g_clear_object (&priv->obj_manager);
+	}
+
+	nm_clear_g_source (&priv->reconnect_id);
+
+	G_OBJECT_CLASS (nm_bus_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_bus_manager_class_init (NMBusManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = dispose;
+
+	signals[DBUS_CONNECTION_CHANGED] =
+	    g_signal_new (NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+	signals[PRIVATE_CONNECTION_NEW] =
+	    g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 2, G_TYPE_DBUS_CONNECTION, G_TYPE_DBUS_OBJECT_MANAGER_SERVER);
+
+	signals[PRIVATE_CONNECTION_DISCONNECTED] =
+	    g_signal_new (NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+
+
diff --git a/src/nm-bus-manager.h b/src/nm-bus-manager.h
new file mode 100644
index 00000000..153f7cdc
--- /dev/null
+++ b/src/nm-bus-manager.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef __NM_BUS_MANAGER_H__
+#define __NM_BUS_MANAGER_H__
+
+#define NM_TYPE_BUS_MANAGER (nm_bus_manager_get_type ())
+#define NM_BUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_BUS_MANAGER, NMBusManager))
+#define NM_BUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_BUS_MANAGER, NMBusManagerClass))
+#define NM_IS_BUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_BUS_MANAGER))
+#define NM_IS_BUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_BUS_MANAGER))
+#define NM_BUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_BUS_MANAGER, NMBusManagerClass))
+
+#define NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED          "dbus-connection-changed"
+#define NM_BUS_MANAGER_PRIVATE_CONNECTION_NEW           "private-connection-new"
+#define NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED  "private-connection-disconnected"
+
+typedef struct _NMBusManagerClass NMBusManagerClass;
+
+GType nm_bus_manager_get_type (void);
+
+NMBusManager * nm_bus_manager_get       (void);
+void           nm_bus_manager_setup     (NMBusManager *instance);
+
+gboolean nm_bus_manager_start_service    (NMBusManager *self);
+
+GDBusConnection * nm_bus_manager_get_connection (NMBusManager *self);
+
+gboolean nm_bus_manager_get_caller_info (NMBusManager *self,
+                                         GDBusMethodInvocation *context,
+                                         char **out_sender,
+                                         gulong *out_uid,
+                                         gulong *out_pid);
+
+gboolean nm_bus_manager_ensure_uid (NMBusManager          *self,
+                                    GDBusMethodInvocation *context,
+                                    gulong uid,
+                                    GQuark error_domain,
+                                    int error_code);
+
+const char *nm_bus_manager_connection_get_private_name (NMBusManager *self,
+                                                        GDBusConnection *connection);
+
+gboolean nm_bus_manager_get_unix_user (NMBusManager *self,
+                                       const char *sender,
+                                       gulong *out_uid);
+
+gboolean nm_bus_manager_get_caller_info_from_message (NMBusManager *self,
+                                                      GDBusConnection *connection,
+                                                      GDBusMessage *message,
+                                                      char **out_sender,
+                                                      gulong *out_uid,
+                                                      gulong *out_pid);
+
+void nm_bus_manager_register_object (NMBusManager *self,
+                                     GDBusObjectSkeleton *object);
+
+void nm_bus_manager_unregister_object (NMBusManager *self,
+                                       GDBusObjectSkeleton *object);
+
+GDBusObjectSkeleton *nm_bus_manager_get_registered_object (NMBusManager *self,
+                                                           const char *path);
+
+void nm_bus_manager_private_server_register (NMBusManager *self,
+                                             const char *path,
+                                             const char *tag);
+
+GDBusProxy *nm_bus_manager_new_proxy (NMBusManager *self,
+                                      GDBusConnection *connection,
+                                      GType proxy_type,
+                                      const char *name,
+                                      const char *path,
+                                      const char *iface);
+
+#endif /* __NM_BUS_MANAGER_H__ */
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
index 8ba19db9..6da220c4 100644
--- a/src/nm-checkpoint-manager.c
+++ b/src/nm-checkpoint-manager.c
@@ -26,16 +26,16 @@
 #include "nm-connection.h"
 #include "nm-core-utils.h"
 #include "devices/nm-device.h"
+#include "nm-exported-object.h"
 #include "nm-manager.h"
 #include "nm-utils.h"
-#include "c-list/src/c-list.h"
 
 /*****************************************************************************/
 
 struct _NMCheckpointManager {
 	NMManager *_manager;
-	GParamSpec *property_spec;
-	CList checkpoints_lst_head;
+	GHashTable *checkpoints;
+	guint rollback_timeout_id;
 };
 
 #define GET_MANAGER(self) \
@@ -56,67 +56,80 @@ struct _NMCheckpointManager {
 
 /*****************************************************************************/
 
-static void
-notify_checkpoints (NMCheckpointManager *self) {
-	g_object_notify_by_pspec ((GObject *) GET_MANAGER (self),
-	                          self->property_spec);
-}
+static void update_rollback_timeout (NMCheckpointManager *self);
 
 static void
-destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy)
+checkpoint_destroy (gpointer checkpoint)
 {
-	nm_assert (NM_IS_CHECKPOINT (checkpoint));
-	nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint)));
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
-
-	nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL);
-
-	c_list_unlink (&checkpoint->checkpoints_lst);
-
-	if (log_destroy)
-		nm_checkpoint_log_destroy (checkpoint);
-
-	notify_checkpoints (self);
-
-	nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint));
-	g_object_unref (checkpoint);
+	nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint));
+	g_object_unref (G_OBJECT (checkpoint));
 }
 
-static GVariant *
-rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
+static gboolean
+rollback_timeout_cb (NMCheckpointManager *self)
 {
+	NMCheckpoint *checkpoint;
+	GHashTableIter iter;
 	GVariant *result;
-	const CList *iter;
+	gint64 ts, now;
+
+	now = nm_utils_get_monotonic_timestamp_ms ();
+
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		ts = nm_checkpoint_get_rollback_ts (checkpoint);
+		if (ts && ts <= now) {
+			result = nm_checkpoint_rollback (checkpoint);
+			if (result)
+				g_variant_unref (result);
+			g_hash_table_iter_remove (&iter);
+		}
+	}
 
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
+	self->rollback_timeout_id = 0;
+	update_rollback_timeout (self);
 
-	/* we destroy first all overlapping checkpoints that are younger/newer. */
-	for (iter = checkpoint->checkpoints_lst.next;
-	     iter != &self->checkpoints_lst_head;
-	     ) {
-		NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst);
+	return G_SOURCE_REMOVE;
+}
 
-		iter = iter->next;
-		if (nm_checkpoint_includes_devices_of (cp, checkpoint)) {
-			/* the younger checkpoint has overlapping devices and gets obsoleted.
-			 * Destroy it. */
-			destroy_checkpoint (self, cp, TRUE);
-		}
+static void
+update_rollback_timeout (NMCheckpointManager *self)
+{
+	NMCheckpoint *checkpoint;
+	GHashTableIter iter;
+	gint64 ts, delta, next = G_MAXINT64;
+
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		ts = nm_checkpoint_get_rollback_ts (checkpoint);
+		if (ts && ts < next)
+			next = ts;
 	}
 
-	result = nm_checkpoint_rollback (checkpoint);
-	destroy_checkpoint (self, checkpoint, FALSE);
-	return result;
+	nm_clear_g_source (&self->rollback_timeout_id);
+
+	if (next != G_MAXINT64) {
+		delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0);
+		self->rollback_timeout_id = g_timeout_add (delta,
+		                                           (GSourceFunc) rollback_timeout_cb,
+		                                           self);
+		_LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta);
+	}
 }
 
-static void
-rollback_timeout_cb (NMCheckpoint *checkpoint,
-                     gpointer user_data)
+static NMCheckpoint *
+find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
 {
-	NMCheckpointManager *self = user_data;
-	gs_unref_variant GVariant *result = NULL;
+	GHashTableIter iter;
+	NMCheckpoint *checkpoint;
 
-	result = rollback_checkpoint (self, checkpoint);
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		if (nm_checkpoint_includes_device (checkpoint, device))
+			return checkpoint;
+	}
+
+	return NULL;
 }
 
 NMCheckpoint *
@@ -128,200 +141,152 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
 {
 	NMManager *manager;
 	NMCheckpoint *checkpoint;
+	const char * const *path;
 	gs_unref_ptrarray GPtrArray *devices = NULL;
 	NMDevice *device;
+	const char *checkpoint_path;
+	gs_free const char **device_paths_free = NULL;
+	guint i;
 
 	g_return_val_if_fail (self, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 	manager = GET_MANAGER (self);
 
-	devices = g_ptr_array_new ();
-
 	if (!device_paths || !device_paths[0]) {
-		const CList *tmp_lst;
-
-		nm_manager_for_each_device (manager, device, tmp_lst) {
-			/* FIXME: there is no strong reason to skip over unrealized devices.
-			 *        Also, NMCheckpoint anticipates to handle them (in parts). */
+		const char *device_path;
+		const GSList *iter;
+		GPtrArray *paths;
+
+		paths = g_ptr_array_new ();
+		for (iter = nm_manager_get_devices (manager);
+		     iter;
+		     iter = g_slist_next (iter)) {
+			device = NM_DEVICE (iter->data);
 			if (!nm_device_is_real (device))
 				continue;
-			nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device)));
-			g_ptr_array_add (devices, device);
+			device_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
+			if (device_path)
+				g_ptr_array_add (paths, (gpointer) device_path);
 		}
+		g_ptr_array_add (paths, NULL);
+		device_paths_free = (const char **) g_ptr_array_free (paths, FALSE);
+		device_paths = (const char *const *) device_paths_free;
 	} else if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
 		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
 		                     "the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list");
 		return NULL;
-	} else {
-		for (; *device_paths; device_paths++) {
-			device = nm_manager_get_device_by_path (manager, *device_paths);
-			if (!device) {
-				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				             "device %s does not exist", *device_paths);
-				return NULL;
-			}
-			if (!nm_device_is_real (device)) {
-				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				             "device %s is not realized", *device_paths);
-				return NULL;
-			}
-			g_ptr_array_add (devices, device);
-		}
 	}
 
-	if (!devices->len) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		                     "no device available");
-		return NULL;
+	devices = g_ptr_array_new ();
+	for (path = device_paths; *path; path++) {
+		device = nm_manager_get_device_by_path (manager, *path);
+		if (!device) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			             "device %s does not exist", *path);
+			return NULL;
+		}
+		g_ptr_array_add (devices, device);
 	}
 
-	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
-		nm_checkpoint_manager_destroy_all (self);
-	else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) {
-		c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
-			device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len);
-			if (device) {
+	if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) {
+		for (i = 0; i < devices->len; i++) {
+			device = devices->pdata[i];
+			checkpoint = find_checkpoint_for_device (self, device);
+			if (checkpoint) {
 				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
 				             "device '%s' is already included in checkpoint %s",
 				             nm_device_get_iface (device),
-				             nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint)));
+				             nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)));
 				return NULL;
 			}
 		}
 	}
 
-	checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags);
+	checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error);
+	if (!checkpoint)
+		return NULL;
+
+	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
+		g_hash_table_remove_all (self->checkpoints);
+
+	nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint));
+	checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
+	if (!nm_g_hash_table_insert (self->checkpoints,
+	                             (gpointer) checkpoint_path,
+	                             checkpoint))
+		g_return_val_if_reached (NULL);
+
+	update_rollback_timeout (self);
 
-	nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self);
-	c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst);
-	notify_checkpoints (self);
 	return checkpoint;
 }
 
-void
-nm_checkpoint_manager_destroy_all (NMCheckpointManager *self)
+gboolean
+nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
+                                   GError **error)
 {
-	NMCheckpoint *checkpoint;
+	g_return_val_if_fail (self, FALSE);
 
-	g_return_if_fail (self);
+	g_hash_table_remove_all (self->checkpoints);
 
-	while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst)))
-		destroy_checkpoint (self, checkpoint, TRUE);
+	return TRUE;
 }
 
 gboolean
 nm_checkpoint_manager_destroy (NMCheckpointManager *self,
-                               const char *path,
+                               const char *checkpoint_path,
                                GError **error)
 {
-	NMCheckpoint *checkpoint;
+	gboolean ret;
 
 	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
+	g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	if (!nm_streq (path, "/")) {
-		nm_checkpoint_manager_destroy_all (self);
-		return TRUE;
-	}
-
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
-		return FALSE;
-
-	destroy_checkpoint (self, checkpoint, TRUE);
-	return TRUE;
+	if (!nm_streq (checkpoint_path, "/")) {
+		ret = g_hash_table_remove (self->checkpoints, checkpoint_path);
+		if (!ret) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR,
+			             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+			             "checkpoint %s does not exist", checkpoint_path);
+		}
+		return ret;
+	} else
+		return nm_checkpoint_manager_destroy_all (self, error);
 }
 
 gboolean
 nm_checkpoint_manager_rollback (NMCheckpointManager *self,
-                                const char *path,
+                                const char *checkpoint_path,
                                 GVariant **results,
                                 GError **error)
 {
-	NMCheckpoint *checkpoint;
+	NMCheckpoint *cp;
 
 	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
+	g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
 	g_return_val_if_fail (results, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
+	cp = g_hash_table_lookup (self->checkpoints, checkpoint_path);
+	if (!cp) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "checkpoint %s does not exist", checkpoint_path);
 		return FALSE;
-
-	*results = rollback_checkpoint (self, checkpoint);
-	return TRUE;
-}
-
-NMCheckpoint *
-nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, const char *path, GError **error)
-{
-	NMCheckpoint *checkpoint;
-
-	g_return_val_if_fail (self, NULL);
-
-	checkpoint = (NMCheckpoint *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (GET_MANAGER (self))),
-	                                                             path);
-	if (   !checkpoint
-	    || !NM_IS_CHECKPOINT (checkpoint)) {
-		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		             "checkpoint %s does not exist", path);
-		return NULL;
 	}
 
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
-	return checkpoint;
-}
-
-const char **
-nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *out_length)
-{
-	NMCheckpoint *checkpoint;
-	const char **strv;
-	guint num, i = 0;
+	*results = nm_checkpoint_rollback (cp);
+	g_hash_table_remove (self->checkpoints, checkpoint_path);
 
-	num = c_list_length (&self->checkpoints_lst_head);
-	NM_SET_OUT (out_length, num);
-	if (!num)
-		return NULL;
-
-	strv = g_new (const char *, num + 1);
-	c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst)
-		strv[i++] = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint));
-	nm_assert (i == num);
-	strv[i] = NULL;
-	return strv;
-}
-
-gboolean
-nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
-                                               const char *path,
-                                               guint32 add_timeout,
-                                               GError **error)
-{
-	NMCheckpoint *checkpoint;
-
-	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
-		return FALSE;
-
-	nm_checkpoint_adjust_rollback_timeout (checkpoint, add_timeout);
 	return TRUE;
 }
 
 /*****************************************************************************/
 
 NMCheckpointManager *
-nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
+nm_checkpoint_manager_new (NMManager *manager)
 {
 	NMCheckpointManager *self;
 
@@ -336,17 +301,20 @@ nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
 	 * of NMManager shall surpass the lifetime of the NMCheckpointManager
 	 * instance. */
 	self->_manager = manager;
-	self->property_spec = spec;
-	c_list_init (&self->checkpoints_lst_head);
+	self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal,
+	                                           NULL, checkpoint_destroy);
+
 	return self;
 }
 
 void
-nm_checkpoint_manager_free (NMCheckpointManager *self)
+nm_checkpoint_manager_unref (NMCheckpointManager *self)
 {
 	if (!self)
 		return;
 
-	nm_checkpoint_manager_destroy_all (self);
+	nm_clear_g_source (&self->rollback_timeout_id);
+	g_hash_table_destroy (self->checkpoints);
+
 	g_slice_free (NMCheckpointManager, self);
 }
diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h
index ca66ef16..30e49041 100644
--- a/src/nm-checkpoint-manager.h
+++ b/src/nm-checkpoint-manager.h
@@ -27,13 +27,8 @@
 
 typedef struct _NMCheckpointManager NMCheckpointManager;
 
-NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec);
-
-void nm_checkpoint_manager_free (NMCheckpointManager *self);
-
-NMCheckpoint *nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self,
-                                                    const char *path,
-                                                    GError **error);
+NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager);
+void nm_checkpoint_manager_unref (NMCheckpointManager *self);
 
 NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self,
                                             const char *const*device_names,
@@ -41,22 +36,15 @@ NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self,
                                             NMCheckpointCreateFlags flags,
                                             GError **error);
 
-void nm_checkpoint_manager_destroy_all (NMCheckpointManager *self);
+gboolean nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
+                                            GError **error);
 
 gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self,
-                                        const char *path,
+                                        const char *checkpoint_path,
                                         GError **error);
 gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self,
-                                         const char *path,
+                                         const char *checkpoint_path,
                                          GVariant **results,
                                          GError **error);
 
-gboolean nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
-                                                        const char *path,
-                                                        guint32 add_timeout,
-                                                        GError **error);
-
-const char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self,
-                                                         guint *out_length);
-
 #endif /* __NM_CHECKPOINT_MANAGER_H__ */
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index a17f7eda..bc57d449 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -25,7 +25,6 @@
 #include <string.h>
 
 #include "nm-active-connection.h"
-#include "nm-act-request.h"
 #include "nm-auth-subject.h"
 #include "nm-core-utils.h"
 #include "nm-dbus-interface.h"
@@ -35,6 +34,7 @@
 #include "settings/nm-settings-connection.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
+#include "introspection/org.freedesktop.NetworkManager.Checkpoint.h"
 
 /*****************************************************************************/
 
@@ -50,35 +50,36 @@ typedef struct {
 	NMActivationReason activation_reason;
 } DeviceCheckpoint;
 
-NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint,
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_DEVICES,
 	PROP_CREATED,
 	PROP_ROLLBACK_TIMEOUT,
 );
 
-struct _NMCheckpointPrivate {
+typedef struct {
 	/* properties */
 	GHashTable *devices;
-	gint64 created_at_ms;
-	guint32 rollback_timeout_s;
-	guint timeout_id;
-	/* private members */
+	gint64 created;
+	guint32 rollback_timeout;
 	/* private members */
 	NMManager *manager;
+	gint64 rollback_ts;
 	NMCheckpointCreateFlags flags;
 	GHashTable *connection_uuids;
+} NMCheckpointPrivate;
 
-	NMCheckpointTimeoutCallback timeout_cb;
-	gpointer timeout_data;
+struct _NMCheckpoint {
+	NMExportedObject parent;
+	NMCheckpointPrivate _priv;
 };
 
 struct _NMCheckpointClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMCheckpoint, NM_IS_CHECKPOINT)
+#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMCheckpoint, NM_IS_CHECKPOINT)
 
 /*****************************************************************************/
 
@@ -102,53 +103,20 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
 
 /*****************************************************************************/
 
-void
-nm_checkpoint_log_destroy (NMCheckpoint *self)
+guint64
+nm_checkpoint_get_rollback_ts (NMCheckpoint *self)
 {
-	_LOGI ("destroy %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
-}
+	g_return_val_if_fail (NM_IS_CHECKPOINT (self), 0);
 
-void
-nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
-                                    NMCheckpointTimeoutCallback callback,
-                                    gpointer user_data)
-{
-	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-
-	/* in glib world, we would have a GSignal for this. But as there
-	 * is only one subscriber, it's simpler to just set and unset(!)
-	 * the callback this way. */
-	priv->timeout_cb = callback;
-	priv->timeout_data = user_data;
+	return NM_CHECKPOINT_GET_PRIVATE (self)->rollback_ts;
 }
 
-NMDevice *
-nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices)
+gboolean
+nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
 {
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-	guint i;
 
-	for (i = 0; i < n_devices; i++) {
-		if (g_hash_table_contains (priv->devices, devices[i]))
-			return devices[i];
-	}
-	return NULL;
-}
-
-NMDevice *
-nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices)
-{
-	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-	NMCheckpointPrivate *priv2 = NM_CHECKPOINT_GET_PRIVATE (cp_for_devices);
-	GHashTableIter iter;
-	NMDevice *device;
-
-	g_hash_table_iter_init (&iter, priv2->devices);
-	while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) {
-		if (g_hash_table_contains (priv->devices, device))
-			return device;
-	}
-	return NULL;
+	return g_hash_table_contains (priv->devices, device);
 }
 
 static NMSettingsConnection *
@@ -216,7 +184,7 @@ nm_checkpoint_rollback (NMCheckpoint *self)
 	GError *local_error = NULL;
 	GVariantBuilder builder;
 
-	_LOGI ("rollback of %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
+	_LOGI ("rollback of %s", nm_exported_object_get_path ((NMExportedObject *) self));
 	 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{su}"));
 
 	/* Start rolling-back each device */
@@ -338,7 +306,7 @@ activate:
 				                                     &local_error)) {
 					_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
 					       nm_connection_get_id ((NMConnection *) connection),
-					       nm_connection_get_uuid ((NMConnection *) connection),
+					       nm_connection_get_uuid ((NMConnection *	) connection),
 					       local_error->message);
 					g_clear_error (&local_error);
 					result = NM_ROLLBACK_RESULT_ERR_FAILED;
@@ -383,19 +351,21 @@ next_dev:
 	}
 
 	if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
-		const CList *tmp_lst;
+		const GSList *list;
 		NMDeviceState state;
-
-		nm_manager_for_each_device (priv->manager, device, tmp_lst) {
-			if (g_hash_table_contains (priv->devices, device))
-				continue;
-			state = nm_device_get_state (device);
-			if (   state > NM_DEVICE_STATE_DISCONNECTED
-			    && state < NM_DEVICE_STATE_DEACTIVATING) {
-				_LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (device));
-				nm_device_state_changed (device,
-				                         NM_DEVICE_STATE_DEACTIVATING,
-				                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+		NMDevice *dev;
+
+		for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) {
+			dev = list->data;
+			if (!g_hash_table_contains (priv->devices, dev)) {
+				state = nm_device_get_state (dev);
+				if (   state > NM_DEVICE_STATE_DISCONNECTED
+				    && state < NM_DEVICE_STATE_DEACTIVATING) {
+					_LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (dev));
+					nm_device_state_changed (dev,
+					                         NM_DEVICE_STATE_DEACTIVATING,
+					                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+				}
 			}
 		}
 
@@ -405,7 +375,8 @@ next_dev:
 }
 
 static DeviceCheckpoint *
-device_checkpoint_create (NMDevice *device)
+device_checkpoint_create (NMDevice *device,
+                          GError **error)
 {
 	DeviceCheckpoint *dev_checkpoint;
 	NMConnection *applied_connection;
@@ -413,10 +384,7 @@ device_checkpoint_create (NMDevice *device)
 	const char *path;
 	NMActRequest *act_request;
 
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (nm_device_is_real (device));
-
-	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
+	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
 
 	dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
 	dev_checkpoint->device = g_object_ref (device);
@@ -425,21 +393,25 @@ device_checkpoint_create (NMDevice *device)
 	dev_checkpoint->realized = nm_device_is_real (device);
 
 	if (nm_device_get_unmanaged_mask (device, NM_UNMANAGED_USER_EXPLICIT)) {
-		dev_checkpoint->unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
-		                                                                      NM_UNMANAGED_USER_EXPLICIT);
+		dev_checkpoint->unmanaged_explicit =
+			!!nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT);
 	} else
 		dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET;
 
-	act_request = nm_device_get_act_request (device);
-	if (act_request) {
-		settings_connection = nm_act_request_get_settings_connection (act_request);
-		applied_connection = nm_act_request_get_applied_connection (act_request);
+	applied_connection = nm_device_get_applied_connection (device);
+	if (applied_connection) {
+		dev_checkpoint->applied_connection =
+			nm_simple_connection_new_clone (applied_connection);
 
-		dev_checkpoint->applied_connection = nm_simple_connection_new_clone (applied_connection);
+		settings_connection = nm_device_get_settings_connection (device);
+		g_return_val_if_fail (settings_connection, NULL);
 		dev_checkpoint->settings_connection =
-		    nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
+			nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
+
+		act_request = nm_device_get_act_request (device);
+		g_return_val_if_fail (act_request, NULL);
 		dev_checkpoint->ac_version_id =
-		    nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
+			nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
 		dev_checkpoint->activation_reason =
 		    nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (act_request));
 	}
@@ -460,57 +432,6 @@ device_checkpoint_destroy (gpointer data)
 	g_slice_free (DeviceCheckpoint, dev_checkpoint);
 }
 
-static gboolean
-_timeout_cb (gpointer user_data)
-{
-	NMCheckpoint *self = user_data;
-	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-
-	priv->timeout_id = 0;
-
-	if (priv->timeout_cb)
-		priv->timeout_cb (self, priv->timeout_data);
-
-	/* beware, @self likely got destroyed! */
-	return G_SOURCE_REMOVE;
-}
-
-void
-nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout)
-{
-	guint32 rollback_timeout_s;
-	gint64 now_ms, add_timeout_ms, rollback_timeout_ms;
-
-	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-
-	nm_clear_g_source (&priv->timeout_id);
-
-	if (add_timeout == 0)
-		rollback_timeout_s = 0;
-	else {
-		now_ms = nm_utils_get_monotonic_timestamp_ms ();
-		add_timeout_ms = ((gint64) add_timeout) * 1000;
-		rollback_timeout_ms = (now_ms - priv->created_at_ms) + add_timeout_ms;
-
-		/* round to nearest integer second. Since NM_CHECKPOINT_ROLLBACK_TIMEOUT is
-		 * in units seconds, it will be able to exactly express the timeout. */
-		rollback_timeout_s = NM_MIN ((rollback_timeout_ms + 500) / 1000, (gint64) G_MAXUINT32);
-
-		/* we expect the timeout to be positive, because add_timeout_ms is positive.
-		 * We cannot accept a zero, because it means "infinity". */
-		nm_assert (rollback_timeout_s > 0);
-
-		priv->timeout_id = g_timeout_add (NM_MIN (add_timeout_ms, (gint64) G_MAXUINT32),
-		                                  _timeout_cb,
-		                                  self);
-	}
-
-	if (rollback_timeout_s != priv->rollback_timeout_s) {
-		priv->rollback_timeout_s = rollback_timeout_s;
-		_notify (self, PROP_ROLLBACK_TIMEOUT);
-	}
-}
-
 /*****************************************************************************/
 
 static void
@@ -519,20 +440,22 @@ get_property (GObject *object, guint prop_id,
 {
 	NMCheckpoint *self = NM_CHECKPOINT (object);
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+	gs_free_slist GSList *devices = NULL;
+	GHashTableIter iter;
+	NMDevice *device;
 
 	switch (prop_id) {
 	case PROP_DEVICES:
-		nm_dbus_utils_g_value_set_object_path_from_hash (value,
-		                                                 priv->devices,
-		                                                 FALSE);
+		g_hash_table_iter_init (&iter, priv->devices);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL))
+			devices = g_slist_append (devices, device);
+		nm_utils_g_value_set_object_path_array (value, devices, NULL, NULL);
 		break;
 	case PROP_CREATED:
-		g_value_set_int64 (value,
-		                   nm_utils_monotonic_timestamp_as_boottime (priv->created_at_ms,
-		                                                             NM_UTILS_NS_PER_MSEC));
+		g_value_set_int64 (value, priv->created);
 		break;
 	case PROP_ROLLBACK_TIMEOUT:
-		g_value_set_uint (value, priv->rollback_timeout_s);
+		g_value_set_uint (value, priv->rollback_timeout);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -545,47 +468,47 @@ get_property (GObject *object, guint prop_id,
 static void
 nm_checkpoint_init (NMCheckpoint *self)
 {
-	NMCheckpointPrivate *priv;
-
-	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_CHECKPOINT, NMCheckpointPrivate);
-
-	self->_priv = priv;
-
-	c_list_init (&self->checkpoints_lst);
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
 
-	priv->devices = g_hash_table_new_full (nm_direct_hash, NULL,
+	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 	                                       NULL, device_checkpoint_destroy);
 }
 
 NMCheckpoint *
-nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s,
-                   NMCheckpointCreateFlags flags)
+nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
+                   NMCheckpointCreateFlags flags, GError **error)
 {
 	NMCheckpoint *self;
 	NMCheckpointPrivate *priv;
 	NMSettingsConnection *const *con;
-	gint64 rollback_timeout_ms;
+	DeviceCheckpoint *dev_checkpoint;
+	NMDevice *device;
 	guint i;
 
 	g_return_val_if_fail (manager, NULL);
 	g_return_val_if_fail (devices, NULL);
-	g_return_val_if_fail (devices->len > 0, NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	if (!devices->len) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+		                     "no device available");
+		return NULL;
+	}
 
 	self = g_object_new (NM_TYPE_CHECKPOINT, NULL);
 
 	priv = NM_CHECKPOINT_GET_PRIVATE (self);
 	priv->manager = manager;
-	priv->rollback_timeout_s = rollback_timeout_s;
-	priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms ();
+	priv->created = nm_utils_monotonic_timestamp_as_boottime (nm_utils_get_monotonic_timestamp_ms (),
+	                                                          NM_UTILS_NS_PER_MSEC);
+	priv->rollback_timeout = rollback_timeout;
+	priv->rollback_ts = rollback_timeout ?
+	    (nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) :
+	    0;
 	priv->flags = flags;
 
-	if (rollback_timeout_s != 0) {
-		rollback_timeout_ms = ((gint64) rollback_timeout_s) * 1000;
-		priv->timeout_id = g_timeout_add (NM_MIN (rollback_timeout_ms, (gint64) G_MAXUINT32),
-		                                  _timeout_cb,
-		                                  self);
-	}
-
 	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
 		priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
 		for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
@@ -595,15 +518,13 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
 	}
 
 	for (i = 0; i < devices->len; i++) {
-		NMDevice *device = devices->pdata[i];
-
-		/* FIXME: as long as the check point instance exists, it won't let go
-		 *        of the device. That is a bug, for example, if you have a ethernet
-		 *        device that gets removed (rmmod), the checkpoint will reference
-		 *        a non-existing D-Bus path of a device. */
-		g_hash_table_insert (priv->devices,
-		                     device,
-		                     device_checkpoint_create (device));
+		device = (NMDevice *) devices->pdata[i];
+		dev_checkpoint = device_checkpoint_create (device, error);
+		if (!dev_checkpoint) {
+			g_object_unref (self);
+			return NULL;
+		}
+		g_hash_table_insert (priv->devices, device, dev_checkpoint);
 	}
 
 	return self;
@@ -615,41 +536,20 @@ dispose (GObject *object)
 	NMCheckpoint *self = NM_CHECKPOINT (object);
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
 
-	nm_assert (c_list_is_empty (&self->checkpoints_lst));
-
 	g_clear_pointer (&priv->devices, g_hash_table_unref);
 	g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
 
-	nm_clear_g_source (&priv->timeout_id);
-
 	G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_checkpoint = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_CHECKPOINT,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Devices",         "ao", NM_CHECKPOINT_DEVICES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Created",         "x",  NM_CHECKPOINT_CREATED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RollbackTimeout", "u",  NM_CHECKPOINT_ROLLBACK_TIMEOUT),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (checkpoint_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (checkpoint_class);
 
-	g_type_class_add_private (object_class, sizeof (NMCheckpointPrivate));
-
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_checkpoint);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint");
+	exported_object_class->export_on_construction = FALSE;
 
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
@@ -673,4 +573,8 @@ nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
 	                       G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (checkpoint_class),
+	                                        NMDBUS_TYPE_CHECKPOINT_SKELETON,
+	                                        NULL);
 }
diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h
index c8598f38..fccf8af3 100644
--- a/src/nm-checkpoint.h
+++ b/src/nm-checkpoint.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_CHECKPOINT_H__
 #define __NETWORKMANAGER_CHECKPOINT_H__
 
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
 
 #define NM_TYPE_CHECKPOINT            (nm_checkpoint_get_type ())
@@ -35,35 +35,16 @@
 #define NM_CHECKPOINT_CREATED "created"
 #define NM_CHECKPOINT_ROLLBACK_TIMEOUT "rollback-timeout"
 
-typedef struct _NMCheckpointPrivate NMCheckpointPrivate;
-
-typedef struct {
-	NMDBusObject parent;
-	NMCheckpointPrivate *_priv;
-	CList checkpoints_lst;
-} NMCheckpoint;
-
+typedef struct _NMCheckpoint NMCheckpoint;
 typedef struct _NMCheckpointClass NMCheckpointClass;
 
 GType nm_checkpoint_get_type (void);
 
 NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
-                                 NMCheckpointCreateFlags flags);
-
-typedef void (*NMCheckpointTimeoutCallback) (NMCheckpoint *self,
-                                             gpointer user_data);
-
-void nm_checkpoint_log_destroy (NMCheckpoint *self);
-
-void nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
-                                         NMCheckpointTimeoutCallback callback,
-                                         gpointer user_data);
+                                 NMCheckpointCreateFlags flags, GError **error);
 
+guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint);
+gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);
 GVariant *nm_checkpoint_rollback (NMCheckpoint *self);
 
-void nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout);
-
-NMDevice *nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices);
-NMDevice *nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices);
-
 #endif /* __NETWORKMANAGER_CHECKPOINT_H__ */
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 5f19eabe..00e5c635 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -54,7 +54,7 @@ struct _NMGlobalDnsConfig {
 	char **searches;
 	char **options;
 	GHashTable *domains;
-	const char **domain_list;
+	char **domain_list;
 	gboolean internal;
 };
 
@@ -238,7 +238,7 @@ nm_config_data_get_plugins (const NMConfigData *self, gboolean allow_default)
 	if (!list && allow_default) {
 		gs_unref_keyfile GKeyFile *kf = nm_config_create_keyfile ();
 
-		/* let keyfile split the default string according to its own escaping rules. */
+		/* let keyfile split the default string according to it's own escaping rules. */
 		g_key_file_set_value (kf, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NM_CONFIG_DEFAULT_MAIN_PLUGINS);
 		list = g_key_file_get_string_list (kf, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL);
 	}
@@ -705,53 +705,54 @@ nm_config_data_log (const NMConfigData *self,
 
 /*****************************************************************************/
 
-const char *const*
-nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns_config)
+const char *const *
+nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns)
 {
-	g_return_val_if_fail (dns_config, NULL);
+	g_return_val_if_fail (dns, NULL);
 
-	return (const char *const*) dns_config->searches;
+	return (const char *const *) dns->searches;
 }
 
 const char *const *
-nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns_config)
+nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns)
 {
-	g_return_val_if_fail (dns_config, NULL);
+	g_return_val_if_fail (dns, NULL);
 
-	return (const char *const*) dns_config->options;
+	return (const char *const *) dns->options;
 }
 
 guint
-nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns_config)
+nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns)
 {
-	g_return_val_if_fail (dns_config, 0);
+	g_return_val_if_fail (dns, 0);
+	g_return_val_if_fail (dns->domains, 0);
 
-	return dns_config->domains ? g_hash_table_size (dns_config->domains) : 0;
+	return g_hash_table_size (dns->domains);
 }
 
 NMGlobalDnsDomain *
-nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns_config, guint i)
+nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i)
 {
 	NMGlobalDnsDomain *domain;
 
-	g_return_val_if_fail (dns_config, NULL);
-	g_return_val_if_fail (dns_config->domains, NULL);
-	g_return_val_if_fail (i < g_hash_table_size (dns_config->domains), NULL);
-
-	nm_assert (NM_PTRARRAY_LEN (dns_config->domain_list) == g_hash_table_size (dns_config->domains));
+	g_return_val_if_fail (dns, NULL);
+	g_return_val_if_fail (dns->domains, NULL);
+	g_return_val_if_fail (dns->domain_list, NULL);
+	g_return_val_if_fail (i < g_strv_length (dns->domain_list), NULL);
 
-	domain = g_hash_table_lookup (dns_config->domains, dns_config->domain_list[i]);
+	domain = g_hash_table_lookup (dns->domains, dns->domain_list[i]);
+	g_return_val_if_fail (domain, NULL);
 
-	nm_assert (domain);
 	return domain;
 }
 
-NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns_config, const char *name)
+NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name)
 {
-	g_return_val_if_fail (dns_config, NULL);
+	g_return_val_if_fail (dns, NULL);
+	g_return_val_if_fail (dns->domains, NULL);
 	g_return_val_if_fail (name, NULL);
 
-	return dns_config->domains ? g_hash_table_lookup (dns_config->domains, name) : NULL;
+	return g_hash_table_lookup (dns->domains, name);
 }
 
 const char *
@@ -774,73 +775,55 @@ const char *const *
 nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain)
 {
 	g_return_val_if_fail (domain, NULL);
-
 	return (const char *const *) domain->options;
 }
 
 gboolean
-nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns_config)
+nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns)
 {
-	return dns_config->internal;
+	return dns->internal;
 }
 
 gboolean
-nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns_config)
+nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns)
 {
-	g_return_val_if_fail (dns_config, TRUE);
+	g_return_val_if_fail (dns, TRUE);
+	g_return_val_if_fail (dns->domains, TRUE);
 
-	return    !dns_config->searches
-	       && !dns_config->options
-	       && !dns_config->domain_list;
+	return    (!dns->searches || g_strv_length (dns->searches) == 0)
+	       && (!dns->options || g_strv_length (dns->options) == 0)
+	       && g_hash_table_size (dns->domains) == 0;
 }
 
 void
-nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns_config, GChecksum *sum)
+nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum)
 {
 	NMGlobalDnsDomain *domain;
-	guint i, j;
-	guint8 v8;
+	GList *keys, *key;
+	guint i;
 
-	g_return_if_fail (dns_config);
+	g_return_if_fail (dns);
+	g_return_if_fail (dns->domains);
 	g_return_if_fail (sum);
 
-	v8 = NM_HASH_COMBINE_BOOLS (guint8,
-	                            !dns_config->searches,
-	                            !dns_config->options,
-	                            !dns_config->domain_list);
-	g_checksum_update (sum, (guchar *) &v8, 1);
-
-	if (dns_config->searches) {
-		for (i = 0; dns_config->searches[i]; i++)
-			g_checksum_update (sum, (guchar *) dns_config->searches[i], strlen (dns_config->searches[i]) + 1);
-	}
-	if (dns_config->options) {
-		for (i = 0; dns_config->options[i]; i++)
-			g_checksum_update (sum, (guchar *) dns_config->options[i], strlen (dns_config->options[i]) + 1);
-	}
-
-	if (dns_config->domain_list) {
-		for (i = 0; dns_config->domain_list[i]; i++) {
-			domain = g_hash_table_lookup (dns_config->domains, dns_config->domain_list[i]);
-			nm_assert (domain);
+	for (i = 0; dns->searches && dns->searches[i]; i++)
+		g_checksum_update (sum, (guchar *) dns->searches[i], strlen (dns->searches[i]));
+	for (i = 0; dns->options && dns->options[i]; i++)
+		g_checksum_update (sum, (guchar *) dns->options[i], strlen (dns->options[i]));
 
-			v8 = NM_HASH_COMBINE_BOOLS (guint8,
-			                            !domain->servers,
-			                            !domain->options);
-			g_checksum_update (sum, (guchar *) &v8, 1);
+	keys = g_list_sort (g_hash_table_get_keys (dns->domains), (GCompareFunc) strcmp);
+	for (key = keys; key; key = g_list_next (key)) {
 
-			g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name) + 1);
+		domain = g_hash_table_lookup (dns->domains, key->data);
+		g_assert (domain != NULL);
+		g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name));
 
-			if (domain->servers) {
-				for (j = 0; domain->servers[j]; j++)
-					g_checksum_update (sum, (guchar *) domain->servers[j], strlen (domain->servers[j]) + 1);
-			}
-			if (domain->options) {
-				for (j = 0; domain->options[j]; j++)
-					g_checksum_update (sum, (guchar *) domain->options[j], strlen (domain->options[j]) + 1);
-			}
-		}
+		for (i = 0; domain->servers && domain->servers[i]; i++)
+			g_checksum_update (sum, (guchar *) domain->servers[i], strlen (domain->servers[i]));
+		for (i = 0; domain->options && domain->options[i]; i++)
+			g_checksum_update (sum, (guchar *) domain->options[i], strlen (domain->options[i]));
 	}
+	g_list_free (keys);
 }
 
 static void
@@ -855,15 +838,14 @@ global_dns_domain_free (NMGlobalDnsDomain  *domain)
 }
 
 void
-nm_global_dns_config_free (NMGlobalDnsConfig *dns_config)
-{
-	if (dns_config) {
-		g_strfreev (dns_config->searches);
-		g_strfreev (dns_config->options);
-		g_free (dns_config->domain_list);
-		if (dns_config->domains)
-			g_hash_table_unref (dns_config->domains);
-		g_free (dns_config);
+nm_global_dns_config_free (NMGlobalDnsConfig *conf)
+{
+	if (conf) {
+		g_strfreev (conf->searches);
+		g_strfreev (conf->options);
+		g_free (conf->domain_list);
+		g_hash_table_unref (conf->domains);
+		g_free (conf);
 	}
 }
 
@@ -876,22 +858,18 @@ nm_config_data_get_global_dns_config (const NMConfigData *self)
 }
 
 static void
-global_dns_config_seal_domains (NMGlobalDnsConfig *dns_config)
+global_dns_config_update_domain_list (NMGlobalDnsConfig *dns)
 {
-	nm_assert (dns_config);
-	nm_assert (dns_config->domains);
-	nm_assert (!dns_config->domain_list);
+	guint length;
 
-	if (g_hash_table_size (dns_config->domains) == 0)
-		nm_clear_pointer (&dns_config->domains, g_hash_table_unref);
-	else
-		dns_config->domain_list = nm_utils_strdict_get_keys (dns_config->domains, TRUE, NULL);
+	g_free (dns->domain_list);
+	dns->domain_list = (char **) g_hash_table_get_keys_as_array (dns->domains, &length);
 }
 
 static NMGlobalDnsConfig *
 load_global_dns (GKeyFile *keyfile, gboolean internal)
 {
-	NMGlobalDnsConfig *dns_config;
+	NMGlobalDnsConfig *conf;
 	char *group, *domain_prefix;
 	gs_strfreev char **groups = NULL;
 	int g, i, j, domain_prefix_len;
@@ -909,18 +887,13 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
 	if (!nm_config_keyfile_has_global_dns_config (keyfile, internal))
 		return NULL;
 
-	dns_config = g_malloc0 (sizeof (NMGlobalDnsConfig));
-	dns_config->domains = g_hash_table_new_full (nm_str_hash, g_str_equal,
-	                                             g_free, (GDestroyNotify) global_dns_domain_free);
+	conf = g_malloc0 (sizeof (NMGlobalDnsConfig));
+	conf->domains = g_hash_table_new_full (nm_str_hash, g_str_equal,
+	                                       g_free, (GDestroyNotify) global_dns_domain_free);
 
 	strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL);
-	if (strv) {
-		_nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
-		if (!strv[0])
-			g_free (strv);
-		else
-			dns_config->searches = strv;
-	}
+	if (strv)
+		conf->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
 
 	strv = g_key_file_get_string_list (keyfile, group, "options", NULL, NULL);
 	if (strv) {
@@ -931,12 +904,8 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
 			else
 				g_free (strv[i]);
 		}
-		if (j == 0)
-			g_free (strv);
-		else {
-			strv[j] = NULL;
-			dns_config->options = strv;
-		}
+		strv[j] = NULL;
+		conf->options = strv;
 	}
 
 	groups = g_key_file_get_groups (keyfile, NULL);
@@ -959,23 +928,20 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
 				else
 					g_free (strv[i]);
 			}
-			if (j == 0)
-				g_free (strv);
-			else {
+			if (j) {
 				strv[j] = NULL;
 				servers = strv;
 			}
+			else
+				g_free (strv);
 		}
 
 		if (!servers)
 			continue;
 
 		strv = g_key_file_get_string_list (keyfile, groups[g], "options", NULL, NULL);
-		if (strv) {
+		if (strv)
 			options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
-			if (!options[0])
-				nm_clear_g_free (&options);
-		}
 
 		name = strdup (&groups[g][domain_prefix_len]);
 		domain = g_malloc0 (sizeof (NMGlobalDnsDomain));
@@ -983,7 +949,7 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
 		domain->servers = servers;
 		domain->options = options;
 
-		g_hash_table_insert (dns_config->domains, strdup (name), domain);
+		g_hash_table_insert (conf->domains, strdup (name), domain);
 
 		if (!strcmp (name, "*"))
 			default_found = TRUE;
@@ -992,61 +958,59 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
 	if (!default_found) {
 		nm_log_dbg (LOGD_CORE, "%s global DNS configuration is missing default domain, ignore it",
 		            internal ? "internal" : "user");
-		nm_global_dns_config_free (dns_config);
+		nm_global_dns_config_free (conf);
 		return NULL;
 	}
 
-	dns_config->internal = internal;
-	global_dns_config_seal_domains (dns_config);
-	return dns_config;
+	conf->internal = internal;
+	global_dns_config_update_domain_list (conf);
+	return conf;
 }
 
 
 void
-nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value)
+nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns, GValue *value)
 {
 	GVariantBuilder conf_builder, domains_builder, domain_builder;
-	guint i;
+	NMGlobalDnsDomain *domain;
+	GHashTableIter iter;
 
 	g_variant_builder_init (&conf_builder, G_VARIANT_TYPE ("a{sv}"));
-	if (!dns_config)
+	if (!dns)
 		goto out;
 
-	if (dns_config->searches) {
+	if (dns->searches) {
 		g_variant_builder_add (&conf_builder, "{sv}", "searches",
-		                       g_variant_new_strv ((const char *const *) dns_config->searches, -1));
+		                       g_variant_new_strv ((const char *const *) dns->searches, -1));
 	}
 
-	if (dns_config->options) {
+	if (dns->options) {
 		g_variant_builder_add (&conf_builder, "{sv}", "options",
-		                       g_variant_new_strv ((const char *const *) dns_config->options, -1));
+		                       g_variant_new_strv ((const char *const *) dns->options, -1));
 	}
 
 	g_variant_builder_init (&domains_builder, G_VARIANT_TYPE ("a{sv}"));
-	if (dns_config->domain_list) {
-		for (i = 0; dns_config->domain_list[i]; i++) {
-			NMGlobalDnsDomain *domain;
-
-			domain = g_hash_table_lookup (dns_config->domains, dns_config->domain_list[i]);
 
-			g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}"));
+	g_hash_table_iter_init (&iter, dns->domains);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &domain)) {
 
-			if (domain->servers) {
-				g_variant_builder_add (&domain_builder, "{sv}", "servers",
-				                       g_variant_new_strv ((const char *const *) domain->servers, -1));
-			}
-			if (domain->options) {
-				g_variant_builder_add (&domain_builder, "{sv}", "options",
-				                       g_variant_new_strv ((const char *const *) domain->options, -1));
-			}
+		g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}"));
 
-			g_variant_builder_add (&domains_builder, "{sv}", domain->name,
-			                       g_variant_builder_end (&domain_builder));
+		if (domain->servers) {
+			g_variant_builder_add (&domain_builder, "{sv}", "servers",
+			                       g_variant_new_strv ((const char *const *) domain->servers, -1));
+		}
+		if (domain->options) {
+			g_variant_builder_add (&domain_builder, "{sv}", "options",
+			                       g_variant_new_strv ((const char *const *) domain->options, -1));
 		}
+
+		g_variant_builder_add (&domains_builder, "{sv}", domain->name,
+		                       g_variant_builder_end (&domain_builder));
 	}
+
 	g_variant_builder_add (&conf_builder, "{sv}", "domains",
 	                       g_variant_builder_end (&domains_builder));
-
 out:
 	g_value_take_variant (value, g_variant_builder_end (&conf_builder));
 }
@@ -1080,20 +1044,15 @@ global_dns_domain_from_dbus (char *name, GVariant *variant)
 				else
 					g_free (strv[i]);
 			}
-			if (j == 0)
-				g_free (strv);
-			else {
+			if (j) {
 				strv[j] = NULL;
-				g_strfreev (domain->servers);
 				domain->servers = strv;
-			}
+			} else
+				g_free (strv);
 		} else if (   !g_strcmp0 (key, "options")
 		           && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) {
 			strv = g_variant_dup_strv (val, NULL);
-			g_strfreev (domain->options);
 			domain->options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE);
-			if (!domain->options[0])
-				nm_clear_g_free (&domain->options);
 		}
 
 		g_variant_unref (val);
@@ -1152,12 +1111,11 @@ nm_global_dns_config_from_dbus (const GValue *value, GError **error)
 				else
 					g_free (strv[i]);
 			}
-			if (j == 0)
-				g_free (strv);
-			else {
+
+			if (strv)
 				strv[j] = NULL;
-				dns_config->options = strv;
-			}
+
+			dns_config->options = strv;
 		} else if (   !g_strcmp0 (key, "domains")
 		           && g_variant_is_of_type (val, G_VARIANT_TYPE ("a{sv}"))) {
 			NMGlobalDnsDomain *domain;
@@ -1187,7 +1145,7 @@ nm_global_dns_config_from_dbus (const GValue *value, GError **error)
 		return NULL;
 	}
 
-	global_dns_config_seal_domains (dns_config);
+	global_dns_config_update_domain_list (dns_config);
 	return dns_config;
 }
 
@@ -1238,8 +1196,6 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos,
                              GKeyFile *keyfile,
                              const char *property,
                              NMDevice *device,
-                             const NMPlatformLink *pllink,
-                             const char *match_device_type,
                              char **out_value)
 {
 	if (!match_section_infos)
@@ -1260,15 +1216,9 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos,
 		if (!value && !match_section_infos->stop_match)
 			continue;
 
-		if (match_section_infos->match_device.has) {
-			if (device)
-				match = nm_device_spec_match_list (device, match_section_infos->match_device.spec);
-			else if (pllink)
-				match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_section_infos->match_device.spec, FALSE);
-			else
-				match = FALSE;
-		} else
-			match = TRUE;
+		match = TRUE;
+		if (match_section_infos->match_device.has)
+			match = device && nm_device_spec_match_list (device, match_section_infos->match_device.spec);
 
 		if (match) {
 			*out_value = value;
@@ -1298,35 +1248,6 @@ nm_config_data_get_device_config (const NMConfigData *self,
 	                                               priv->keyfile,
 	                                               property,
 	                                               device,
-	                                               NULL,
-	                                               NULL,
-	                                               &value);
-	NM_SET_OUT (has_match, !!connection_info);
-	return value;
-}
-
-char *
-nm_config_data_get_device_config_by_pllink (const NMConfigData *self,
-                                            const char *property,
-                                            const NMPlatformLink *pllink,
-                                            const char *match_device_type,
-                                            gboolean *has_match)
-{
-	const NMConfigDataPrivate *priv;
-	const MatchSectionInfo *connection_info;
-	char *value = NULL;
-
-	g_return_val_if_fail (self, NULL);
-	g_return_val_if_fail (property && *property, NULL);
-
-	priv = NM_CONFIG_DATA_GET_PRIVATE (self);
-
-	connection_info = _match_section_infos_lookup (&priv->device_infos[0],
-	                                               priv->keyfile,
-	                                               property,
-	                                               NULL,
-	                                               pllink,
-	                                               match_device_type,
 	                                               &value);
 	NM_SET_OUT (has_match, !!connection_info);
 	return value;
@@ -1366,8 +1287,6 @@ nm_config_data_get_connection_default (const NMConfigData *self,
 	                             priv->keyfile,
 	                             property,
 	                             device,
-	                             NULL,
-	                             NULL,
 	                             &value);
 	return value;
 }
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index a11b38b2..3092a87f 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -188,12 +188,6 @@ char *nm_config_data_get_device_config (const NMConfigData *self,
                                         NMDevice *device,
                                         gboolean *has_match);
 
-char *nm_config_data_get_device_config_by_pllink (const NMConfigData *self,
-                                                  const char *property,
-                                                  const NMPlatformLink *pllink,
-                                                  const char *match_device_type,
-                                                  gboolean *has_match);
-
 gboolean nm_config_data_get_device_config_boolean (const NMConfigData *self,
                                                    const char *property,
                                                    NMDevice *device,
@@ -206,18 +200,18 @@ gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const
 
 GKeyFile *nm_config_data_clone_keyfile_intern (const NMConfigData *self);
 
-const char *const *nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns_config);
-const char *const *nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns_config);
-guint nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns_config);
-NMGlobalDnsDomain *nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns_config, guint i);
-NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns_config, const char *name);
+const char *const *nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns);
+const char *const *nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns);
+guint nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns);
+NMGlobalDnsDomain *nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i);
+NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name);
 const char *nm_global_dns_domain_get_name (const NMGlobalDnsDomain *domain);
 const char *const *nm_global_dns_domain_get_servers (const NMGlobalDnsDomain *domain);
 const char *const *nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain);
-gboolean nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns_config);
-gboolean nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns_config);
-void nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns_config, GChecksum *sum);
-void nm_global_dns_config_free (NMGlobalDnsConfig *dns_config);
+gboolean nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns);
+gboolean nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns);
+void nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum);
+void nm_global_dns_config_free (NMGlobalDnsConfig *conf);
 
 NMGlobalDnsConfig *nm_global_dns_config_from_dbus (const GValue *value, GError **error);
 void nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value);
diff --git a/src/nm-config.c b/src/nm-config.c
index bef05fc7..29f0d517 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -553,7 +553,7 @@ nm_config_create_keyfile ()
 
 /* this is an external variable, to make loading testable. Other then that,
  * no code is supposed to change this. */
-guint _nm_config_match_nm_version = NM_VERSION;
+guint _nm_config_match_nm_version = NM_VERSION_CUR_STABLE;
 char *_nm_config_match_env = NULL;
 
 static gboolean
@@ -615,7 +615,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 	if (a_is_connection) {
 		/* both are [connection.\+] entries. Reverse their order.
 		 * One of the sections might be literally [connection]. That section
-		 * is special and its order will be fixed later. It doesn't actually
+		 * is special and it's order will be fixed later. It doesn't actually
 		 * matter here how it compares with [connection.\+] sections. */
 		return pa > pb ? -1 : 1;
 	}
@@ -633,7 +633,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 	if (a_is_device) {
 		/* both are [device.\+] entries. Reverse their order.
 		 * One of the sections might be literally [device]. That section
-		 * is special and its order will be fixed later. It doesn't actually
+		 * is special and it's order will be fixed later. It doesn't actually
 		 * matter here how it compares with [device.\+] sections. */
 		return pa > pb ? -1 : 1;
 	}
@@ -1383,6 +1383,7 @@ intern_config_write (const char *filename,
 	GKeyFile *keyfile;
 	gs_strfreev char **groups = NULL;
 	guint g, k;
+	gboolean has_intern = FALSE;
 	gboolean success = FALSE;
 	GError *local = NULL;
 
@@ -1448,7 +1449,10 @@ intern_config_write (const char *filename,
 
 			value_set = g_key_file_get_value (keyfile_intern, group, key, NULL);
 
-			if (is_intern || is_atomic)
+			if (is_intern) {
+				has_intern = TRUE;
+				g_key_file_set_value (keyfile, group, key, value_set);
+			} else if (is_atomic)
 				g_key_file_set_value (keyfile, group, key, value_set);
 			else {
 				gs_free char *value_was = NULL;
@@ -2113,7 +2117,7 @@ nm_config_device_state_load_all (void)
 		if (!state)
 			continue;
 
-		if (!g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state))
+		if (!nm_g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state))
 			nm_assert_not_reached ();
 	}
 	g_dir_close (dir);
@@ -2397,15 +2401,15 @@ _set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags re
 	}
 
 	if (new_data) {
-		_LOGI ("signal: %s (%s)",
+		_LOGI ("config: signal %s (%s)",
 		       nm_config_change_flags_to_string (changes, NULL, 0),
 		       nm_config_data_get_config_description (new_data));
 		nm_config_data_log (new_data, "CONFIG: ", "  ", NULL);
 		priv->config_data = new_data;
 	} else if (had_new_data)
-		_LOGI ("signal: %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0));
+		_LOGI ("config: signal %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0));
 	else
-		_LOGI ("signal: %s", nm_config_change_flags_to_string (changes, NULL, 0));
+		_LOGI ("config: signal %s", nm_config_change_flags_to_string (changes, NULL, 0));
 	g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0,
 	               new_data ? new_data : old_data,
 	               changes, old_data);
diff --git a/src/nm-config.h b/src/nm-config.h
index 5d027ce0..42ab4682 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -57,6 +57,7 @@
 
 #define NM_CONFIG_KEYFILE_GROUP_KEYFILE                     "keyfile"
 #define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN                    "ifupdown"
+#define NM_CONFIG_KEYFILE_GROUP_IFNET                       "ifnet"
 
 #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT              "auth-polkit"
 #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT "autoconnect-retries-default"
@@ -78,8 +79,6 @@
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED                "managed"
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER         "ignore-carrier"
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS          "sriov-num-vfs"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND           "wifi.backend"
-#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address"
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT   "carrier-wait-timeout"
 
 #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS                     ".was."
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 389e72ad..8861f261 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -25,82 +25,22 @@
 #include "nm-connectivity.h"
 
 #include <string.h>
-
-#if WITH_CONCHECK
 #include <curl/curl.h>
-#endif
 
-#include "c-list/src/c-list.h"
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
 
-#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
-
 /*****************************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, int /*NMConnectivityState*/,
-	NM_UTILS_LOOKUP_DEFAULT_WARN ("???"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN,  "UNKNOWN"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_NONE,     "NONE"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_LIMITED,  "LIMITED"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL,   "PORTAL"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL,     "FULL"),
-
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_ERROR,    "ERROR"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FAKE,     "FAKE"),
-);
-
-const char *
-nm_connectivity_state_to_string (NMConnectivityState state)
-{
-	return _state_to_string (state);
-}
-
-/*****************************************************************************/
-
-struct _NMConnectivityCheckHandle {
-	CList handles_lst;
-	NMConnectivity *self;
-	NMConnectivityCheckCallback callback;
-	gpointer user_data;
-
-	char *ifspec;
-
-#if WITH_CONCHECK
-	struct {
-		char *response;
-
-		CURL *curl_ehandle;
-		struct curl_slist *request_headers;
-
-		GString *recv_msg;
-	} concheck;
-#endif
-
-	guint timeout_id;
-};
-
-enum {
-	CONFIG_CHANGED,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
 typedef struct {
-	CList handles_lst_head;
 	char *uri;
 	char *response;
 	gboolean enabled;
 	guint interval;
 	NMConfig *config;
-#if WITH_CONCHECK
-	struct {
-		CURLM *curl_mhandle;
-		guint curl_timer;
-	} concheck;
-#endif
+	guint periodic_check_id;
+	CURLM *curl_mhandle;
+	guint curl_timer;
 } NMConnectivityPrivate;
 
 struct _NMConnectivity {
@@ -118,6 +58,14 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
 
 NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY);
 
+enum {
+	PERIODIC_CHECK,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 /*****************************************************************************/
 
 #define _NMLOG_DOMAIN      LOGD_CONCHECK
@@ -130,172 +78,116 @@ NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECT
         \
         if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \
             _nm_log (__level, _NMLOG2_DOMAIN, 0, \
-                     (cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \
-                     NULL, \
-                     "connectivity: (%s) " \
-                     _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     (cb_data->ifspec ? &cb_data->ifspec[3] : "") \
-                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+                        &cb_data->ifspec[3], NULL, \
+                        "connectivity: (%s) " \
+                        _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                        &cb_data->ifspec[3] \
+                        _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
 
 /*****************************************************************************/
 
-static void
-cb_data_invoke_callback (NMConnectivityCheckHandle *cb_data,
-                         NMConnectivityState state,
-                         GError *error,
-                         const char *log_message)
-{
-	NMConnectivityCheckCallback callback;
-
-	nm_assert (cb_data);
-	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
-
-	callback = cb_data->callback;
-	if (!callback)
-		return;
-
-	cb_data->callback = NULL;
-
-	nm_assert (log_message);
+NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState,
+	NM_UTILS_LOOKUP_DEFAULT_WARN ("???"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN,  "UNKNOWN"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_NONE,     "NONE"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_LIMITED,  "LIMITED"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL,   "PORTAL"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL,     "FULL"),
+);
 
-	_LOG2D ("check completed: %s; %s",
-	        nm_connectivity_state_to_string (state),
-	        log_message);
+/*****************************************************************************/
 
-	callback (cb_data->self,
-	          cb_data,
-	          state,
-	          error,
-	          cb_data->user_data);
-}
+typedef struct {
+	GSimpleAsyncResult *simple;
+	char *response;
+	CURL *curl_ehandle;
+	size_t msg_size;
+	char *msg;
+	struct curl_slist *request_headers;
+	guint timeout_id;
+	char *ifspec;
+} ConCheckCbData;
 
 static void
-cb_data_free (NMConnectivityCheckHandle *cb_data,
-              NMConnectivityState state,
-              GError *error,
-              const char *log_message)
+finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state)
 {
-	NMConnectivity *self;
-
-	nm_assert (cb_data);
-
-	self = cb_data->self;
-
-	nm_assert (NM_IS_CONNECTIVITY (self));
-
-	c_list_unlink (&cb_data->handles_lst);
-
-#if WITH_CONCHECK
-	if (cb_data->concheck.curl_ehandle) {
-		NMConnectivityPrivate *priv;
-
-		/* Contrary to what cURL manual claim it is *not* safe to remove
-		 * the easy handle "at any moment"; specifically not from the
-		 * write function. Thus here we just dissociate the cb_data from
-		 * the easy handle and the easy handle will be cleaned up when the
-		 * message goes to CURLMSG_DONE in curl_check_connectivity(). */
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEFUNCTION, NULL);
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEDATA, NULL);
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERFUNCTION, NULL);
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERDATA, NULL);
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL);
-		curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL);
-
-		priv = NM_CONNECTIVITY_GET_PRIVATE (self);
-
-		curl_multi_remove_handle (priv->concheck.curl_mhandle, cb_data->concheck.curl_ehandle);
-		curl_easy_cleanup (cb_data->concheck.curl_ehandle);
-
-		curl_slist_free_all (cb_data->concheck.request_headers);
-	}
-#endif
-
-	nm_clear_g_source (&cb_data->timeout_id);
-
-	cb_data_invoke_callback (cb_data, state, error, log_message);
-
-#if WITH_CONCHECK
-	g_free (cb_data->concheck.response);
-	if (cb_data->concheck.recv_msg)
-		g_string_free (cb_data->concheck.recv_msg, TRUE);
-#endif
+	/* Contrary to what cURL manual claim it is *not* safe to remove
+	 * the easy handle "at any moment"; specifically not from the
+	 * write function. Thus here we just dissociate the cb_data from
+	 * the easy handle and the easy handle will be cleaned up when the
+	 * message goes to CURLMSG_DONE in curl_check_connectivity(). */
+	curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_PRIVATE, NULL);
+
+	g_simple_async_result_set_op_res_gssize (cb_data->simple, new_state);
+	g_simple_async_result_complete (cb_data->simple);
+	g_object_unref (cb_data->simple);
+	curl_slist_free_all (cb_data->request_headers);
+	g_free (cb_data->response);
+	g_free (cb_data->msg);
 	g_free (cb_data->ifspec);
-	g_slice_free (NMConnectivityCheckHandle, cb_data);
-}
-
-/*****************************************************************************/
-
-#if WITH_CONCHECK
-static const char *
-_check_handle_get_response (NMConnectivityCheckHandle *cb_data)
-{
-	return cb_data->concheck.response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE;
+	g_source_remove (cb_data->timeout_id);
+	g_slice_free (ConCheckCbData, cb_data);
 }
 
 static void
-curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
+curl_check_connectivity (CURLM *mhandle, CURLMcode ret)
 {
-	NMConnectivityCheckHandle *cb_data;
+	ConCheckCbData *cb_data;
 	CURLMsg *msg;
 	CURLcode eret;
 	gint m_left;
-	long response_code;
-	CURLMcode ret;
-	int running_handles;
 
-	ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles);
 	if (ret != CURLM_OK)
-		_LOGE ("connectivity check failed: %d", ret);
+		_LOGW ("connectivity check failed");
 
 	while ((msg = curl_multi_info_read (mhandle, &m_left))) {
-
 		if (msg->msg != CURLMSG_DONE)
 			continue;
 
 		/* Here we have completed a session. Check easy session result. */
 		eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data);
 		if (eret != CURLE_OK) {
-			_LOGE ("curl cannot extract cb_data for easy handle, skipping msg");
+			_LOG2E ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle);
 			continue;
 		}
 
-		if (!cb_data->callback) {
-			/* callback was already invoked earlier. */
-			cb_data_free (cb_data, NM_CONNECTIVITY_UNKNOWN, NULL, NULL);
-		} else if (msg->data.result != CURLE_OK) {
-			gs_free char *log_message = NULL;
-
-			log_message = g_strdup_printf ("check failed with curl status %d", msg->data.result);
-			cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL,
-			              log_message);
-		} else if (   !((_check_handle_get_response (cb_data))[0])
-		           && (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK)
-		           && response_code == 204) {
-			/* If we got a 204 response code (no content) and we actually
-			 * requested no content, report full connectivity. */
-			cb_data_free (cb_data, NM_CONNECTIVITY_FULL, NULL,
-			              "no content, as expected");
-		} else {
-			/* If we get here, it means that easy_write_cb() didn't read enough
-			 * bytes to be able to do a match, or that we were asking for no content
-			 * (204 response code) and we actually got some. Either way, that is
-			 * an indication of a captive portal */
-			cb_data_free (cb_data, NM_CONNECTIVITY_PORTAL, NULL,
-			              "unexpected short response");
+		if (cb_data) {
+			/* If cb_data is still there this message hasn't been
+			 * taken care of. Do so now. */
+			if (msg->data.result == CURLE_OK) {
+				/* If we get here, it means that easy_write_cb() didn't read enough
+				 * bytes to be able to do a match. */
+				_LOG2I ("response shorter than expected '%s'; assuming captive portal.",
+				        cb_data->response);
+				finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL);
+			} else {
+				_LOG2D ("check failed (%d)", msg->data.result);
+				finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED);
+			}
 		}
+
+		curl_multi_remove_handle (mhandle, msg->easy_handle);
+		curl_easy_cleanup (msg->easy_handle);
 	}
 }
 
 static gboolean
 curl_timeout_cb (gpointer user_data)
 {
-	gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data));
+	NMConnectivity *self = NM_CONNECTIVITY (user_data);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLMcode ret;
+	int pending_conn;
+
+	priv->curl_timer = 0;
+
+	ret = curl_multi_socket_action (priv->curl_mhandle, CURL_SOCKET_TIMEOUT, 0, &pending_conn);
+	_LOGT ("timeout elapsed - multi_socket_action (%d conn remaining)", pending_conn);
+
+	curl_check_connectivity (priv->curl_mhandle, ret);
 
-	priv->concheck.curl_timer = 0;
-	curl_check_connectivity (priv->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
 	return G_SOURCE_REMOVE;
 }
 
@@ -305,17 +197,21 @@ multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata)
 	NMConnectivity *self = NM_CONNECTIVITY (userdata);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->concheck.curl_timer);
+	nm_clear_g_source (&priv->curl_timer);
 	if (timeout_ms != -1)
-		priv->concheck.curl_timer = g_timeout_add (timeout_ms, curl_timeout_cb, self);
+		priv->curl_timer = g_timeout_add (timeout_ms, curl_timeout_cb, self);
+
 	return 0;
 }
 
 static gboolean
-curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data)
+curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data)
 {
-	gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data));
+	NMConnectivity *self = NM_CONNECTIVITY (data);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLMcode ret;
+	int pending_conn = 0;
+	gboolean bret = TRUE;
 	int fd = g_io_channel_unix_get_fd (ch);
 	int action = 0;
 
@@ -323,11 +219,16 @@ curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data)
 		action |= CURL_CSELECT_IN;
 	if (condition & G_IO_OUT)
 		action |= CURL_CSELECT_OUT;
-	if (condition & G_IO_ERR)
-		action |= CURL_CSELECT_ERR;
 
-	curl_check_connectivity (priv->concheck.curl_mhandle, fd, action);
-	return G_SOURCE_CONTINUE;
+	ret = curl_multi_socket_action (priv->curl_mhandle, fd, 0, &pending_conn);
+
+	curl_check_connectivity (priv->curl_mhandle, ret);
+
+	if (pending_conn == 0) {
+		nm_clear_g_source (&priv->curl_timer);
+		bret = FALSE;
+	}
+	return bret;
 }
 
 typedef struct {
@@ -340,12 +241,11 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void
 {
 	NMConnectivity *self = NM_CONNECTIVITY (userdata);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
-	CurlSockData *fdp = socketp;
+	CurlSockData *fdp = (CurlSockData *) socketp;
 	GIOCondition condition = 0;
 
 	if (what == CURL_POLL_REMOVE) {
 		if (fdp) {
-			curl_multi_assign (priv->concheck.curl_mhandle, s, NULL);
 			nm_clear_g_source (&fdp->ev);
 			g_io_channel_unref (fdp->ch);
 			g_slice_free (CurlSockData, fdp);
@@ -354,7 +254,6 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void
 		if (!fdp) {
 			fdp = g_slice_new0 (CurlSockData);
 			fdp->ch = g_io_channel_unix_new (s);
-			curl_multi_assign (priv->concheck.curl_mhandle, s, fdp);
 		} else
 			nm_clear_g_source (&fdp->ev);
 
@@ -362,26 +261,29 @@ multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void
 			condition = G_IO_IN;
 		else if (what == CURL_POLL_OUT)
 			condition = G_IO_OUT;
-		else if (what == CURL_POLL_INOUT)
+		else if (condition == CURL_POLL_INOUT)
 			condition = G_IO_IN | G_IO_OUT;
 
 		if (condition)
 			fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self);
+		curl_multi_assign (priv->curl_mhandle, s, fdp);
 	}
 
 	return CURLM_OK;
 }
 
+#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
+
 static size_t
 easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata)
 {
-	NMConnectivityCheckHandle *cb_data = userdata;
+	ConCheckCbData *cb_data = userdata;
 	size_t len = size * nitems;
 
 	if (   len >= sizeof (HEADER_STATUS_ONLINE) - 1
 	    && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) {
-		cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL,
-		                         NULL, "status header found");
+		_LOG2D ("status header found, check successful");
+		finish_cb_data (cb_data, NM_CONNECTIVITY_FULL);
 		return 0;
 	}
 
@@ -391,26 +293,23 @@ easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata)
 static size_t
 easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata)
 {
-	NMConnectivityCheckHandle *cb_data = userdata;
+	ConCheckCbData *cb_data = userdata;
 	size_t len = size * nmemb;
-	const char *response = _check_handle_get_response (cb_data);;
-
-	if (!cb_data->concheck.recv_msg)
-		cb_data->concheck.recv_msg = g_string_sized_new (len + 10);
 
-	g_string_append_len (cb_data->concheck.recv_msg, buffer, len);
+	cb_data->msg = g_realloc (cb_data->msg, cb_data->msg_size + len);
+	memcpy (cb_data->msg + cb_data->msg_size, buffer, len);
+	cb_data->msg_size += len;
 
-	if (   response
-	    && cb_data->concheck.recv_msg->len >= strlen (response)) {
+	if (cb_data->msg_size >= strlen (cb_data->response)) {
 		/* We already have enough data -- check response */
-		if (g_str_has_prefix (cb_data->concheck.recv_msg->str, response)) {
-			cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_FULL, NULL,
-			                         "expected response");
+		if (g_str_has_prefix (cb_data->msg, cb_data->response)) {
+			_LOG2D ("check successful.");
+			finish_cb_data (cb_data, NM_CONNECTIVITY_FULL);
 		} else {
-			cb_data_invoke_callback (cb_data, NM_CONNECTIVITY_PORTAL, NULL,
-			                         "unexpected response");
+			_LOG2I ("response did not match expected response '%s'; assuming captive portal.",
+			        cb_data->response);
+			finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL);
 		}
-
 		return 0;
 	}
 
@@ -418,139 +317,105 @@ easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata)
 }
 
 static gboolean
-_timeout_cb (gpointer user_data)
-{
-	NMConnectivityCheckHandle *cb_data = user_data;
-	NMConnectivity *self;
-
-	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
-
-	self = cb_data->self;
-
-	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst));
-
-	cb_data_free (cb_data, NM_CONNECTIVITY_LIMITED, NULL, "timeout");
-	return G_SOURCE_REMOVE;
-}
-#endif
-
-static gboolean
-_idle_cb (gpointer user_data)
+timeout_cb (gpointer user_data)
 {
-	NMConnectivityCheckHandle *cb_data = user_data;
-
-	nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
-	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
+	ConCheckCbData *cb_data = user_data;
+	NMConnectivity *self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple)));
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURL *ehandle = cb_data->curl_ehandle;
 
-	cb_data->timeout_id = 0;
-	if (!cb_data->ifspec) {
-		gs_free_error GError *error = NULL;
+	_LOG2I ("timed out");
+	finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED);
+	curl_multi_remove_handle (priv->curl_mhandle, ehandle);
+	curl_easy_cleanup (ehandle);
 
-		/* the invocation was with an invalid ifname. It is a fail. */
-		g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
-		             "no interface specified for connectivity check");
-		cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, NULL, "missing interface");
-	} else
-		cb_data_free (cb_data, NM_CONNECTIVITY_FAKE, NULL, "fake result");
 	return G_SOURCE_REMOVE;
 }
 
-NMConnectivityCheckHandle *
-nm_connectivity_check_start (NMConnectivity *self,
-                             const char *iface,
-                             NMConnectivityCheckCallback callback,
-                             gpointer user_data)
+void
+nm_connectivity_check_async (NMConnectivity      *self,
+                             const char          *iface,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
 {
 	NMConnectivityPrivate *priv;
-	NMConnectivityCheckHandle *cb_data;
-
-	g_return_val_if_fail (NM_IS_CONNECTIVITY (self), NULL);
-	g_return_val_if_fail (!iface || iface[0], NULL);
-	g_return_val_if_fail (callback, NULL);
+	GSimpleAsyncResult *simple;
+	CURL *ehandle = NULL;
 
+	g_return_if_fail (NM_IS_CONNECTIVITY (self));
 	priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
-	cb_data = g_slice_new0 (NMConnectivityCheckHandle);
-	cb_data->self = self;
-	c_list_link_tail (&priv->handles_lst_head, &cb_data->handles_lst);
-	cb_data->callback = callback;
-	cb_data->user_data = user_data;
+	simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+	                                    nm_connectivity_check_async);
 
-	if (iface)
-		cb_data->ifspec = g_strdup_printf ("if!%s", iface);
+	if (priv->enabled)
+		ehandle = curl_easy_init ();
 
-#if WITH_CONCHECK
-	if (iface) {
-		CURL *ehandle;
-
-		if (   priv->enabled
-		    && (ehandle = curl_easy_init ())) {
-
-			cb_data->concheck.response = g_strdup (priv->response);
-			cb_data->concheck.curl_ehandle = ehandle;
-			cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close");
-			curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri);
-			curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb);
-			curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data);
-			curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb);
-			curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data);
-			curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data);
-			curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers);
-			curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
-			curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle);
-
-			cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data);
-
-			_LOG2D ("start request to '%s'", priv->uri);
-			return cb_data;
-		}
+	if (ehandle) {
+		ConCheckCbData *cb_data = g_slice_new0 (ConCheckCbData);
+
+		cb_data->curl_ehandle = ehandle;
+		cb_data->request_headers = curl_slist_append (NULL, "Connection: close");
+		cb_data->ifspec = g_strdup_printf ("if!%s", iface);
+		cb_data->simple = simple;
+		if (priv->response)
+			cb_data->response = g_strdup (priv->response);
+		else
+			cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE);
+
+		curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri);
+		curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb);
+		curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb);
+		curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers);
+		curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
+		curl_multi_add_handle (priv->curl_mhandle, ehandle);
+
+		cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data);
+
+		_LOG2D ("sending request to '%s'", priv->uri);
+		return;
+	} else {
+		_LOGD ("(%s) faking request. Connectivity check disabled", iface);
 	}
-#endif
 
-	_LOG2D ("start fake request");
-	cb_data->timeout_id = g_idle_add (_idle_cb, cb_data);
-	return cb_data;
+	g_simple_async_result_set_op_res_gssize (simple, NM_CONNECTIVITY_UNKNOWN);
+	g_simple_async_result_complete_in_idle (simple);
+	g_object_unref (simple);
 }
 
-void
-nm_connectivity_check_cancel (NMConnectivityCheckHandle *cb_data)
+NMConnectivityState
+nm_connectivity_check_finish (NMConnectivity  *self,
+                              GAsyncResult    *result,
+                              GError         **error)
 {
-	NMConnectivity *self;
-	gs_free_error GError *error = NULL;
-
-	g_return_if_fail (cb_data);
-
-	self = cb_data->self;
+	GSimpleAsyncResult *simple;
 
-	g_return_if_fail (NM_IS_CONNECTIVITY (self));
-	g_return_if_fail (!c_list_is_empty (&cb_data->handles_lst));
-	g_return_if_fail (cb_data->callback);
-
-	nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (self)->handles_lst_head, &cb_data->handles_lst));
-
-	nm_utils_error_set_cancelled (&error, FALSE, "NMConnectivity");
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), NM_CONNECTIVITY_UNKNOWN);
 
-	cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "cancelled");
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NM_CONNECTIVITY_UNKNOWN;
+	return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple);
 }
 
-/*****************************************************************************/
-
 gboolean
 nm_connectivity_check_enabled (NMConnectivity *self)
 {
-	g_return_val_if_fail (NM_IS_CONNECTIVITY (self), FALSE);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
-	return NM_CONNECTIVITY_GET_PRIVATE (self)->enabled;
+	return priv->enabled;
 }
 
 /*****************************************************************************/
 
-guint
-nm_connectivity_get_interval (NMConnectivity *self)
+static gboolean
+periodic_check (gpointer user_data)
 {
-	return nm_connectivity_check_enabled (self)
-	       ? NM_CONNECTIVITY_GET_PRIVATE (self)->interval
-	       : 0;
+	g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0);
+	return G_SOURCE_CONTINUE;
 }
 
 static void
@@ -590,22 +455,18 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
 
 	/* Set the interval. */
 	interval = nm_config_data_get_connectivity_interval (config_data);
-	interval = MIN (interval, (7 * 24 * 3600));
 	if (priv->interval != interval) {
 		priv->interval = interval;
 		changed = TRUE;
 	}
 
-	enabled = FALSE;
-#if WITH_CONCHECK
+	/* Set enabled flag. */
+	enabled = nm_config_data_get_connectivity_enabled (config_data);
 	/* connectivity checking also requires a valid URI, interval and
 	 * curl_mhandle */
-	if (   priv->uri
-	    && priv->interval
-	    && priv->concheck.curl_mhandle)
-		enabled = nm_config_data_get_connectivity_enabled (config_data);
-#endif
-
+	if (!(priv->uri && priv->interval && priv->curl_mhandle)) {
+		enabled = FALSE;
+	}
 	if (priv->enabled != enabled) {
 		priv->enabled = enabled;
 		changed = TRUE;
@@ -613,7 +474,7 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
 
 	/* Set the response. */
 	response = nm_config_data_get_connectivity_response (config_data);
-	if (!nm_streq0 (response, priv->response)) {
+	if (g_strcmp0 (response, priv->response) != 0) {
 		/* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response
 		 * (including "") is accepted. */
 		g_free (priv->response);
@@ -621,8 +482,11 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
 		changed = TRUE;
 	}
 
-	if (changed)
-		g_signal_emit (self, signals[CONFIG_CHANGED], 0);
+	if (changed) {
+		nm_clear_g_source (&priv->periodic_check_id);
+		if (nm_connectivity_check_enabled (self))
+			priv->periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self);
+	}
 }
 
 static void
@@ -635,37 +499,34 @@ config_changed_cb (NMConfig *config,
 	update_config (self, config_data);
 }
 
-/*****************************************************************************/
-
 static void
 nm_connectivity_init (NMConnectivity *self)
 {
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLcode retv;
 
-	c_list_init (&priv->handles_lst_head);
+	retv = curl_global_init (CURL_GLOBAL_ALL);
+	if (retv == CURLE_OK)
+		priv->curl_mhandle = curl_multi_init ();
+
+	if (!priv->curl_mhandle)
+		 _LOGE ("unable to init cURL, connectivity check will not work");
+	else {
+		curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
+		curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETDATA, self);
+		curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
+		curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERDATA, self);
+		curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1);
+	}
 
 	priv->config = g_object_ref (nm_config_get ());
+
+	update_config (self, nm_config_get_data (priv->config));
 	g_signal_connect (G_OBJECT (priv->config),
 	                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
 	                  G_CALLBACK (config_changed_cb),
 	                  self);
 
-#if WITH_CONCHECK
-	if (curl_global_init (CURL_GLOBAL_ALL) == CURLE_OK)
-		priv->concheck.curl_mhandle = curl_multi_init ();
-
-	if (!priv->concheck.curl_mhandle)
-		 _LOGE ("unable to init cURL, connectivity check will not work");
-	else {
-		curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
-		curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETDATA, self);
-		curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
-		curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERDATA, self);
-		curl_multi_setopt (priv->concheck.curl_mhandle, CURLOPT_VERBOSE, 1);
-	}
-#endif
-
-	update_config (self, nm_config_get_data (priv->config));
 }
 
 static void
@@ -673,33 +534,19 @@ dispose (GObject *object)
 {
 	NMConnectivity *self = NM_CONNECTIVITY (object);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
-	NMConnectivityCheckHandle *cb_data;
-	GError *error = NULL;
-
-again:
-	c_list_for_each_entry (cb_data, &priv->handles_lst_head, handles_lst) {
-		if (!error)
-			nm_utils_error_set_cancelled (&error, TRUE, "NMConnectivity");
-		cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, error, "shutting down");
-		goto again;
-	}
-	g_clear_error (&error);
 
 	g_clear_pointer (&priv->uri, g_free);
 	g_clear_pointer (&priv->response, g_free);
 
-#if WITH_CONCHECK
-	nm_clear_g_source (&priv->concheck.curl_timer);
-
-	curl_multi_cleanup (priv->concheck.curl_mhandle);
-	curl_global_cleanup ();
-#endif
-
 	if (priv->config) {
 		g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
 		g_clear_object (&priv->config);
 	}
 
+	curl_multi_cleanup (priv->curl_mhandle);
+	curl_global_cleanup ();
+	nm_clear_g_source (&priv->periodic_check_id);
+
 	G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object);
 }
 
@@ -708,8 +555,8 @@ nm_connectivity_class_init (NMConnectivityClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-	signals[CONFIG_CHANGED] =
-	    g_signal_new (NM_CONNECTIVITY_CONFIG_CHANGED,
+	signals[PERIODIC_CHECK] =
+	    g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index df9295e0..d9a9f233 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -24,9 +24,6 @@
 
 #include "nm-dbus-interface.h"
 
-#define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1)
-#define NM_CONNECTIVITY_FAKE  ((NMConnectivityState) -2)
-
 #define NM_TYPE_CONNECTIVITY            (nm_connectivity_get_type ())
 #define NM_CONNECTIVITY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTIVITY, NMConnectivity))
 #define NM_CONNECTIVITY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
@@ -34,7 +31,7 @@
 #define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY))
 #define NM_CONNECTIVITY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
 
-#define NM_CONNECTIVITY_CONFIG_CHANGED  "config-changed"
+#define NM_CONNECTIVITY_PERIODIC_CHECK  "nm-connectivity-periodic-check"
 
 typedef struct _NMConnectivityClass NMConnectivityClass;
 
@@ -44,23 +41,13 @@ NMConnectivity *nm_connectivity_get (void);
 
 const char *nm_connectivity_state_to_string (NMConnectivityState state);
 
+void                 nm_connectivity_check_async  (NMConnectivity       *self,
+                                                   const char           *iface,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              user_data);
+NMConnectivityState  nm_connectivity_check_finish (NMConnectivity       *self,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
 gboolean nm_connectivity_check_enabled (NMConnectivity *self);
 
-guint nm_connectivity_get_interval (NMConnectivity *self);
-
-typedef struct _NMConnectivityCheckHandle NMConnectivityCheckHandle;
-
-typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self,
-                                             NMConnectivityCheckHandle *handle,
-                                             NMConnectivityState state,
-                                             GError *error,
-                                             gpointer user_data);
-
-NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self,
-                                                        const char *iface,
-                                                        NMConnectivityCheckCallback callback,
-                                                        gpointer user_data);
-
-void nm_connectivity_check_cancel (NMConnectivityCheckHandle *handle);
-
 #endif /* __NETWORKMANAGER_CONNECTIVITY_H__ */
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index b97be0b8..f6b33a14 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -452,6 +452,83 @@ nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *
 	return exit_status;
 }
 
+/**
+ * nm_utils_get_start_time_for_pid:
+ * @pid: the process identifier
+ * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
+ * @out_ppid: parent process id
+ *
+ * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
+ * and adjusted.
+ *
+ * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
+ * If an error occurs (e.g. the process does not exist), 0 is returned.
+ *
+ * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
+ **/
+guint64
+nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
+{
+	guint64 start_time;
+	char filename[256];
+	gs_free gchar *contents = NULL;
+	size_t length;
+	gs_strfreev gchar **tokens = NULL;
+	guint num_tokens;
+	gchar *p;
+	char state = ' ';
+	gint64 ppid = 0;
+
+	start_time = 0;
+	contents = NULL;
+
+	g_return_val_if_fail (pid > 0, 0);
+
+	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+
+	if (!g_file_get_contents (filename, &contents, &length, NULL))
+		goto fail;
+
+	/* start time is the token at index 19 after the '(process name)' entry - since only this
+	 * field can contain the ')' character, search backwards for this to avoid malicious
+	 * processes trying to fool us
+	 */
+	p = strrchr (contents, ')');
+	if (p == NULL)
+		goto fail;
+	p += 2; /* skip ') ' */
+	if (p - contents >= (int) length)
+		goto fail;
+
+	state = p[0];
+
+	tokens = g_strsplit (p, " ", 0);
+
+	num_tokens = g_strv_length (tokens);
+
+	if (num_tokens < 20)
+		goto fail;
+
+	if (out_ppid) {
+		ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+		if (ppid == 0)
+			goto fail;
+	}
+
+	start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0);
+	if (start_time == 0)
+		goto fail;
+
+	NM_SET_OUT (out_state, state);
+	NM_SET_OUT (out_ppid, ppid);
+	return start_time;
+
+fail:
+	NM_SET_OUT (out_state, ' ');
+	NM_SET_OUT (out_ppid, 0);
+	return 0;
+}
+
 /*****************************************************************************/
 
 typedef struct {
@@ -1169,8 +1246,8 @@ typedef struct {
 static gboolean
 match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *out_a, guint32 *out_b, guint32 *out_c)
 {
-	char buf[30 + 1];
-	const int BUFSIZE = G_N_ELEMENTS (buf) - 1;
+	const int BUFSIZE = 30;
+	char buf[BUFSIZE + 1];
 	guint i = 0;
 	char *pa = NULL, *pb = NULL, *pc = NULL;
 	gint64 a, b, c;
@@ -1800,6 +1877,110 @@ nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key)
 
 /*****************************************************************************/
 
+gboolean
+nm_utils_resolve_conf_parse (int addr_family,
+                             const char *rc_contents,
+                             GArray *nameservers,
+                             GPtrArray *dns_options)
+{
+	guint i;
+	gboolean changed = FALSE;
+	gs_free const char **lines = NULL;
+	gsize l;
+
+	g_return_val_if_fail (rc_contents, FALSE);
+	g_return_val_if_fail (nameservers, FALSE);
+	g_return_val_if_fail (   (   addr_family == AF_INET
+	                          && g_array_get_element_size (nameservers) == sizeof (in_addr_t))
+	                      || (   addr_family == AF_INET6
+	                          && g_array_get_element_size (nameservers) == sizeof (struct in6_addr)), FALSE);
+
+	lines = nm_utils_strsplit_set (rc_contents, "\r\n");
+	if (!lines)
+		return FALSE;
+
+/* like glibc's MATCH() macro in resolv/res_init.c. */
+#define RC_MATCH(line, option, out_arg) \
+    ({ \
+        const char *const _line = (line); \
+        gboolean _match = FALSE; \
+        \
+        if (   (strncmp (_line, option, NM_STRLEN (option)) == 0) \
+            && (NM_IN_SET (_line[NM_STRLEN (option)], ' ', '\t'))) { \
+            _match = TRUE;\
+            (out_arg) = &_line[NM_STRLEN (option) + 1]; \
+        } \
+        _match; \
+    })
+
+	for (l = 0; lines[l]; l++) {
+		const char *const line = lines[l];
+		const char *s = NULL;
+
+		if (RC_MATCH (line, "nameserver", s)) {
+			gs_free char *s_cpy = NULL;
+			NMIPAddr ns;
+
+			s = nm_strstrip_avoid_copy (s, &s_cpy);
+			if (inet_pton (addr_family, s, &ns) != 1)
+				continue;
+
+			if (addr_family == AF_INET) {
+				if (!ns.addr4)
+					continue;
+				for (i = 0; i < nameservers->len; i++) {
+					if (g_array_index (nameservers, guint32, i) == ns.addr4)
+						break;
+				}
+			} else {
+				if (IN6_IS_ADDR_UNSPECIFIED (&ns.addr6))
+					continue;
+				for (i = 0; i < nameservers->len; i++) {
+					struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, i);
+
+					if (IN6_ARE_ADDR_EQUAL (t, &ns.addr6))
+						break;
+				}
+			}
+
+			if (i == nameservers->len) {
+				g_array_append_val (nameservers, ns);
+				changed = TRUE;
+			}
+			continue;
+		}
+
+		if (RC_MATCH (line, "options", s)) {
+			if (!dns_options)
+				continue;
+
+			s = nm_str_skip_leading_spaces (s);
+			if (s[0]) {
+				gs_free const char **tokens = NULL;
+				gsize i_tokens;
+
+				tokens = nm_utils_strsplit_set (s, " \t");
+				for (i_tokens = 0; tokens && tokens[i_tokens]; i_tokens++) {
+					gs_free char *t = g_strstrip (g_strdup (tokens[i_tokens]));
+
+					if (   _nm_utils_dns_option_validate (t, NULL, NULL,
+					                                      addr_family == AF_INET6,
+					                                      _nm_utils_dns_option_descs)
+					    && _nm_utils_dns_option_find_idx (dns_options, t) < 0) {
+						g_ptr_array_add (dns_options, g_steal_pointer (&t));
+						changed = TRUE;
+					}
+				}
+			}
+			continue;
+		}
+	}
+
+	return changed;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_cmp_connection_by_autoconnect_priority:
  * @a:
@@ -2157,13 +2338,7 @@ _log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sort
 }
 
 void
-nm_utils_log_connection_diff (NMConnection *connection,
-                              NMConnection *diff_base,
-                              guint32 level,
-                              guint64 domain,
-                              const char *name,
-                              const char *prefix,
-                              const char *dbus_path)
+nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix)
 {
 	GHashTable *connection_diff = NULL;
 	GArray *sorted_hashes;
@@ -2239,6 +2414,7 @@ nm_utils_log_connection_diff (NMConnection *connection,
 
 			if (print_header) {
 				GError *err_verify = NULL;
+				const char *path = nm_connection_get_path (connection);
 				const char *t1, *t2;
 
 				t1 = nm_connection_get_connection_type (connection);
@@ -2248,12 +2424,12 @@ nm_utils_log_connection_diff (NMConnection *connection,
 					        prefix, name,
 					        connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
 					        diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2),
-					        NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				} else {
 					nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s):%s%s%s",
 					        prefix, name,
 					        connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
-					        NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				}
 				print_header = FALSE;
 
@@ -2571,7 +2747,7 @@ _get_contents_error (GError **error, int errsv, const char *format, ...)
 /**
  * nm_utils_fd_get_contents:
  * @fd: open file descriptor to read. The fd will not be closed,
- *   but don't rely on its state afterwards.
+ *   but don't rely on it's state afterwards.
  * @close_fd: if %TRUE, @fd will be closed by the function.
  *  Passing %TRUE here might safe a syscall for dup().
  * @max_length: allocate at most @max_length bytes. If the
@@ -3573,8 +3749,7 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED)
 /**
  * nm_utils_g_value_set_strv:
  * @value: a #GValue, initialized to store a #G_TYPE_STRV
- * @strings: a #GPtrArray of strings. %NULL values are not
- *   allowed.
+ * @strings: a #GPtrArray of strings
  *
  * Converts @strings to a #GStrv and stores it in @value.
  */
@@ -3582,13 +3757,11 @@ void
 nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
 {
 	char **strv;
-	guint i;
+	int i;
 
 	strv = g_new (char *, strings->len + 1);
-	for (i = 0; i < strings->len; i++) {
-		nm_assert (strings->pdata[i]);
+	for (i = 0; i < strings->len; i++)
 		strv[i] = g_strdup (strings->pdata[i]);
-	}
 	strv[i] = NULL;
 
 	g_value_take_boxed (value, strv);
@@ -3718,11 +3891,12 @@ nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
 	return t;
 }
 
-guint32
+gboolean
 nm_utils_lifetime_get (guint32 timestamp,
                        guint32 lifetime,
                        guint32 preferred,
                        gint32 now,
+                       guint32 *out_lifetime,
                        guint32 *out_preferred)
 {
 	guint32 t_lifetime, t_preferred;
@@ -3730,39 +3904,38 @@ nm_utils_lifetime_get (guint32 timestamp,
 	nm_assert (now >= 0);
 
 	if (timestamp == 0 && lifetime == 0) {
-		/* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy
+		/* We treat lifetime==0 && timestamp == 0 addresses as permanent addresses to allow easy
 		 * creation of such addresses (without requiring to set the lifetime fields to
 		 * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us
 		 * to drop an address will have timestamp set.
 		 */
-		NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT);
-		g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
-		return NM_PLATFORM_LIFETIME_PERMANENT;
-	}
+		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+		g_return_val_if_fail (preferred == 0, TRUE);
+	} else {
+		if (now <= 0)
+			now = nm_utils_get_monotonic_timestamp_s ();
+		t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
+		if (!t_lifetime) {
+			*out_lifetime = 0;
+			*out_preferred = 0;
+			return FALSE;
+		}
+		t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
 
-	if (now <= 0)
-		now = nm_utils_get_monotonic_timestamp_s ();
+		*out_lifetime = t_lifetime;
+		*out_preferred = MIN (t_preferred, t_lifetime);
 
-	t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
-	if (!t_lifetime) {
-		NM_SET_OUT (out_preferred, 0);
-		return 0;
+		/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
+		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
+		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
+		 */
+		g_return_val_if_fail (   timestamp != 0
+		                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
+		                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
+		g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
 	}
-
-	t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
-
-	NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime));
-
-	/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
-	 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
-	 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
-	 */
-	g_return_val_if_fail (   timestamp != 0
-	                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
-	                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime);
-	g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime);
-
-	return t_lifetime;
+	return TRUE;
 }
 
 const char *
@@ -4156,21 +4329,6 @@ nm_utils_format_con_diff_for_audit (GHashTable *diff)
 	return g_string_free (str, FALSE);
 }
 
-const char *
-nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing)
-{
-	g_return_val_if_fail (domain, NULL);
-	g_return_val_if_fail (domain[0], NULL);
-
-	if (domain[0] == '~') {
-		domain++;
-		NM_SET_OUT (is_routing, TRUE);
-	} else
-		NM_SET_OUT (is_routing, FALSE);
-
-	return domain;
-}
-
 /*****************************************************************************/
 
 NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref,
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index ec9e2947..cc784724 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -25,6 +25,8 @@
 #include <stdio.h>
 #include <arpa/inet.h>
 
+#include "nm-utils/nm-hash-utils.h"
+
 #include "nm-connection.h"
 
 /*****************************************************************************/
@@ -178,6 +180,8 @@ nm_utils_ip_route_metric_penalize (int addr_family, guint32 metric, guint32 pena
 
 int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED;
 
+guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
+
 void nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_domain,
                                  const char *log_name, guint32 wait_before_kill_msec,
                                  guint32 sleep_duration_msec, guint32 max_wait_msec);
@@ -227,14 +231,14 @@ gboolean nm_utils_connection_has_default_route (NMConnection *connection,
 char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id);
 const char *nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key);
 
+gboolean nm_utils_resolve_conf_parse (int addr_family,
+                                      const char *rc_contents,
+                                      GArray *nameservers,
+                                      GPtrArray *dns_options);
+
 int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b);
 
-void nm_utils_log_connection_diff (NMConnection *connection,
-                                   NMConnection *diff_base,
-                                   guint32 level, guint64 domain,
-                                   const char *name,
-                                   const char *prefix,
-                                   const char *dbus_path);
+void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix);
 
 gint64 nm_utils_get_monotonic_timestamp_ns (void);
 gint64 nm_utils_get_monotonic_timestamp_us (void);
@@ -242,13 +246,6 @@ gint64 nm_utils_get_monotonic_timestamp_ms (void);
 gint32 nm_utils_get_monotonic_timestamp_s (void);
 gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
 
-static inline gint64
-nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now)
-{
-	return    (*cache_now)
-	       ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ());
-}
-
 gboolean    nm_utils_is_valid_path_component (const char *name);
 const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name);
 
@@ -413,11 +410,12 @@ guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
                                                        guint32 duration,
                                                        gint32 now);
 
-guint32 nm_utils_lifetime_get (guint32 timestamp,
-                               guint32 lifetime,
-                               guint32 preferred,
-                               gint32 now,
-                               guint32 *out_preferred);
+gboolean nm_utils_lifetime_get (guint32 timestamp,
+                                guint32 lifetime,
+                                guint32 preferred,
+                                gint32 now,
+                                guint32 *out_lifetime,
+                                guint32 *out_preferred);
 
 gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr);
 
@@ -452,6 +450,4 @@ const char *nm_activation_type_to_string (NMActivationType activation_type);
 
 /*****************************************************************************/
 
-const char *nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing);
-
 #endif /* __NM_CORE_UTILS_H__ */
diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c
deleted file mode 100644
index 3e369129..00000000
--- a/src/nm-dbus-manager.c
+++ /dev/null
@@ -1,1626 +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) 2006 - 2013 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-dbus-manager.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <string.h>
-
-#include "c-list/src/c-list.h"
-#include "nm-dbus-interface.h"
-#include "nm-core-internal.h"
-#include "nm-dbus-compat.h"
-#include "nm-dbus-object.h"
-#include "NetworkManagerUtils.h"
-
-/* The base path for our GDBusObjectManagerServers.  They do not contain
- * "NetworkManager" because GDBusObjectManagerServer requires that all
- * exported objects be *below* the base path, and eg the Manager object
- * is the base path already.
- */
-#define OBJECT_MANAGER_SERVER_BASE_PATH "/org/freedesktop"
-
-/*****************************************************************************/
-
-typedef struct {
-	GVariant *value;
-} PropertyCacheData;
-
-typedef struct {
-	CList registration_lst;
-	NMDBusObject *obj;
-	NMDBusObjectClass *klass;
-	guint info_idx;
-	guint registration_id;
-	PropertyCacheData property_cache[];
-} RegistrationData;
-
-/* we require that @path is the first member of NMDBusManagerData
- * because _objects_by_path_hash() requires that. */
-G_STATIC_ASSERT (G_STRUCT_OFFSET (struct _NMDBusObjectInternal, path) == 0);
-
-enum {
-	PRIVATE_CONNECTION_NEW,
-	PRIVATE_CONNECTION_DISCONNECTED,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL];
-
-typedef struct {
-	GHashTable *objects_by_path;
-	CList objects_lst_head;
-
-	CList private_servers_lst_head;
-
-	NMDBusManagerSetPropertyHandler set_property_handler;
-	gpointer set_property_handler_data;
-
-	GDBusConnection *connection;
-	GDBusProxy *proxy;
-	guint objmgr_registration_id;
-} NMDBusManagerPrivate;
-
-struct _NMDBusManager {
-	GObject parent;
-	NMDBusManagerPrivate _priv;
-};
-
-struct _NMDBusManagerClass {
-	GObjectClass parent;
-};
-
-G_DEFINE_TYPE(NMDBusManager, nm_dbus_manager, G_TYPE_OBJECT)
-
-#define NM_DBUS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDBusManager, NM_IS_DBUS_MANAGER)
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_CORE
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bus-manager", __VA_ARGS__)
-
-NM_DEFINE_SINGLETON_GETTER (NMDBusManager, nm_dbus_manager_get, NM_TYPE_DBUS_MANAGER);
-
-/*****************************************************************************/
-
-static const GDBusInterfaceInfo interface_info_objmgr;
-static const GDBusSignalInfo signal_info_objmgr_interfaces_added;
-static const GDBusSignalInfo signal_info_objmgr_interfaces_removed;
-static GVariantBuilder *_obj_collect_properties_all (NMDBusObject *obj,
-                                                     GVariantBuilder *builder);
-
-/*****************************************************************************/
-
-static guint
-_objects_by_path_hash (gconstpointer user_data)
-{
-	const char *const*p_data = user_data;
-
-	nm_assert (p_data);
-	nm_assert (*p_data);
-	nm_assert ((*p_data)[0] == '/');
-
-	return nm_hash_str (*p_data);
-}
-
-static gboolean
-_objects_by_path_equal (gconstpointer user_data_a, gconstpointer user_data_b)
-{
-	const char *const*p_data_a = user_data_a;
-	const char *const*p_data_b = user_data_b;
-
-	nm_assert (p_data_a);
-	nm_assert (*p_data_a);
-	nm_assert ((*p_data_a)[0] == '/');
-	nm_assert (p_data_b);
-	nm_assert (*p_data_b);
-	nm_assert ((*p_data_b)[0] == '/');
-
-	return nm_streq (*p_data_a, *p_data_b);
-}
-
-/*****************************************************************************/
-
-typedef struct {
-	CList private_servers_lst;
-
-	const char *tag;
-	GQuark detail;
-	char *address;
-	GDBusServer *server;
-
-	/* With peer bus connections, we'll get a new connection for each
-	 * client.  For each connection we create an ObjectManager for
-	 * that connection to handle exporting our objects.
-	 *
-	 * Note that even for connections that don't export any objects
-	 * we'll still create GDBusObjectManager since that's where we store
-	 * the pointer to the GDBusConnection.
-	 */
-	CList object_mgr_lst_head;
-
-	NMDBusManager *manager;
-} PrivateServer;
-
-typedef struct {
-	CList object_mgr_lst;
-	GDBusObjectManagerServer *manager;
-	char *fake_sender;
-} ObjectMgrData;
-
-typedef struct {
-	GDBusConnection *connection;
-	PrivateServer *server;
-	gboolean remote_peer_vanished;
-} CloseConnectionInfo;
-
-/*****************************************************************************/
-
-static void
-_object_mgr_data_free (ObjectMgrData *obj_mgr_data)
-{
-	GDBusConnection *connection;
-
-	c_list_unlink_stale (&obj_mgr_data->object_mgr_lst);
-
-	connection = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager);
-	if (!g_dbus_connection_is_closed (connection))
-		g_dbus_connection_close (connection, NULL, NULL, NULL);
-	g_dbus_object_manager_server_set_connection (obj_mgr_data->manager, NULL);
-	g_object_unref (obj_mgr_data->manager);
-	g_object_unref (connection);
-
-	g_free (obj_mgr_data->fake_sender);
-
-	g_slice_free (ObjectMgrData, obj_mgr_data);
-}
-
-/*****************************************************************************/
-
-static gboolean
-close_connection_in_idle (gpointer user_data)
-{
-	CloseConnectionInfo *info = user_data;
-	PrivateServer *server = info->server;
-	ObjectMgrData *obj_mgr_data, *obj_mgr_data_safe;
-
-	/* Emit this for the manager */
-	g_signal_emit (server->manager,
-	               signals[PRIVATE_CONNECTION_DISCONNECTED],
-	               server->detail,
-	               info->connection);
-
-	/* FIXME: there's a bug (754730) in GLib for which the connection
-	 * is marked as closed when the remote peer vanishes but its
-	 * resources are not cleaned up.  Work around it by explicitly
-	 * closing the connection in that case. */
-	if (info->remote_peer_vanished)
-		g_dbus_connection_close (info->connection, NULL, NULL, NULL);
-
-	c_list_for_each_entry_safe (obj_mgr_data, obj_mgr_data_safe, &server->object_mgr_lst_head, object_mgr_lst) {
-		gs_unref_object GDBusConnection *connection = NULL;
-
-		connection = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager);
-		if (connection == info->connection) {
-			_object_mgr_data_free (obj_mgr_data);
-			break;
-		}
-	}
-
-	g_object_unref (server->manager);
-	g_slice_free (CloseConnectionInfo, info);
-
-	return G_SOURCE_REMOVE;
-}
-
-static void
-private_server_closed_connection (GDBusConnection *conn,
-                                  gboolean remote_peer_vanished,
-                                  GError *error,
-                                  gpointer user_data)
-{
-	PrivateServer *s = user_data;
-	CloseConnectionInfo *info;
-
-	/* Clean up after the connection */
-	_LOGD ("(%s) closed connection %p on private socket", s->tag, conn);
-
-	info = g_slice_new0 (CloseConnectionInfo);
-	info->connection = conn;
-	info->server = s;
-	info->remote_peer_vanished = remote_peer_vanished;
-
-	g_object_ref (s->manager);
-
-	/* Delay the close of connection to ensure that D-Bus signals
-	 * are handled */
-	g_idle_add (close_connection_in_idle, info);
-}
-
-static gboolean
-private_server_new_connection (GDBusServer *server,
-                               GDBusConnection *conn,
-                               gpointer user_data)
-{
-	PrivateServer *s = user_data;
-	ObjectMgrData *obj_mgr_data;
-	static guint32 counter = 0;
-	GDBusObjectManagerServer *manager;
-	char *sender;
-
-	g_signal_connect (conn, "closed", G_CALLBACK (private_server_closed_connection), s);
-
-	/* Fake a sender since private connections don't have one */
-	sender = g_strdup_printf ("x:y:%d", counter++);
-
-	manager = g_dbus_object_manager_server_new (OBJECT_MANAGER_SERVER_BASE_PATH);
-	g_dbus_object_manager_server_set_connection (manager, conn);
-
-	obj_mgr_data = g_slice_new (ObjectMgrData);
-	obj_mgr_data->manager = manager;
-	obj_mgr_data->fake_sender = sender;
-	c_list_link_tail (&s->object_mgr_lst_head, &obj_mgr_data->object_mgr_lst);
-
-	_LOGD ("(%s) accepted connection %p on private socket", s->tag, conn);
-
-	/* Emit this for the manager.
-	 *
-	 * It is essential to do this from the "new-connection" signal handler, as
-	 * at that point no messages from the connection are yet processed
-	 * (which avoids races with registering objects). */
-	g_signal_emit (s->manager,
-	               signals[PRIVATE_CONNECTION_NEW],
-	               s->detail,
-	               conn,
-	               manager);
-	return TRUE;
-}
-
-static gboolean
-private_server_authorize (GDBusAuthObserver *observer,
-                          GIOStream         *stream,
-                          GCredentials      *credentials,
-                          gpointer           user_data)
-{
-	return g_credentials_get_unix_user (credentials, NULL) == 0;
-}
-
-static gboolean
-private_server_allow_mechanism (GDBusAuthObserver *observer,
-                                const char *mechanism,
-                                gpointer user_data)
-{
-	return NM_IN_STRSET (mechanism, "EXTERNAL");
-}
-
-static void
-private_server_free (gpointer ptr)
-{
-	PrivateServer *s = ptr;
-	ObjectMgrData *obj_mgr_data, *obj_mgr_data_safe;
-
-	c_list_unlink_stale (&s->private_servers_lst);
-
-	unlink (s->address);
-	g_free (s->address);
-
-	c_list_for_each_entry_safe (obj_mgr_data, obj_mgr_data_safe, &s->object_mgr_lst_head, object_mgr_lst)
-		_object_mgr_data_free (obj_mgr_data);
-
-	g_dbus_server_stop (s->server);
-
-	g_signal_handlers_disconnect_by_func (s->server, G_CALLBACK (private_server_new_connection), s);
-
-	g_object_unref (s->server);
-
-	g_slice_free (PrivateServer, s);
-}
-
-void
-nm_dbus_manager_private_server_register (NMDBusManager *self,
-                                         const char *path,
-                                         const char *tag)
-{
-	NMDBusManagerPrivate *priv;
-	PrivateServer *s;
-	gs_unref_object GDBusAuthObserver *auth_observer = NULL;
-	GDBusServer *server;
-	GError *error = NULL;
-	gs_free char *address = NULL;
-	gs_free char *guid = NULL;
-
-	g_return_if_fail (NM_IS_DBUS_MANAGER (self));
-	g_return_if_fail (path);
-	g_return_if_fail (tag);
-
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	/* Only one instance per tag; but don't warn */
-	c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) {
-		if (nm_streq0 (tag, s->tag))
-			return;
-	}
-
-	unlink (path);
-	address = g_strdup_printf ("unix:path=%s", path);
-
-	_LOGD ("(%s) creating private socket %s", tag, address);
-
-	guid = g_dbus_generate_guid ();
-	auth_observer = g_dbus_auth_observer_new ();
-	g_signal_connect (auth_observer, "authorize-authenticated-peer",
-	                  G_CALLBACK (private_server_authorize), NULL);
-	g_signal_connect (auth_observer, "allow-mechanism",
-	                  G_CALLBACK (private_server_allow_mechanism), NULL);
-	server = g_dbus_server_new_sync (address,
-	                                 G_DBUS_SERVER_FLAGS_NONE,
-	                                 guid,
-	                                 auth_observer,
-	                                 NULL, &error);
-
-	if (!server) {
-		_LOGW ("(%s) failed to set up private socket %s: %s",
-		       tag, address, error->message);
-		g_error_free (error);
-		return;
-	}
-
-	s = g_slice_new0 (PrivateServer);
-	s->address = g_steal_pointer (&address);
-	s->server = server;
-	g_signal_connect (server, "new-connection",
-	                  G_CALLBACK (private_server_new_connection), s);
-
-	c_list_init (&s->object_mgr_lst_head);
-
-	s->manager = self;
-	s->detail = g_quark_from_string (tag);
-	s->tag = g_quark_to_string (s->detail);
-
-	c_list_link_tail (&priv->private_servers_lst_head, &s->private_servers_lst);
-
-	g_dbus_server_start (server);
-}
-
-static const char *
-private_server_get_connection_owner (PrivateServer *s, GDBusConnection *connection)
-{
-	ObjectMgrData *obj_mgr_data;
-
-	nm_assert (s);
-	nm_assert (G_IS_DBUS_CONNECTION (connection));
-
-	c_list_for_each_entry (obj_mgr_data, &s->object_mgr_lst_head, object_mgr_lst) {
-		gs_unref_object GDBusConnection *c = NULL;
-
-		c = g_dbus_object_manager_server_get_connection (obj_mgr_data->manager);
-		if (c == connection)
-			return obj_mgr_data->fake_sender;
-	}
-	return NULL;
-}
-
-static GDBusConnection *
-private_server_get_connection_by_owner (PrivateServer *s, const char *owner)
-{
-	ObjectMgrData *obj_mgr_data;
-
-	nm_assert (s);
-	nm_assert (owner);
-
-	c_list_for_each_entry (obj_mgr_data, &s->object_mgr_lst_head, object_mgr_lst) {
-		if (nm_streq (owner, obj_mgr_data->fake_sender))
-			return g_dbus_object_manager_server_get_connection (obj_mgr_data->manager);
-	}
-	return NULL;
-}
-
-/*****************************************************************************/
-
-static gboolean
-_bus_get_unix_pid (NMDBusManager *self,
-                   const char *sender,
-                   gulong *out_pid,
-                   GError **error)
-{
-	guint32 unix_pid = G_MAXUINT32;
-	gs_unref_variant GVariant *ret = NULL;
-
-	ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
-	                                "GetConnectionUnixProcessID",
-	                                g_variant_new ("(s)", sender),
-	                                G_VARIANT_TYPE ("(u)"),
-	                                G_DBUS_CALL_FLAGS_NONE, 2000,
-	                                NULL, error);
-	if (!ret)
-		return FALSE;
-
-	g_variant_get (ret, "(u)", &unix_pid);
-
-	*out_pid = (gulong) unix_pid;
-	return TRUE;
-}
-
-static gboolean
-_bus_get_unix_user (NMDBusManager *self,
-                    const char *sender,
-                    gulong *out_user,
-                    GError **error)
-{
-	guint32 unix_uid = G_MAXUINT32;
-	gs_unref_variant GVariant *ret = NULL;
-
-	ret = _nm_dbus_proxy_call_sync (NM_DBUS_MANAGER_GET_PRIVATE (self)->proxy,
-	                                "GetConnectionUnixUser",
-	                                g_variant_new ("(s)", sender),
-	                                G_VARIANT_TYPE ("(u)"),
-	                                G_DBUS_CALL_FLAGS_NONE, 2000,
-	                                NULL, error);
-	if (!ret)
-		return FALSE;
-
-	g_variant_get (ret, "(u)", &unix_uid);
-
-	*out_user = (gulong) unix_uid;
-	return TRUE;
-}
-
-/**
- * _get_caller_info():
- *
- * Given a GDBus method invocation, or a GDBusConnection + GDBusMessage,
- * return the sender and the UID of the sender.
- */
-static gboolean
-_get_caller_info (NMDBusManager *self,
-                  GDBusMethodInvocation *context,
-                  GDBusConnection *connection,
-                  GDBusMessage *message,
-                  char **out_sender,
-                  gulong *out_uid,
-                  gulong *out_pid)
-{
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	const char *sender;
-
-	if (context) {
-		connection = g_dbus_method_invocation_get_connection (context);
-
-		/* only bus connections will have a sender */
-		sender = g_dbus_method_invocation_get_sender (context);
-	} else {
-		g_assert (message);
-		sender = g_dbus_message_get_sender (message);
-	}
-	g_assert (connection);
-
-	if (!sender) {
-		PrivateServer *s;
-
-		/* Might be a private connection, for which we fake a sender */
-		c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) {
-			sender = private_server_get_connection_owner (s, connection);
-			if (sender) {
-				if (out_uid)
-					*out_uid = 0;
-				if (out_sender)
-					*out_sender = g_strdup (sender);
-				if (out_pid) {
-					GCredentials *creds;
-
-					creds = g_dbus_connection_get_peer_credentials (connection);
-					if (creds) {
-						pid_t pid;
-
-						pid = g_credentials_get_unix_pid (creds, NULL);
-						if (pid == -1)
-							*out_pid = G_MAXULONG;
-						else
-							*out_pid = pid;
-					} else
-						*out_pid = G_MAXULONG;
-				}
-				return TRUE;
-			}
-		}
-		return FALSE;
-	}
-
-	/* Bus connections always have a sender */
-	g_assert (sender);
-	if (out_uid) {
-		if (!_bus_get_unix_user (self, sender, out_uid, NULL)) {
-			*out_uid = G_MAXULONG;
-			return FALSE;
-		}
-	}
-
-	if (out_pid) {
-		if (!_bus_get_unix_pid (self, sender, out_pid, NULL)) {
-			*out_pid = G_MAXULONG;
-			return FALSE;
-		}
-	}
-
-	if (out_sender)
-		*out_sender = g_strdup (sender);
-
-	return TRUE;
-}
-
-gboolean
-nm_dbus_manager_get_caller_info (NMDBusManager *self,
-                                 GDBusMethodInvocation *context,
-                                 char **out_sender,
-                                 gulong *out_uid,
-                                 gulong *out_pid)
-{
-	return _get_caller_info (self, context, NULL, NULL, out_sender, out_uid, out_pid);
-}
-
-gboolean
-nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self,
-                                              GDBusConnection *connection,
-                                              GDBusMessage *message,
-                                              char **out_sender,
-                                              gulong *out_uid,
-                                              gulong *out_pid)
-{
-	return _get_caller_info (self, NULL, connection, message, out_sender, out_uid, out_pid);
-}
-
-/**
- * nm_dbus_manager_ensure_uid:
- *
- * @self: bus manager instance
- * @context: D-Bus method invocation
- * @uid: a user-id
- * @error_domain: error domain to return on failure
- * @error_code: error code to return on failure
- *
- * Retrieves the uid of the D-Bus method caller and
- * checks that it matches @uid, unless @uid is G_MAXULONG.
- * In case of failure the function returns FALSE and finishes
- * handling the D-Bus method with an error.
- *
- * Returns: %TRUE if the check succeeded, %FALSE otherwise
- */
-gboolean
-nm_dbus_manager_ensure_uid (NMDBusManager          *self,
-                            GDBusMethodInvocation *context,
-                            gulong uid,
-                            GQuark error_domain,
-                            int error_code)
-{
-	gulong caller_uid;
-	GError *error = NULL;
-
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
-	g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (context), FALSE);
-
-	if (!nm_dbus_manager_get_caller_info (self, context, NULL, &caller_uid, NULL)) {
-		error = g_error_new_literal (error_domain,
-		                             error_code,
-		                             "Unable to determine request UID.");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-
-	if (uid != G_MAXULONG && caller_uid != uid) {
-		error = g_error_new_literal (error_domain,
-		                             error_code,
-		                             "Permission denied");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-gboolean
-nm_dbus_manager_get_unix_user (NMDBusManager *self,
-                               const char *sender,
-                               gulong *out_uid)
-{
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	PrivateServer *s;
-	GError *error = NULL;
-
-	g_return_val_if_fail (sender != NULL, FALSE);
-	g_return_val_if_fail (out_uid != NULL, FALSE);
-
-	/* Check if it's a private connection sender, which we fake */
-	c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) {
-		gs_unref_object GDBusConnection *connection = NULL;
-
-		connection = private_server_get_connection_by_owner (s, sender);
-		if (connection) {
-			*out_uid = 0;
-			return TRUE;
-		}
-	}
-
-	/* Otherwise, a bus connection */
-	if (!_bus_get_unix_user (self, sender, out_uid, &error)) {
-		_LOGW ("failed to get unix user for dbus sender '%s': %s",
-		       sender, error->message);
-		g_error_free (error);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-/*****************************************************************************/
-
-const char *
-nm_dbus_manager_connection_get_private_name (NMDBusManager *self,
-                                             GDBusConnection *connection)
-{
-	NMDBusManagerPrivate *priv;
-	PrivateServer *s;
-	const char *owner;
-
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
-	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
-
-	if (g_dbus_connection_get_unique_name (connection)) {
-		/* Shortcut. The connection is not a private connection. */
-		return NULL;
-	}
-
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	c_list_for_each_entry (s, &priv->private_servers_lst_head, private_servers_lst) {
-		if ((owner = private_server_get_connection_owner (s, connection)))
-			return owner;
-	}
-	g_return_val_if_reached (NULL);
-}
-
-/**
- * nm_dbus_manager_new_proxy:
- * @self: the #NMDBusManager
- * @connection: the GDBusConnection for which this connection should be created
- * @proxy_type: the type of #GDBusProxy to create
- * @name: any name on the message bus
- * @path: name of the object instance to call methods on
- * @iface: name of the interface to call methods on
- *
- * Creates a new proxy (of type @proxy_type) for a name on a given bus.  Since
- * the process which called the D-Bus method could be coming from a private
- * connection or the system bus connection, different proxies must be created
- * for each case.  This function abstracts that.
- *
- * Returns: a #GDBusProxy capable of calling D-Bus methods of the calling process
- */
-GDBusProxy *
-nm_dbus_manager_new_proxy (NMDBusManager *self,
-                           GDBusConnection *connection,
-                           GType proxy_type,
-                           const char *name,
-                           const char *path,
-                           const char *iface)
-{
-	const char *owner;
-	GDBusProxy *proxy;
-	GError *error = NULL;
-
-	g_return_val_if_fail (g_type_is_a (proxy_type, G_TYPE_DBUS_PROXY), NULL);
-	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-
-	/* Might be a private connection, for which @name is fake */
-	owner = nm_dbus_manager_connection_get_private_name (self, connection);
-	if (owner) {
-		g_return_val_if_fail (!g_strcmp0 (owner, name), NULL);
-		name = NULL;
-	}
-
-	proxy = g_initable_new (proxy_type, NULL, &error,
-	                        "g-connection", connection,
-	                        "g-flags", (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
-	                        "g-name", name,
-	                        "g-object-path", path,
-	                        "g-interface-name", iface,
-	                        NULL);
-	if (!proxy) {
-		_LOGW ("could not create proxy for %s on connection %s: %s",
-		       iface, name, error->message);
-		g_error_free (error);
-	}
-	return proxy;
-}
-
-/*****************************************************************************/
-
-GDBusConnection *
-nm_dbus_manager_get_connection (NMDBusManager *self)
-{
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
-
-	return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection;
-}
-
-/*****************************************************************************/
-
-static const NMDBusInterfaceInfoExtended *
-_reg_data_get_interface_info (RegistrationData *reg_data)
-{
-	nm_assert (reg_data);
-
-	return reg_data->klass->interface_infos[reg_data->info_idx];
-}
-
-/*****************************************************************************/
-
-static void
-dbus_vtable_method_call (GDBusConnection *connection,
-                         const char *sender,
-                         const char *object_path,
-                         const char *interface_name,
-                         const char *method_name,
-                         GVariant *parameters,
-                         GDBusMethodInvocation *invocation,
-                         gpointer user_data)
-{
-	RegistrationData *reg_data = user_data;
-	NMDBusObject *obj = reg_data->obj;
-	const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-	const NMDBusMethodInfoExtended *method_info = NULL;
-	gboolean on_same_interface;
-
-	on_same_interface = nm_streq (interface_info->parent.name, interface_name);
-
-	/* handle property setter first... */
-	if (   !on_same_interface
-	    && nm_streq (interface_name, DBUS_INTERFACE_PROPERTIES)
-	    && nm_streq (method_name, "Set")) {
-		NMDBusManager *self = nm_dbus_object_get_manager (obj);
-		NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-		const NMDBusPropertyInfoExtended *property_info = NULL;
-		const char *property_interface;
-		const char *property_name;
-		gs_unref_variant GVariant *value = NULL;
-
-		g_variant_get (parameters, "(&s&sv)", &property_interface, &property_name, &value);
-
-		nm_assert (nm_streq (property_interface, interface_info->parent.name));
-
-		property_info = (const NMDBusPropertyInfoExtended *) nm_dbus_utils_interface_info_lookup_property (&interface_info->parent,
-		                                                                                                   property_name,
-		                                                                                                   NULL);
-		if (   !property_info
-		    || !NM_FLAGS_HAS (property_info->parent.flags, G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
-			g_return_if_reached ();
-
-		if (!priv->set_property_handler) {
-			g_dbus_method_invocation_return_error (invocation,
-			                                       G_DBUS_ERROR,
-			                                       G_DBUS_ERROR_AUTH_FAILED,
-			                                       "Cannot authenticate setting property %s",
-			                                       property_name);
-			return;
-		}
-
-		priv->set_property_handler (obj,
-		                            interface_info,
-		                            property_info,
-		                            connection,
-		                            sender,
-		                            invocation,
-		                            value,
-		                            priv->set_property_handler_data);
-		return;
-	}
-
-	if (on_same_interface) {
-		method_info = (const NMDBusMethodInfoExtended *) nm_dbus_utils_interface_info_lookup_method (&interface_info->parent,
-		                                                                                             method_name);
-	}
-	if (!method_info) {
-		g_dbus_method_invocation_return_error (invocation,
-		                                       G_DBUS_ERROR,
-		                                       G_DBUS_ERROR_UNKNOWN_METHOD,
-		                                       "Unknown method %s",
-		                                       method_name);
-		return;
-	}
-
-	method_info->handle (reg_data->obj,
-	                     interface_info,
-	                     method_info,
-	                     connection,
-	                     sender,
-	                     invocation,
-	                     parameters);
-}
-
-static GVariant *
-_obj_get_property (RegistrationData *reg_data,
-                   guint property_idx,
-                   gboolean refetch)
-{
-	const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-	const NMDBusPropertyInfoExtended *property_info;
-	GVariant *value;
-
-	property_info = (const NMDBusPropertyInfoExtended *) (interface_info->parent.properties[property_idx]);
-
-	if (refetch)
-		nm_clear_g_variant (&reg_data->property_cache[property_idx].value);
-	else {
-		value = reg_data->property_cache[property_idx].value;
-		if (value)
-			goto out;
-	}
-
-	value = nm_dbus_utils_get_property (G_OBJECT (reg_data->obj),
-	                                    property_info->parent.signature,
-	                                    property_info->property_name);
-	reg_data->property_cache[property_idx].value = value;
-out:
-	return g_variant_ref (value);
-}
-
-static GVariant *
-dbus_vtable_get_property (GDBusConnection *connection,
-                          const char *sender,
-                          const char *object_path,
-                          const char *interface_name,
-                          const char *property_name,
-                          GError **error,
-                          gpointer user_data)
-{
-	RegistrationData *reg_data = user_data;
-	const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-	guint property_idx;
-
-	if (!nm_dbus_utils_interface_info_lookup_property (&interface_info->parent,
-	                                                   property_name,
-	                                                   &property_idx))
-		g_return_val_if_reached (NULL);
-
-	return _obj_get_property (reg_data, property_idx, FALSE);
-}
-
-static const GDBusInterfaceVTable dbus_vtable = {
-	.method_call = dbus_vtable_method_call,
-	.get_property = dbus_vtable_get_property,
-
-	/* set_property is handled via method_call as well. We need to authenticate
-	 * which requires an asynchronous handler. */
-	.set_property = NULL,
-};
-
-static void
-_obj_register (NMDBusManager *self,
-               NMDBusObject *obj)
-{
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	guint i, k;
-	guint n_klasses;
-	GType gtype;
-	NMDBusObjectClass *klasses[10];
-	const NMDBusInterfaceInfoExtended *const*prev_interface_infos = NULL;
-	GVariantBuilder builder;
-
-	nm_assert (c_list_is_empty (&obj->internal.registration_lst_head));
-	nm_assert (priv->connection);
-
-	n_klasses = 0;
-	gtype = G_OBJECT_TYPE (obj);
-	while (gtype != NM_TYPE_DBUS_OBJECT) {
-		nm_assert (n_klasses < G_N_ELEMENTS (klasses));
-		klasses[n_klasses++] = g_type_class_ref (gtype);
-		gtype = g_type_parent (gtype);
-	}
-
-	for (k = n_klasses; k > 0; ) {
-		NMDBusObjectClass *klass = NM_DBUS_OBJECT_CLASS (klasses[--k]);
-
-		if (!klass->interface_infos)
-			continue;
-
-		if (prev_interface_infos == klass->interface_infos) {
-			/* derived classes inherrit the interface-infos from the parent class.
-			 * For convenience, we allow the subclass to leave interface-infos untouched,
-			 * but it means we must ignore the parent's interface, because we already
-			 * handled it.
-			 *
-			 * Note that the loop goes from the parent classes to child classes */
-			continue;
-		}
-		prev_interface_infos = klass->interface_infos;
-
-		for (i = 0; klass->interface_infos[i]; i++) {
-			const NMDBusInterfaceInfoExtended *interface_info = klass->interface_infos[i];
-			RegistrationData *reg_data;
-			gs_free_error GError *error = NULL;
-			guint registration_id;
-			guint prop_len = NM_PTRARRAY_LEN (interface_info->parent.properties);
-
-			reg_data = g_malloc0 (sizeof (RegistrationData) + (sizeof (PropertyCacheData) * prop_len));
-
-			registration_id = g_dbus_connection_register_object (priv->connection,
-			                                                     obj->internal.path,
-			                                                     NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info->parent),
-			                                                     &dbus_vtable,
-			                                                     reg_data,
-			                                                     NULL,
-			                                                     &error);
-			if (!registration_id) {
-				_LOGE ("failure to register object %s: %s", obj->internal.path, error->message);
-				g_free (reg_data);
-				continue;
-			}
-
-			reg_data->obj = obj;
-			reg_data->klass = g_type_class_ref (G_TYPE_FROM_CLASS (klass));
-			reg_data->info_idx = i;
-			reg_data->registration_id = registration_id;
-			c_list_link_tail (&obj->internal.registration_lst_head, &reg_data->registration_lst);
-		}
-	}
-
-	for (k = 0; k < n_klasses; k++)
-		g_type_class_unref (klasses[k]);
-
-	nm_assert (!c_list_is_empty (&obj->internal.registration_lst_head));
-
-	/* Currently the interfaces of an object do not changed and strictly depend on the object glib type.
-	 * We don't need more flixibility, and it simplifies the code. Hence, now emit interface-added
-	 * signal for the new object.
-	 *
-	 * Warning: note that if @obj's notify signal is currently blocked via g_object_freeze_notify(),
-	 * we might emit properties with an inconsistent (internal) state. There is no easy solution,
-	 * because we have to emit the signal now, and we don't know what the correct desired state
-	 * of the properties is.
-	 * Another problem is, upon unfreezing the signals, we immediately send PropertiesChanged
-	 * notifications out. Which is a bit odd, as we just export the object.
-	 *
-	 * In general, it's ok to export an object with frozen signals. But you better make sure
-	 * that all properties are in a self-consistent state when exporting the object. */
-	g_dbus_connection_emit_signal (priv->connection,
-	                               NULL,
-	                               OBJECT_MANAGER_SERVER_BASE_PATH,
-	                               interface_info_objmgr.name,
-	                               signal_info_objmgr_interfaces_added.name,
-	                               g_variant_new ("(oa{sa{sv}})",
-	                                              obj->internal.path,
-	                                              _obj_collect_properties_all (obj, &builder)),
-	                               NULL);
-}
-
-static void
-_obj_unregister (NMDBusManager *self,
-                 NMDBusObject *obj)
-{
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	RegistrationData *reg_data;
-	GVariantBuilder builder;
-
-	nm_assert (NM_IS_DBUS_OBJECT (obj));
-
-	if (!priv->connection) {
-		/* nothing to do for the moment. */
-		nm_assert (c_list_is_empty (&obj->internal.registration_lst_head));
-		return;
-	}
-
-	nm_assert (!c_list_is_empty (&obj->internal.registration_lst_head));
-	nm_assert (priv->objmgr_registration_id);
-
-	g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
-
-	while ((reg_data = c_list_last_entry (&obj->internal.registration_lst_head, RegistrationData, registration_lst))) {
-		const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-		guint i;
-
-		g_variant_builder_add (&builder,
-		                       "s",
-		                       interface_info->parent.name);
-		c_list_unlink_stale (&reg_data->registration_lst);
-		if (!g_dbus_connection_unregister_object (priv->connection, reg_data->registration_id))
-			nm_assert_not_reached ();
-
-		if (interface_info->parent.properties) {
-			for (i = 0; interface_info->parent.properties[i]; i++)
-				nm_clear_g_variant (&reg_data->property_cache[i].value);
-		}
-
-		g_type_class_unref (reg_data->klass);
-		g_free (reg_data);
-	}
-
-	g_dbus_connection_emit_signal (priv->connection,
-	                               NULL,
-	                               OBJECT_MANAGER_SERVER_BASE_PATH,
-	                               interface_info_objmgr.name,
-	                               signal_info_objmgr_interfaces_removed.name,
-	                               g_variant_new ("(oas)",
-	                                              obj->internal.path,
-	                                              &builder),
-	                               NULL);
-}
-
-NMDBusObject *
-nm_dbus_manager_lookup_object (NMDBusManager *self, const char *path)
-{
-	NMDBusManagerPrivate *priv;
-	gpointer ptr;
-	NMDBusObject *obj;
-
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL);
-	g_return_val_if_fail (path, NULL);
-
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	ptr = g_hash_table_lookup (priv->objects_by_path, &path);
-	if (!ptr)
-		return NULL;
-
-	obj = (NMDBusObject *) (((char *) ptr) - G_STRUCT_OFFSET (NMDBusObject, internal));
-	nm_assert (NM_IS_DBUS_OBJECT (obj));
-	return obj;
-}
-
-void
-_nm_dbus_manager_obj_export (NMDBusObject *obj)
-{
-	NMDBusManager *self;
-	NMDBusManagerPrivate *priv;
-
-	g_return_if_fail (NM_IS_DBUS_OBJECT (obj));
-	g_return_if_fail (obj->internal.path);
-	g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager));
-	g_return_if_fail (c_list_is_empty (&obj->internal.objects_lst));
-	nm_assert (c_list_is_empty (&obj->internal.registration_lst_head));
-
-	self = obj->internal.bus_manager;
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	if (!g_hash_table_add (priv->objects_by_path, &obj->internal))
-		nm_assert_not_reached ();
-	c_list_link_tail (&priv->objects_lst_head, &obj->internal.objects_lst);
-
-	if (priv->connection)
-		_obj_register (self, obj);
-}
-
-void
-_nm_dbus_manager_obj_unexport (NMDBusObject *obj)
-{
-	NMDBusManager *self;
-	NMDBusManagerPrivate *priv;
-
-	g_return_if_fail (NM_IS_DBUS_OBJECT (obj));
-	g_return_if_fail (obj->internal.path);
-	g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager));
-	g_return_if_fail (!c_list_is_empty (&obj->internal.objects_lst));
-
-	self = obj->internal.bus_manager;
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	nm_assert (&obj->internal == g_hash_table_lookup (priv->objects_by_path, &obj->internal));
-	nm_assert (c_list_contains (&priv->objects_lst_head, &obj->internal.objects_lst));
-
-	_obj_unregister (self, obj);
-
-	if (!g_hash_table_remove (priv->objects_by_path, &obj->internal))
-		nm_assert_not_reached ();
-	c_list_unlink (&obj->internal.objects_lst);
-}
-
-void
-_nm_dbus_manager_obj_notify (NMDBusObject *obj,
-                             guint n_pspecs,
-                             const GParamSpec *const*pspecs)
-{
-	NMDBusManager *self;
-	NMDBusManagerPrivate *priv;
-	RegistrationData *reg_data;
-	guint i, p;
-	gboolean any_legacy_signals = FALSE;
-	gboolean any_legacy_properties = FALSE;
-	GVariantBuilder legacy_builder;
-	GVariant *device_statistics_args = NULL;
-
-	nm_assert (NM_IS_DBUS_OBJECT (obj));
-	nm_assert (obj->internal.path);
-	nm_assert (NM_IS_DBUS_MANAGER (obj->internal.bus_manager));
-	nm_assert (!c_list_is_empty (&obj->internal.objects_lst));
-
-	c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-		if (_reg_data_get_interface_info (reg_data)->legacy_property_changed) {
-			any_legacy_signals = TRUE;
-			break;
-		}
-	}
-
-	self = obj->internal.bus_manager;
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	/* do a naive search for the matching NMDBusPropertyInfoExtended infos. Since the number of
-	 * (interaces x properties) is static and possibly small, this naive search is effectively
-	 * O(1). We might wanna introduce some index to lookup the properties in question faster.
-	 *
-	 * The nice part of this implementation is however, that the order in which properties
-	 * are added to the GVariant is strictly defined to be the order in which the D-Bus property-info
-	 * is declared. Getting a defined ordering with some smart lookup would be hard. */
-	c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-		const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-		gboolean has_properties = FALSE;
-		GVariantBuilder builder;
-		GVariantBuilder invalidated_builder;
-		GVariant *args;
-
-		if (!interface_info->parent.properties)
-			continue;
-
-		for (i = 0; interface_info->parent.properties[i]; i++) {
-			const NMDBusPropertyInfoExtended *property_info = (const NMDBusPropertyInfoExtended *) interface_info->parent.properties[i];
-
-			for (p = 0; p < n_pspecs; p++) {
-				const GParamSpec *pspec = pspecs[p];
-				gs_unref_variant GVariant *value = NULL;
-
-				if (!nm_streq (property_info->property_name, pspec->name))
-					continue;
-
-				value = _obj_get_property (reg_data, i, TRUE);
-
-				if (   property_info->include_in_legacy_property_changed
-				    && any_legacy_signals) {
-					/* also track the value in the legacy_builder to emit legacy signals below. */
-					if (!any_legacy_properties) {
-						any_legacy_properties = TRUE;
-						g_variant_builder_init (&legacy_builder, G_VARIANT_TYPE ("a{sv}"));
-					}
-					g_variant_builder_add (&legacy_builder, "{sv}", property_info->parent.name, value);
-				}
-
-				if (!has_properties) {
-					has_properties = TRUE;
-					g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
-				}
-				g_variant_builder_add (&builder, "{sv}", property_info->parent.name, value);
-			}
-		}
-
-		if (!has_properties)
-			continue;
-
-		args = g_variant_builder_end (&builder);
-
-		if (G_UNLIKELY (interface_info == &nm_interface_info_device_statistics)) {
-			/* we treat the Device.Statistics signal special, because we need to
-			 * emit a signal also for it (below). */
-			nm_assert (!device_statistics_args);
-			device_statistics_args = g_variant_ref_sink (args);
-		}
-
-		g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
-		g_dbus_connection_emit_signal (priv->connection,
-		                               NULL,
-		                               obj->internal.path,
-		                               "org.freedesktop.DBus.Properties",
-		                               "PropertiesChanged",
-		                               g_variant_new ("(s@a{sv}as)",
-		                                              interface_info->parent.name,
-		                                              args,
-		                                              &invalidated_builder),
-		                               NULL);
-	}
-
-	if (G_UNLIKELY (device_statistics_args)) {
-		/* this is a special interface: it has a legacy PropertiesChanged signal,
-		 * however, contrary to other interfaces with ~regular~ legacy signals,
-		 * we only notify about properties that actually belong to this interface. */
-		g_dbus_connection_emit_signal (priv->connection,
-		                               NULL,
-		                               obj->internal.path,
-		                               nm_interface_info_device_statistics.parent.name,
-		                               "PropertiesChanged",
-		                               g_variant_new ("(@a{sv})",
-		                                              device_statistics_args),
-		                               NULL);
-		g_variant_unref (device_statistics_args);
-	}
-
-	if (any_legacy_properties) {
-		gs_unref_variant GVariant *args = NULL;
-
-		/* The legacy PropertyChanged signal on the NetworkManager D-Bus interface is
-		 * deprecated for the standard signal on org.freedesktop.DBus.Properties. However,
-		 * for backward compatibility, we still need to emit it.
-		 *
-		 * Due to a bug in dbus-glib in NetworkManager <= 1.0, the signal would
-		 * not only notify about properties that were actually on the corresponding
-		 * D-Bus interface. Instead, it would notify about all relevant properties
-		 * on all interfaces that had such a signal.
-		 *
-		 * For example, "HwAddress" gets emitted both on "fdo.NM.Device.Ethernet"
-		 * and "fdo.NM.Device.Veth" for veth interfaces, although only the former
-		 * actually has such a property.
-		 * Also note that "fdo.NM.Device" interface has no legacy signal. All notifications
-		 * about its properties are instead emitted on the interfaces of the subtypes.
-		 *
-		 * See bgo#770629 and commit bef26a2e69f51259095fa080221db73de09fd38d.
-		 */
-		args = g_variant_ref_sink (g_variant_new ("(a{sv})",
-		                                          &legacy_builder));
-		c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-			const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-
-			if (interface_info->legacy_property_changed) {
-				g_dbus_connection_emit_signal (priv->connection,
-				                               NULL,
-				                               obj->internal.path,
-				                               interface_info->parent.name,
-				                               "PropertiesChanged",
-				                               args,
-				                               NULL);
-			}
-		}
-	}
-}
-
-void
-_nm_dbus_manager_obj_emit_signal (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const GDBusSignalInfo *signal_info,
-                                  GVariant *args)
-{
-	NMDBusManager *self;
-	NMDBusManagerPrivate *priv;
-
-	g_return_if_fail (NM_IS_DBUS_OBJECT (obj));
-	g_return_if_fail (obj->internal.path);
-	g_return_if_fail (NM_IS_DBUS_MANAGER (obj->internal.bus_manager));
-	g_return_if_fail (!c_list_is_empty (&obj->internal.objects_lst));
-
-	self = obj->internal.bus_manager;
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	if (!priv->connection) {
-		nm_g_variant_unref_floating (args);
-		return;
-	}
-
-	g_dbus_connection_emit_signal (priv->connection,
-	                               NULL,
-	                               obj->internal.path,
-	                               interface_info->parent.name,
-	                               signal_info->name,
-	                               args,
-	                               NULL);
-}
-
-/*****************************************************************************/
-
-static GVariantBuilder *
-_obj_collect_properties_per_interface (NMDBusObject *obj,
-                                       RegistrationData *reg_data,
-                                       GVariantBuilder *builder)
-{
-	const NMDBusInterfaceInfoExtended *interface_info = _reg_data_get_interface_info (reg_data);
-	guint i;
-
-	g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sv}"));
-	if (interface_info->parent.properties) {
-		for (i = 0; interface_info->parent.properties[i]; i++) {
-			const NMDBusPropertyInfoExtended *property_info = (const NMDBusPropertyInfoExtended *) interface_info->parent.properties[i];
-			gs_unref_variant GVariant *variant = NULL;
-
-			variant = _obj_get_property (reg_data, i, FALSE);
-			g_variant_builder_add (builder,
-			                       "{sv}",
-			                       property_info->parent.name,
-			                       variant);
-		}
-	}
-	return builder;
-}
-
-static GVariantBuilder *
-_obj_collect_properties_all (NMDBusObject *obj,
-                             GVariantBuilder *builder)
-{
-	RegistrationData *reg_data;
-
-	g_variant_builder_init (builder, G_VARIANT_TYPE ("a{sa{sv}}"));
-
-	c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) {
-		GVariantBuilder properties_builder;
-
-		g_variant_builder_add (builder,
-		                       "{sa{sv}}",
-		                       _reg_data_get_interface_info (reg_data)->parent.name,
-		                       _obj_collect_properties_per_interface (obj,
-		                                                              reg_data,
-		                                                              &properties_builder));
-	}
-
-	return builder;
-}
-
-static void
-dbus_vtable_objmgr_method_call (GDBusConnection *connection,
-                                const char *sender,
-                                const char *object_path,
-                                const char *interface_name,
-                                const char *method_name,
-                                GVariant *parameters,
-                                GDBusMethodInvocation *invocation,
-                                gpointer user_data)
-{
-	NMDBusManager *self = user_data;
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	GVariantBuilder array_builder;
-	NMDBusObject *obj;
-
-	nm_assert (nm_streq0 (object_path, OBJECT_MANAGER_SERVER_BASE_PATH));
-
-	if (   !nm_streq (method_name, "GetManagedObjects")
-	    || !nm_streq (interface_name, interface_info_objmgr.name)) {
-		g_dbus_method_invocation_return_error (invocation,
-		                                       G_DBUS_ERROR,
-		                                       G_DBUS_ERROR_UNKNOWN_METHOD,
-		                                       "Unknown method %s - only GetManagedObjects() is supported",
-		                                       method_name);
-		return;
-	}
-
-	g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}"));
-	c_list_for_each_entry (obj, &priv->objects_lst_head, internal.objects_lst) {
-		GVariantBuilder interfaces_builder;
-
-		/* note that we are called on an idle handler. Hence, all properties are
-		 * supposed to be in a consistent state. That is true, if you always
-		 * g_object_thaw_notify() before returning to the mainloop. Keeping
-		 * signals frozen between while returning from the current call stack
-		 * is anyway a very fragile thing, easy to get wrong. Don't do that. */
-		g_variant_builder_add (&array_builder,
-		                       "{oa{sa{sv}}}",
-		                       obj->internal.path,
-		                       _obj_collect_properties_all (obj,
-		                                                    &interfaces_builder));
-	}
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(a{oa{sa{sv}}})",
-	                                                      &array_builder));
-}
-
-static const GDBusInterfaceVTable dbus_vtable_objmgr = {
-	.method_call = dbus_vtable_objmgr_method_call
-};
-
-static const GDBusSignalInfo signal_info_objmgr_interfaces_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"InterfacesAdded",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("object_path",               "o"),
-		NM_DEFINE_GDBUS_ARG_INFO ("interfaces_and_properties", "a{sa{sv}}"),
-	),
-);
-
-static const GDBusSignalInfo signal_info_objmgr_interfaces_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"InterfacesRemoved",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("object_path", "o"),
-		NM_DEFINE_GDBUS_ARG_INFO ("interfaces",  "as"),
-	),
-);
-
-static const GDBusInterfaceInfo interface_info_objmgr = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-	"org.freedesktop.DBus.ObjectManager",
-	.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-		NM_DEFINE_GDBUS_METHOD_INFO (
-			"GetManagedObjects",
-			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-				NM_DEFINE_GDBUS_ARG_INFO ("object_paths_interfaces_and_properties", "a{oa{sa{sv}}}"),
-			),
-		),
-	),
-	.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-		&signal_info_objmgr_interfaces_added,
-		&signal_info_objmgr_interfaces_removed,
-	),
-);
-
-/*****************************************************************************/
-
-gboolean
-nm_dbus_manager_start (NMDBusManager *self,
-                       NMDBusManagerSetPropertyHandler set_property_handler,
-                       gpointer set_property_handler_data)
-{
-	NMDBusManagerPrivate *priv;
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *ret = NULL;
-	gs_unref_object GDBusConnection *connection = NULL;
-	gs_unref_object GDBusProxy *proxy = NULL;
-	guint32 result;
-	guint registration_id;
-	NMDBusObject *obj;
-
-	g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), FALSE);
-
-	priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	priv->set_property_handler = set_property_handler;
-	priv->set_property_handler_data = set_property_handler_data;
-
-	g_return_val_if_fail (!priv->connection, FALSE);
-
-	/* we will create the D-Bus connection and registering the name synchronously.
-	 * The reason why that is necessary is because:
-	 *  (1) if we are unable to create a D-Bus connection, it means D-Bus is not
-	 *    available and we run in D-Bus less mode. We do not support creating
-	 *    a D-Bus connection later on. This disconnected mode is useful for initrd
-	 *    (well, currently not yet, but will be).
-	 *  (2) if we are able to create the connection and register the name,
-	 *    all is good and we run with D-Bus. Note that D-Bus disconnects
-	 *    from D-Bus are ignored. Essentially, we do not support restarting
-	 *    D-Bus.
-	 *  (3) if we are able to create the connection but registration fails,
-	 *    it means that something is borked. Quite possibly another NetworkManager
-	 *    instance is running. We need to exit right away.
-	 * To appease (1) and (3), we cannot initalize synchronously, because we need
-	 * to know right away whether another NetworkManager instance is running (3).
-	 **/
-
-	connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
-	                             NULL,
-	                             &error);
-	if (!connection) {
-		_LOGI ("cannot connect to D-Bus and proceed without (%s)", error->message);
-		return TRUE;
-	}
-
-	g_dbus_connection_set_exit_on_close (connection, FALSE);
-
-	proxy = g_dbus_proxy_new_sync (connection,
-	                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
-	                               | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                               NULL,
-	                               DBUS_SERVICE_DBUS,
-	                               DBUS_PATH_DBUS,
-	                               DBUS_INTERFACE_DBUS,
-	                               NULL,
-	                               &error);
-	if (!proxy) {
-		_LOGE ("fatal failure to initialize D-Bus: %s", error->message);
-		return FALSE;
-	}
-
-	ret = _nm_dbus_proxy_call_sync (proxy,
-	                                "RequestName",
-	                                g_variant_new ("(su)",
-	                                               NM_DBUS_SERVICE,
-	                                               DBUS_NAME_FLAG_DO_NOT_QUEUE),
-	                                G_VARIANT_TYPE ("(u)"),
-	                                G_DBUS_CALL_FLAGS_NONE, -1,
-	                                NULL,
-	                                &error);
-	if (!ret) {
-		_LOGE ("fatal failure to aquire D-Bus service \"%s"": %s",
-		       NM_DBUS_SERVICE, error->message);
-		return FALSE;
-	}
-
-	g_variant_get (ret, "(u)", &result);
-	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-		_LOGE ("fatal failure to acquire D-Bus service \"%s\" (%u). Service already taken",
-		       NM_DBUS_SERVICE, (guint) result);
-		return FALSE;
-	}
-
-	registration_id = g_dbus_connection_register_object (connection,
-	                                                     OBJECT_MANAGER_SERVER_BASE_PATH,
-	                                                     NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr),
-	                                                     &dbus_vtable_objmgr,
-	                                                     self,
-	                                                     NULL,
-	                                                     &error);
-	if (!registration_id) {
-		_LOGE ("failure to register object manager: %s", error->message);
-		return FALSE;
-	}
-
-	priv->objmgr_registration_id = registration_id;
-	priv->connection = g_steal_pointer (&connection);
-	priv->proxy = g_steal_pointer (&proxy);
-
-	_LOGI ("aquired D-Bus service \"%s\"", NM_DBUS_SERVICE);
-
-	c_list_for_each_entry (obj, &priv->objects_lst_head, internal.objects_lst)
-		_obj_register (self, obj);
-
-	return TRUE;
-}
-
-/*****************************************************************************/
-
-static void
-nm_dbus_manager_init (NMDBusManager *self)
-{
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-
-	c_list_init (&priv->private_servers_lst_head);
-	c_list_init (&priv->objects_lst_head);
-	priv->objects_by_path = g_hash_table_new ((GHashFunc) _objects_by_path_hash, (GEqualFunc) _objects_by_path_equal);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMDBusManager *self = NM_DBUS_MANAGER (object);
-	NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self);
-	PrivateServer *s, *s_safe;
-
-	/* All exported NMDBusObject instances keep the manager alive, so we don't
-	 * expect any remaining objects. */
-	nm_assert (!priv->objects_by_path || g_hash_table_size (priv->objects_by_path) == 0);
-	nm_assert (c_list_is_empty (&priv->objects_lst_head));
-
-	g_clear_pointer (&priv->objects_by_path, g_hash_table_destroy);
-
-	c_list_for_each_entry_safe (s, s_safe, &priv->private_servers_lst_head, private_servers_lst)
-		private_server_free (s);
-
-	if (priv->objmgr_registration_id) {
-		g_dbus_connection_unregister_object (priv->connection,
-		                                     nm_steal_int (&priv->objmgr_registration_id));
-	}
-
-	g_clear_object (&priv->proxy);
-	g_clear_object (&priv->connection);
-
-	G_OBJECT_CLASS (nm_dbus_manager_parent_class)->dispose (object);
-}
-
-static void
-nm_dbus_manager_class_init (NMDBusManagerClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->dispose = dispose;
-
-	signals[PRIVATE_CONNECTION_NEW] =
-	    g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 2, G_TYPE_DBUS_CONNECTION, G_TYPE_DBUS_OBJECT_MANAGER_SERVER);
-
-	signals[PRIVATE_CONNECTION_DISCONNECTED] =
-	    g_signal_new (NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-	                  0, NULL, NULL, NULL,
-	                  G_TYPE_NONE, 1, G_TYPE_POINTER);
-}
diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h
deleted file mode 100644
index 617f8a67..00000000
--- a/src/nm-dbus-manager.h
+++ /dev/null
@@ -1,107 +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) 2006 - 2008 Red Hat, Inc.
- * Copyright (C) 2006 - 2008 Novell, Inc.
- */
-
-#ifndef __NM_DBUS_MANAGER_H__
-#define __NM_DBUS_MANAGER_H__
-
-#include "nm-dbus-utils.h"
-
-#define NM_TYPE_DBUS_MANAGER (nm_dbus_manager_get_type ())
-#define NM_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DBUS_MANAGER, NMDBusManager))
-#define NM_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass))
-#define NM_IS_DBUS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DBUS_MANAGER))
-#define NM_IS_DBUS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DBUS_MANAGER))
-#define NM_DBUS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DBUS_MANAGER, NMDBusManagerClass))
-
-#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW           "private-connection-new"
-#define NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED  "private-connection-disconnected"
-
-typedef struct _NMDBusManagerClass NMDBusManagerClass;
-
-GType nm_dbus_manager_get_type (void);
-
-NMDBusManager *nm_dbus_manager_get (void);
-
-typedef void (*NMDBusManagerSetPropertyHandler) (NMDBusObject *obj,
-                                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                                 const NMDBusPropertyInfoExtended *property_info,
-                                                 GDBusConnection *connection,
-                                                 const char *sender,
-                                                 GDBusMethodInvocation *invocation,
-                                                 GVariant *value,
-                                                 gpointer user_data);
-
-gboolean nm_dbus_manager_start (NMDBusManager *self,
-                                NMDBusManagerSetPropertyHandler handler,
-                                gpointer handler_data);
-
-GDBusConnection *nm_dbus_manager_get_connection (NMDBusManager *self);
-
-NMDBusObject *nm_dbus_manager_lookup_object (NMDBusManager *self, const char *path);
-
-void _nm_dbus_manager_obj_export (NMDBusObject *obj);
-void _nm_dbus_manager_obj_unexport (NMDBusObject *obj);
-void _nm_dbus_manager_obj_notify (NMDBusObject *obj,
-                                  guint n_pspecs,
-                                  const GParamSpec *const*pspecs);
-void _nm_dbus_manager_obj_emit_signal (NMDBusObject *obj,
-                                       const NMDBusInterfaceInfoExtended *interface_info,
-                                       const GDBusSignalInfo *signal_info,
-                                       GVariant *args);
-
-gboolean nm_dbus_manager_get_caller_info (NMDBusManager *self,
-                                          GDBusMethodInvocation *context,
-                                          char **out_sender,
-                                          gulong *out_uid,
-                                          gulong *out_pid);
-
-gboolean nm_dbus_manager_ensure_uid (NMDBusManager          *self,
-                                     GDBusMethodInvocation *context,
-                                     gulong uid,
-                                     GQuark error_domain,
-                                     int error_code);
-
-const char *nm_dbus_manager_connection_get_private_name (NMDBusManager *self,
-                                                         GDBusConnection *connection);
-
-gboolean nm_dbus_manager_get_unix_user (NMDBusManager *self,
-                                        const char *sender,
-                                        gulong *out_uid);
-
-gboolean nm_dbus_manager_get_caller_info_from_message (NMDBusManager *self,
-                                                       GDBusConnection *connection,
-                                                       GDBusMessage *message,
-                                                       char **out_sender,
-                                                       gulong *out_uid,
-                                                       gulong *out_pid);
-
-void nm_dbus_manager_private_server_register (NMDBusManager *self,
-                                              const char *path,
-                                              const char *tag);
-
-GDBusProxy *nm_dbus_manager_new_proxy (NMDBusManager *self,
-                                       GDBusConnection *connection,
-                                       GType proxy_type,
-                                       const char *name,
-                                       const char *path,
-                                       const char *iface);
-
-#endif /* __NM_DBUS_MANAGER_H__ */
diff --git a/src/nm-dbus-object.c b/src/nm-dbus-object.c
deleted file mode 100644
index 514fda49..00000000
--- a/src/nm-dbus-object.c
+++ /dev/null
@@ -1,320 +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 2018 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-dbus-object.h"
-
-#include "nm-dbus-manager.h"
-
-/*****************************************************************************/
-
-static gboolean quitting = FALSE;
-
-void
-nm_dbus_object_set_quitting (void)
-{
-	nm_assert (!quitting);
-	quitting = TRUE;
-}
-
-/*****************************************************************************/
-
-enum {
-	EXPORTED_CHANGED,
-
-	LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-G_DEFINE_ABSTRACT_TYPE (NMDBusObject, nm_dbus_object, G_TYPE_OBJECT);
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN      LOGD_CORE
-#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "dbus-object", __VA_ARGS__)
-
-#define _NMLOG2_DOMAIN      LOGD_DBUS_PROPS
-#define _NMLOG2(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG2_DOMAIN, "properties-changed", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void
-_emit_exported_changed (NMDBusObject *self)
-{
-	g_signal_emit (self, signals[EXPORTED_CHANGED], 0);
-}
-
-static char *
-_create_export_path (NMDBusObjectClass *klass)
-{
-	nm_assert (NM_IS_DBUS_OBJECT_CLASS (klass));
-	nm_assert (klass->export_path.path);
-
-#if NM_MORE_ASSERTS
-	{
-		const char *p;
-
-		p = strchr (klass->export_path.path, '%');
-		if (klass->export_path.int_counter) {
-			nm_assert (p);
-			nm_assert (p[1] == 'l');
-			nm_assert (p[2] == 'l');
-			nm_assert (p[3] == 'u');
-			nm_assert (p[4] == '\0');
-		} else
-			nm_assert (!p);
-	}
-#endif
-
-	if (klass->export_path.int_counter) {
-		NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral")
-		return g_strdup_printf (klass->export_path.path,
-		                        ++(*klass->export_path.int_counter));
-		NM_PRAGMA_WARNING_REENABLE
-	}
-	return g_strdup (klass->export_path.path);
-}
-
-/**
- * nm_dbus_object_export:
- * @self: an #NMDBusObject
- *
- * Exports @self on all active and future D-Bus connections.
- *
- * The path to export @self on is taken from its #NMObjectClass's %export_path
- * member. If the %export_path contains "%u", then it will be replaced with a
- * monotonically increasing integer ID (with each distinct %export_path having
- * its own counter). Otherwise, %export_path will be used literally (implying
- * that @self must be a singleton).
- *
- * Returns: the path @self was exported under
- */
-const char *
-nm_dbus_object_export (NMDBusObject *self)
-{
-	static guint64 id_counter = 0;
-
-	g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL);
-
-	g_return_val_if_fail (!self->internal.path, self->internal.path);
-
-	nm_assert (!self->internal.is_unexporting);
-
-	self->internal.path = _create_export_path (NM_DBUS_OBJECT_GET_CLASS (self));
-
-	self->internal.export_version_id = ++id_counter;
-
-	_LOGT ("export: \"%s\"", self->internal.path);
-
-	_nm_dbus_manager_obj_export (self);
-
-	_emit_exported_changed (self);
-	return self->internal.path;
-}
-
-/**
- * nm_dbus_object_unexport:
- * @self: an #NMDBusObject
- *
- * Unexports @self on all active D-Bus connections (and prevents it from being
- * auto-exported on future connections).
- */
-void
-nm_dbus_object_unexport (NMDBusObject *self)
-{
-	g_return_if_fail (NM_IS_DBUS_OBJECT (self));
-
-	g_return_if_fail (self->internal.path);
-
-	_LOGT ("unexport: \"%s\"", self->internal.path);
-
-	/* note that we emit the signal *before* actually unexporting the object.
-	 * The reason is, that listeners want to use this signal to know that
-	 * the object goes away, and clear their D-Bus path to this object.
-	 *
-	 * But this must happen before we actually unregister the object, so
-	 * that we first emit a D-Bus signal that other objects no longer
-	 * reference this object, before finally unregistering the object itself.
-	 *
-	 * The inconvenient part is, that at this point nm_dbus_object_get_path()
-	 * still returns the path. So, the callee needs to handle that. Possibly
-	 * by using "nm_dbus_object_get_path_still_exported()". */
-	self->internal.is_unexporting = TRUE;
-
-	_emit_exported_changed (self);
-
-	_nm_dbus_manager_obj_unexport (self);
-
-	g_clear_pointer (&self->internal.path, g_free);
-	self->internal.export_version_id = 0;
-
-	self->internal.is_unexporting = FALSE;
-}
-
-/*****************************************************************************/
-
-void
-_nm_dbus_object_clear_and_unexport (NMDBusObject **location)
-{
-	NMDBusObject *self;
-
-	g_return_if_fail (location);
-	if (!*location)
-		return;
-
-	self = g_steal_pointer (location);
-
-	g_return_if_fail (NM_IS_DBUS_OBJECT (self));
-
-	if (self->internal.path)
-		nm_dbus_object_unexport (self);
-
-	g_object_unref (self);
-}
-
-/*****************************************************************************/
-
-void
-nm_dbus_object_emit_signal_variant (NMDBusObject *self,
-                                    const NMDBusInterfaceInfoExtended *interface_info,
-                                    const GDBusSignalInfo *signal_info,
-                                    GVariant *args)
-{
-	if (!self->internal.path) {
-		nm_g_variant_unref_floating (args);
-		return;
-	}
-	_nm_dbus_manager_obj_emit_signal (self, interface_info, signal_info, args);
-}
-
-void
-nm_dbus_object_emit_signal (NMDBusObject *self,
-                            const NMDBusInterfaceInfoExtended *interface_info,
-                            const GDBusSignalInfo *signal_info,
-                            const char *format,
-                            ...)
-{
-	va_list ap;
-
-	nm_assert (NM_IS_DBUS_OBJECT (self));
-	nm_assert (format);
-
-	if (!self->internal.path)
-		return;
-
-	va_start (ap, format);
-	_nm_dbus_manager_obj_emit_signal (self,
-	                                 interface_info,
-	                                 signal_info,
-	                                 g_variant_new_va (format, NULL, &ap));
-	va_end (ap);
-}
-
-/*****************************************************************************/
-
-static void
-dispatch_properties_changed (GObject *object,
-                             guint n_pspecs,
-                             GParamSpec **pspecs)
-{
-	NMDBusObject *self = NM_DBUS_OBJECT (object);
-
-	if (self->internal.path)
-		_nm_dbus_manager_obj_notify (self, n_pspecs, (const GParamSpec *const*) pspecs);
-
-	G_OBJECT_CLASS (nm_dbus_object_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs);
-}
-
-/*****************************************************************************/
-
-static void
-nm_dbus_object_init (NMDBusObject *self)
-{
-	c_list_init (&self->internal.objects_lst);
-	c_list_init (&self->internal.registration_lst_head);
-	self->internal.bus_manager = nm_g_object_ref (nm_dbus_manager_get ());
-}
-
-static void
-constructed (GObject *object)
-{
-	NMDBusObjectClass *klass;
-
-	G_OBJECT_CLASS (nm_dbus_object_parent_class)->constructed (object);
-
-	klass = NM_DBUS_OBJECT_GET_CLASS (object);
-
-	if (klass->export_on_construction)
-		nm_dbus_object_export ((NMDBusObject *) object);
-
-	/* NMDBusObject types should be very careful when overwriting notify().
-	 * It is possible to do, but this is a reminder that it's probably not
-	 * a good idea.
-	 *
-	 * It's not a good idea, because NMDBusObject uses dispatch_properties_changed()
-	 * to emit signals about a bunch of property changes. So, we want to make
-	 * use of g_object_freeze_notify() / g_object_thaw_notify() to combine multiple
-	 * property changes in one signal on D-Bus. Note that notify() is not invoked
-	 * while the signal is frozen, that means, whatever you do inside notify()
-	 * will not make it into the same batch of PropertiesChanged signal. That is
-	 * confusing, and probably not what you want.
-	 *
-	 * Simple solution: don't overwrite notify(). */
-	nm_assert (!G_OBJECT_CLASS (klass)->notify);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMDBusObject *self = NM_DBUS_OBJECT (object);
-
-	/* Objects should have already been unexported by their owner, unless
-	 * we are quitting, where many objects stick around until exit.
-	 */
-	if (self->internal.path) {
-		if (!quitting)
-			g_warn_if_reached ();
-		nm_dbus_object_unexport (self);
-	}
-
-	G_OBJECT_CLASS (nm_dbus_object_parent_class)->dispose (object);
-
-	g_clear_object (&self->internal.bus_manager);
-}
-
-static void
-nm_dbus_object_class_init (NMDBusObjectClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-	object_class->constructed = constructed;
-	object_class->dispose = dispose;
-	object_class->dispatch_properties_changed = dispatch_properties_changed;
-
-	signals[EXPORTED_CHANGED] =
-	    g_signal_new (NM_DBUS_OBJECT_EXPORTED_CHANGED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
-}
diff --git a/src/nm-dbus-object.h b/src/nm-dbus-object.h
deleted file mode 100644
index 43613630..00000000
--- a/src/nm-dbus-object.h
+++ /dev/null
@@ -1,202 +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 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_DBUS_OBJECT_H__
-#define __NM_DBUS_OBJECT_H__
-
-/*****************************************************************************/
-
-#include "c-list/src/c-list.h"
-#include "nm-dbus-utils.h"
-
-/*****************************************************************************/
-
-void nm_dbus_object_set_quitting (void);
-
-/*****************************************************************************/
-
-typedef struct {
-	const char *path;
-
-	/* if path is of type NM_DBUS_EXPORT_PATH_NUMBERED(), we need a
-	 * per-class counter when generating a new numbered path.
-	 *
-	 * Each NMDBusObjectClass instance has a shallow clone of the NMDBusObjectClass parent
-	 * instance in every derived type. Hence we cannot embed the counter there directly,
-	 * because it must be shared, e.g. between NMDeviceBond and NMDeviceEthernet.
-	 * Make int_counter a pointer to the actual counter that is used by ever sibling
-	 * class. */
-	long long unsigned *int_counter;
-} NMDBusExportPath;
-
-#define NM_DBUS_EXPORT_PATH_STATIC(basepath) \
-	({ \
-		((NMDBusExportPath) { \
-			.path = ""basepath"", \
-		}); \
-	})
-
-#define NM_DBUS_EXPORT_PATH_NUMBERED(basepath) \
-	({ \
-		static long long unsigned _int_counter = 0; \
-		((NMDBusExportPath) { \
-			.path = ""basepath"/%llu", \
-			.int_counter = &_int_counter, \
-		}); \
-	})
-
-/*****************************************************************************/
-
-/* "org.freedesktop.NetworkManager.Device.Statistics" is a special interface,
- * because although it has a legacy PropertiesChanged signal, it only notifies
- * about properties that actually exist on that interface. That is, because it
- * was added with 1.4.0 release, and thus didn't have the broken behavior like
- * other legacy interfaces. Those notify about *all* properties, even if they
- * are not part of that D-Bus interface. See also "include_in_legacy_property_changed"
- * and "legacy_property_changed". */
-extern const NMDBusInterfaceInfoExtended nm_interface_info_device_statistics;
-
-/*****************************************************************************/
-
-#define NM_TYPE_DBUS_OBJECT            (nm_dbus_object_get_type ())
-#define NM_DBUS_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DBUS_OBJECT, NMDBusObject))
-#define NM_DBUS_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DBUS_OBJECT, NMDBusObjectClass))
-#define NM_IS_DBUS_OBJECT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DBUS_OBJECT))
-#define NM_IS_DBUS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DBUS_OBJECT))
-#define NM_DBUS_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DBUS_OBJECT, NMDBusObjectClass))
-
-#define NM_DBUS_OBJECT_EXPORTED_CHANGED "exported-changed"
-
-/* NMDBusObject and NMDBusManager cooperate strongly. Hence, there is an
- * internal data structure attached to the NMDBusObject accessible to both of them. */
-struct _NMDBusObjectInternal {
-	char *path;
-	NMDBusManager *bus_manager;
-	CList objects_lst;
-	CList registration_lst_head;
-
-	/* we perform asynchronous operation on exported objects. For example, we receive
-	 * a Set property call, and asynchronously validate the operation. We must make
-	 * sure that when the authentication is complete, that we are still looking at
-	 * the same (exported) object. In the meantime, the object could have been
-	 * unexported, or even re-exported afterwards. If that happens, we want
-	 * to fail the request. For that, we keep track of a version id.  */
-	guint64 export_version_id;
-	bool is_unexporting:1;
-};
-
-struct _NMDBusObject {
-	GObject parent;
-	struct _NMDBusObjectInternal internal;
-};
-
-#define NM_DEFINE_DBUS_INTERFACE_INFO(...) \
-	((NMDBusInterfaceInfo *) (&((const NMDBusInterfaceInfo) { \
-		__VA_ARGS__ \
-	})))
-
-typedef struct {
-	GObjectClass parent;
-
-	NMDBusExportPath export_path;
-
-	const NMDBusInterfaceInfoExtended *const*interface_infos;
-
-	bool export_on_construction;
-} NMDBusObjectClass;
-
-GType nm_dbus_object_get_type (void);
-
-static inline NMDBusManager *
-nm_dbus_object_get_manager (NMDBusObject *obj)
-{
-	nm_assert (NM_IS_DBUS_OBJECT (obj));
-
-	return obj->internal.bus_manager;
-}
-
-static inline guint64
-nm_dbus_object_get_export_version_id (NMDBusObject *obj)
-{
-	nm_assert (NM_IS_DBUS_OBJECT (obj));
-
-	return obj->internal.export_version_id;
-}
-
-/**
- * nm_dbus_object_get_path:
- * @self: an #NMDBusObject
- *
- * Gets @self's D-Bus path.
- *
- * Returns: @self's D-Bus path, or %NULL if @self is not exported.
- */
-static inline const char *
-nm_dbus_object_get_path (NMDBusObject *self)
-{
-	g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL);
-
-	return self->internal.path;
-}
-
-/**
- * nm_dbus_object_is_exported:
- * @self: an #NMDBusObject
- *
- * Checks if @self is exported
- *
- * Returns: %TRUE if @self is exported
- */
-static inline gboolean
-nm_dbus_object_is_exported (NMDBusObject *self)
-{
-	return !!nm_dbus_object_get_path (self);
-}
-
-static inline const char *
-nm_dbus_object_get_path_still_exported (NMDBusObject *self)
-{
-	g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL);
-
-	/* like nm_dbus_object_get_path(), however, while unexporting
-	 * (exported-changed signal), returns %NULL instead of the path. */
-	return self->internal.is_unexporting
-	       ? NULL
-	       : self->internal.path;
-}
-
-const char *nm_dbus_object_export      (NMDBusObject *self);
-void        nm_dbus_object_unexport    (NMDBusObject *self);
-
-void        _nm_dbus_object_clear_and_unexport (NMDBusObject **location);
-#define nm_dbus_object_clear_and_unexport(location) _nm_dbus_object_clear_and_unexport ((NMDBusObject **) (location))
-
-void        nm_dbus_object_emit_signal_variant (NMDBusObject *self,
-                                                const NMDBusInterfaceInfoExtended *interface_info,
-                                                const GDBusSignalInfo *signal_info,
-                                                GVariant *args);
-
-void        nm_dbus_object_emit_signal (NMDBusObject *self,
-                                        const NMDBusInterfaceInfoExtended *interface_info,
-                                        const GDBusSignalInfo *signal_info,
-                                        const char *format,
-                                        ...);
-
-#endif /* __NM_DBUS_OBJECT_H__ */
diff --git a/src/nm-dbus-utils.c b/src/nm-dbus-utils.c
deleted file mode 100644
index 8e7dd122..00000000
--- a/src/nm-dbus-utils.c
+++ /dev/null
@@ -1,312 +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 2018 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-dbus-utils.h"
-
-#include "nm-dbus-object.h"
-
-/*****************************************************************************/
-
-const GDBusSignalInfo nm_signal_info_property_changed_legacy = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"PropertiesChanged",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sv}"),
-	),
-);
-
-GDBusPropertyInfo *
-nm_dbus_utils_interface_info_lookup_property (const GDBusInterfaceInfo *interface_info,
-                                              const char *property_name,
-                                              guint *property_idx)
-{
-	guint i;
-
-	nm_assert (interface_info);
-	nm_assert (property_name);
-
-	/* there is also g_dbus_interface_info_lookup_property(), however that makes use
-	 * of a global cache. */
-	if (interface_info->properties) {
-		for (i = 0; interface_info->properties[i]; i++) {
-			GDBusPropertyInfo *info = interface_info->properties[i];
-
-			if (nm_streq (info->name, property_name)) {
-				NM_SET_OUT (property_idx, i);
-				return info;
-			}
-		}
-	}
-
-	return NULL;
-}
-
-GDBusMethodInfo *
-nm_dbus_utils_interface_info_lookup_method (const GDBusInterfaceInfo *interface_info,
-                                            const char *method_name)
-{
-	guint i;
-
-	nm_assert (interface_info);
-	nm_assert (method_name);
-
-	/* there is also g_dbus_interface_info_lookup_property(), however that makes use
-	 * of a global cache. */
-	if (interface_info->methods) {
-		for (i = 0; interface_info->methods[i]; i++) {
-			GDBusMethodInfo *info = interface_info->methods[i];
-
-			if (nm_streq (info->name, method_name))
-				return info;
-		}
-	}
-
-	return NULL;
-}
-
-GVariant *
-nm_dbus_utils_get_property (GObject *obj,
-                            const char *signature,
-                            const char *property_name)
-{
-	GParamSpec *pspec;
-	nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
-
-	nm_assert (G_IS_OBJECT (obj));
-	nm_assert (g_variant_type_string_is_valid (signature));
-	nm_assert (property_name && property_name[0]);
-
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), property_name);
-	if (!pspec)
-		g_return_val_if_reached (NULL);
-
-	g_value_init (&value, pspec->value_type);
-	g_object_get_property (obj, property_name, &value);
-	/* returns never-floating variant */
-	return g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (signature));
-}
-
-/*****************************************************************************/
-
-void
-nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object)
-{
-	const char *path;
-
-	g_return_if_fail (!object || NM_IS_DBUS_OBJECT (object));
-
-	if (   object
-	    && (path = nm_dbus_object_get_path (object)))
-		g_value_set_string (value, path);
-	else
-		g_value_set_string (value, NULL);
-}
-
-void
-nm_dbus_utils_g_value_set_object_path_still_exported (GValue *value, gpointer object)
-{
-	const char *path;
-
-	g_return_if_fail (!object || NM_IS_DBUS_OBJECT (object));
-
-	if (   object
-	    && (path = nm_dbus_object_get_path_still_exported (object)))
-		g_value_set_string (value, path);
-	else
-		g_value_set_string (value, "/");
-}
-
-void
-nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value,
-                                                 GHashTable *hash /* has keys of NMDBusObject type. */,
-                                                 gboolean expect_all_exported)
-{
-	NMDBusObject *obj;
-	char **strv;
-	guint i, n;
-	GHashTableIter iter;
-
-	nm_assert (value);
-	nm_assert (hash);
-
-	n = g_hash_table_size (hash);
-	strv = g_new (char *, n + 1);
-	i = 0;
-	g_hash_table_iter_init (&iter, hash);
-	while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) {
-		const char *path;
-
-		path = nm_dbus_object_get_path (obj);
-		if (!path) {
-			nm_assert (!expect_all_exported);
-			continue;
-		}
-		strv[i++] = g_strdup (path);
-	}
-	nm_assert (i <= n);
-	strv[i] = NULL;
-
-	/* sort the names, to give a well-defined, stable order. */
-	nm_utils_strv_sort (strv, i);
-
-	g_value_take_boxed (value, strv);
-}
-
-const char **
-nm_dbus_utils_get_paths_for_clist (const CList *lst_head,
-                                   gssize lst_len,
-                                   guint member_offset,
-                                   gboolean expect_all_exported)
-{
-	const CList *iter;
-	const char **strv;
-	const char *path;
-	gsize i, n;
-
-	nm_assert (lst_head);
-
-	if (lst_len < 0)
-		n = c_list_length (lst_head);
-	else {
-		n = lst_len;
-		nm_assert (n == c_list_length (lst_head));
-	}
-
-	i = 0;
-	strv = g_new (const char *, n + 1);
-	c_list_for_each (iter, lst_head) {
-		NMDBusObject *obj = (NMDBusObject *) (((const char *) iter) - member_offset);
-
-		path = nm_dbus_object_get_path (obj);
-		if (!path) {
-			nm_assert (expect_all_exported);
-			continue;
-		}
-
-		nm_assert (i < n);
-		strv[i++] = path;
-	}
-	nm_assert (i <= n);
-	strv[i] = NULL;
-
-	return strv;
-}
-
-/*****************************************************************************/
-
-void
-nm_dbus_track_obj_path_init (NMDBusTrackObjPath *track,
-                             GObject *target,
-                             const GParamSpec *pspec)
-{
-	nm_assert (track);
-	nm_assert (G_IS_OBJECT (target));
-	nm_assert (G_IS_PARAM_SPEC (pspec));
-
-	track->_obj = NULL;
-	track->_notify_target = target;
-	track->_notify_pspec = pspec;
-	track->_notify_signal_id = 0;
-	track->_visible = FALSE;
-}
-
-void
-nm_dbus_track_obj_path_deinit (NMDBusTrackObjPath *track)
-{
-	/* we allow deinit() to be called multiple times (e.g. from
-	 * dispose(), which must be re-entrant). */
-	nm_assert (track);
-	nm_assert (!track->_notify_target || G_IS_OBJECT (track->_notify_target));
-
-	nm_clear_g_signal_handler (track->obj, &track->_notify_signal_id);
-	track->_notify_target = NULL;
-	track->_notify_pspec = NULL;
-	track->_visible = FALSE;
-	nm_clear_g_object (&track->_obj);
-}
-
-void
-nm_dbus_track_obj_path_notify (const NMDBusTrackObjPath *track)
-{
-	nm_assert (track);
-	nm_assert (G_IS_OBJECT (track->_notify_target));
-	nm_assert (G_IS_PARAM_SPEC (track->_notify_pspec));
-
-	g_object_notify_by_pspec (track->_notify_target,
-	                          (GParamSpec *) track->_notify_pspec);
-}
-
-const char *
-nm_dbus_track_obj_path_get (const NMDBusTrackObjPath *track)
-{
-	nm_assert (track);
-	nm_assert (G_IS_OBJECT (track->_notify_target));
-
-	return track->obj && track->visible
-	       ? nm_dbus_object_get_path_still_exported (track->obj)
-	       : NULL;
-}
-
-static void
-_track_obj_exported_changed (NMDBusObject *obj,
-                             NMDBusTrackObjPath *track)
-{
-	nm_dbus_track_obj_path_notify (track);
-}
-
-void
-nm_dbus_track_obj_path_set (NMDBusTrackObjPath *track,
-                            gpointer obj,
-                            gboolean visible)
-{
-	gs_unref_object NMDBusObject *old_obj = NULL;
-	const char *old_path;
-
-	nm_assert (track);
-	nm_assert (G_IS_OBJECT (track->_notify_target));
-
-	g_return_if_fail (!obj || NM_IS_DBUS_OBJECT (obj));
-
-	if (   track->obj == obj
-	    && track->visible == !!visible)
-		return;
-
-	old_path = nm_dbus_track_obj_path_get (track);
-
-	track->_visible = visible;
-
-	if (track->obj != obj) {
-		nm_clear_g_signal_handler (track->obj, &track->_notify_signal_id);
-
-		old_obj = track->obj;
-		track->_obj = nm_g_object_ref (obj);
-
-		if (obj) {
-			track->_notify_signal_id = g_signal_connect (obj,
-			                                             NM_DBUS_OBJECT_EXPORTED_CHANGED,
-			                                             G_CALLBACK (_track_obj_exported_changed),
-			                                             track);
-		}
-	}
-
-	if (!nm_streq0 (old_path, nm_dbus_track_obj_path_get (track)))
-		nm_dbus_track_obj_path_notify (track);
-}
diff --git a/src/nm-dbus-utils.h b/src/nm-dbus-utils.h
deleted file mode 100644
index e7e930e9..00000000
--- a/src/nm-dbus-utils.h
+++ /dev/null
@@ -1,212 +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 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_DBUS_UTILS_H__
-#define __NM_DBUS_UTILS_H__
-
-/*****************************************************************************/
-
-struct _NMDBusInterfaceInfoExtended;
-struct _NMDBusMethodInfoExtended;
-
-struct _NMDBusPropertyInfoExtendedBase {
-	GDBusPropertyInfo _parent;
-	const char *property_name;
-
-	/* Whether the properties needs to be notified on the legacy
-	 * PropertyChanged signal. This is only to preserve API, new
-	 * properties should not use this. */
-	bool include_in_legacy_property_changed;
-};
-
-struct _NMDBusPropertyInfoExtendedReadWritable {
-	struct _NMDBusPropertyInfoExtendedBase _base;
-
-	/* this is the polkit permission type for authenticating setting
-	 * the property. */
-	const char *permission;
-
-	/* this is the audit operation type for writing the property. */
-	const char *audit_op;
-};
-
-typedef struct {
-	union {
-
-		GDBusPropertyInfo _parent;
-		struct _NMDBusPropertyInfoExtendedBase _base;
-		struct _NMDBusPropertyInfoExtendedReadWritable writable;
-
-		/* duplicate the base structure in the union, so that the common fields
-		 * are accessible directly in the parent struct. */
-		struct {
-			GDBusPropertyInfo parent;
-			const char *property_name;
-
-			/* Whether the properties needs to be notified on the legacy
-			 * PropertyChanged signal. This is only to preserve API, new
-			 * properties should not use this. */
-			bool include_in_legacy_property_changed;
-		};
-	};
-} NMDBusPropertyInfoExtended;
-
-G_STATIC_ASSERT (G_STRUCT_OFFSET (NMDBusPropertyInfoExtended, property_name) == G_STRUCT_OFFSET (struct _NMDBusPropertyInfoExtendedBase, property_name));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (NMDBusPropertyInfoExtended, include_in_legacy_property_changed) == G_STRUCT_OFFSET (struct _NMDBusPropertyInfoExtendedBase, include_in_legacy_property_changed));
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL(m_name, m_signature, m_property_name, m_include_in_legacy_property_changed) \
-	((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedBase) { \
-		._parent = { \
-			.ref_count = -1, \
-			.name = m_name, \
-			.signature = m_signature, \
-			.flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \
-		}, \
-		.property_name = m_property_name, \
-		.include_in_legacy_property_changed = m_include_in_legacy_property_changed, \
-	}))
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE(m_name, m_signature, m_property_name) \
-	NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL (m_name, m_signature, m_property_name, FALSE)
-
-/* define a legacy property. Do not use for new code. */
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L(m_name, m_signature, m_property_name) \
-	NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_FULL (m_name, m_signature, m_property_name, TRUE)
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL(m_name, m_signature, m_property_name, m_permission, m_audit_op, m_include_in_legacy_property_changed) \
-	((GDBusPropertyInfo *) &((const struct _NMDBusPropertyInfoExtendedReadWritable) { \
-		._base = { \
-			._parent = { \
-				.ref_count = -1, \
-				.name = m_name, \
-				.signature = m_signature, \
-				.flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, \
-			}, \
-			.property_name = m_property_name, \
-			.include_in_legacy_property_changed = m_include_in_legacy_property_changed, \
-		}, \
-		.permission = m_permission, \
-		.audit_op = m_audit_op, \
-	}))
-
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE(m_name, m_signature, m_property_name, m_permission, m_audit_op) \
-	NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL (m_name, m_signature, m_property_name, m_permission, m_audit_op, FALSE)
-
-/* define a legacy property. Do not use for new code. */
-#define NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L(m_name, m_signature, m_property_name, m_permission, m_audit_op) \
-	NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_FULL (m_name, m_signature, m_property_name, m_permission, m_audit_op, TRUE)
-
-typedef struct _NMDBusMethodInfoExtended {
-	GDBusMethodInfo parent;
-	void (*handle) (NMDBusObject *obj,
-	                const struct _NMDBusInterfaceInfoExtended *interface_info,
-	                const struct _NMDBusMethodInfoExtended *method_info,
-	                GDBusConnection *connection,
-	                const char *sender,
-	                GDBusMethodInvocation *invocation,
-	                GVariant *parameters);
-} NMDBusMethodInfoExtended;
-
-#define NM_DEFINE_DBUS_METHOD_INFO_EXTENDED(parent_, ...) \
-	((GDBusMethodInfo *) (&((const NMDBusMethodInfoExtended) { \
-		.parent = parent_, \
-		__VA_ARGS__ \
-	})))
-
-typedef struct _NMDBusInterfaceInfoExtended {
-	GDBusInterfaceInfo parent;
-
-	/* Whether the interface has a legacy property changed signal (@nm_signal_info_property_changed_legacy).
-	 * New interfaces should not use this. */
-	bool legacy_property_changed:1;
-} NMDBusInterfaceInfoExtended;
-
-extern const GDBusSignalInfo nm_signal_info_property_changed_legacy;
-
-#define NM_DBUS_INTERFACE_INFOS(...) \
-	({ \
-		static const NMDBusInterfaceInfoExtended *const _interface_infos[] = { \
-			__VA_ARGS__, \
-			NULL, \
-		}; \
-		_interface_infos; \
-	});
-
-/*****************************************************************************/
-
-GDBusPropertyInfo *nm_dbus_utils_interface_info_lookup_property (const GDBusInterfaceInfo *interface_info,
-                                                                 const char *property_name,
-                                                                 guint *property_idx);
-
-GDBusMethodInfo *nm_dbus_utils_interface_info_lookup_method (const GDBusInterfaceInfo *interface_info,
-                                                             const char *method_name);
-
-GVariant *nm_dbus_utils_get_property (GObject *obj,
-                                      const char *signature,
-                                      const char *property_name);
-
-/*****************************************************************************/
-
-struct CList;
-
-const char **nm_dbus_utils_get_paths_for_clist (const struct CList *lst_head,
-                                                gssize lst_len,
-                                                guint member_offset,
-                                                gboolean expect_all_exported);
-
-void nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object);
-
-void nm_dbus_utils_g_value_set_object_path_still_exported (GValue *value, gpointer object);
-
-void nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value,
-                                                      GHashTable *hash,
-                                                      gboolean expect_all_exported);
-
-/*****************************************************************************/
-
-typedef struct {
-	union {
-		gpointer const obj;
-		gpointer _obj;
-	};
-	GObject *_notify_target;
-	const GParamSpec *_notify_pspec;
-	gulong _notify_signal_id;
-	union {
-		const bool visible;
-		bool _visible;
-	};
-} NMDBusTrackObjPath;
-
-void nm_dbus_track_obj_path_init (NMDBusTrackObjPath *track,
-                                  GObject *target,
-                                  const GParamSpec *pspec);
-
-void nm_dbus_track_obj_path_deinit (NMDBusTrackObjPath *track);
-
-void nm_dbus_track_obj_path_notify (const NMDBusTrackObjPath *track);
-
-const char *nm_dbus_track_obj_path_get (const NMDBusTrackObjPath *track);
-
-void nm_dbus_track_obj_path_set (NMDBusTrackObjPath *track,
-                                 gpointer obj,
-                                 gboolean visible);
-
-#endif /* __NM_DBUS_UTILS_H__ */
diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c
index 08becf3c..10f58bc6 100644
--- a/src/nm-dhcp4-config.c
+++ b/src/nm-dhcp4-config.c
@@ -26,7 +26,9 @@
 
 #include "nm-dbus-interface.h"
 #include "nm-utils.h"
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
+
+#include "introspection/org.freedesktop.NetworkManager.DHCP4Config.h"
 
 /*****************************************************************************/
 
@@ -39,15 +41,15 @@ typedef struct {
 } NMDhcp4ConfigPrivate;
 
 struct _NMDhcp4Config {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMDhcp4ConfigPrivate _priv;
 };
 
 struct _NMDhcp4ConfigClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMDhcp4Config, nm_dhcp4_config, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMDhcp4Config, nm_dhcp4_config, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DHCP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcp4Config, NM_IS_DHCP4_CONFIG)
 
@@ -145,31 +147,17 @@ finalize (GObject *object)
 	G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_dhcp4_config = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DHCP4_CONFIG,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Options", "a{sv}",  NM_DHCP4_CONFIG_OPTIONS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_dhcp4_config_class_init (NMDhcp4ConfigClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
 	object_class->get_property = get_property;
 	object_class->finalize = finalize;
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP4Config");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dhcp4_config);
-	dbus_object_class->export_on_construction = TRUE;
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP4Config");
+	exported_object_class->export_on_construction = TRUE;
 
 	obj_properties[PROP_OPTIONS] =
 	     g_param_spec_variant (NM_DHCP4_CONFIG_OPTIONS, "", "",
@@ -179,4 +167,8 @@ nm_dhcp4_config_class_init (NMDhcp4ConfigClass *config_class)
 	                           G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
+	                                        NMDBUS_TYPE_DHCP4_CONFIG_SKELETON,
+	                                        NULL);
 }
diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c
index 5bb6c740..d51d77ef 100644
--- a/src/nm-dhcp6-config.c
+++ b/src/nm-dhcp6-config.c
@@ -26,7 +26,9 @@
 
 #include "nm-dbus-interface.h"
 #include "nm-utils.h"
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
+
+#include "introspection/org.freedesktop.NetworkManager.DHCP6Config.h"
 
 /*****************************************************************************/
 
@@ -39,15 +41,15 @@ typedef struct {
 } NMDhcp6ConfigPrivate;
 
 struct _NMDhcp6Config {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMDhcp6ConfigPrivate _priv;
 };
 
 struct _NMDhcp6ConfigClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMDhcp6Config, nm_dhcp6_config, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMDhcp6Config, nm_dhcp6_config, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DHCP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcp6Config, NM_IS_DHCP6_CONFIG)
 
@@ -143,31 +145,17 @@ finalize (GObject *object)
 	G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_dhcp6_config = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_DHCP6_CONFIG,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Options", "a{sv}",  NM_DHCP6_CONFIG_OPTIONS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_dhcp6_config_class_init (NMDhcp6ConfigClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
 	object_class->get_property = get_property;
 	object_class->finalize = finalize;
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP6Config");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_dhcp6_config);
-	dbus_object_class->export_on_construction = TRUE;
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/DHCP6Config");
+	exported_object_class->export_on_construction = TRUE;
 
 	obj_properties[PROP_OPTIONS] =
 	     g_param_spec_variant (NM_DHCP6_CONFIG_OPTIONS, "", "",
@@ -177,4 +165,8 @@ nm_dhcp6_config_class_init (NMDhcp6ConfigClass *config_class)
 	                           G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
+	                                        NMDBUS_TYPE_DHCP6_CONFIG_SKELETON,
+	                                        NULL);
 }
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index 235134c8..237afdef 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -284,10 +284,9 @@ fill_device_props (NMDevice *device,
 	                       g_variant_new_uint32 (nm_device_get_device_type (device)));
 	g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_STATE,
 	                       g_variant_new_uint32 (nm_device_get_state (device)));
-	if (nm_dbus_object_is_exported (NM_DBUS_OBJECT (device))) {
+	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (device)))
 		g_variant_builder_add (dev_builder, "{sv}", NMD_DEVICE_PROPS_PATH,
-		                       g_variant_new_object_path (nm_dbus_object_get_path (NM_DBUS_OBJECT (device))));
-	}
+		                       g_variant_new_object_path (nm_exported_object_get_path (NM_EXPORTED_OBJECT (device))));
 
 	proxy_config = nm_device_get_proxy_config (device);
 	if (proxy_config)
@@ -346,8 +345,8 @@ static void
 _ensure_requests (void)
 {
 	if (G_UNLIKELY (requests == NULL)) {
-		requests = g_hash_table_new_full (nm_direct_hash,
-		                                  NULL,
+		requests = g_hash_table_new_full (g_direct_hash,
+		                                  g_direct_equal,
 		                                  NULL,
 		                                  (GDestroyNotify) dispatcher_info_free);
 	}
@@ -579,7 +578,7 @@ _dispatcher_call (NMDispatcherAction action,
 		const char *connection_path;
 		const char *filename;
 
-		connection_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (settings_connection));
+		connection_path = nm_connection_get_path (NM_CONNECTION (settings_connection));
 		if (connection_path) {
 			g_variant_builder_add (&connection_props, "{sv}",
 			                       NMD_CONNECTION_PROPS_PATH,
@@ -631,7 +630,9 @@ _dispatcher_call (NMDispatcherAction action,
 	if (!device_dhcp6_props)
 		device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
 
+#if WITH_CONCHECK
 	connectivity_state_string = nm_connectivity_state_to_string (connectivity_state);
+#endif
 
 	/* Send the action to the dispatcher */
 	if (blocking) {
diff --git a/src/nm-exported-object.c b/src/nm-exported-object.c
new file mode 100644
index 00000000..94264caa
--- /dev/null
+++ b/src/nm-exported-object.c
@@ -0,0 +1,1055 @@
+/* -*- 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 2014-2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-exported-object.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "nm-bus-manager.h"
+
+#include "devices/nm-device.h"
+#include "nm-active-connection.h"
+#include "introspection/org.freedesktop.NetworkManager.Device.Statistics.h"
+
+#if NM_MORE_ASSERTS >= 2
+#define _ASSERT_NO_EARLY_EXPORT
+#endif
+
+/*****************************************************************************/
+
+static gboolean quitting = FALSE;
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMExportedObject,
+	PROP_PATH,
+);
+
+typedef struct {
+	GDBusInterfaceSkeleton *interface;
+	guint property_changed_signal_id;
+	GHashTable *pending_notifies;
+} InterfaceData;
+
+typedef struct _NMExportedObjectPrivate {
+	NMBusManager *bus_mgr;
+	char *path;
+
+	InterfaceData *interfaces;
+	guint num_interfaces;
+
+	guint notify_idle_id;
+
+#ifdef _ASSERT_NO_EARLY_EXPORT
+	bool _constructed:1;
+#endif
+} NMExportedObjectPrivate;
+
+G_DEFINE_ABSTRACT_TYPE (NMExportedObject, nm_exported_object, G_TYPE_DBUS_OBJECT_SKELETON);
+
+#define NM_EXPORTED_OBJECT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMExportedObject, NM_IS_EXPORTED_OBJECT)
+
+/*****************************************************************************/
+
+typedef struct {
+	GHashTable *properties;
+	GSList *skeleton_types;
+	GArray *methods;
+} NMExportedObjectClassInfo;
+
+static NM_CACHED_QUARK_FCN ("NMExportedObjectClassInfo", nm_exported_object_class_info_quark)
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_CORE
+#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "exported-object", __VA_ARGS__)
+
+#define _NMLOG2_DOMAIN      LOGD_DBUS_PROPS
+#define _NMLOG2(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG2_DOMAIN, "properties-changed", __VA_ARGS__)
+
+/*****************************************************************************/
+
+/* "AddConnectionUnsaved" -> "handle-add-connection-unsaved" */
+char *
+nm_exported_object_skeletonify_method_name (const char *dbus_method_name)
+{
+	GString *out;
+	const char *p;
+
+	out = g_string_new ("handle");
+	for (p = dbus_method_name; *p; p++) {
+		if (g_ascii_isupper (*p) || p == dbus_method_name) {
+			g_string_append_c (out, '-');
+			g_string_append_c (out, g_ascii_tolower (*p));
+		} else
+			g_string_append_c (out, *p);
+	}
+
+	return g_string_free (out, FALSE);
+}
+
+/* "can-modify" -> "CanModify" */
+static char *
+dbusify_name (const char *gobject_name)
+{
+	GString *out;
+	const char *p;
+	gboolean capitalize = TRUE;
+
+	out = g_string_new ("");
+	for (p = gobject_name; *p; p++) {
+		if (capitalize) {
+			g_string_append_c (out, g_ascii_toupper (*p));
+			capitalize = FALSE;
+		} else if (*p == '-')
+			capitalize = TRUE;
+		else
+			g_string_append_c (out, *p);
+	}
+
+	return g_string_free (out, FALSE);
+}
+
+/* "can_modify" -> "can-modify". Returns %NULL if @gobject_name contains no underscores */
+static char *
+hyphenify_name (const char *gobject_name)
+{
+	char *hyphen_name, *p;
+
+	if (!strchr (gobject_name, '_'))
+		return NULL;
+
+	hyphen_name = g_strdup (gobject_name);
+	for (p = hyphen_name; *p; p++) {
+		if (*p == '_')
+			*p = '-';
+	}
+	return hyphen_name;
+}
+
+/* Called when an #NMExportedObject emits a signal that corresponds to a D-Bus
+ * signal, and re-emits that signal on the correct skeleton object as well.
+ */
+static gboolean
+nm_exported_object_signal_hook (GSignalInvocationHint *ihint,
+                                guint                  n_param_values,
+                                const GValue          *param_values,
+                                gpointer               data)
+{
+	NMExportedObject *self = g_value_get_object (&param_values[0]);
+	NMExportedObjectPrivate *priv;
+	GSignalQuery *signal_info = data;
+	GDBusInterfaceSkeleton *interface = NULL;
+	GValue *dbus_param_values;
+	guint i;
+
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+	if (!priv->path)
+		return TRUE;
+
+	for (i = 0; i < priv->num_interfaces; i++) {
+		InterfaceData *ifdata = &priv->interfaces[i];
+
+		if (g_type_is_a (G_OBJECT_TYPE (ifdata->interface), signal_info->itype)) {
+			interface = ifdata->interface;
+			break;
+		}
+	}
+	g_return_val_if_fail (interface != NULL, TRUE);
+
+	dbus_param_values = g_newa (GValue, n_param_values);
+	memset (dbus_param_values, 0, sizeof (GValue) * n_param_values);
+	g_value_init (&dbus_param_values[0], G_OBJECT_TYPE (interface));
+	g_value_set_object (&dbus_param_values[0], interface);
+	for (i = 1; i < n_param_values; i++) {
+		if (g_type_is_a (param_values[i].g_type, NM_TYPE_EXPORTED_OBJECT)) {
+			NMExportedObject *arg = g_value_get_object (&param_values[i]);
+
+			g_value_init (&dbus_param_values[i], G_TYPE_STRING);
+			if (arg && nm_exported_object_is_exported (arg))
+				g_value_set_string (&dbus_param_values[i], nm_exported_object_get_path (arg));
+			else
+				g_value_set_string (&dbus_param_values[i], "/");
+		} else {
+			g_value_init (&dbus_param_values[i], param_values[i].g_type);
+			g_value_copy (&param_values[i], &dbus_param_values[i]);
+		}
+	}
+
+	g_signal_emitv (dbus_param_values, signal_info->signal_id, 0, NULL);
+
+	for (i = 0; i < n_param_values; i++)
+		g_value_unset (&dbus_param_values[i]);
+
+	return TRUE;
+}
+
+/**
+ * nm_exported_object_class_add_interface:
+ * @object_class: an #NMExportedObjectClass
+ * @dbus_skeleton_type: the type of the #GDBusInterfaceSkeleton to add
+ * @...: method name / handler pairs, %NULL-terminated
+ *
+ * Adds @dbus_skeleton_type to the list of D-Bus interfaces implemented by
+ * @object_class. Instances of @object_class will automatically have a skeleton
+ * of that type created, which will be exported when you call
+ * nm_exported_object_export().
+ *
+ * The skeleton's properties will be initialized from the #NMExportedObject's,
+ * and bidirectional bindings will be set up between them. When exported
+ * properties change, both the org.freedesktop.DBus.Properties.PropertiesChanged
+ * signal and the traditional NetworkManager PropertiesChanged signal will be
+ * emitted.
+ *
+ * When a signal is emitted on an #NMExportedObject that has the same name as a
+ * signal on @dbus_skeleton_type, it will automatically be emitted on the
+ * skeleton as well; #NMExportedObject arguments in the signal will be converted
+ * to D-Bus object paths in the skeleton signal.
+ *
+ * The arguments after @dbus_skeleton_type are pairs of D-Bus method names (in
+ * CamelCase), and the corresponding handlers for them (which must have the same
+ * prototype as the corresponding "handle-..." signal on @dbus_skeleton_type,
+ * except with no return value, and with the first argument being an object of
+ * @object_class's type, not of @dbus_skeleton_type).
+ *
+ * It is a programmer error if:
+ *   - @object_class does not define a property of the same name and type as
+ *     each of @dbus_skeleton_type's properties.
+ *   - @object_class does not define a signal with the same name and arguments
+ *     as each of @dbus_skeleton_type's signals.
+ *   - the list of method names includes any names that do not correspond to
+ *     "handle-" signals on @dbus_skeleton_type.
+ *   - the list of method names does not include every method defined by
+ *     @dbus_skeleton_type.
+ */
+void
+nm_exported_object_class_add_interface (NMExportedObjectClass *object_class,
+                                        GType                  dbus_skeleton_type,
+                                        ...)
+{
+	NMExportedObjectClassInfo *classinfo;
+	NMExportedObjectDBusMethodImpl method;
+	va_list ap;
+	const char *method_name;
+	GCallback impl;
+	gs_free GType *interfaces = NULL;
+	guint n_interfaces;
+	guint n_signals, n_method_signals;
+	guint object_signal_id;
+	GSignalQuery query;
+	int i, s;
+	GObjectClass *dbus_object_class;
+	gs_free GParamSpec **dbus_properties = NULL;
+	GParamSpec *object_property;
+	guint n_dbus_properties;
+
+	g_return_if_fail (NM_IS_EXPORTED_OBJECT_CLASS (object_class));
+	g_return_if_fail (g_type_is_a (dbus_skeleton_type, G_TYPE_DBUS_INTERFACE_SKELETON));
+
+	classinfo = g_type_get_qdata (G_TYPE_FROM_CLASS (object_class),
+	                              nm_exported_object_class_info_quark ());
+	if (!classinfo) {
+		classinfo = g_slice_new (NMExportedObjectClassInfo);
+		classinfo->skeleton_types = NULL;
+		classinfo->methods = g_array_new (FALSE, FALSE, sizeof (NMExportedObjectDBusMethodImpl));
+		classinfo->properties = g_hash_table_new (nm_str_hash, g_str_equal);
+		g_type_set_qdata (G_TYPE_FROM_CLASS (object_class),
+		                  nm_exported_object_class_info_quark (), classinfo);
+	}
+
+	classinfo->skeleton_types = g_slist_prepend (classinfo->skeleton_types,
+	                                             GSIZE_TO_POINTER (dbus_skeleton_type));
+
+	/* Ensure @dbus_skeleton_type's class_init has run, so its signals/properties
+	 * will be defined.
+	 */
+	dbus_object_class = g_type_class_ref (dbus_skeleton_type);
+
+	/* Add method implementations from the varargs */
+	va_start (ap, dbus_skeleton_type);
+	while ((method_name = va_arg (ap, const char *)) && (impl = va_arg (ap, GCallback))) {
+		method.dbus_skeleton_type = dbus_skeleton_type;
+		method.method_name = nm_exported_object_skeletonify_method_name (method_name);
+		g_assert (g_signal_lookup (method.method_name, dbus_skeleton_type) != 0);
+		method.impl = impl;
+
+		g_array_append_val (classinfo->methods, method);
+	}
+	va_end (ap);
+
+	/* Properties */
+	dbus_properties = g_object_class_list_properties (dbus_object_class, &n_dbus_properties);
+	for (i = 0; i < n_dbus_properties; i++) {
+		char *hyphen_name;
+
+		if (g_str_has_prefix (dbus_properties[i]->name, "g-"))
+			continue;
+
+		object_property = g_object_class_find_property (G_OBJECT_CLASS (object_class),
+		                                                dbus_properties[i]->name);
+		g_assert (object_property != NULL);
+		g_assert (object_property->value_type == dbus_properties[i]->value_type);
+
+		g_assert (!g_hash_table_contains (classinfo->properties, dbus_properties[i]->name));
+		g_hash_table_insert (classinfo->properties,
+		                     g_strdup (dbus_properties[i]->name),
+		                     dbusify_name (dbus_properties[i]->name));
+		hyphen_name = hyphenify_name (dbus_properties[i]->name);
+		if (hyphen_name) {
+			g_assert (!g_hash_table_contains (classinfo->properties, hyphen_name));
+			g_hash_table_insert (classinfo->properties,
+			                     hyphen_name,
+			                     dbusify_name (dbus_properties[i]->name));
+		}
+	}
+
+	/* Signals. Unlike g_object_class_list_properties(), g_signal_list_ids() is
+	 * "shallow", so we need to query each implemented gdbus-generated interface
+	 * separately.
+	 */
+	interfaces = g_type_interfaces (dbus_skeleton_type, &n_interfaces);
+	n_method_signals = 0;
+	for (i = 0; i < n_interfaces; i++) {
+		gs_free guint *dbus_signals = NULL;
+
+		dbus_signals = g_signal_list_ids (interfaces[i], &n_signals);
+		for (s = 0; s < n_signals; s++) {
+			g_signal_query (dbus_signals[s], &query);
+
+			/* PropertiesChanged is handled specially */
+			if (!strcmp (query.signal_name, "properties-changed"))
+				continue;
+
+			if (g_str_has_prefix (query.signal_name, "handle-")) {
+				n_method_signals++;
+				continue;
+			}
+
+			object_signal_id = g_signal_lookup (query.signal_name, G_TYPE_FROM_CLASS (object_class));
+			g_assert (object_signal_id != 0);
+
+			g_signal_add_emission_hook (object_signal_id, 0,
+			                            nm_exported_object_signal_hook,
+			                            g_memdup (&query, sizeof (query)),
+			                            g_free);
+		}
+	}
+
+	g_type_class_unref (dbus_object_class);
+}
+
+/*****************************************************************************/
+
+/* "meta-marshaller" that receives the skeleton "handle-foo" signal, replaces
+ * the skeleton object with an #NMExportedObject in the parameters, drops the
+ * user_data parameter, and adds a "TRUE" return value (indicating to gdbus that
+ * the signal was handled).
+ */
+static void
+nm_exported_object_meta_marshal (GClosure *closure, GValue *return_value,
+                                 guint n_param_values, const GValue *param_values,
+                                 gpointer invocation_hint, gpointer marshal_data)
+{
+	GValue *local_param_values;
+
+	local_param_values = g_new0 (GValue, n_param_values);
+	g_value_init (&local_param_values[0], G_TYPE_POINTER);
+	g_value_set_pointer (&local_param_values[0], closure->data);
+	memcpy (local_param_values + 1, param_values + 1, (n_param_values - 1) * sizeof (GValue));
+
+	g_cclosure_marshal_generic (closure, NULL,
+	                            n_param_values, local_param_values,
+	                            invocation_hint,
+	                            ((GCClosure *)closure)->callback);
+	g_value_set_boolean (return_value, TRUE);
+
+	g_value_unset (&local_param_values[0]);
+	g_free (local_param_values);
+}
+
+static NM_CACHED_QUARK_FCN ("skeleton-data", _skeleton_data_quark)
+
+typedef struct {
+	GBinding **prop_bindings;
+	gulong *method_signals;
+} SkeletonData;
+
+GDBusInterfaceSkeleton *
+nm_exported_object_skeleton_create (GType dbus_skeleton_type,
+                                    GObjectClass *object_class,
+                                    const NMExportedObjectDBusMethodImpl *methods,
+                                    guint methods_len,
+                                    GObject *target)
+{
+	GDBusInterfaceSkeleton *interface;
+	gs_free GParamSpec **properties = NULL;
+	SkeletonData *skeleton_data;
+	guint n_properties;
+	guint i, j;
+
+	interface = G_DBUS_INTERFACE_SKELETON (g_object_new (dbus_skeleton_type, NULL));
+
+	skeleton_data = g_slice_new (SkeletonData);
+
+	/* Bind properties */
+	properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (interface), &n_properties);
+	skeleton_data->prop_bindings = g_new (GBinding *, n_properties + 1);
+	for (i = 0, j = 0; i < n_properties; i++) {
+		GParamSpec *nm_property;
+		GBindingFlags flags;
+		GBinding *prop_binding;
+
+		nm_property = g_object_class_find_property (object_class, properties[i]->name);
+		if (!nm_property)
+			continue;
+
+		flags = G_BINDING_SYNC_CREATE;
+		if (   (nm_property->flags & G_PARAM_WRITABLE)
+			&& !(nm_property->flags & G_PARAM_CONSTRUCT_ONLY))
+			flags |= G_BINDING_BIDIRECTIONAL;
+		prop_binding = g_object_bind_property (target, properties[i]->name,
+		                                       interface, properties[i]->name,
+		                                       flags);
+		if (prop_binding)
+			skeleton_data->prop_bindings[j++] = prop_binding;
+	}
+	skeleton_data->prop_bindings[j++] = NULL;
+
+	/* Bind methods */
+	skeleton_data->method_signals = g_new (gulong, methods_len + 1);
+	for (i = 0, j = 0; i < methods_len; i++) {
+		const NMExportedObjectDBusMethodImpl *method = &methods[i];
+		GClosure *closure;
+		gulong method_signal;
+
+		/* ignore methods that are for a different skeleton-type. */
+		if (   method->dbus_skeleton_type
+		    && method->dbus_skeleton_type != dbus_skeleton_type)
+			continue;
+
+		closure = g_cclosure_new_swap (method->impl, target, NULL);
+		g_closure_set_meta_marshal (closure, NULL, nm_exported_object_meta_marshal);
+		method_signal = g_signal_connect_closure (interface, method->method_name, closure, FALSE);
+
+		if (method_signal != 0)
+			skeleton_data->method_signals[j++] = method_signal;
+	}
+	skeleton_data->method_signals[j++] = 0;
+
+	g_object_set_qdata ((GObject *) interface, _skeleton_data_quark (), skeleton_data);
+
+	return interface;
+}
+
+static void
+nm_exported_object_create_skeletons (NMExportedObject *self,
+                                     GType object_type)
+{
+	NMExportedObjectPrivate *priv;
+	GObjectClass *object_class;
+	NMExportedObjectClassInfo *classinfo;
+	GSList *iter;
+	const NMExportedObjectDBusMethodImpl *methods;
+	guint i, methods_len;
+	guint num_interfaces;
+	InterfaceData *interfaces;
+
+	classinfo = g_type_get_qdata (object_type, nm_exported_object_class_info_quark ());
+	if (!classinfo)
+		return;
+
+	object_class = g_type_class_peek (object_type);
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	methods = classinfo->methods->len ? &g_array_index (classinfo->methods, NMExportedObjectDBusMethodImpl, 0) : NULL;
+	methods_len = classinfo->methods->len;
+
+	num_interfaces = g_slist_length (classinfo->skeleton_types);
+	g_return_if_fail (num_interfaces > 0);
+
+	interfaces = g_slice_alloc (sizeof (InterfaceData) * (num_interfaces + priv->num_interfaces));
+
+	for (i = num_interfaces, iter = classinfo->skeleton_types; iter; iter = iter->next) {
+		InterfaceData *ifdata = &interfaces[--i];
+
+		ifdata->interface = nm_exported_object_skeleton_create (GPOINTER_TO_SIZE (iter->data),
+		                                                        object_class,
+		                                                        methods,
+		                                                        methods_len,
+		                                                        (GObject *) self);
+		g_dbus_object_skeleton_add_interface ((GDBusObjectSkeleton *) self, ifdata->interface);
+
+		ifdata->property_changed_signal_id = g_signal_lookup ("properties-changed", G_OBJECT_TYPE (ifdata->interface));
+
+		ifdata->pending_notifies = g_hash_table_new_full (g_direct_hash,
+		                                                  g_direct_equal,
+		                                                  NULL,
+		                                                  (GDestroyNotify) g_variant_unref);
+	}
+	nm_assert (i == 0);
+
+	/* The list of interfaces priv->interfaces is to be sorted from parent-class to derived-class.
+	 * On the other hand, if one class defines multiple interfaces, the interfaces are sorted in
+	 * the order of calls to nm_exported_object_class_add_interface(). */
+	if (priv->num_interfaces > 0) {
+		memcpy (&interfaces[num_interfaces], priv->interfaces, sizeof (InterfaceData) * priv->num_interfaces);
+		g_slice_free1 (sizeof (InterfaceData) * priv->num_interfaces, priv->interfaces);
+	}
+
+	priv->num_interfaces = num_interfaces + priv->num_interfaces;
+	priv->interfaces = interfaces;
+}
+
+void
+nm_exported_object_skeleton_release (GDBusInterfaceSkeleton *interface)
+{
+	SkeletonData *skeleton_data;
+	guint j;
+
+	g_return_if_fail (G_IS_DBUS_INTERFACE_SKELETON (interface));
+
+	skeleton_data = g_object_steal_qdata ((GObject *) interface, _skeleton_data_quark ());
+
+	for (j = 0; skeleton_data->prop_bindings[j]; j++)
+		g_object_unref (skeleton_data->prop_bindings[j]);
+	for (j = 0; skeleton_data->method_signals[j]; j++)
+		g_signal_handler_disconnect (interface, skeleton_data->method_signals[j]);
+
+	g_free (skeleton_data->prop_bindings);
+	g_free (skeleton_data->method_signals);
+	g_slice_free (SkeletonData, skeleton_data);
+
+	g_object_unref (interface);
+}
+
+static void
+nm_exported_object_destroy_skeletons (NMExportedObject *self)
+{
+	NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+	guint n;
+
+	g_return_if_fail (priv->num_interfaces > 0);
+	nm_assert (priv->interfaces);
+
+	n = priv->num_interfaces;
+
+	while (priv->num_interfaces > 0) {
+		InterfaceData *ifdata = &priv->interfaces[--priv->num_interfaces];
+
+		g_dbus_object_skeleton_remove_interface ((GDBusObjectSkeleton *) self, ifdata->interface);
+		nm_exported_object_skeleton_release (ifdata->interface);
+		g_hash_table_destroy (ifdata->pending_notifies);
+	}
+
+	g_slice_free1 (sizeof (InterfaceData) * n, priv->interfaces);
+	priv->interfaces = NULL;
+}
+
+static char *
+_create_export_path (NMExportedObjectClass *klass)
+{
+	const char *class_export_path, *p;
+	static GHashTable *prefix_counters;
+	guint64 *counter;
+
+	class_export_path = klass->export_path;
+
+	nm_assert (class_export_path);
+
+	p = strchr (class_export_path, '%');
+	if (p) {
+		if (G_UNLIKELY (!prefix_counters))
+			prefix_counters = g_hash_table_new (nm_str_hash, g_str_equal);
+
+		nm_assert (p[1] == 'l');
+		nm_assert (p[2] == 'l');
+		nm_assert (p[3] == 'u');
+		nm_assert (p[4] == '\0');
+
+		counter = g_hash_table_lookup (prefix_counters, class_export_path);
+		if (!counter) {
+			counter = g_slice_new0 (guint64);
+			g_hash_table_insert (prefix_counters, (char *) class_export_path, counter);
+		}
+
+		NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral")
+		return g_strdup_printf (class_export_path, (unsigned long long) (++(*counter)));
+		NM_PRAGMA_WARNING_REENABLE
+	}
+
+	return g_strdup (class_export_path);
+}
+
+/**
+ * nm_exported_object_get_path:
+ * @self: an #NMExportedObject
+ *
+ * Gets @self's D-Bus path.
+ *
+ * Returns: @self's D-Bus path, or %NULL if @self is not exported.
+ */
+const char *
+nm_exported_object_get_path (NMExportedObject *self)
+{
+	g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
+
+	return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path;
+}
+
+/**
+ * nm_exported_object_is_exported:
+ * @self: an #NMExportedObject
+ *
+ * Checks if @self is exported
+ *
+ * Returns: %TRUE if @self is exported
+ */
+gboolean
+nm_exported_object_is_exported (NMExportedObject *self)
+{
+	g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), FALSE);
+
+	return NM_EXPORTED_OBJECT_GET_PRIVATE (self)->path != NULL;
+}
+
+/**
+ * nm_exported_object_export:
+ * @self: an #NMExportedObject
+ *
+ * Exports @self on all active and future D-Bus connections.
+ *
+ * The path to export @self on is taken from its #NMObjectClass's %export_path
+ * member. If the %export_path contains "%u", then it will be replaced with a
+ * monotonically increasing integer ID (with each distinct %export_path having
+ * its own counter). Otherwise, %export_path will be used literally (implying
+ * that @self must be a singleton).
+ *
+ * Returns: the path @self was exported under
+ */
+const char *
+nm_exported_object_export (NMExportedObject *self)
+{
+	NMExportedObjectPrivate *priv;
+	GType type;
+
+	g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	g_return_val_if_fail (!priv->path, priv->path);
+	g_return_val_if_fail (!priv->bus_mgr, priv->path);
+
+#ifdef _ASSERT_NO_EARLY_EXPORT
+	nm_assert (priv->_constructed);
+#endif
+
+	priv->bus_mgr = nm_bus_manager_get ();
+	if (!priv->bus_mgr)
+		g_return_val_if_reached (NULL);
+	g_object_add_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr);
+
+	type = G_OBJECT_TYPE (self);
+	while (type != NM_TYPE_EXPORTED_OBJECT) {
+		nm_exported_object_create_skeletons (self, type);
+		type = g_type_parent (type);
+	}
+
+	priv->path = _create_export_path (NM_EXPORTED_OBJECT_GET_CLASS (self));
+
+	_LOGT ("export: \"%s\"", priv->path);
+	g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (self), priv->path);
+
+	/* Important: priv->path and priv->interfaces must not change while
+	 * the object is registered. */
+
+	nm_bus_manager_register_object (priv->bus_mgr, (GDBusObjectSkeleton *) self);
+
+	_notify (self, PROP_PATH);
+
+	return priv->path;
+}
+
+/**
+ * nm_exported_object_unexport:
+ * @self: an #NMExportedObject
+ *
+ * Unexports @self on all active D-Bus connections (and prevents it from being
+ * auto-exported on future connections).
+ */
+void
+nm_exported_object_unexport (NMExportedObject *self)
+{
+	NMExportedObjectPrivate *priv;
+
+	g_return_if_fail (NM_IS_EXPORTED_OBJECT (self));
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->path);
+
+	/* Important: priv->path and priv->interfaces must not change while
+	 * the object is registered. */
+
+	_LOGT ("unexport: \"%s\"", priv->path);
+
+	if (priv->bus_mgr) {
+		nm_bus_manager_unregister_object (priv->bus_mgr, (GDBusObjectSkeleton *) self);
+		g_object_remove_weak_pointer ((GObject *) priv->bus_mgr, (gpointer *) &priv->bus_mgr);
+		priv->bus_mgr = NULL;
+	}
+
+	nm_exported_object_destroy_skeletons (self);
+
+	g_dbus_object_skeleton_set_object_path ((GDBusObjectSkeleton *) self, NULL);
+
+	g_clear_pointer (&priv->path, g_free);
+
+	nm_clear_g_source (&priv->notify_idle_id);
+
+	_notify (self, PROP_PATH);
+}
+
+/*****************************************************************************/
+
+void
+_nm_exported_object_clear_and_unexport (NMExportedObject **location)
+{
+	NMExportedObject *self;
+	NMExportedObjectPrivate *priv;
+
+	if (!location || !*location)
+		return;
+
+	self = *location;
+	*location = NULL;
+
+	g_return_if_fail (NM_IS_EXPORTED_OBJECT (self));
+
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	if (priv->path)
+		nm_exported_object_unexport (self);
+
+	g_object_unref (self);
+}
+
+/*****************************************************************************/
+
+GDBusInterfaceSkeleton *
+nm_exported_object_get_interface_by_type (NMExportedObject *self, GType interface_type)
+{
+	NMExportedObjectPrivate *priv;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_EXPORTED_OBJECT (self), NULL);
+
+	priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	g_return_val_if_fail (priv->path, NULL);
+	g_return_val_if_fail (priv->num_interfaces > 0, NULL);
+
+	nm_assert (priv->interfaces);
+
+	for (i = 0; i < priv->num_interfaces; i++) {
+		InterfaceData *ifdata = &priv->interfaces[i];
+
+		if (G_TYPE_CHECK_INSTANCE_TYPE (ifdata->interface, interface_type))
+			return ifdata->interface;
+	}
+	return NULL;
+}
+
+/*****************************************************************************/
+
+void
+nm_exported_object_class_set_quitting (void)
+{
+	quitting = TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	const char *property_name;
+	GVariant *variant;
+} PendingNotifiesItem;
+
+static int
+_sort_pending_notifies (gconstpointer a, gconstpointer b, gpointer       user_data)
+{
+	return strcmp (((const PendingNotifiesItem *) a)->property_name,
+	               ((const PendingNotifiesItem *) b)->property_name);
+}
+
+static gboolean
+idle_emit_properties_changed (gpointer self)
+{
+	NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (NM_EXPORTED_OBJECT (self));
+	guint k;
+
+	priv->notify_idle_id = 0;
+
+	for (k = 0; k < priv->num_interfaces; k++) {
+		InterfaceData *ifdata = &priv->interfaces[k];
+		gs_unref_variant GVariant *variant = NULL;
+		PendingNotifiesItem *values;
+		GVariantBuilder notifies;
+		GHashTableIter hash_iter;
+		guint i, n;
+
+		n = g_hash_table_size (ifdata->pending_notifies);
+		if (n == 0)
+			continue;
+
+		nm_assert (ifdata->property_changed_signal_id);
+
+		/* We use here alloca in a loop, something that is usually avoided.
+		 * But the number of interfaces "priv->num_interfaces" is small (determined by
+		 * the depth of the type inheritance) and the number of possible pending_notifies
+		 * "n" is small (determined by the number of GObject properties). */
+		values = g_alloca (sizeof (values[0]) * n);
+
+		i = 0;
+		g_hash_table_iter_init (&hash_iter, ifdata->pending_notifies);
+		while (g_hash_table_iter_next (&hash_iter, (gpointer) &values[i].property_name, (gpointer) &values[i].variant))
+			i++;
+		nm_assert (i == n);
+
+		g_qsort_with_data (values, n, sizeof (values[0]), _sort_pending_notifies, NULL);
+
+		g_variant_builder_init (&notifies, G_VARIANT_TYPE_VARDICT);
+		for (i = 0; i < n; i++)
+			g_variant_builder_add (&notifies, "{sv}", values[i].property_name, values[i].variant);
+		variant = g_variant_ref_sink (g_variant_builder_end (&notifies));
+
+
+		if (_LOG2D_ENABLED ()) {
+			gs_free char *notification = g_variant_print (variant, TRUE);
+
+			_LOG2D ("type %s, iface %s: %s",
+			        G_OBJECT_TYPE_NAME (self), G_OBJECT_TYPE_NAME (ifdata->interface),
+			        notification);
+		}
+
+		g_signal_emit (ifdata->interface, ifdata->property_changed_signal_id, 0, variant);
+
+		g_hash_table_remove_all (ifdata->pending_notifies);
+	}
+
+	return G_SOURCE_REMOVE;
+}
+
+static void
+nm_exported_object_notify (GObject *object, GParamSpec *pspec)
+{
+	NMExportedObject *self = (NMExportedObject *) object;
+	NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+	NMExportedObjectClassInfo *classinfo;
+	GType type;
+	const char *dbus_property_name = NULL;
+	GValue value = G_VALUE_INIT;
+	GVariant *value_variant;
+	InterfaceData *ifdata = NULL;
+	const GVariantType *vtype;
+	guint i, j;
+
+	/* Hook to emit deprecated "PropertiesChanged" signal on NetworkManager interfaces.
+	 * This is to preserve deprecated D-Bus API, nowadays we use instead
+	 * the "PropertiesChanged" signal of "org.freedesktop.DBus.Properties". */
+
+	if (priv->num_interfaces == 0)
+		return;
+
+	for (type = G_OBJECT_TYPE (self); type; type = g_type_parent (type)) {
+		classinfo = g_type_get_qdata (type, nm_exported_object_class_info_quark ());
+		if (!classinfo)
+			continue;
+
+		dbus_property_name = g_hash_table_lookup (classinfo->properties, pspec->name);
+		if (dbus_property_name)
+			break;
+	}
+	if (!dbus_property_name) {
+		_LOG2T ("ignoring notification for prop %s on type %s",
+		        pspec->name, G_OBJECT_TYPE_NAME (self));
+		return;
+	}
+
+	for (i = 0; i < priv->num_interfaces; i++) {
+		GDBusInterfaceInfo *iinfo;
+
+		ifdata = &priv->interfaces[i];
+		iinfo = g_dbus_interface_skeleton_get_info (ifdata->interface);
+		for (j = 0; iinfo->properties[j]; j++) {
+			if (nm_streq (iinfo->properties[j]->name, dbus_property_name)) {
+				vtype = G_VARIANT_TYPE (iinfo->properties[j]->signature);
+				goto vtype_found;
+			}
+		}
+	}
+	g_return_if_reached ();
+
+vtype_found:
+	g_value_init (&value, pspec->value_type);
+	g_object_get_property ((GObject *) self, pspec->name, &value);
+	value_variant = g_dbus_gvalue_to_gvariant (&value, vtype);
+	g_value_unset (&value);
+
+	if (   (   NM_IS_DEVICE (self)
+	        && !NMDBUS_IS_DEVICE_STATISTICS_SKELETON (ifdata->interface))
+	    || NM_IS_ACTIVE_CONNECTION (self)) {
+		/* This PropertiesChanged signal is nodaways deprecated in favor
+		 * of "org.freedesktop.DBus.Properties"'s PropertiesChanged signal.
+		 * This function solely exists to raise the NM version of PropertiesChanged.
+		 *
+		 * With types exported on D-Bus that are implemented as derived
+		 * types in glib (NMDevice and NMActiveConnection), multiple types
+		 * in the inheritance tree define a "PropertiesChanged" signal.
+		 *
+		 * In 1.0.0 and earlier, the signal was emitted once for every interface
+		 * that had a "PropertiesChanged" signal. For example:
+		 *   - NMDeviceEthernet.HwAddress was emitted on "fdo.NM.Device.Ethernet"
+		 *     and "fdo.NM.Device.Veth" (if the device was of type NMDeviceVeth).
+		 *   - NMVpnConnection.VpnState was emitted on "fdo.NM.Connecion.Active"
+		 *     and "fdo.NM.VPN.Connection".
+		 *
+		 * NMDevice is special in that it didn't have a "PropertiesChanged" signal.
+		 * Thus, a change to "NMDevice.StateReason" would be emitted on "fdo.NM.Device.Ethernet"
+		 * and also on "fdo.NM.Device.Veth" (in case of a device of type NMDeviceVeth).
+		 *
+		 * The releases of 1.2.0 and 1.4.0 failed to realize above and broke this behavior.
+		 * This special handling here is to bring back the 1.0.0 behavior.
+		 *
+		 * The Device.Statistics signal is special, because it was only added with 1.4.0
+		 * and didn't have above behavior. So let's save the overhead of emitting multiple
+		 * deprecated signals for wrong interfaces. */
+		for (i = 0, j = 0; i < priv->num_interfaces; i++) {
+			ifdata = &priv->interfaces[i];
+			if (   ifdata->property_changed_signal_id
+			    && !NMDBUS_IS_DEVICE_STATISTICS_SKELETON (ifdata->interface)) {
+				j++;
+				g_hash_table_insert (ifdata->pending_notifies,
+				                     (gpointer) dbus_property_name,
+				                     g_variant_ref (value_variant));
+			}
+		}
+		nm_assert (j > 0);
+		g_variant_unref (value_variant);
+	} else if (ifdata->property_changed_signal_id) {
+		/* @dbus_property_name is inside classinfo and never freed, thus we don't clone it.
+		 * Also, we do a pointer, not string comparison. */
+		g_hash_table_insert (ifdata->pending_notifies,
+		                     (gpointer) dbus_property_name,
+		                     value_variant);
+	} else
+		g_variant_unref (value_variant);
+
+	if (!priv->notify_idle_id)
+		priv->notify_idle_id = g_idle_add (idle_emit_properties_changed, self);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMExportedObject *self = NM_EXPORTED_OBJECT (object);
+	NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_PATH:
+		g_value_set_string (value, priv->path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_exported_object_init (NMExportedObject *self)
+{
+	NMExportedObjectPrivate *priv;
+
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_EXPORTED_OBJECT, NMExportedObjectPrivate);
+	self->_priv = priv;
+}
+
+static void
+constructed (GObject *object)
+{
+	NMExportedObjectClass *klass;
+
+	G_OBJECT_CLASS (nm_exported_object_parent_class)->constructed (object);
+
+#ifdef _ASSERT_NO_EARLY_EXPORT
+	NM_EXPORTED_OBJECT_GET_PRIVATE (NM_EXPORTED_OBJECT (object))->_constructed = TRUE;
+#endif
+
+	klass = NM_EXPORTED_OBJECT_GET_CLASS (object);
+
+	if (klass->export_on_construction)
+		nm_exported_object_export ((NMExportedObject *) object);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMExportedObject *self = NM_EXPORTED_OBJECT (object);
+	NMExportedObjectPrivate *priv = NM_EXPORTED_OBJECT_GET_PRIVATE (self);
+
+	/* Objects should have already been unexported by their owner, unless
+	 * we are quitting, where many objects stick around until exit.
+	 */
+	if (!quitting) {
+		if (priv->path) {
+			g_warn_if_reached ();
+			nm_exported_object_unexport (self);
+		}
+	} else if (nm_clear_g_free (&priv->path))
+		_notify (self, PROP_PATH);
+
+	nm_clear_g_source (&priv->notify_idle_id);
+
+	G_OBJECT_CLASS (nm_exported_object_parent_class)->dispose (object);
+}
+
+static void
+nm_exported_object_class_init (NMExportedObjectClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMExportedObjectPrivate));
+
+	object_class->constructed = constructed;
+	object_class->notify = nm_exported_object_notify;
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+
+	obj_properties[PROP_PATH] =
+	    g_param_spec_string (NM_EXPORTED_OBJECT_PATH, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/nm-exported-object.h b/src/nm-exported-object.h
new file mode 100644
index 00000000..47559a20
--- /dev/null
+++ b/src/nm-exported-object.h
@@ -0,0 +1,85 @@
+/* -*- 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 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_EXPORTED_OBJECT_H
+#define NM_EXPORTED_OBJECT_H
+
+/*****************************************************************************/
+
+#define NM_EXPORT_PATH_NUMBERED(basepath) ""basepath"/%llu"
+
+char *nm_exported_object_skeletonify_method_name (const char *dbus_method_name);
+
+typedef struct {
+	GType dbus_skeleton_type;
+	char *method_name;
+	GCallback impl;
+} NMExportedObjectDBusMethodImpl;
+
+GDBusInterfaceSkeleton *nm_exported_object_skeleton_create (GType dbus_skeleton_type,
+                                                            GObjectClass *object_class,
+                                                            const NMExportedObjectDBusMethodImpl *methods,
+                                                            guint methods_len,
+                                                            GObject *target);
+void nm_exported_object_skeleton_release (GDBusInterfaceSkeleton *interface);
+
+/*****************************************************************************/
+
+#define NM_TYPE_EXPORTED_OBJECT            (nm_exported_object_get_type ())
+#define NM_EXPORTED_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_EXPORTED_OBJECT, NMExportedObject))
+#define NM_EXPORTED_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_EXPORTED_OBJECT, NMExportedObjectClass))
+#define NM_IS_EXPORTED_OBJECT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_EXPORTED_OBJECT))
+#define NM_IS_EXPORTED_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_EXPORTED_OBJECT))
+#define NM_EXPORTED_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_EXPORTED_OBJECT, NMExportedObjectClass))
+
+#define NM_EXPORTED_OBJECT_PATH "path"
+
+struct _NMExportedObjectPrivate;
+
+struct _NMExportedObject {
+	GDBusObjectSkeleton parent;
+	struct _NMExportedObjectPrivate *_priv;
+};
+
+typedef struct {
+	GDBusObjectSkeletonClass parent;
+
+	const char *export_path;
+	char export_on_construction;
+} NMExportedObjectClass;
+
+GType nm_exported_object_get_type (void);
+
+void nm_exported_object_class_set_quitting  (void);
+
+void nm_exported_object_class_add_interface (NMExportedObjectClass *object_class,
+                                             GType                  dbus_skeleton_type,
+                                             ...) G_GNUC_NULL_TERMINATED;
+
+const char *nm_exported_object_export      (NMExportedObject *self);
+const char *nm_exported_object_get_path    (NMExportedObject *self);
+gboolean    nm_exported_object_is_exported (NMExportedObject *self);
+void        nm_exported_object_unexport    (NMExportedObject *self);
+GDBusInterfaceSkeleton *nm_exported_object_get_interface_by_type (NMExportedObject *self, GType interface_type);
+
+void        _nm_exported_object_clear_and_unexport (NMExportedObject **location);
+#define nm_exported_object_clear_and_unexport(location) _nm_exported_object_clear_and_unexport ((NMExportedObject **) (location))
+
+#endif /* NM_EXPORTED_OBJECT_H */
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c
index 134a46f7..216d5fc8 100644
--- a/src/nm-firewall-manager.c
+++ b/src/nm-firewall-manager.c
@@ -25,7 +25,7 @@
 #include <string.h>
 
 #include "NetworkManagerUtils.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 
 /*****************************************************************************/
 
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 601c72ac..1493ef19 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -33,8 +33,6 @@
 #include <signal.h>
 #include <linux/rtnetlink.h>
 
-#include "nm-utils/nm-c-list.h"
-
 #include "main-utils.h"
 #include "NetworkManagerUtils.h"
 #include "platform/nm-linux-platform.h"
@@ -57,9 +55,6 @@
 static struct {
 	GMainLoop *main_loop;
 	int ifindex;
-
-	guint dad_failed_id;
-	CList dad_failed_lst_head;
 } gl/*obal*/ = {
 	.ifindex = -1,
 };
@@ -125,7 +120,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 		g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex);
 
 		existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET),
-		                                  NM_PLATFORM_GET, gl.ifindex);
+		                                  NM_PLATFORM_GET, gl.ifindex, FALSE);
 		if (last_config)
 			nm_ip4_config_subtract (existing, last_config, 0);
 
@@ -171,7 +166,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	NMIP6Config *existing;
 
 	existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET),
-	                                  NM_PLATFORM_GET, gl.ifindex, global_opt.tempaddr);
+	                                  NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr);
 	if (ndisc_config)
 		nm_ip6_config_subtract (existing, ndisc_config, 0);
 	else {
@@ -321,58 +316,19 @@ do_early_setup (int *argc, char **argv[])
 	return TRUE;
 }
 
-typedef struct {
-	NMPlatform *platform;
-	NMNDisc *ndisc;
-} DadFailedHandleData;
-
-static gboolean
-dad_failed_handle_idle (gpointer user_data)
-{
-	DadFailedHandleData *data = user_data;
-	NMCListElem *elem;
-
-	while ((elem = c_list_first_entry (&gl.dad_failed_lst_head, NMCListElem, lst))) {
-		nm_auto_nmpobj const NMPObject *obj = elem->data;
-
-		nm_c_list_elem_free (elem);
-
-		if (nm_ndisc_dad_addr_is_fail_candidate (data->platform, obj)) {
-			nm_ndisc_dad_failed (data->ndisc,
-			                     &NMP_OBJECT_CAST_IP6_ADDRESS (obj)->address);
-		}
-	}
-
-	gl.dad_failed_id = 0;
-	return G_SOURCE_REMOVE;
-}
-
 static void
 ip6_address_changed (NMPlatform *platform,
                      int obj_type_i,
                      int iface,
-                     const NMPlatformIP6Address *addr,
+                     NMPlatformIP6Address *addr,
                      int change_type_i,
                      NMNDisc *ndisc)
 {
 	const NMPlatformSignalChangeType change_type = change_type_i;
-	DadFailedHandleData *data;
-
-	if (!nm_ndisc_dad_addr_is_fail_candidate_event (change_type, addr))
-		return;
-
-	c_list_link_tail (&gl.dad_failed_lst_head,
-	                  &nm_c_list_elem_new_stale ((gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr)))->lst);
-	if (gl.dad_failed_id)
-		return;
-
-	data = g_slice_new (DadFailedHandleData);
-	data->platform = platform;
-	data->ndisc = ndisc;
-	gl.dad_failed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
-	                                    dad_failed_handle_idle,
-	                                    data,
-	                                    nm_g_slice_free_fcn (DadFailedHandleData));
+
+	if (   (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->n_ifa_flags & IFA_F_DADFAILED)
+	    || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->n_ifa_flags & IFA_F_TENTATIVE))
+		nm_ndisc_dad_failed (ndisc, &addr->address);
 }
 
 int
@@ -384,13 +340,14 @@ main (int argc, char *argv[])
 	gs_free char *pidfile = NULL;
 	gs_unref_object NMDhcpClient *dhcp4_client = NULL;
 	gs_unref_object NMNDisc *ndisc = NULL;
-	gs_unref_bytes GBytes *hwaddr = NULL;
-	gs_unref_bytes GBytes *client_id = NULL;
+	GByteArray *hwaddr = NULL;
+	size_t hwaddr_len = 0;
+	gconstpointer tmp;
 	gs_free NMUtilsIPv6IfaceId *iid = NULL;
 	guint sd_id;
 	char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
 
-	c_list_init (&gl.dad_failed_lst_head);
+	nm_g_type_init ();
 
 	setpgid (getpid (), getpid ());
 
@@ -474,7 +431,11 @@ main (int argc, char *argv[])
 	/* Set up platform interaction layer */
 	nm_linux_platform_setup ();
 
-	hwaddr = nm_platform_link_get_address_as_bytes (NM_PLATFORM_GET, gl.ifindex);
+	tmp = nm_platform_link_get_address (NM_PLATFORM_GET, gl.ifindex, &hwaddr_len);
+	if (tmp) {
+		hwaddr = g_byte_array_sized_new (hwaddr_len);
+		g_byte_array_append (hwaddr, tmp, hwaddr_len);
+	}
 
 	if (global_opt.iid_str) {
 		GBytes *bytes;
@@ -488,16 +449,6 @@ main (int argc, char *argv[])
 		iid = g_bytes_unref_to_data (bytes, &ignored);
 	}
 
-	if (global_opt.dhcp4_clientid) {
-		/* this string is just a plain hex-string. Unlike ipv4.dhcp-client-id, which
-		 * is parsed via nm_dhcp_utils_client_id_string_to_bytes(). */
-		client_id = nm_utils_hexstr2bin (global_opt.dhcp4_clientid);
-		if (!client_id || g_bytes_get_size (client_id) < 2) {
-			fprintf (stderr, _("(%s): Invalid DHCP client-id %s\n"), global_opt.ifname, global_opt.dhcp4_clientid);
-			return 1;
-		}
-	}
-
 	if (global_opt.dhcp4_address) {
 		nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, sysctl_path_buf, global_opt.ifname, "promote_secondaries")), "1");
 
@@ -512,7 +463,7 @@ main (int argc, char *argv[])
 		                                          !!global_opt.dhcp4_hostname,
 		                                          global_opt.dhcp4_hostname,
 		                                          global_opt.dhcp4_fqdn,
-		                                          client_id,
+		                                          global_opt.dhcp4_clientid,
 		                                          NM_DHCP_TIMEOUT_DEFAULT,
 		                                          NULL,
 		                                          global_opt.dhcp4_address);
@@ -572,8 +523,7 @@ main (int argc, char *argv[])
 
 	g_main_loop_run (gl.main_loop);
 
-	nm_clear_g_source (&gl.dad_failed_id);
-	nm_c_list_elem_free_all (&gl.dad_failed_lst_head, (GDestroyNotify) nmp_object_unref);
+	g_clear_pointer (&hwaddr, g_byte_array_unref);
 
 	if (pidfile && wrote_pidfile)
 		unlink (pidfile);
@@ -597,7 +547,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = {
 #include "nm-config.h"
 #include "devices/nm-device.h"
 #include "nm-active-connection.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 
 void
 nm_main_config_reload (int signal)
@@ -629,34 +579,21 @@ nm_config_get_configure_and_quit (NMConfig *config)
 	return TRUE;
 }
 
-NMDBusManager *
-nm_dbus_manager_get (void)
-{
-	return NULL;
-}
-
-void
-_nm_dbus_manager_obj_export (NMDBusObject *obj)
-{
-}
-
-void
-_nm_dbus_manager_obj_unexport (NMDBusObject *obj)
+NMBusManager *
+nm_bus_manager_get (void)
 {
+	return GUINT_TO_POINTER (1);
 }
 
 void
-_nm_dbus_manager_obj_notify (NMDBusObject *obj,
-                             guint n_pspecs,
-                             const GParamSpec *const*pspecs)
+nm_bus_manager_register_object (NMBusManager *bus_manager,
+                                GDBusObjectSkeleton *object)
 {
 }
 
 void
-_nm_dbus_manager_obj_emit_signal (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const GDBusSignalInfo *signal_info,
-                                  GVariant *args)
+nm_bus_manager_unregister_object (NMBusManager *bus_manager,
+                                  GDBusObjectSkeleton *object)
 {
 }
 
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 0a9591d2..15157739 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -36,7 +36,8 @@
 #include "platform/nm-platform-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
-#include "nm-dbus-object.h"
+
+#include "introspection/org.freedesktop.NetworkManager.IP4Config.h"
 
 /*****************************************************************************/
 
@@ -292,7 +293,6 @@ typedef struct {
 	int ifindex;
 	NMIPConfigSource mtu_source;
 	gint dns_priority;
-	NMSettingConnectionMdns mdns;
 	GArray *nameservers;
 	GPtrArray *domains;
 	GPtrArray *searches;
@@ -317,15 +317,15 @@ typedef struct {
 } NMIP4ConfigPrivate;
 
 struct _NMIP4Config {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMIP4ConfigPrivate _priv;
 };
 
 struct _NMIP4ConfigClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_IP4_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP4Config, NM_IS_IP4_CONFIG)
 
@@ -582,24 +582,14 @@ sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer u
 }
 
 NMIP4Config *
-nm_ip4_config_clone (const NMIP4Config *self)
-{
-	NMIP4Config *copy;
-
-	copy = nm_ip4_config_new (nm_ip4_config_get_multi_idx (self), -1);
-	nm_ip4_config_replace (copy, self, NULL);
-
-	return copy;
-}
-
-NMIP4Config *
-nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex)
+nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf)
 {
 	NMIP4Config *self;
 	NMIP4ConfigPrivate *priv;
 	const NMDedupMultiHeadEntry *head_entry;
 	NMDedupMultiIter iter;
 	const NMPObject *plobj = NULL;
+	gboolean has_addresses = FALSE;
 
 	nm_assert (ifindex > 0);
 
@@ -631,6 +621,7 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 		nm_dedup_multi_head_entry_sort (head_entry,
 		                                sort_captured_addresses,
 		                                NULL);
+		has_addresses = TRUE;
 		_notify_addresses (self);
 	}
 
@@ -642,6 +633,23 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 	nmp_cache_iter_for_each (&iter, head_entry, &plobj)
 		_add_route (self, plobj, NULL, NULL);
 
+	/* If the interface has the default route, and has IPv4 addresses, capture
+	 * nameservers from /etc/resolv.conf.
+	 */
+	if (   has_addresses
+	    && priv->best_default_route
+	    && capture_resolv_conf) {
+		gs_free char *rc_contents = NULL;
+
+		if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) {
+			if (nm_utils_resolve_conf_parse (AF_INET,
+			                                 rc_contents,
+			                                 priv->nameservers,
+			                                 priv->dns_options))
+				_notify (self, PROP_NAMESERVERS);
+		}
+	}
+
 	return self;
 }
 
@@ -651,6 +659,7 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self,
                                     guint32 route_metric,
                                     GPtrArray **out_ip4_dev_route_blacklist)
 {
+	const NMIP4ConfigPrivate *priv;
 	GPtrArray *ip4_dev_route_blacklist = NULL;
 	const NMPlatformIP4Address *my_addr;
 	const NMPlatformIP4Route *my_route;
@@ -659,6 +668,8 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self,
 
 	g_return_if_fail (NM_IS_IP4_CONFIG (self));
 
+	priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+
 	ifindex = nm_ip4_config_get_ifindex (self);
 	g_return_if_fail (ifindex > 0);
 
@@ -882,10 +893,10 @@ _nm_ip_config_merge_route_attributes (int addr_family,
 void
 nm_ip4_config_merge_setting (NMIP4Config *self,
                              NMSettingIPConfig *setting,
-                             NMSettingConnectionMdns mdns,
                              guint32 route_table,
                              guint32 route_metric)
 {
+	NMIP4ConfigPrivate *priv;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	int i, priority;
 	const char *gateway_str;
@@ -896,6 +907,8 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 
 	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
 
+	priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+
 	g_object_freeze_notify (G_OBJECT (self));
 
 	naddresses = nm_setting_ip_config_get_num_addresses (setting);
@@ -933,7 +946,7 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 		address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
 		address.addr_source = NM_IP_CONFIG_SOURCE_USER;
 
-		label = nm_ip_address_get_attribute (s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		label = nm_ip_address_get_attribute (s_addr, "label");
 		if (label)
 			g_strlcpy (address.label, g_variant_get_string (label, NULL), sizeof (address.label));
 
@@ -999,8 +1012,6 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 	if (priority)
 		nm_ip4_config_set_dns_priority (self, priority);
 
-	nm_ip4_config_mdns_set (self, mdns);
-
 	g_object_thaw_notify (G_OBJECT (self));
 }
 
@@ -1047,7 +1058,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self)
 
 		s_addr = nm_ip_address_new_binary (AF_INET, &address->address, address->plen, NULL);
 		if (*address->label)
-			nm_ip_address_set_attribute (s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (address->label));
+			nm_ip_address_set_attribute (s_addr, "label", g_variant_new_string (address->label));
 
 		nm_setting_ip_config_add_address (s_ip4, s_addr);
 		nm_ip_address_unref (s_addr);
@@ -1216,11 +1227,6 @@ nm_ip4_config_merge (NMIP4Config *dst,
 	if (nm_ip4_config_get_dns_priority (src))
 		nm_ip4_config_set_dns_priority (dst, nm_ip4_config_get_dns_priority (src));
 
-	/* mdns */
-	nm_ip4_config_mdns_set (dst,
-	                        NM_MAX (nm_ip4_config_mdns_get (src),
-	                                nm_ip4_config_mdns_get (dst)));
-
 	g_object_thaw_notify (G_OBJECT (dst));
 }
 
@@ -1457,18 +1463,13 @@ nm_ip4_config_subtract (NMIP4Config *dst,
 	if (nm_ip4_config_get_dns_priority (src) == nm_ip4_config_get_dns_priority (dst))
 		nm_ip4_config_set_dns_priority (dst, 0);
 
-	/* mdns */
-	if (nm_ip4_config_mdns_get (src) == nm_ip4_config_mdns_get (dst))
-		nm_ip4_config_mdns_set (dst, NM_SETTING_CONNECTION_MDNS_DEFAULT);
-
 	g_object_thaw_notify (G_OBJECT (dst));
 }
 
-static gboolean
-_nm_ip4_config_intersect_helper (NMIP4Config *dst,
-                                 const NMIP4Config *src,
-                                 guint32 default_route_metric_penalty,
-                                 gboolean update_dst)
+void
+nm_ip4_config_intersect (NMIP4Config *dst,
+                         const NMIP4Config *src,
+                         guint32 default_route_metric_penalty)
 {
 	NMIP4ConfigPrivate *dst_priv;
 	const NMIP4ConfigPrivate *src_priv;
@@ -1476,16 +1477,15 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 	const NMPlatformIP4Address *a;
 	const NMPlatformIP4Route *r;
 	const NMPObject *new_best_default_route;
-	gboolean changed, result = FALSE;
+	gboolean changed;
 
-	g_return_val_if_fail (src, FALSE);
-	g_return_val_if_fail (dst, FALSE);
+	g_return_if_fail (src);
+	g_return_if_fail (dst);
 
 	dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst);
 	src_priv = NM_IP4_CONFIG_GET_PRIVATE (src);
 
-	if (update_dst)
-		g_object_freeze_notify (G_OBJECT (dst));
+	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
 	changed = FALSE;
@@ -1495,18 +1495,13 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 		                                     NMP_OBJECT_UP_CAST (a)))
 			continue;
 
-		if (!update_dst)
-			return TRUE;
-
 		if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx,
 		                                       ipconf_iter.current) != 1)
 			nm_assert_not_reached ();
 		changed = TRUE;
 	}
-	if (changed) {
+	if (changed)
 		_notify_addresses (dst);
-		result = TRUE;
-	}
 
 	/* ignore nameservers */
 
@@ -1538,9 +1533,6 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 			continue;
 		}
 
-		if (!update_dst)
-			return TRUE;
-
 		if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx,
 		                                       ipconf_iter.current) != 1)
 			nm_assert_not_reached ();
@@ -1550,71 +1542,18 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst,
 		nm_assert (changed);
 		_notify (dst, PROP_GATEWAY);
 	}
-
-	if (changed) {
+	if (changed)
 		_notify_routes (dst);
-		result = TRUE;
-	}
 
 	/* ignore domains */
 	/* ignore dns searches */
 	/* ignore dns options */
 	/* ignore NIS */
 	/* ignore WINS */
-	/* ignore mdns */
 
-	if (update_dst)
-		g_object_thaw_notify (G_OBJECT (dst));
-	return result;
+	g_object_thaw_notify (G_OBJECT (dst));
 }
 
-/**
- * nm_ip4_config_intersect:
- * @dst: a configuration to be updated
- * @src: another configuration
- * @default_route_metric_penalty: the default route metric penalty
- *
- * Computes the intersection between @src and @dst and updates @dst in place
- * with the result.
- */
-void
-nm_ip4_config_intersect (NMIP4Config *dst,
-                         const NMIP4Config *src,
-                         guint32 default_route_metric_penalty)
-{
-	_nm_ip4_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
-}
-
-/**
- * nm_ip4_config_intersect_alloc:
- * @a: a configuration
- * @b: another configuration
- * @default_route_metric_penalty: the default route metric penalty
- *
- * Computes the intersection between @a and @b and returns the result in a newly
- * allocated configuration.  As a special case, if @a and @b are identical (with
- * respect to the only properties considered - addresses and routes) the
- * functions returns NULL so that one of existing configuration can be reused
- * without allocation.
- *
- * Returns: the intersection between @a and @b, or %NULL if the result is equal
- * to @a and @b.
- */
-NMIP4Config *
-nm_ip4_config_intersect_alloc (const NMIP4Config *a,
-                               const NMIP4Config *b,
-                               guint32 default_route_metric_penalty)
-{
-	NMIP4Config *a_copy;
-
-	if (_nm_ip4_config_intersect_helper ((NMIP4Config *) a, b,
-	                                     default_route_metric_penalty, FALSE)) {
-		a_copy = nm_ip4_config_clone (a);
-		_nm_ip4_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
-		return a_copy;
-	} else
-		return NULL;
-}
 
 /**
  * nm_ip4_config_replace:
@@ -1840,8 +1779,6 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 		has_relevant_changes = TRUE;
 	}
 
-	dst_priv->mdns = src_priv->mdns;
-
 	/* DNS priority */
 	if (src_priv->dns_priority != dst_priv->dns_priority) {
 		nm_ip4_config_set_dns_priority (dst, src_priv->dns_priority);
@@ -1934,7 +1871,7 @@ nm_ip4_config_dump (const NMIP4Config *self, const char *detail)
 		return;
 	}
 
-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+	str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
 	if (str)
 		g_message ("   path: %s", str);
 
@@ -2519,21 +2456,6 @@ nm_ip4_config_get_dns_option (const NMIP4Config *self, guint i)
 
 /*****************************************************************************/
 
-NMSettingConnectionMdns
-nm_ip4_config_mdns_get (const NMIP4Config *self)
-{
-	return NM_IP4_CONFIG_GET_PRIVATE (self)->mdns;
-}
-
-void
-nm_ip4_config_mdns_set (NMIP4Config *self,
-                        NMSettingConnectionMdns mdns)
-{
-	NM_IP4_CONFIG_GET_PRIVATE (self)->mdns = mdns;
-}
-
-/*****************************************************************************/
-
 void
 nm_ip4_config_set_dns_priority (NMIP4Config *self, gint priority)
 {
@@ -2884,9 +2806,9 @@ nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b)
 {
 	GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1);
 	GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1);
-	guchar a_data[20], b_data[20];
-	gsize a_len = sizeof (a_data);
-	gsize b_len = sizeof (b_data);
+	gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
+	gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
+	guchar a_data[a_len], b_data[b_len];
 	gboolean equal;
 
 	if (a)
@@ -2897,8 +2819,7 @@ nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b)
 	g_checksum_get_digest (a_checksum, a_data, &a_len);
 	g_checksum_get_digest (b_checksum, b_data, &b_len);
 
-	nm_assert (a_len == sizeof (a_data));
-	nm_assert (b_len == sizeof (b_data));
+	g_assert (a_len == b_len);
 	equal = !memcmp (a_data, b_data, a_len);
 
 	g_checksum_free (a_checksum);
@@ -2968,7 +2889,7 @@ get_property (GObject *object, guint prop_id,
 
 				if (*address->label) {
 					g_variant_builder_add (&addr_builder, "{sv}",
-					                       NM_IP_ADDRESS_ATTRIBUTE_LABEL,
+					                       "label",
 					                       g_variant_new_string (address->label));
 				}
 
@@ -3144,7 +3065,6 @@ nm_ip4_config_init (NMIP4Config *self)
 	nm_ip_config_dedup_multi_idx_type_init ((NMIPConfigDedupMultiIdxType *) &priv->idx_ip4_routes,
 	                                        NMP_OBJECT_TYPE_IP4_ROUTE);
 
-	priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
 	priv->nameservers = g_array_new (FALSE, FALSE, sizeof (guint32));
 	priv->domains = g_ptr_array_new_with_free_func (g_free);
 	priv->searches = g_ptr_array_new_with_free_func (g_free);
@@ -3192,37 +3112,13 @@ finalize (GObject *object)
 	nm_dedup_multi_index_unref (priv->multi_idx);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_ip4_config = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_IP4_CONFIG,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Addresses",   "aau",    NM_IP4_CONFIG_ADDRESSES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AddressData", "aa{sv}", NM_IP4_CONFIG_ADDRESS_DATA),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Gateway",     "s",      NM_IP4_CONFIG_GATEWAY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Routes",      "aau",    NM_IP4_CONFIG_ROUTES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RouteData",   "aa{sv}", NM_IP4_CONFIG_ROUTE_DATA),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Nameservers", "au",     NM_IP4_CONFIG_NAMESERVERS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Domains",     "as",     NM_IP4_CONFIG_DOMAINS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Searches",    "as",     NM_IP4_CONFIG_SEARCHES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsOptions",  "as",     NM_IP4_CONFIG_DNS_OPTIONS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsPriority", "i",      NM_IP4_CONFIG_DNS_PRIORITY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WinsServers", "au",     NM_IP4_CONFIG_WINS_SERVERS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP4Config");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_ip4_config);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP4Config");
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
@@ -3302,4 +3198,8 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
+	                                        NMDBUS_TYPE_IP4_CONFIG_SKELETON,
+	                                        NULL);
 }
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index 1c5222df..7c345d20 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -21,8 +21,7 @@
 #ifndef __NETWORKMANAGER_IP4_CONFIG_H__
 #define __NETWORKMANAGER_IP4_CONFIG_H__
 
-#include "nm-setting-connection.h"
-
+#include "nm-exported-object.h"
 #include "nm-setting-ip4-config.h"
 
 #include "nm-utils/nm-dedup-multi.h"
@@ -155,12 +154,11 @@ GType nm_ip4_config_get_type (void);
 NMIP4Config * nm_ip4_config_new (NMDedupMultiIndex *multi_idx,
                                  int ifindex);
 
-NMIP4Config *nm_ip4_config_clone (const NMIP4Config *self);
 int nm_ip4_config_get_ifindex (const NMIP4Config *self);
 
 NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self);
 
-NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex);
+NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf);
 
 void nm_ip4_config_add_dependent_routes (NMIP4Config *self,
                                          guint32 route_table,
@@ -173,7 +171,6 @@ gboolean nm_ip4_config_commit (const NMIP4Config *self,
 
 void nm_ip4_config_merge_setting (NMIP4Config *self,
                                   NMSettingIPConfig *setting,
-                                  NMSettingConnectionMdns mdns,
                                   guint32 route_table,
                                   guint32 route_metric);
 NMSetting *nm_ip4_config_create_setting (const NMIP4Config *self);
@@ -189,9 +186,6 @@ void nm_ip4_config_subtract (NMIP4Config *dst,
 void nm_ip4_config_intersect (NMIP4Config *dst,
                               const NMIP4Config *src,
                               guint32 default_route_metric_penalty);
-NMIP4Config *nm_ip4_config_intersect_alloc (const NMIP4Config *a,
-                                            const NMIP4Config *b,
-                                            guint32 default_route_metric_penalty);
 gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes);
 void nm_ip4_config_dump (const NMIP4Config *self, const char *detail);
 
@@ -200,10 +194,6 @@ const NMPObject *_nm_ip4_config_best_default_route_find (const NMIP4Config *self
 
 in_addr_t nmtst_ip4_config_get_gateway (NMIP4Config *config);
 
-NMSettingConnectionMdns nm_ip4_config_mdns_get (const NMIP4Config *self);
-void                    nm_ip4_config_mdns_set (NMIP4Config *self,
-                                                NMSettingConnectionMdns mdns);
-
 const NMDedupMultiHeadEntry *nm_ip4_config_lookup_addresses (const NMIP4Config *self);
 void nm_ip4_config_reset_addresses (NMIP4Config *self);
 void nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *address);
@@ -228,13 +218,6 @@ const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Co
 
 void nm_ip4_config_reset_nameservers (NMIP4Config *self);
 void nm_ip4_config_add_nameserver (NMIP4Config *self, guint32 nameserver);
-
-static inline void
-_nm_ip4_config_add_nameserver (NMIP4Config *self, const guint32 *nameserver)
-{
-	nm_ip4_config_add_nameserver (self, *nameserver);
-}
-
 void nm_ip4_config_del_nameserver (NMIP4Config *self, guint i);
 guint nm_ip4_config_get_num_nameservers (const NMIP4Config *self);
 guint32 nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i);
@@ -355,7 +338,7 @@ nm_ip_config_get_addr_family (const NMIPConfig *config)
 	g_return_val_if_reached (AF_UNSPEC);
 }
 
-#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, ...) \
+#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, dflt, ...) \
 	G_STMT_START { \
 		gconstpointer _config = (config); \
 		\
@@ -367,217 +350,58 @@ nm_ip_config_get_addr_family (const NMIPConfig *config)
 		} \
 	} G_STMT_END
 
-#define _NM_IP_CONFIG_DISPATCH_VOID(config, v4_func, v6_func, ...) \
-	G_STMT_START { \
-		gconstpointer _config = (config); \
-		\
-		if (NM_IS_IP4_CONFIG (_config)) { \
-			v4_func ((NMIP4Config *) _config, ##__VA_ARGS__); \
-		} else { \
-			nm_assert (NM_IS_IP6_CONFIG (_config)); \
-			v6_func ((NMIP6Config *) _config, ##__VA_ARGS__); \
-		} \
-	} G_STMT_END
-
-static inline int
-nm_ip_config_get_ifindex (const NMIPConfig *self)
-{
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_ifindex, nm_ip6_config_get_ifindex);
-}
-
-static inline void
-nm_ip_config_hash (const NMIPConfig *self, GChecksum *sum, gboolean dns_only)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_hash, nm_ip6_config_hash, sum, dns_only);
-}
-
-static inline void
-nm_ip_config_add_address (NMIPConfig *self, const NMPlatformIPAddress *address)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_add_address, nm_ip6_config_add_address, (gconstpointer) address);
-}
-
-static inline void
-nm_ip_config_reset_addresses (NMIPConfig *self)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_reset_addresses, nm_ip6_config_reset_addresses);
-}
-
-static inline void
-nm_ip_config_add_route (NMIPConfig *self,
-                        const NMPlatformIPRoute *new,
-                        const NMPObject **out_obj_new)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_add_route, nm_ip6_config_add_route, (gpointer) new, out_obj_new);
-}
-
-static inline void
-nm_ip_config_reset_routes (NMIPConfig *self)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_reset_routes, nm_ip6_config_reset_routes);
-}
-
 static inline int
 nm_ip_config_get_dns_priority (const NMIPConfig *self)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_priority, nm_ip6_config_get_dns_priority);
-}
-
-static inline void
-nm_ip_config_set_dns_priority (NMIPConfig *self, gint priority)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_set_dns_priority, nm_ip6_config_set_dns_priority, priority);
-}
-
-static inline void
-nm_ip_config_add_nameserver (NMIPConfig *self, const NMIPAddr *ns)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, _nm_ip4_config_add_nameserver, nm_ip6_config_add_nameserver, (gconstpointer) ns);
-}
-
-static inline void
-nm_ip_config_reset_nameservers (const NMIPConfig *self)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_reset_nameservers, nm_ip6_config_reset_nameservers);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_priority, nm_ip6_config_get_dns_priority, 0);
 }
 
 static inline guint
 nm_ip_config_get_num_nameservers (const NMIPConfig *self)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_nameservers, nm_ip6_config_get_num_nameservers);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_nameservers, nm_ip6_config_get_num_nameservers, 0);
 }
 
 static inline gconstpointer
 nm_ip_config_get_nameserver (const NMIPConfig *self, guint i)
 {
-	_NM_IP_CONFIG_DISPATCH (self, _nm_ip4_config_get_nameserver, nm_ip6_config_get_nameserver, i);
+	_NM_IP_CONFIG_DISPATCH (self, _nm_ip4_config_get_nameserver, nm_ip6_config_get_nameserver, 0, i);
 }
 
 static inline guint
 nm_ip_config_get_num_domains (const NMIPConfig *self)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_domains, nm_ip6_config_get_num_domains);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_domains, nm_ip6_config_get_num_domains, 0);
 }
 
 static inline const char *
 nm_ip_config_get_domain (const NMIPConfig *self, guint i)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_domain, nm_ip6_config_get_domain, i);
-}
-
-static inline void
-nm_ip_config_reset_searches (const NMIPConfig *self)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_reset_searches, nm_ip6_config_reset_searches);
-}
-
-static inline void
-nm_ip_config_add_search (const NMIPConfig *self, const char *new)
-{
-	_NM_IP_CONFIG_DISPATCH_VOID (self, nm_ip4_config_add_search, nm_ip6_config_add_search, new);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_domain, nm_ip6_config_get_domain, NULL, i);
 }
 
 static inline guint
 nm_ip_config_get_num_searches (const NMIPConfig *self)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_searches, nm_ip6_config_get_num_searches);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_searches, nm_ip6_config_get_num_searches, 0);
 }
 
 static inline const char *
 nm_ip_config_get_search (const NMIPConfig *self, guint i)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_search, nm_ip6_config_get_search, i);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_search, nm_ip6_config_get_search, NULL, i);
 }
 
 static inline guint
 nm_ip_config_get_num_dns_options (const NMIPConfig *self)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_dns_options, nm_ip6_config_get_num_dns_options);
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_dns_options, nm_ip6_config_get_num_dns_options, 0);
 }
 
 static inline const char *
 nm_ip_config_get_dns_option (const NMIPConfig *self, guint i)
 {
-	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_option, nm_ip6_config_get_dns_option, i);
-}
-
-#define _NM_IP_CONFIG_DISPATCH_SET_OP(_return, dst, src, v4_func, v6_func, ...) \
-	G_STMT_START { \
-		gpointer _dst = (dst); \
-		gconstpointer _src = (src); \
-		\
-		if (NM_IS_IP4_CONFIG (_dst)) { \
-			nm_assert (NM_IS_IP4_CONFIG (_src)); \
-			_return v4_func ((NMIP4Config *) _dst, (const NMIP4Config *) _src, ##__VA_ARGS__); \
-		} else { \
-			nm_assert (NM_IS_IP6_CONFIG (_src)); \
-			_return v6_func ((NMIP6Config *) _dst, (const NMIP6Config *) _src, ##__VA_ARGS__); \
-		} \
-	} G_STMT_END
-
-static inline void
-nm_ip_config_intersect (NMIPConfig *dst,
-                        const NMIPConfig *src,
-                        guint32 default_route_metric_penalty)
-{
-	_NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src,
-	                               nm_ip4_config_intersect,
-	                               nm_ip6_config_intersect,
-	                               default_route_metric_penalty);
-}
-
-static inline void
-nm_ip_config_subtract (NMIPConfig *dst,
-                       const NMIPConfig *src,
-                       guint32 default_route_metric_penalty)
-{
-	_NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src,
-	                               nm_ip4_config_subtract,
-	                               nm_ip6_config_subtract,
-	                               default_route_metric_penalty);
-}
-
-static inline void
-nm_ip_config_merge (NMIPConfig *dst,
-                    const NMIPConfig *src,
-                    NMIPConfigMergeFlags merge_flags,
-                    guint32 default_route_metric_penalty)
-{
-	_NM_IP_CONFIG_DISPATCH_SET_OP (, dst, src,
-	                               nm_ip4_config_merge,
-	                               nm_ip6_config_merge,
-	                               merge_flags,
-	                               default_route_metric_penalty);
-}
-
-static inline gboolean
-nm_ip_config_replace (NMIPConfig *dst,
-                      const NMIPConfig *src,
-                      gboolean *relevant_changes)
-{
-	_NM_IP_CONFIG_DISPATCH_SET_OP (return, dst, src,
-	                               nm_ip4_config_replace,
-	                               nm_ip6_config_replace,
-	                               relevant_changes);
-}
-
-static inline NMIPConfig *
-nm_ip_config_intersect_alloc (const NMIPConfig *a,
-                              const NMIPConfig *b,
-                              guint32 default_route_metric_penalty)
-{
-	if (NM_IS_IP4_CONFIG (a)) {
-		nm_assert (NM_IS_IP4_CONFIG (b));
-		return (NMIPConfig *) nm_ip4_config_intersect_alloc ((const NMIP4Config *) a,
-		                                                     (const NMIP4Config *) b,
-		                                                     default_route_metric_penalty);
-	} else {
-		nm_assert (NM_IS_IP6_CONFIG (a));
-		nm_assert (NM_IS_IP6_CONFIG (b));
-		return (NMIPConfig *) nm_ip6_config_intersect_alloc ((const NMIP6Config *) a,
-		                                                     (const NMIP6Config *) b,
-		                                                     default_route_metric_penalty);
-	}
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_option, nm_ip6_config_get_dns_option, NULL, i);
 }
 
 #endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index c76da995..ea3a2029 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -38,7 +38,8 @@
 #include "NetworkManagerUtils.h"
 #include "nm-ip4-config.h"
 #include "ndisc/nm-ndisc.h"
-#include "nm-dbus-object.h"
+
+#include "introspection/org.freedesktop.NetworkManager.IP6Config.h"
 
 /*****************************************************************************/
 
@@ -81,15 +82,15 @@ typedef struct {
 } NMIP6ConfigPrivate;
 
 struct _NMIP6Config {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMIP6ConfigPrivate _priv;
 };
 
 struct _NMIP6ConfigClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG)
 
@@ -365,24 +366,14 @@ _nmtst_ip6_config_addresses_sort (NMIP6Config *self)
 }
 
 NMIP6Config *
-nm_ip6_config_clone (const NMIP6Config *self)
-{
-	NMIP6Config *copy;
-
-	copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (self), -1);
-	nm_ip6_config_replace (copy, self, NULL);
-
-	return copy;
-}
-
-NMIP6Config *
-nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, NMSettingIP6ConfigPrivacy use_temporary)
+nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary)
 {
 	NMIP6Config *self;
 	NMIP6ConfigPrivate *priv;
 	const NMDedupMultiHeadEntry *head_entry;
 	NMDedupMultiIter iter;
 	const NMPObject *plobj = NULL;
+	gboolean has_addresses = FALSE;
 
 	nm_assert (ifindex > 0);
 
@@ -408,6 +399,7 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 			                            NULL,
 			                            NULL))
 				nm_assert_not_reached ();
+			has_addresses = TRUE;
 		}
 		head_entry = nm_ip6_config_lookup_addresses (self);
 		nm_assert (head_entry);
@@ -424,6 +416,23 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 	nmp_cache_iter_for_each (&iter, head_entry, &plobj)
 		_add_route (self, plobj, NULL, NULL);
 
+	/* If the interface has the default route, and has IPv6 addresses, capture
+	 * nameservers from /etc/resolv.conf.
+	 */
+	if (   has_addresses
+	    && priv->best_default_route
+	    && capture_resolv_conf) {
+		gs_free char *rc_contents = NULL;
+
+		if (g_file_get_contents (_PATH_RESCONF, &rc_contents, NULL, NULL)) {
+			if (nm_utils_resolve_conf_parse (AF_INET6,
+			                                 rc_contents,
+			                                 priv->nameservers,
+			                                 priv->dns_options))
+				_notify (self, PROP_NAMESERVERS);
+		}
+	}
+
 	return self;
 }
 
@@ -432,6 +441,7 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self,
                                     guint32 route_table,
                                     guint32 route_metric)
 {
+	const NMIP6ConfigPrivate *priv;
 	const NMPlatformIP6Address *my_addr;
 	const NMPlatformIP6Route *my_route;
 	int ifindex;
@@ -439,6 +449,8 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self,
 
 	g_return_if_fail (NM_IS_IP6_CONFIG (self));
 
+	priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+
 	ifindex = nm_ip6_config_get_ifindex (self);
 	g_return_if_fail (ifindex > 0);
 
@@ -546,7 +558,7 @@ nm_ip6_config_commit (const NMIP6Config *self,
 	                                                    ifindex,
 	                                                    route_table_sync);
 
-	nm_platform_ip6_address_sync (platform, ifindex, addresses, FALSE);
+	nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE);
 
 	if (!nm_platform_ip_route_sync (platform,
 	                                AF_INET6,
@@ -565,6 +577,7 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
                              guint32 route_table,
                              guint32 route_metric)
 {
+	NMIP6ConfigPrivate *priv;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *gateway_str;
 	struct in6_addr gateway_bin;
@@ -575,6 +588,8 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
 
 	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
 
+	priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+
 	naddresses = nm_setting_ip_config_get_num_addresses (setting);
 	nroutes = nm_setting_ip_config_get_num_routes (setting);
 	nnameservers = nm_setting_ip_config_get_num_dns (setting);
@@ -802,6 +817,8 @@ nm_ip6_config_merge (NMIP6Config *dst,
                      NMIPConfigMergeFlags merge_flags,
                      guint32 default_route_metric_penalty)
 {
+	NMIP6ConfigPrivate *dst_priv;
+	const NMIP6ConfigPrivate *src_priv;
 	guint32 i;
 	NMDedupMultiIter ipconf_iter;
 	const NMPlatformIP6Address *address = NULL;
@@ -809,6 +826,9 @@ nm_ip6_config_merge (NMIP6Config *dst,
 	g_return_if_fail (src != NULL);
 	g_return_if_fail (dst != NULL);
 
+	dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
+	src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
+
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
@@ -1050,28 +1070,26 @@ nm_ip6_config_subtract (NMIP6Config *dst,
 	g_object_thaw_notify (G_OBJECT (dst));
 }
 
-static gboolean
-_nm_ip6_config_intersect_helper (NMIP6Config *dst,
-                                 const NMIP6Config *src,
-                                 guint32 default_route_metric_penalty,
-                                 gboolean update_dst)
+void
+nm_ip6_config_intersect (NMIP6Config *dst,
+                         const NMIP6Config *src,
+                         guint32 default_route_metric_penalty)
 {
 	NMIP6ConfigPrivate *dst_priv;
 	const NMIP6ConfigPrivate *src_priv;
 	NMDedupMultiIter ipconf_iter;
 	const NMPlatformIP6Address *a;
 	const NMPlatformIP6Route *r;
-	gboolean changed, result = FALSE;
+	gboolean changed;
 	const NMPObject *new_best_default_route;
 
-	g_return_val_if_fail (src, FALSE);
-	g_return_val_if_fail (dst, FALSE);
+	g_return_if_fail (src);
+	g_return_if_fail (dst);
 
 	dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
 	src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
 
-	if (update_dst)
-		g_object_freeze_notify (G_OBJECT (dst));
+	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
 	changed = FALSE;
@@ -1081,18 +1099,13 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 		                                     NMP_OBJECT_UP_CAST (a)))
 			continue;
 
-		if (!update_dst)
-			return TRUE;
-
 		if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx,
 		                                       ipconf_iter.current) != 1)
 			nm_assert_not_reached ();
 		changed = TRUE;
 	}
-	if (changed) {
+	if (changed)
 		_notify_addresses (dst);
-		result = TRUE;
-	}
 
 	/* ignore nameservers */
 
@@ -1124,9 +1137,6 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 			continue;
 		}
 
-		if (!update_dst)
-			return TRUE;
-
 		if (nm_dedup_multi_index_remove_entry (dst_priv->multi_idx,
 		                                       ipconf_iter.current) != 1)
 			nm_assert_not_reached ();
@@ -1136,67 +1146,14 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst,
 		nm_assert (changed);
 		_notify (dst, PROP_GATEWAY);
 	}
-	if (changed) {
+	if (changed)
 		_notify_routes (dst);
-		result = TRUE;
-	}
 
 	/* ignore domains */
 	/* ignore dns searches */
 	/* ignore dns options */
 
-	if (update_dst)
-		g_object_thaw_notify (G_OBJECT (dst));
-
-	return result;
-}
-
-/**
- * nm_ip6_config_intersect:
- * @dst: a configuration to be updated
- * @src: another configuration
- * @default_route_metric_penalty: the default route metric penalty
- *
- * Computes the intersection between @src and @dst and updates @dst in place
- * with the result.
- */
-void
-nm_ip6_config_intersect (NMIP6Config *dst,
-                         const NMIP6Config *src,
-                         guint32 default_route_metric_penalty)
-{
-	_nm_ip6_config_intersect_helper (dst, src, default_route_metric_penalty, TRUE);
-}
-
-/**
- * nm_ip6_config_intersect_alloc:
- * @a: a configuration
- * @b: another configuration
- * @default_route_metric_penalty: the default route metric penalty
- *
- * Computes the intersection between @a and @b and returns the result in a newly
- * allocated configuration.  As a special case, if @a and @b are identical (with
- * respect to the only properties considered - addresses and routes) the
- * functions returns NULL so that one of existing configuration can be reused
- * without allocation.
- *
- * Returns: the intersection between @a and @b, or %NULL if the result is equal
- * to @a and @b.
- */
-NMIP6Config *
-nm_ip6_config_intersect_alloc (const NMIP6Config *a,
-                               const NMIP6Config *b,
-                               guint32 default_route_metric_penalty)
-{
-	NMIP6Config *a_copy;
-
-	if (_nm_ip6_config_intersect_helper ((NMIP6Config *) a, b,
-	                                     default_route_metric_penalty, FALSE)) {
-		a_copy = nm_ip6_config_clone (a);
-		_nm_ip6_config_intersect_helper (a_copy, b, default_route_metric_penalty, TRUE);
-		return a_copy;
-	} else
-		return NULL;
+	g_object_thaw_notify (G_OBJECT (dst));
 }
 
 /**
@@ -1466,7 +1423,7 @@ nm_ip6_config_dump (const NMIP6Config *self, const char *detail)
 
 	g_message ("--------- NMIP6Config %p (%s)", self, detail);
 
-	str = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+	str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
 	if (str)
 		g_message ("   path: %s", str);
 
@@ -1672,43 +1629,22 @@ nm_ip6_config_lookup_address (const NMIP6Config *self,
 }
 
 const NMPlatformIP6Address *
-nm_ip6_config_find_first_address (const NMIP6Config *self,
-                                  NMPlatformMatchFlags match_flag)
+nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal)
 {
+	const NMIP6ConfigPrivate *priv;
 	const NMPlatformIP6Address *addr;
 	NMDedupMultiIter iter;
 
 	g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL);
 
-	nm_assert (!NM_FLAGS_ANY (match_flag, ~(  NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY
-	                                        | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)));
+	priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 
-	nm_assert (NM_FLAGS_ANY (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY));
-	nm_assert (NM_FLAGS_ANY (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY));
+	linklocal = !!linklocal;
 
 	nm_ip_config_iter_ip6_address_for_each (&iter, self, &addr) {
-
-		if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) {
-			if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL))
-				continue;
-		} else {
-			if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL))
-				continue;
-		}
-
-		if (NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED)) {
-			if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED))
-				continue;
-		} else if (   NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TENTATIVE)
-		           && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
-			if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE))
-				continue;
-		} else {
-			if (!NM_FLAGS_HAS (match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL))
-				continue;
-		}
-
-		return addr;
+		if (   ((!!IN6_IS_ADDR_LINKLOCAL (&addr->address)) == linklocal)
+		    && !(addr->n_ifa_flags & IFA_F_TENTATIVE))
+			return addr;
 	}
 
 	return NULL;
@@ -2467,9 +2403,9 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
 {
 	GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1);
 	GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1);
-	guchar a_data[20], b_data[20];
-	gsize a_len = sizeof (a_data);
-	gsize b_len = sizeof (b_data);
+	gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
+	gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
+	guchar a_data[a_len], b_data[b_len];
 	gboolean equal;
 
 	if (a)
@@ -2480,8 +2416,7 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
 	g_checksum_get_digest (a_checksum, a_data, &a_len);
 	g_checksum_get_digest (b_checksum, b_data, &b_len);
 
-	nm_assert (a_len == sizeof (a_data));
-	nm_assert (b_len == sizeof (b_data));
+	g_assert (a_len == b_len);
 	equal = !memcmp (a_data, b_data, a_len);
 
 	g_checksum_free (a_checksum);
@@ -2777,36 +2712,13 @@ finalize (GObject *object)
 	nm_dedup_multi_index_unref (priv->multi_idx);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_ip6_config = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_IP6_CONFIG,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Addresses",   "a(ayuay)",  NM_IP6_CONFIG_ADDRESSES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AddressData", "aa{sv}",    NM_IP6_CONFIG_ADDRESS_DATA),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Gateway",     "s",         NM_IP6_CONFIG_GATEWAY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Routes",      "a(ayuayu)", NM_IP6_CONFIG_ROUTES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("RouteData",   "aa{sv}",    NM_IP6_CONFIG_ROUTE_DATA),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Nameservers", "aay",       NM_IP6_CONFIG_NAMESERVERS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Domains",     "as",        NM_IP6_CONFIG_DOMAINS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Searches",    "as",        NM_IP6_CONFIG_SEARCHES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsOptions",  "as",        NM_IP6_CONFIG_DNS_OPTIONS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("DnsPriority", "i",         NM_IP6_CONFIG_DNS_PRIORITY),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (config_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP6Config");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_ip6_config);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/IP6Config");
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
@@ -2880,4 +2792,8 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
 	                      G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
+	                                        NMDBUS_TYPE_IP6_CONFIG_SKELETON,
+	                                        NULL);
 }
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index cd01ed02..2fb8b8a4 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -23,6 +23,7 @@
 
 #include <netinet/in.h>
 
+#include "nm-exported-object.h"
 #include "nm-setting-ip6-config.h"
 
 #include "nm-utils/nm-dedup-multi.h"
@@ -100,13 +101,12 @@ GType nm_ip6_config_get_type (void);
 NMIP6Config * nm_ip6_config_new (struct _NMDedupMultiIndex *multi_idx, int ifindex);
 NMIP6Config * nm_ip6_config_new_cloned (const NMIP6Config *src);
 
-NMIP6Config *nm_ip6_config_clone (const NMIP6Config *self);
 int nm_ip6_config_get_ifindex (const NMIP6Config *self);
 
 struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self);
 
 NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex,
-                                    NMSettingIP6ConfigPrivacy use_temporary);
+                                    gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary);
 
 void nm_ip6_config_add_dependent_routes (NMIP6Config *self,
                                          guint32 route_table,
@@ -133,9 +133,6 @@ void nm_ip6_config_subtract (NMIP6Config *dst,
 void nm_ip6_config_intersect (NMIP6Config *dst,
                               const NMIP6Config *src,
                               guint32 default_route_metric_penalty);
-NMIP6Config *nm_ip6_config_intersect_alloc (const NMIP6Config *a,
-                                            const NMIP6Config *b,
-                                            guint32 default_route_metric_penalty);
 gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes);
 void nm_ip6_config_dump (const NMIP6Config *self, const char *detail);
 
@@ -149,8 +146,7 @@ void _nmtst_ip6_config_del_address (NMIP6Config *self, guint i);
 guint nm_ip6_config_get_num_addresses (const NMIP6Config *self);
 const NMPlatformIP6Address *nm_ip6_config_get_first_address (const NMIP6Config *self);
 const NMPlatformIP6Address *_nmtst_ip6_config_get_address (const NMIP6Config *self, guint i);
-const NMPlatformIP6Address *nm_ip6_config_find_first_address (const NMIP6Config *self,
-                                                              NMPlatformMatchFlags match_flag);
+const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *self, gboolean linklocal);
 gboolean nm_ip6_config_address_exists (const NMIP6Config *self, const NMPlatformIP6Address *address);
 const NMPlatformIP6Address *nm_ip6_config_lookup_address (const NMIP6Config *self,
                                                           const struct in6_addr *addr);
diff --git a/src/nm-logging.c b/src/nm-logging.c
index 11e05c31..f19f0de1 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -663,7 +663,7 @@ _nm_log_impl (const char *file,
 				NMLogDomain dom = dom_all & _nm_logging_enabled_state[level];
 
 				for (diter = &global.domain_desc[0]; diter->name; diter++) {
-					if (!NM_FLAGS_ANY (dom_all, diter->num))
+					if (!NM_FLAGS_HAS (dom_all, diter->num))
 						continue;
 
 					/* construct a list of all domains (not only the enabled ones).
@@ -681,7 +681,7 @@ _nm_log_impl (const char *file,
 						g_string_append (s_domain_all, diter->name);
 					}
 
-					if (NM_FLAGS_ANY (dom, diter->num)) {
+					if (NM_FLAGS_HAS (dom, diter->num)) {
 						if (i_domain > 0) {
 							/* SYSLOG_FACILITY is specified multiple times for each domain that is actually enabled. */
 							_iovec_set_format_a (iov++, _MAX_LEN (30, diter->name), "SYSLOG_FACILITY=%s", diter->name);
@@ -768,9 +768,6 @@ nm_log_handler (const gchar *log_domain,
 		break;
 	}
 
-	if (global.debug_stderr)
-		g_printerr ("%s%s\n", global.prefix, message ?: "");
-
 	switch (global.log_backend) {
 #if SYSTEMD_JOURNAL
 	case LOG_BACKEND_JOURNAL:
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 36ba6a5a..8fcbc8cc 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -83,16 +83,6 @@ typedef enum  { /*< skip >*/
 	LOGD_IP         = LOGD_IP4 | LOGD_IP6,
 } NMLogDomain;
 
-static inline NMLogDomain
-LOGD_IP_from_af (int addr_family)
-{
-	switch (addr_family) {
-	case AF_INET:  return LOGD_IP4;
-	case AF_INET6: return LOGD_IP6;
-	}
-	g_return_val_if_reached (LOGD_NONE);
-}
-
 /* Log levels */
 typedef enum  { /*< skip >*/
 	LOGL_TRACE,
@@ -133,7 +123,7 @@ typedef enum  { /*< skip >*/
                       ""__VA_ARGS__); \
     } G_STMT_END
 
-/* nm_log() only evaluates its argument list after checking
+/* nm_log() only evaluates it's argument list after checking
  * whether logging for the given level/domain is enabled.  */
 #define nm_log(level, domain, ifname, con_uuid, ...) \
     G_STMT_START { \
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 1ccfad8e..f3bbebd0 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -30,7 +30,7 @@
 #include <unistd.h>
 
 #include "nm-common-macros.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "vpn/nm-vpn-manager.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-generic.h"
@@ -56,10 +56,12 @@
 #include "nm-dbus-compat.h"
 #include "nm-checkpoint.h"
 #include "nm-checkpoint-manager.h"
-#include "nm-dbus-object.h"
 #include "nm-dispatcher.h"
 #include "NetworkManagerUtils.h"
 
+#include "introspection/org.freedesktop.NetworkManager.h"
+#include "introspection/org.freedesktop.NetworkManager.Device.h"
+
 /*****************************************************************************/
 
 typedef struct {
@@ -78,6 +80,8 @@ enum {
 	INTERNAL_DEVICE_ADDED,
 	DEVICE_REMOVED,
 	INTERNAL_DEVICE_REMOVED,
+	STATE_CHANGED,
+	CHECK_PERMISSIONS,
 	ACTIVE_CONNECTION_ADDED,
 	ACTIVE_CONNECTION_REMOVED,
 	CONFIGURE_QUIT,
@@ -110,7 +114,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 	PROP_METERED,
 	PROP_GLOBAL_DNS_CONFIGURATION,
 	PROP_ALL_DEVICES,
-	PROP_CHECKPOINTS,
 
 	/* Not exported */
 	PROP_SLEEPING,
@@ -128,14 +131,16 @@ typedef struct {
 	NMActiveConnection *activating_connection;
 	NMMetered metered;
 
-	CList devices_lst_head;
-
+	GSList *devices;
 	NMState state;
 	NMConfig *config;
-	NMConnectivity *concheck_mgr;
+	NMConnectivityState connectivity_state;
+
 	NMPolicy *policy;
+
 	NMHostnameManager *hostname_manager;
 
+	NMBusManager  *dbus_mgr;
 	struct {
 		GDBusConnection *connection;
 		guint            id;
@@ -168,30 +173,26 @@ typedef struct {
 
 	guint devices_inited_id;
 
-	NMConnectivityState connectivity_state;
-
 	bool startup:1;
 	bool devices_inited:1;
 
 	bool sleeping:1;
 	bool net_enabled:1;
 
-	unsigned connectivity_check_enabled_last:2;
-
 	guint delete_volatile_connection_idle_id;
 	CList delete_volatile_connection_lst_head;
 } NMManagerPrivate;
 
 struct _NMManager {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMManagerPrivate _priv;
 };
 
 typedef struct {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 } NMManagerClass;
 
-G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMManager, NM_IS_MANAGER)
 
@@ -268,20 +269,9 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
 /*****************************************************************************/
 
-static const NMDBusInterfaceInfoExtended interface_info_manager;
-static const GDBusSignalInfo signal_info_check_permissions;
-static const GDBusSignalInfo signal_info_state_changed;
-static const GDBusSignalInfo signal_info_device_added;
-static const GDBusSignalInfo signal_info_device_removed;
-
 static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
 
-static void _emit_device_added_removed (NMManager *self,
-                                        NMDevice *device,
-                                        gboolean is_added);
-
 static NMActiveConnection *_new_active_connection (NMManager *self,
-                                                   gboolean is_vpn,
                                                    NMConnection *connection,
                                                    NMConnection *applied,
                                                    const char *specific_object,
@@ -329,8 +319,6 @@ static NMActiveConnection *active_connection_find_first (NMManager *self,
                                                          const char *uuid,
                                                          NMActiveConnectionState max_state);
 
-static NMConnectivity *concheck_get_mgr (NMManager *self);
-
 /*****************************************************************************/
 
 static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark)
@@ -339,73 +327,6 @@ static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
 
 /*****************************************************************************/
 
-static gboolean
-_connection_is_vpn (NMConnection *connection)
-{
-	const char *type;
-
-	type = nm_connection_get_connection_type (connection);
-	if (type)
-		return nm_streq (type, NM_SETTING_VPN_SETTING_NAME);
-
-	/* we have an incomplete (invalid) connection at hand. That can only
-	 * happen during AddAndActivate. Determine whether it's VPN type based
-	 * on the existance of a [vpn] section. */
-	return !!nm_connection_get_setting_vpn (connection);
-}
-
-/*****************************************************************************/
-
-static gboolean
-concheck_enabled (NMManager *self, gboolean *out_changed)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	guint check_enabled;
-
-	check_enabled = nm_connectivity_check_enabled (concheck_get_mgr (self))
-	                ? 1 : 2;
-	if (priv->connectivity_check_enabled_last == check_enabled)
-		NM_SET_OUT (out_changed, FALSE);
-	else {
-		NM_SET_OUT (out_changed, TRUE);
-		priv->connectivity_check_enabled_last = check_enabled;
-	}
-	return check_enabled == 1;
-}
-
-static void
-concheck_config_changed_cb (NMConnectivity *connectivity,
-                            NMManager *self)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-	gboolean changed;
-
-	concheck_enabled (self, &changed);
-	if (changed)
-		_notify (self, PROP_CONNECTIVITY_CHECK_ENABLED);
-
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
-		nm_device_check_connectivity_update_interval (device);
-}
-
-static NMConnectivity *
-concheck_get_mgr (NMManager *self)
-{
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-
-	if (G_UNLIKELY (!priv->concheck_mgr)) {
-		priv->concheck_mgr = g_object_ref (nm_connectivity_get ());
-		g_signal_connect (priv->concheck_mgr,
-		                  NM_CONNECTIVITY_CONFIG_CHANGED,
-		                  G_CALLBACK (concheck_config_changed_cb),
-		                  self);
-	}
-	return priv->concheck_mgr;
-}
-
-/*****************************************************************************/
-
 typedef struct {
 	int ifindex;
 	guint32 aspired_metric;
@@ -514,10 +435,10 @@ _device_route_metric_get (NMManager *self,
 				 * hence we skip it. */
 				continue;
 			}
-			if (!g_hash_table_add (priv->device_route_metrics,
-			                       _device_route_metric_data_new (device_state->ifindex,
-			                                                      device_state->route_metric_default_aspired,
-			                                                      device_state->route_metric_default_effective)))
+			if (!nm_g_hash_table_add (priv->device_route_metrics,
+			                          _device_route_metric_data_new (device_state->ifindex,
+			                                                         device_state->route_metric_default_aspired,
+			                                                         device_state->route_metric_default_effective)))
 				nm_assert_not_reached ();
 		}
 	}
@@ -610,7 +531,7 @@ again:
 	_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d reserves metric %u (aspired %u)",
 	       data->ifindex, data->effective_metric, data->aspired_metric);
 
-	if (!g_hash_table_add (priv->device_route_metrics, data))
+	if (!nm_g_hash_table_add (priv->device_route_metrics, data))
 		nm_assert_not_reached ();
 
 out:
@@ -657,7 +578,7 @@ _delete_volatile_connection_do (NMManager *self,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
 		return;
 	if (active_connection_find_first (self,
 	                                  connection,
@@ -683,7 +604,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 	nm_assert (NM_IS_ACTIVE_CONNECTION (active));
 	nm_assert (c_list_contains (&priv->active_connections_lst_head, &active->active_connections_lst));
 
-	notify = nm_dbus_object_is_exported (NM_DBUS_OBJECT (active));
+	notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active));
 
 	c_list_unlink (&active->active_connections_lst);
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
@@ -693,7 +614,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 
 	connection = nm_g_object_ref (nm_active_connection_get_settings_connection (active));
 
-	nm_dbus_object_clear_and_unexport (&active);
+	nm_exported_object_clear_and_unexport (&active);
 
 	if (connection)
 		_delete_volatile_connection_do (self, connection);
@@ -789,8 +710,8 @@ active_connection_add (NMManager *self,
 	                  G_CALLBACK (active_connection_default_changed),
 	                  self);
 
-	if (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (active)))
-		nm_dbus_object_export (NM_DBUS_OBJECT (active));
+	if (!nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active)))
+		nm_exported_object_export (NM_EXPORTED_OBJECT (active));
 
 	g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active);
 
@@ -854,7 +775,7 @@ _get_activatable_connections_filter (NMSettings *settings,
                                      gpointer user_data)
 {
 	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+	                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
 		return FALSE;
 	return !active_connection_find_first (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
 }
@@ -872,20 +793,18 @@ nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboo
 }
 
 static NMActiveConnection *
-active_connection_get_by_path (NMManager *self, const char *path)
+active_connection_get_by_path (NMManager *manager, const char *path)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	NMActiveConnection *ac;
 
-	ac = (NMActiveConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                           path);
-	if (   !ac
-	    || !NM_IS_ACTIVE_CONNECTION (ac)
-	    || c_list_is_empty (&ac->active_connections_lst))
-		return NULL;
+	nm_assert (path);
 
-	nm_assert (c_list_contains (&priv->active_connections_lst_head, &ac->active_connections_lst));
-	return ac;
+	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
+		if (nm_streq0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac))))
+			return ac;
+	}
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -893,14 +812,8 @@ active_connection_get_by_path (NMManager *self, const char *path)
 static void
 _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self)
 {
-	g_object_freeze_notify (G_OBJECT (self));
-
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
 		_notify (self, PROP_GLOBAL_DNS_CONFIGURATION);
-	if ((!nm_config_data_get_connectivity_uri (config_data)) != (!nm_config_data_get_connectivity_uri (old_data)))
-		_notify (self, PROP_CONNECTIVITY_CHECK_AVAILABLE);
-
-	g_object_thaw_notify (G_OBJECT (self));
 }
 
 static void
@@ -971,31 +884,28 @@ _reload_auth_cb (NMAuthChain *chain,
 	g_dbus_method_invocation_return_value (context, NULL);
 
 out:
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_reload (NMDBusObject *obj,
-                     const NMDBusInterfaceInfoExtended *interface_info,
-                     const NMDBusMethodInfoExtended *method_info,
-                     GDBusConnection *connection,
-                     const char *sender,
-                     GDBusMethodInvocation *invocation,
-                     GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_reload (NMManager *self,
+                     GDBusMethodInvocation *context,
+                     guint32 flags)
+{
+	NMManagerPrivate *priv;
 	NMAuthChain *chain;
-	guint32 flags;
+	GError *error = NULL;
 
-	g_variant_get (parameters, "(u)", &flags);
+	g_return_if_fail (NM_IS_MANAGER (self));
 
-	chain = nm_auth_chain_new_context (invocation, _reload_auth_cb, self);
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	chain = nm_auth_chain_new_context (context, _reload_auth_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -1007,31 +917,27 @@ impl_manager_reload (NMDBusObject *obj,
 /*****************************************************************************/
 
 NMDevice *
-nm_manager_get_device_by_path (NMManager *self, const char *path)
+nm_manager_get_device_by_path (NMManager *manager, const char *path)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
-
-	g_return_val_if_fail (path, NULL);
+	GSList *iter;
 
-	device = (NMDevice *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                     path);
-	if (   !device
-	    || !NM_IS_DEVICE (device)
-	    || c_list_is_empty (&device->devices_lst))
-		return NULL;
+	g_return_val_if_fail (path != NULL, NULL);
 
-	nm_assert (c_list_contains (&priv->devices_lst_head, &device->devices_lst));
-	return device;
+	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+		if (!strcmp (nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)), path))
+			return NM_DEVICE (iter->data);
+	}
+	return NULL;
 }
 
 NMDevice *
-nm_manager_get_device_by_ifindex (NMManager *self, int ifindex)
+nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
+
+	for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+		NMDevice *device = NM_DEVICE (iter->data);
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		if (nm_device_get_ifindex (device) == ifindex)
 			return device;
 	}
@@ -1040,24 +946,19 @@ nm_manager_get_device_by_ifindex (NMManager *self, int ifindex)
 }
 
 static NMDevice *
-find_device_by_permanent_hw_addr (NMManager *self, const char *hwaddr)
+find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
 	const char *device_addr;
-	guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
-	gsize hwaddr_len;
 
 	g_return_val_if_fail (hwaddr != NULL, NULL);
 
-	if (!_nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, sizeof (hwaddr_bin), &hwaddr_len))
-		return NULL;
-
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		device_addr = nm_device_get_permanent_hw_address (device);
-		if (   device_addr
-		    && nm_utils_hwaddr_matches (hwaddr_bin, hwaddr_len, device_addr, -1))
-			return device;
+	if (nm_utils_hwaddr_valid (hwaddr, -1)) {
+		for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
+			device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data));
+			if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1))
+				return NM_DEVICE (iter->data);
+		}
 	}
 	return NULL;
 }
@@ -1065,15 +966,16 @@ find_device_by_permanent_hw_addr (NMManager *self, const char *hwaddr)
 static NMDevice *
 find_device_by_ip_iface (NMManager *self, const gchar *iface)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
+
+	g_return_val_if_fail (iface != NULL, NULL);
 
-	g_return_val_if_fail (iface, NULL);
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = iter->data;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (   nm_device_is_real (device)
-		    && nm_streq0 (nm_device_get_ip_iface (device), iface))
-			return device;
+		if (   nm_device_is_real (candidate)
+		    && g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0)
+			return candidate;
 	}
 	return NULL;
 }
@@ -1101,11 +1003,12 @@ find_device_by_iface (NMManager *self,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *fallback = NULL;
-	NMDevice *candidate;
+	GSList *iter;
 
 	g_return_val_if_fail (iface != NULL, NULL);
 
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
 
 		if (strcmp (nm_device_get_iface (candidate), iface))
 			continue;
@@ -1160,6 +1063,22 @@ _nm_state_to_string (NMState state)
 	}
 }
 
+static void
+set_state (NMManager *self, NMState state)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (priv->state == state)
+		return;
+
+	priv->state = state;
+
+	_LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state));
+
+	_notify (self, PROP_STATE);
+	g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state);
+}
+
 static NMState
 find_best_device_state (NMManager *manager)
 {
@@ -1230,38 +1149,26 @@ nm_manager_update_metered (NMManager *self)
 }
 
 static void
-nm_manager_update_state (NMManager *self)
+nm_manager_update_state (NMManager *manager)
 {
 	NMManagerPrivate *priv;
 	NMState new_state = NM_STATE_DISCONNECTED;
 
-	g_return_if_fail (NM_IS_MANAGER (self));
+	g_return_if_fail (NM_IS_MANAGER (manager));
 
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	priv = NM_MANAGER_GET_PRIVATE (manager);
 
-	if (manager_sleeping (self))
+	if (manager_sleeping (manager))
 		new_state = NM_STATE_ASLEEP;
 	else
-		new_state = find_best_device_state (self);
+		new_state = find_best_device_state (manager);
 
 	if (   new_state >= NM_STATE_CONNECTED_LOCAL
 	    && priv->connectivity_state == NM_CONNECTIVITY_FULL) {
 		new_state = NM_STATE_CONNECTED_GLOBAL;
 	}
 
-	if (priv->state == new_state)
-		return;
-
-	priv->state = new_state;
-
-	_LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (new_state));
-
-	_notify (self, PROP_STATE);
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_manager,
-	                            &signal_info_state_changed,
-	                            "(u)",
-	                            (guint32) priv->state);
+	set_state (manager, new_state);
 }
 
 static void
@@ -1303,7 +1210,7 @@ static void
 check_if_startup_complete (NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
 
 	if (!priv->startup)
 		return;
@@ -1316,10 +1223,12 @@ check_if_startup_complete (NMManager *self)
 		return;
 	}
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_has_pending_action (device)) {
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *dev = iter->data;
+
+		if (nm_device_has_pending_action (dev)) {
 			_LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s",
-			       nm_device_get_iface (device));
+			       nm_device_get_iface (dev));
 			return;
 		}
 	}
@@ -1331,8 +1240,8 @@ check_if_startup_complete (NMManager *self)
 	/* we no longer care about these signals. Startup-complete only
 	 * happens once. */
 	g_signal_handlers_disconnect_by_func (priv->settings, G_CALLBACK (settings_startup_complete_changed), self);
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		g_signal_handlers_disconnect_by_func (device,
+	for (iter = priv->devices; iter; iter = iter->next) {
+		g_signal_handlers_disconnect_by_func (iter->data,
 		                                      G_CALLBACK (device_has_pending_action_changed),
 		                                      self);
 	}
@@ -1364,18 +1273,18 @@ _parent_notify_changed (NMManager *self,
                         NMDevice *device,
                         gboolean device_removed)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *candidate;
+	GSList *iter;
 
 	nm_assert (NM_IS_DEVICE (device));
+	nm_assert (NM_IS_MANAGER (self));
 
-again:
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_parent_notify_changed (candidate, device, device_removed)) {
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; ) {
+		if (nm_device_parent_notify_changed (iter->data, device, device_removed)) {
 			/* in the unlikely event that this changes anything, we start iterating
 			 * again, to be sure that the device list is up-to-date. */
-			goto again;
-		}
+			iter = NM_MANAGER_GET_PRIVATE (self)->devices;
+		} else
+			iter = iter->next;
 	}
 }
 
@@ -1415,8 +1324,7 @@ remove_device (NMManager *self,
 	g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
 
 	nm_settings_device_removed (priv->settings, device, quitting);
-
-	c_list_unlink (&device->devices_lst);
+	priv->devices = g_slist_remove (priv->devices, device);
 
 	_parent_notify_changed (self, device, TRUE);
 
@@ -1434,7 +1342,8 @@ remove_device (NMManager *self,
 		 * Control that by passing @unconfigure_ip_config.  */
 		nm_device_removed (device, unconfigure_ip_config);
 
-		_emit_device_added_removed (self, device, FALSE);
+		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+		_notify (self, PROP_DEVICES);
 	} else {
 		/* unrealize() does not release a slave device from master and
 		 * clear IP configurations, do it here */
@@ -1444,7 +1353,7 @@ remove_device (NMManager *self,
 	g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device);
 	_notify (self, PROP_ALL_DEVICES);
 
-	nm_dbus_object_clear_and_unexport (&device);
+	nm_exported_object_clear_and_unexport (&device);
 
 	check_if_startup_complete (self);
 }
@@ -1473,7 +1382,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	const char *parent_name = NULL;
 	NMSettingsConnection *parent_connection;
 	NMDevice *parent, *first_compatible = NULL;
-	NMDevice *candidate;
+	GSList *iter;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
@@ -1506,7 +1415,9 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 	/* Check if the parent connection is currently activated or is comaptible
 	 * with some known device.
 	 */
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
+
 		/* Unmanaged devices are not compatible with any connection */
 		if (!nm_device_get_managed (candidate, FALSE))
 			continue;
@@ -1622,15 +1533,18 @@ NMDevice *
 nm_manager_get_device (NMManager *self, const char *ifname, NMDeviceType device_type)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
+	NMDevice *d;
 
 	g_return_val_if_fail (ifname, NULL);
 	g_return_val_if_fail (device_type != NM_DEVICE_TYPE_UNKNOWN, NULL);
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (   nm_device_get_device_type (device) == device_type
-		    && nm_streq0 (nm_device_get_iface (device), ifname))
-			return device;
+	for (iter = priv->devices; iter; iter = iter->next) {
+		d = iter->data;
+
+		if (   nm_device_get_device_type (d) == device_type
+		    && nm_streq0 (nm_device_get_iface (d), ifname))
+			return d;
 	}
 
 	return NULL;
@@ -1666,9 +1580,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	NMDeviceFactory *factory;
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i;
+	GSList *iter;
 	gs_free char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
-	NMDevice *dev_candidate;
 	GError *error = NULL;
 	NMLogLevel log_level;
 
@@ -1684,15 +1598,17 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	}
 
 	/* See if there's a device that is already compatible with this connection */
-	c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_check_connection_compatible (dev_candidate, connection)) {
-			if (nm_device_is_real (dev_candidate)) {
+	for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *candidate = iter->data;
+
+		if (nm_device_check_connection_compatible (candidate, connection)) {
+			if (nm_device_is_real (candidate)) {
 				_LOG3D (LOGD_DEVICE, connection, "already created virtual interface name %s",
 				       iface);
 				return NULL;
 			}
 
-			device = dev_candidate;
+			device = candidate;
 			break;
 		}
 	}
@@ -1888,7 +1804,7 @@ connection_flags_changed (NMSettings *settings,
 	DeleteVolatileConnectionData *data;
 
 	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
 		return;
 
 	if (active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) {
@@ -1913,10 +1829,10 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	const GSList *iter;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
-		nm_device_set_unmanaged_by_user_settings (device);
+	for (iter = priv->devices; iter; iter = g_slist_next (iter))
+		nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data));
 }
 
 static void
@@ -1962,7 +1878,7 @@ manager_update_radio_enabled (NMManager *self,
                               gboolean enabled)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
 
 	/* Do nothing for radio types not yet implemented */
 	if (!rstate->prop)
@@ -1975,7 +1891,9 @@ manager_update_radio_enabled (NMManager *self,
 		return;
 
 	/* enable/disable wireless devices as required */
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *device = NM_DEVICE (iter->data);
+
 		if (nm_device_get_rfkill_type (device) == rstate->rtype) {
 			_LOG2D (LOGD_RFKILL, device, "rfkill: setting radio %s", enabled ? "enabled" : "disabled");
 			nm_device_set_enabled (device, enabled);
@@ -2118,7 +2036,7 @@ device_auth_done_cb (NMAuthChain *chain,
 	          nm_auth_chain_get_data (chain, "user-data"));
 
 	g_clear_error (&error);
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
@@ -2134,6 +2052,7 @@ device_auth_request_cb (NMDevice *device,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
+	char *error_desc = NULL;
 	NMAuthChain *chain;
 
 	/* Validate the caller */
@@ -2146,13 +2065,15 @@ device_auth_request_cb (NMDevice *device,
 	}
 
 	/* Ensure the subject has permissions for this connection */
-	if (   connection
-	    && !nm_auth_is_subject_in_acl_set_error (connection,
-	                                             subject,
-	                                             NM_MANAGER_ERROR,
-	                                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-	                                             &error))
+	if (connection && !nm_auth_is_subject_in_acl (connection,
+	                                              subject,
+	                                              &error_desc)) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
 		goto done;
+	}
 
 	/* Validate the request */
 	chain = nm_auth_chain_new_subject (subject, context, device_auth_done_cb, self);
@@ -2349,8 +2270,8 @@ get_existing_connection (NMManager *self,
 	}
 
 	nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added),
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
+	                                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 	                                  TRUE);
 	NM_SET_OUT (out_generated, TRUE);
 	return added;
@@ -2414,20 +2335,15 @@ recheck_assume_connection (NMManager *self,
 		GError *error = NULL;
 
 		subject = nm_auth_subject_new_internal ();
-		active = _new_active_connection (self,
-		                                 FALSE,
-		                                 NM_CONNECTION (connection),
-		                                 NULL,
-		                                 NULL,
-		                                 device,
-		                                 subject,
+		active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL,
+		                                 device, subject,
 		                                 generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME,
 		                                 NM_ACTIVATION_REASON_AUTOCONNECT,
 		                                 &error);
 
 		if (!active) {
 			_LOGW (LOGD_DEVICE, "assume: assumed connection %s failed to activate: %s",
-			       nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
+			       nm_connection_get_path (NM_CONNECTION (connection)),
 			       error->message);
 			g_error_free (error);
 
@@ -2478,17 +2394,18 @@ device_ip_iface_changed (NMDevice *device,
                          GParamSpec *pspec,
                          NMManager *self)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMDeviceType device_type = nm_device_get_device_type (device);
-	NMDevice *candidate;
+	GSList *iter;
 
 	/* Remove NMDevice objects that are actually child devices of others,
 	 * when the other device finally knows its IP interface name.  For example,
 	 * remove the PPP interface that's a child of a WWAN device, since it's
 	 * not really a standalone NMDevice.
 	 */
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+
 		if (   candidate != device
 		    && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0
 		    && nm_device_get_device_type (candidate) == device_type
@@ -2510,56 +2427,35 @@ device_iface_changed (NMDevice *device,
 	retry_connections_for_parent_device (self, device);
 }
 
-static void
-_emit_device_added_removed (NMManager *self,
-                            NMDevice *device,
-                            gboolean is_added)
-{
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_manager,
-	                            is_added
-	                              ? &signal_info_device_added
-	                              : &signal_info_device_removed,
-	                            "(o)",
-	                            nm_dbus_object_get_path (NM_DBUS_OBJECT (device)));
-	g_signal_emit (self,
-	               signals[is_added ? DEVICE_ADDED : DEVICE_REMOVED],
-	               0,
-	               device);
-	_notify (self, PROP_DEVICES);
-}
 
 static void
 device_realized (NMDevice *device,
                  GParamSpec *pspec,
                  NMManager *self)
 {
-	_emit_device_added_removed (self, device, nm_device_is_real (device));
+	gboolean real = nm_device_is_real (device);
+
+	/* Emit D-Bus signals */
+	g_signal_emit (self, signals[real ? DEVICE_ADDED : DEVICE_REMOVED], 0, device);
+	_notify (self, PROP_DEVICES);
 }
 
+#if WITH_CONCHECK
 static void
 device_connectivity_changed (NMDevice *device,
+                             GParamSpec *pspec,
                              NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN;
 	NMConnectivityState state;
-	NMDevice *dev;
+	const GSList *devices;
 
-	best_state = nm_device_get_connectivity_state (device);
-	if (best_state < NM_CONNECTIVITY_FULL) {
-		c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) {
-			state = nm_device_get_connectivity_state (dev);
-			if (state <= best_state)
-				continue;
+	for (devices = priv->devices; devices; devices = devices->next) {
+		state = nm_device_get_connectivity_state (NM_DEVICE (devices->data));
+		if (state > best_state)
 			best_state = state;
-			if (best_state >= NM_CONNECTIVITY_FULL) {
-				/* it doesn't get better than this. */
-				break;
-			}
-		}
 	}
-	nm_assert (best_state <= NM_CONNECTIVITY_FULL);
 
 	if (best_state != priv->connectivity_state) {
 		priv->connectivity_state = best_state;
@@ -2572,6 +2468,7 @@ device_connectivity_changed (NMDevice *device,
 		nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL);
 	}
 }
+#endif
 
 static void
 _device_realize_finish (NMManager *self,
@@ -2617,7 +2514,6 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	GSList *iter, *remove = NULL;
 	int ifindex;
 	const char *dbus_path;
-	NMDevice *candidate;
 
 	/* No duplicates */
 	ifindex = nm_device_get_ifindex (device);
@@ -2634,20 +2530,18 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	 * FIXME: use parent/child device relationships instead of removing
 	 * the child NMDevice entirely
 	 */
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
-		if (   nm_device_is_real (candidate)
-		    && (iface = nm_device_get_ip_iface (candidate))
-		    && nm_device_owns_iface (device, iface))
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
+
+		iface = nm_device_get_ip_iface (candidate);
+		if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface))
 			remove = g_slist_prepend (remove, candidate);
 	}
 	for (iter = remove; iter; iter = iter->next)
 		remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE);
 	g_slist_free (remove);
 
-	g_object_ref (device);
-
-	nm_assert (c_list_is_empty (&device->devices_lst));
-	c_list_link_tail (&priv->devices_lst_head, &device->devices_lst);
+	priv->devices = g_slist_append (priv->devices, g_object_ref (device));
 
 	g_signal_connect (device, NM_DEVICE_STATE_CHANGED,
 	                  G_CALLBACK (manager_device_state_changed),
@@ -2681,9 +2575,11 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                  G_CALLBACK (device_realized),
 	                  self);
 
-	g_signal_connect (device, NM_DEVICE_CONNECTIVITY_CHANGED,
+#if WITH_CONCHECK
+	g_signal_connect (device, "notify::" NM_DEVICE_CONNECTIVITY,
 	                  G_CALLBACK (device_connectivity_changed),
 	                  self);
+#endif
 
 	if (priv->startup) {
 		g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
@@ -2712,7 +2608,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                               NM_UNMANAGED_SLEEPING,
 	                               manager_sleeping (self));
 
-	dbus_path = nm_dbus_object_export (NM_DBUS_OBJECT (device));
+	dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device));
 	_LOG2I (LOGD_DEVICE, device, "new %s device (%s)", type_desc, dbus_path);
 
 	nm_settings_device_added (priv->settings, device);
@@ -2759,11 +2655,12 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             gpointer user_data)
 {
 	NMManager *self = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_notify_component_added (device, component))
+	g_return_val_if_fail (self, FALSE);
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		if (nm_device_notify_component_added ((NMDevice *) iter->data, component))
 			return TRUE;
 	}
 	return FALSE;
@@ -2793,10 +2690,9 @@ platform_link_added (NMManager *self,
                      gboolean guess_assume,
                      const NMConfigDeviceStateData *dev_state)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
 	NMDevice *device = NULL;
-	NMDevice *candidate;
+	GSList *iter;
 
 	g_return_if_fail (ifindex > 0);
 
@@ -2804,7 +2700,8 @@ platform_link_added (NMManager *self,
 		return;
 
 	/* Let unrealized devices try to realize themselves with the link */
-	c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
+		NMDevice *candidate = iter->data;
 		gboolean compatible = TRUE;
 		gs_free_error GError *error = NULL;
 
@@ -3031,12 +2928,12 @@ rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr,
 	nm_manager_rfkill_update (NM_MANAGER (user_data), rtype);
 }
 
-const CList *
+const GSList *
 nm_manager_get_devices (NMManager *manager)
 {
 	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
 
-	return &NM_MANAGER_GET_PRIVATE (manager)->devices_lst_head;
+	return NM_MANAGER_GET_PRIVATE (manager)->devices;
 }
 
 static NMDevice *
@@ -3045,10 +2942,9 @@ nm_manager_get_best_device_for_connection (NMManager *self,
                                            gboolean for_user_request,
                                            GHashTable *unavailable_devices)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	const GSList *devices, *iter;
 	NMActiveConnection *ac;
 	NMDevice *act_device;
-	NMDevice *device;
 	NMDeviceCheckConAvailableFlags flags;
 
 	ac = active_connection_find_first_by_connection (self, connection);
@@ -3061,7 +2957,9 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
 
 	/* Pick the first device that's compatible with the connection. */
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+	devices = nm_manager_get_devices (self);
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		NMDevice *device = NM_DEVICE (iter->data);
 
 		if (unavailable_devices && g_hash_table_contains (unavailable_devices, device))
 			continue;
@@ -3074,100 +2972,67 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	return NULL;
 }
 
-static const char **
-_get_devices_paths (NMManager *self,
-                    gboolean all_devices)
+static void
+_get_devices (NMManager *self,
+              GDBusMethodInvocation *context,
+              gboolean all_devices)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	const char **paths = NULL;
+	gs_free const char **paths = NULL;
 	guint i;
-	NMDevice *device;
+	GSList *iter;
 
-	paths = g_new (const char *, c_list_length (&priv->devices_lst_head) + 1);
+	paths = g_new (const char *, g_slist_length (priv->devices) + 1);
 
-	i = 0;
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+	for (i = 0, iter = priv->devices; iter; iter = iter->next) {
 		const char *path;
 
-		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
-		if (!path)
-			continue;
-
-		if (   !all_devices
-		    && !nm_device_is_real (device))
-			continue;
-
-		paths[i++] = path;
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
+		if (   path
+		    && (all_devices || nm_device_is_real (iter->data)))
+			paths[i++] = path;
 	}
 	paths[i++] = NULL;
 
-	return paths;
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(^ao)", (char **) paths));
 }
 
 static void
-impl_manager_get_devices (NMDBusObject *obj,
-                          const NMDBusInterfaceInfoExtended *interface_info,
-                          const NMDBusMethodInfoExtended *method_info,
-                          GDBusConnection *connection,
-                          const char *sender,
-                          GDBusMethodInvocation *invocation,
-                          GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	gs_free const char **paths = NULL;
-
-	paths = _get_devices_paths (self, FALSE);
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(^ao)", (char **) paths));
+impl_manager_get_devices (NMManager *self,
+                          GDBusMethodInvocation *context)
+{
+	_get_devices (self, context, FALSE);
 }
 
 static void
-impl_manager_get_all_devices (NMDBusObject *obj,
-                              const NMDBusInterfaceInfoExtended *interface_info,
-                              const NMDBusMethodInfoExtended *method_info,
-                              GDBusConnection *connection,
-                              const char *sender,
-                              GDBusMethodInvocation *invocation,
-                              GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	gs_free const char **paths = NULL;
-
-	paths = _get_devices_paths (self, TRUE);
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(^ao)", (char **) paths));
+impl_manager_get_all_devices (NMManager *self,
+                              GDBusMethodInvocation *context)
+{
+	_get_devices (self, context, TRUE);
 }
 
 static void
-impl_manager_get_device_by_ip_iface (NMDBusObject *obj,
-                                     const NMDBusInterfaceInfoExtended *interface_info,
-                                     const NMDBusMethodInfoExtended *method_info,
-                                     GDBusConnection *connection,
-                                     const char *sender,
-                                     GDBusMethodInvocation *invocation,
-                                     GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_get_device_by_ip_iface (NMManager *self,
+                                     GDBusMethodInvocation *context,
+                                     const char *iface)
+{
 	NMDevice *device;
 	const char *path = NULL;
-	const char *iface;
-
-	g_variant_get (parameters, "(&s)", &iface);
 
 	device = find_device_by_ip_iface (self, iface);
 	if (device)
-		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
 
-	if (!path) {
-		g_dbus_method_invocation_return_error (invocation,
+	if (path == NULL) {
+		g_dbus_method_invocation_return_error (context,
 		                                       NM_MANAGER_ERROR,
 		                                       NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 		                                       "No device found for the requested iface.");
-		return;
+	} else {
+		g_dbus_method_invocation_return_value (context,
+		                                       g_variant_new ("(o)", path));
 	}
-
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(o)", path));
 }
 
 static gboolean
@@ -3240,6 +3105,7 @@ find_master (NMManager *self,
 	const char *master;
 	NMDevice *master_device = NULL;
 	NMSettingsConnection *master_connection = NULL;
+	GSList *iter;
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
@@ -3268,10 +3134,10 @@ find_master (NMManager *self,
 		/* Try master as a connection UUID */
 		master_connection = nm_settings_get_connection_by_uuid (priv->settings, master);
 		if (master_connection) {
-			NMDevice *candidate;
-
 			/* Check if the master connection is activated on some device already */
-			c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+			for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+				NMDevice *candidate = NM_DEVICE (iter->data);
+
 				if (candidate == device)
 					continue;
 
@@ -3343,6 +3209,7 @@ ensure_master_active_connection (NMManager *self,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *master_ac = NULL;
 	NMDeviceState master_state;
+	GSList *iter;
 
 	g_assert (connection);
 	g_assert (master_connection || master_device);
@@ -3419,10 +3286,12 @@ ensure_master_active_connection (NMManager *self,
 		             NM_MANAGER_ERROR_DEPENDENCY_FAILED,
 		             "Device unmanaged or not available for activation");
 	} else if (master_connection) {
-		NMDevice *candidate;
+		gboolean found_device = FALSE;
 
 		/* Find a compatible device and activate it using this connection */
-		c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) {
+		for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+			NMDevice *candidate = NM_DEVICE (iter->data);
+
 			if (candidate == device) {
 				/* A device obviously can't be its own master */
 				continue;
@@ -3431,6 +3300,7 @@ ensure_master_active_connection (NMManager *self,
 			if (!nm_device_check_connection_available (candidate, NM_CONNECTION (master_connection), NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, NULL))
 				continue;
 
+			found_device = TRUE;
 			if (!nm_device_is_software (candidate)) {
 				master_state = nm_device_get_state (candidate);
 				if (nm_device_is_real (candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED)
@@ -3496,7 +3366,7 @@ find_slaves (NMManager *manager,
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 	g_return_val_if_fail (s_con, NULL);
 
-	devices = g_hash_table_new (nm_direct_hash, NULL);
+	devices = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	/* Search through all connections, not only inactive ones, because
 	 * even if a slave was already active, it might be deactivated during
@@ -3682,11 +3552,11 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er
 {
 	nm_assert (NM_IS_VPN_CONNECTION (active));
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (active));
+	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
 	if (!nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager,
 	                                         NM_VPN_CONNECTION (active),
 	                                         error)) {
-		nm_dbus_object_unexport (NM_DBUS_OBJECT (active));
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (active));
 		return FALSE;
 	}
 
@@ -3781,6 +3651,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	NMConnection *existing_connection = NULL;
 	NMActiveConnection *master_ac = NULL;
 	NMAuthSubject *subject;
+	char *error_desc = NULL;
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE);
@@ -3788,14 +3659,14 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 
 	g_assert (NM_IS_VPN_CONNECTION (active) == FALSE);
 
-	device = nm_active_connection_get_device (active);
-	g_return_val_if_fail (device != NULL, FALSE);
-
 	connection = nm_active_connection_get_settings_connection (active);
-	nm_assert (connection);
+	g_assert (connection);
 
 	applied = nm_active_connection_get_applied_connection (active);
 
+	device = nm_active_connection_get_device (active);
+	g_return_val_if_fail (device != NULL, FALSE);
+
 	/* If the device is active and its connection is not visible to the
 	 * user that's requesting this new activation, fail, since other users
 	 * should not be allowed to implicitly deactivate private connections
@@ -3803,13 +3674,16 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	 */
 	existing_connection = nm_device_get_applied_connection (device);
 	subject = nm_active_connection_get_subject (active);
-	if (   existing_connection
-	    && !nm_auth_is_subject_in_acl_set_error (existing_connection,
-	                                             subject,
-	                                             NM_MANAGER_ERROR,
-	                                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-	                                             error)) {
-		g_prefix_error (error, "Private connection already active on the device: ");
+	if (existing_connection &&
+	    !nm_auth_is_subject_in_acl (existing_connection,
+	                                subject,
+	                                &error_desc)) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		             "Private connection already active on the device: %s",
+		             error_desc);
+		g_free (error_desc);
 		return FALSE;
 	}
 
@@ -3821,9 +3695,6 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		return FALSE;
 	}
 
-	if (nm_active_connection_get_activation_type (active) == NM_ACTIVATION_TYPE_MANAGED)
-		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
-
 	/* Create any backing resources the device needs */
 	if (!nm_device_is_real (device)) {
 		NMDevice *parent;
@@ -3932,7 +3803,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		_LOGD (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
 		       nm_settings_connection_get_id (connection),
 		       master_ac,
-		       nm_dbus_object_get_path (NM_DBUS_OBJECT  (master_ac)) ?: "");
+		       nm_exported_object_get_path (NM_EXPORTED_OBJECT  (master_ac)) ?: "");
 	}
 
 	/* Check slaves for master connection and possibly activate them */
@@ -4004,8 +3875,52 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError
 }
 
 static NMActiveConnection *
+_new_vpn_active_connection (NMManager *self,
+                            NMSettingsConnection *settings_connection,
+                            const char *specific_object,
+                            NMAuthSubject *subject,
+                            NMActivationReason activation_reason,
+                            GError **error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMActiveConnection *parent = NULL;
+	NMDevice *device = NULL;
+
+	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
+
+	if (specific_object) {
+		/* Find the specific connection the client requested we use */
+		parent = active_connection_get_by_path (self, specific_object);
+		if (!parent) {
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+			                     "Base connection for VPN connection not active.");
+			return NULL;
+		}
+	} else
+		parent = priv->primary_connection;
+
+	if (!parent) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		                     "Could not find source connection.");
+		return NULL;
+	}
+
+	device = nm_active_connection_get_device (parent);
+	if (!device) {
+		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+		                     "Source connection had no active device.");
+		return NULL;
+	}
+
+	return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
+	                                                     device,
+	                                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (parent)),
+	                                                     activation_reason,
+	                                                     subject);
+}
+
+static NMActiveConnection *
 _new_active_connection (NMManager *self,
-                        gboolean is_vpn,
                         NMConnection *connection,
                         NMConnection *applied,
                         const char *specific_object,
@@ -4015,78 +3930,45 @@ _new_active_connection (NMManager *self,
                         NMActivationReason activation_reason,
                         GError **error)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection = NULL;
-	NMDevice *parent_device;
+	NMActiveConnection *existing_ac;
+	gboolean is_vpn;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
 
-	nm_assert (is_vpn == _connection_is_vpn (connection));
-	nm_assert (is_vpn || NM_IS_DEVICE (device));
-	nm_assert (!nm_streq0 (specific_object, "/"));
+	/* Can't create new AC for already-active connection */
+	existing_ac = active_connection_find_first_by_connection (self, connection);
+	if (NM_IS_VPN_CONNECTION (existing_ac)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
+		             "Connection '%s' is already active",
+		             nm_connection_get_id (connection));
+		return NULL;
+	}
+
+	/* Normalize the specific object */
+	if (specific_object && g_strcmp0 (specific_object, "/") == 0)
+		specific_object = NULL;
+
+	is_vpn = nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_VPN_SETTING_NAME);
 
 	if (NM_IS_SETTINGS_CONNECTION (connection))
 		settings_connection = (NMSettingsConnection *) connection;
 
 	if (is_vpn) {
-		NMActiveConnection *parent;
-
-		/* FIXME: for VPN connections, we don't allow re-activating an
-		 * already active connection. It's a bug, and should be fixed together
-		 * when reworking VPN handling. */
-		if (active_connection_find_first_by_connection (self, connection)) {
-			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
-			             "Connection '%s' is already active",
-			             nm_connection_get_id (connection));
-			return NULL;
-		}
-
-		/* FIXME: apparently, activation here only works if @connection is
-		 * a settings-connection. Which is not the case during AddAndActivatate.
-		 * Probably, AddAndActivate is broken for VPN. */
 		if (activation_type != NM_ACTIVATION_TYPE_MANAGED)
 			g_return_val_if_reached (NULL);
-
-		g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
-
-		if (specific_object) {
-			/* Find the specific connection the client requested we use */
-			parent = active_connection_get_by_path (self, specific_object);
-			if (!parent) {
-				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
-				                     "Base connection for VPN connection not active.");
-				return NULL;
-			}
-		} else
-			parent = priv->primary_connection;
-
-		if (!parent) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-			                     "Could not find source connection.");
-			return NULL;
-		}
-
-		parent_device = nm_active_connection_get_device (parent);
-		if (!parent_device) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                     "Source connection had no active device");
-			return NULL;
-		}
-
-		if (device && device != parent_device) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-			                     "The device doesn't match the active connection.");
-			return NULL;
-		}
-
-		return (NMActiveConnection *) nm_vpn_connection_new (settings_connection,
-		                                                     parent_device,
-		                                                     nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)),
-		                                                     activation_reason,
-		                                                     subject);
+		return _new_vpn_active_connection (self,
+		                                   settings_connection,
+		                                   specific_object,
+		                                   subject,
+		                                   activation_reason,
+		                                   error);
 	}
 
+	if (device && (activation_type == NM_ACTIVATION_TYPE_MANAGED))
+		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+
 	return (NMActiveConnection *) nm_act_request_new (settings_connection,
 	                                                  applied,
 	                                                  specific_object,
@@ -4111,16 +3993,14 @@ _internal_activation_auth_done (NMActiveConnection *active,
 
 	priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active);
 
-	if (!success)
-		goto fail;
-
 	/* Don't continue with an internal activation if an equivalent active
 	 * connection already exists. Note that slave autoconnections always force a
 	 * reconnection.  We also check this earlier, but there we may fail to
 	 * detect a duplicate if the existing active connection is undergoing
 	 * authorization in impl_manager_activate_connection().
 	 */
-	if (   nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
+	if (   success
+	    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
 	    && nm_active_connection_get_activation_reason (active) != NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES) {
 		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
 			if (   nm_active_connection_get_device (ac) == nm_active_connection_get_device (active)
@@ -4133,15 +4013,17 @@ _internal_activation_auth_done (NMActiveConnection *active,
 				             NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
 				             "Connection '%s' is already active",
 				             nm_active_connection_get_settings_connection_id (active));
-				goto fail;
+				success = FALSE;
+				break;
 			}
 		}
 	}
 
-	if (_internal_activate_generic (self, active, &error))
-		return;
+	if (success) {
+		if (_internal_activate_generic (self, active, &error))
+			return;
+	}
 
-fail:
 	nm_assert (error_desc || error);
 	nm_active_connection_set_state_fail (active,
 	                                     NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
@@ -4182,24 +4064,27 @@ nm_manager_activate_connection (NMManager *self,
                                 NMActivationReason activation_reason,
                                 GError **error)
 {
-	NMManagerPrivate *priv;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *active;
+	char *error_desc = NULL;
 	GSList *iter;
 
-	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-	g_return_val_if_fail (!error || !*error, NULL);
-	nm_assert (!nm_streq0 (specific_object, "/"));
-
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (error != NULL, NULL);
+	g_return_val_if_fail (*error == NULL, NULL);
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
-	                                          subject,
-	                                          NM_MANAGER_ERROR,
-	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
-	                                          error))
+	/* Ensure the subject has permissions for this connection */
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
+	                                subject,
+	                                &error_desc)) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     error_desc);
+		g_free (error_desc);
 		return NULL;
+	}
 
 	/* Look for a active connection that's equivalent and is already pending authorization
 	 * and eventual activation. This is used to de-duplicate concurrent activations which would
@@ -4210,7 +4095,7 @@ nm_manager_activate_connection (NMManager *self,
 		active = iter->data;
 
 		if (   connection == nm_active_connection_get_settings_connection (active)
-		    && nm_streq0 (nm_active_connection_get_specific_object (active), specific_object)
+		    && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0
 		    && nm_active_connection_get_device (active) == device
 		    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))
 		    && nm_auth_subject_is_internal (subject)
@@ -4219,7 +4104,6 @@ nm_manager_activate_connection (NMManager *self,
 	}
 
 	active = _new_active_connection (self,
-	                                 _connection_is_vpn (NM_CONNECTION (connection)),
 	                                 NM_CONNECTION (connection),
 	                                 applied,
 	                                 specific_object,
@@ -4228,11 +4112,10 @@ nm_manager_activate_connection (NMManager *self,
 	                                 activation_type,
 	                                 activation_reason,
 	                                 error);
-	if (!active)
-		return NULL;
-
-	priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
-	nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL);
+	if (active) {
+		priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
+		nm_active_connection_authorize (active, NULL, _internal_activation_auth_done, self, NULL);
+	}
 	return active;
 }
 
@@ -4241,12 +4124,9 @@ nm_manager_activate_connection (NMManager *self,
  * @self: the #NMManager
  * @context: the D-Bus context of the requestor
  * @connection: the partial or complete #NMConnection to be activated
- * @device_path: the object path of the device to be activated, or NULL
+ * @device_path: the object path of the device to be activated, or "/"
  * @out_device: on successful reutrn, the #NMDevice to be activated with @connection
- *   The caller may pass in a device which shortcuts the lookup by path.
- *   In this case, the passed in device must have the matching @device_path
- *   already.
- * @out_is_vpn: on successful return, %TRUE if @connection is a VPN connection
+ * @out_vpn: on successful return, %TRUE if @connection is a VPN connection
  * @error: location to store an error on failure
  *
  * Performs basic validation on an activation request, including ensuring that
@@ -4262,16 +4142,17 @@ validate_activation_request (NMManager *self,
                              NMConnection *connection,
                              const char *device_path,
                              NMDevice **out_device,
-                             gboolean *out_is_vpn,
+                             gboolean *out_vpn,
                              GError **error)
 {
 	NMDevice *device = NULL;
-	gboolean is_vpn = FALSE;
-	gs_unref_object NMAuthSubject *subject = NULL;
+	gboolean vpn = FALSE;
+	NMAuthSubject *subject = NULL;
+	char *error_desc = NULL;
 
-	nm_assert (NM_IS_CONNECTION (connection));
-	nm_assert (out_device);
-	nm_assert (out_is_vpn);
+	g_assert (connection);
+	g_assert (out_device);
+	g_assert (out_vpn);
 
 	/* Validate the caller */
 	subject = nm_auth_subject_new_unix_process_from_context (context);
@@ -4283,80 +4164,76 @@ validate_activation_request (NMManager *self,
 		return NULL;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (connection,
-	                                          subject,
-	                                          NM_MANAGER_ERROR,
-	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
-	                                          error))
-		return NULL;
+	/* Ensure the subject has permissions for this connection */
+	if (!nm_auth_is_subject_in_acl (connection,
+	                                subject,
+	                                &error_desc)) {
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     error_desc);
+		g_free (error_desc);
+		goto error;
+	}
+
+	/* Check whether it's a VPN or not */
+	if (   nm_connection_get_setting_vpn (connection)
+	    || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
+		vpn = TRUE;
 
-	is_vpn = _connection_is_vpn (connection);
+	/* Normalize device path */
+	if (device_path && g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
 
-	if (*out_device) {
-		device = *out_device;
-		nm_assert (NM_IS_DEVICE (device));
-		nm_assert (device_path);
-		nm_assert (nm_streq0 (device_path, nm_dbus_object_get_path (NM_DBUS_OBJECT (device))));
-		nm_assert (device == nm_manager_get_device_by_path (self, device_path));
-	} else if (device_path) {
+	/* And validate it */
+	if (device_path) {
 		device = nm_manager_get_device_by_path (self, device_path);
 		if (!device) {
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR,
 			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 			                     "Device not found");
-			return NULL;
+			goto error;
 		}
-	} else if (!is_vpn) {
+	} else
 		device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL);
-		if (!device) {
-			gs_free char *iface = NULL;
-
-			/* VPN and software-device connections don't need a device yet,
-			 * but non-virtual connections do ... */
-			if (!nm_connection_is_virtual (connection)) {
-				g_set_error_literal (error,
-				                     NM_MANAGER_ERROR,
-				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                     "No suitable device found for this connection.");
-				return NULL;
-			}
 
-			/* Look for an existing device with the connection's interface name */
-			iface = nm_manager_get_connection_iface (self, connection, NULL, error);
-			if (!iface)
-				return NULL;
+	if (!device && !vpn) {
+		gs_free char *iface = NULL;
 
-			device = find_device_by_iface (self, iface, connection, NULL);
-			if (!device) {
-				g_set_error_literal (error,
-				                     NM_MANAGER_ERROR,
-				                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				                     "Failed to find a compatible device for this connection");
-				return NULL;
-			}
+		/* VPN and software-device connections don't need a device yet,
+		 * but non-virtual connections do ... */
+		if (!nm_connection_is_virtual (connection)) {
+			g_set_error_literal (error,
+			                     NM_MANAGER_ERROR,
+			                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			                     "No suitable device found for this connection.");
+			goto error;
 		}
+
+		/* Look for an existing device with the connection's interface name */
+		iface = nm_manager_get_connection_iface (self, connection, NULL, error);
+		if (!iface)
+			goto error;
+
+		device = find_device_by_iface (self, iface, connection, NULL);
 	}
 
-	if (is_vpn && device) {
-		/* VPN's are treated specially. Maybe the should accept a device as well,
-		 * however, later on during activation, we don't handle the device.
-		 *
-		 * Maybe we should, and maybe it makes sense to specify a device
-		 * when activating a VPN. But for now, just error out.  */
+	if ((!vpn || device_path) && !device) {
 		g_set_error_literal (error,
 		                     NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-		                     "Cannot specify device when activating VPN");
-		return NULL;
+		                     "Failed to find a compatible device for this connection");
+		goto error;
 	}
 
-	nm_assert (   ( is_vpn && !device)
-	           || (!is_vpn && NM_IS_DEVICE (device)));
-
 	*out_device = device;
-	*out_is_vpn = is_vpn;
-	return g_steal_pointer (&subject);
+	*out_vpn = vpn;
+	return subject;
+
+error:
+	g_object_unref (subject);
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -4378,27 +4255,24 @@ _activation_auth_done (NMActiveConnection *active,
 	subject = nm_active_connection_get_subject (active);
 	connection = nm_active_connection_get_settings_connection (active);
 
-	if (!success) {
+	if (success) {
+		if (_internal_activate_generic (self, active, &error)) {
+			nm_settings_connection_autoconnect_blocked_reason_set (connection,
+			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+			                                                       FALSE);
+			g_dbus_method_invocation_return_value (context,
+			                                       g_variant_new ("(o)",
+			                                       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL,
+			                            subject, NULL);
+			return;
+		}
+	} else {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
-		goto fail;
 	}
 
-	if (!_internal_activate_generic (self, active, &error))
-		goto fail;
-
-	nm_settings_connection_autoconnect_blocked_reason_set (connection,
-	                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
-	                                                       FALSE);
-	g_dbus_method_invocation_return_value (context,
-	                                       g_variant_new ("(o)",
-	                                       nm_dbus_object_get_path (NM_DBUS_OBJECT (active))));
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL,
-	                            subject, NULL);
-	return;
-
-fail:
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 	                            subject, error->message);
 	nm_active_connection_set_state_fail (active,
@@ -4409,15 +4283,12 @@ fail:
 }
 
 static void
-impl_manager_activate_connection (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const NMDBusMethodInfoExtended *method_info,
-                                  GDBusConnection *dbus_connection,
-                                  const char *sender,
-                                  GDBusMethodInvocation *invocation,
-                                  GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_activate_connection (NMManager *self,
+                                  GDBusMethodInvocation *context,
+                                  const char *connection_path,
+                                  const char *device_path,
+                                  const char *specific_object_path)
+{
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gs_unref_object NMActiveConnection *active = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
@@ -4425,15 +4296,14 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	NMDevice *device = NULL;
 	gboolean is_vpn = FALSE;
 	GError *error = NULL;
-	const char *connection_path;
-	const char *device_path;
-	const char *specific_object_path;
 
-	g_variant_get (parameters, "(&o&o&o)", &connection_path, &device_path, &specific_object_path);
-
-	connection_path = nm_utils_dbus_normalize_object_path (connection_path);
-	specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path);
-	device_path = nm_utils_dbus_normalize_object_path (device_path);
+	/* Normalize object paths */
+	if (g_strcmp0 (connection_path, "/") == 0)
+		connection_path = NULL;
+	if (g_strcmp0 (specific_object_path, "/") == 0)
+		specific_object_path = NULL;
+	if (g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
 
 	/* If the connection path is given and valid, that connection is activated.
 	 * Otherwise the "best" connection for the device is chosen and activated,
@@ -4468,7 +4338,7 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	}
 
 	subject = validate_activation_request (self,
-	                                       invocation,
+	                                       context,
 	                                       NM_CONNECTION (connection),
 	                                       device_path,
 	                                       &device,
@@ -4478,7 +4348,6 @@ impl_manager_activate_connection (NMDBusObject *obj,
 		goto error;
 
 	active = _new_active_connection (self,
-	                                 is_vpn,
 	                                 NM_CONNECTION (connection),
 	                                 NULL,
 	                                 specific_object_path,
@@ -4497,7 +4366,7 @@ impl_manager_activate_connection (NMDBusObject *obj,
 	                                NULL,
 	                                _activation_auth_done,
 	                                self,
-	                                invocation);
+	                                context);
 	return;
 
 error:
@@ -4505,7 +4374,7 @@ error:
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 		                            subject, error->message);
 	}
-	g_dbus_method_invocation_take_error (invocation, error);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 /*****************************************************************************/
@@ -4526,7 +4395,7 @@ activation_add_done (NMSettings *settings,
 	AddAndActivateInfo *info = user_data;
 	NMManager *self;
 	gs_unref_object NMActiveConnection *active = NULL;
-	gs_free_error GError *local = NULL;
+	GError *local = NULL;
 
 	self = info->manager;
 	active = info->active;
@@ -4545,8 +4414,8 @@ activation_add_done (NMSettings *settings,
 			g_dbus_method_invocation_return_value (
 			    context,
 			    g_variant_new ("(oo)",
-			                   nm_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)),
-			                   nm_dbus_object_get_path (NM_DBUS_OBJECT (active))));
+			                   nm_connection_get_path (NM_CONNECTION (new_connection)),
+			                   nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 			                            nm_active_connection_get_settings_connection (active),
 			                            TRUE,
@@ -4559,7 +4428,6 @@ activation_add_done (NMSettings *settings,
 	}
 
 	nm_assert (error);
-
 	nm_active_connection_set_state_fail (active,
 	                                     NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN,
 	                                     error->message);
@@ -4572,6 +4440,7 @@ activation_add_done (NMSettings *settings,
 	                            NULL,
 	                            nm_active_connection_get_subject (active),
 	                            error->message);
+	g_clear_error (&local);
 }
 
 static void
@@ -4586,12 +4455,27 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 	GDBusMethodInvocation *context = user_data2;
 	AddAndActivateInfo *info;
 	GError *error = NULL;
-	gs_unref_object NMConnection *connection = NULL;
 
-	connection = g_object_steal_qdata (G_OBJECT (active),
-	                                   active_connection_add_and_activate_quark ());
+	if (success) {
+		NMConnection *connection;
+
+		connection = g_object_steal_qdata (G_OBJECT (active),
+		                                   active_connection_add_and_activate_quark ());
+
+		info = g_slice_new (AddAndActivateInfo);
+		info->manager = self;
+		info->active = g_object_ref (active);
 
-	if (!success) {
+		/* Basic sender auth checks performed; try to add the connection */
+		nm_settings_add_connection_dbus (priv->settings,
+		                                 connection,
+		                                 FALSE,
+		                                 context,
+		                                 activation_add_done,
+		                                 info);
+		g_object_unref (connection);
+	} else {
+		g_assert (error_desc);
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             error_desc);
@@ -4602,50 +4486,32 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 		                            nm_active_connection_get_subject (active),
 		                            error->message);
 		g_dbus_method_invocation_take_error (context, error);
-		g_object_unref (active);
-		return;
 	}
 
-	info = g_slice_new (AddAndActivateInfo);
-	info->manager = self;
-
-	/* we pass on the reference to @active. */
-	info->active = active;
-
-	/* Basic sender auth checks performed; try to add the connection */
-	nm_settings_add_connection_dbus (priv->settings,
-	                                 connection,
-	                                 FALSE,
-	                                 context,
-	                                 activation_add_done,
-	                                 info);
+	g_object_unref (active);
 }
 
 static void
-impl_manager_add_and_activate_connection (NMDBusObject *obj,
-                                          const NMDBusInterfaceInfoExtended *interface_info,
-                                          const NMDBusMethodInfoExtended *method_info,
-                                          GDBusConnection *dbus_connection,
-                                          const char *sender,
-                                          GDBusMethodInvocation *invocation,
-                                          GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_add_and_activate_connection (NMManager *self,
+                                          GDBusMethodInvocation *context,
+                                          GVariant *settings,
+                                          const char *device_path,
+                                          const char *specific_object_path)
+{
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gs_unref_object NMConnection *connection = NULL;
+	NMConnection *connection = NULL;
+	GSList *all_connections = NULL;
 	NMActiveConnection *active = NULL;
-	gs_unref_object NMAuthSubject *subject = NULL;
+	NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 	NMDevice *device = NULL;
-	gboolean is_vpn = FALSE;
-	gs_unref_variant GVariant *settings = NULL;
-	const char *device_path;
-	const char *specific_object_path;
+	gboolean vpn = FALSE;
 
-	g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path);
-
-	specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path);
-	device_path = nm_utils_dbus_normalize_object_path (device_path);
+	/* Normalize object paths */
+	if (g_strcmp0 (specific_object_path, "/") == 0)
+		specific_object_path = NULL;
+	if (g_strcmp0 (device_path, "/") == 0)
+		device_path = NULL;
 
 	/* Try to create a new connection with the given settings.
 	 * We allow empty settings for AddAndActivateConnection(). In that case,
@@ -4659,16 +4525,29 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 		_nm_connection_replace_settings (connection, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL);
 
 	subject = validate_activation_request (self,
-	                                       invocation,
+	                                       context,
 	                                       connection,
 	                                       device_path,
 	                                       &device,
-	                                       &is_vpn,
+	                                       &vpn,
 	                                       &error);
 	if (!subject)
 		goto error;
 
-	if (is_vpn) {
+	{
+		gs_free NMSettingsConnection **connections = NULL;
+		guint i, len;
+
+		connections = nm_settings_get_connections_clone (priv->settings, &len,
+		                                                 NULL, NULL,
+		                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
+		all_connections = NULL;
+		for (i = len; i > 0; ) {
+			i--;
+			all_connections = g_slist_prepend (all_connections, connections[i]);
+		}
+	}
+	if (vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
 		if (!nm_connection_get_setting_vpn (connection)) {
 			error = g_error_new_literal (NM_CONNECTION_ERROR,
@@ -4681,7 +4560,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 		nm_utils_complete_generic (priv->platform,
 		                           connection,
 		                           NM_SETTING_VPN_SETTING_NAME,
-		                           (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
+		                           all_connections,
 		                           NULL,
 		                           _("VPN connection"),
 		                           NULL,
@@ -4691,13 +4570,14 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 		if (!nm_device_complete_connection (device,
 		                                    connection,
 		                                    specific_object_path,
-		                                    (NMConnection *const*) nm_settings_get_connections (priv->settings, NULL),
+		                                    all_connections,
 		                                    &error))
 			goto error;
 	}
+	g_slist_free (all_connections);
+	all_connections = NULL;
 
 	active = _new_active_connection (self,
-	                                 is_vpn,
 	                                 connection,
 	                                 NULL,
 	                                 specific_object_path,
@@ -4709,26 +4589,24 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
 	if (!active)
 		goto error;
 
-	/* FIXME: nm_active_connection_authorize() already has two user-data pointers
-	 *        to piggyback additional data. Instead of attaching the third argument to
-	 *        @active's user-data, add a third paramter.
-	 *        Or alternatively, allocate a data structure to pass on additional data.
-	 *        Then we don't need two user-data pointers. */
 	g_object_set_qdata_full (G_OBJECT (active),
 	                         active_connection_add_and_activate_quark (),
 	                         connection,
 	                         g_object_unref);
 
-	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, invocation);
-
-	/* we passed the pointers on to the callback of authorize. */
-	g_steal_pointer (&connection);
-	g_steal_pointer (&active);
+	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context);
+	g_object_unref (subject);
 	return;
 
 error:
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, NULL, subject, error->message);
-	g_dbus_method_invocation_take_error (invocation, error);
+	g_clear_object (&connection);
+	g_slist_free (all_connections);
+	g_clear_object (&subject);
+	g_clear_object (&active);
+
+	g_assert (error);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 /*****************************************************************************/
@@ -4824,28 +4702,21 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, NULL);
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_deactivate_connection (NMDBusObject *obj,
-                                    const NMDBusInterfaceInfoExtended *interface_info,
-                                    const NMDBusMethodInfoExtended *method_info,
-                                    GDBusConnection *dbus_connection,
-                                    const char *sender,
-                                    GDBusMethodInvocation *invocation,
-                                    GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_deactivate_connection (NMManager *self,
+                                    GDBusMethodInvocation *context,
+                                    const char *active_path)
+{
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnection *ac;
 	NMSettingsConnection *connection = NULL;
 	GError *error = NULL;
 	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
-	const char *active_path;
-
-	g_variant_get (parameters, "(&o)", &active_path);
+	char *error_desc = NULL;
 
 	/* Find the connection by its object path */
 	ac = active_connection_get_by_path (self, active_path);
@@ -4860,7 +4731,7 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 	}
 
 	/* Validate the caller */
-	subject = nm_auth_subject_new_unix_process_from_context (invocation);
+	subject = nm_auth_subject_new_unix_process_from_context (context);
 	if (!subject) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -4868,15 +4739,19 @@ impl_manager_deactivate_connection (NMDBusObject *obj,
 		goto done;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
-	                                          subject,
-	                                          NM_MANAGER_ERROR,
-	                                          NM_MANAGER_ERROR_PERMISSION_DENIED,
-	                                          &error))
+	/* Ensure the subject has permissions for this connection */
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
+	                                subject,
+	                                &error_desc)) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
 		goto done;
+	}
 
 	/* Validate the user request */
-	chain = nm_auth_chain_new_subject (subject, invocation, deactivate_net_auth_done_cb, self);
+	chain = nm_auth_chain_new_subject (subject, context, deactivate_net_auth_done_cb, self);
 	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -4894,7 +4769,7 @@ done:
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL,
 			                            subject, error->message);
 		}
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 	}
 	g_clear_object (&subject);
 }
@@ -5002,7 +4877,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	gboolean suspending, waking_from_suspend;
-	NMDevice *device;
+	GSList *iter;
 
 	suspending = sleeping_changed && priv->sleeping;
 	waking_from_suspend = sleeping_changed && !priv->sleeping;
@@ -5013,7 +4888,9 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 		/* FIXME: are there still hardware devices that need to be disabled around
 		 * suspend/resume?
 		 */
-		c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		for (iter = priv->devices; iter; iter = iter->next) {
+			NMDevice *device = iter->data;
+
 			/* FIXME: shouldn't we be unmanaging software devices if !suspending? */
 			if (nm_device_is_software (device))
 				continue;
@@ -5041,7 +4918,9 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 
 		if (waking_from_suspend) {
 			sleep_devices_clear (self);
-			c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+			for (iter = priv->devices; iter; iter = iter->next) {
+				NMDevice *device = iter->data;
+
 				if (nm_device_is_software (device))
 					continue;
 
@@ -5068,7 +4947,8 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 		nm_manager_rfkill_update (self, RFKILL_TYPE_UNKNOWN);
 
 		/* Re-manage managed devices */
-		c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+		for (iter = priv->devices; iter; iter = iter->next) {
+			NMDevice *device = NM_DEVICE (iter->data);
 			guint i;
 
 			if (nm_device_is_software (device)) {
@@ -5161,28 +5041,27 @@ sleep_auth_done_cb (NMAuthChain *chain,
 		g_dbus_method_invocation_return_value (context, NULL);
 	}
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 #endif
 
 static void
-impl_manager_sleep (NMDBusObject *obj,
-                    const NMDBusInterfaceInfoExtended *interface_info,
-                    const NMDBusMethodInfoExtended *method_info,
-                    GDBusConnection *connection,
-                    const char *sender,
-                    GDBusMethodInvocation *invocation,
-                    GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_sleep (NMManager *self,
+                    GDBusMethodInvocation *context,
+                    gboolean do_sleep)
+{
+	NMManagerPrivate *priv;
 	GError *error = NULL;
 	gs_unref_object NMAuthSubject *subject = NULL;
-	gboolean do_sleep;
+#if 0
+	NMAuthChain *chain;
+	const char *error_desc = NULL;
+#endif
 
-	g_variant_get (parameters, "(b)", &do_sleep);
+	g_return_if_fail (NM_IS_MANAGER (self));
 
-	subject = nm_auth_subject_new_unix_process_from_context (invocation);
+	priv = NM_MANAGER_GET_PRIVATE (self);
+	subject = nm_auth_subject_new_unix_process_from_context (context);
 
 	if (priv->sleeping == do_sleep) {
 		error = g_error_new (NM_MANAGER_ERROR,
@@ -5190,7 +5069,7 @@ impl_manager_sleep (NMDBusObject *obj,
 		                     "Already %s", do_sleep ? "asleep" : "awake");
 		nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", FALSE, subject,
 		                         error->message);
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -5204,8 +5083,22 @@ impl_manager_sleep (NMDBusObject *obj,
 	 */
 	_internal_sleep (self, do_sleep);
 	nm_audit_log_control_op (NM_AUDIT_OP_SLEEP_CONTROL, do_sleep ? "on" : "off", TRUE, subject, NULL);
-	g_dbus_method_invocation_return_value (invocation, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 	return;
+
+#if 0
+	chain = nm_auth_chain_new (context, sleep_auth_done_cb, self, &error_desc);
+	if (chain) {
+		priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+		nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL);
+		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE);
+	} else {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_dbus_method_invocation_take_error (context, error);
+	}
+#endif
 }
 
 static void
@@ -5281,25 +5174,21 @@ enable_net_done_cb (NMAuthChain *chain,
 		g_dbus_method_invocation_take_error (context, ret_error);
 	}
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_enable (NMDBusObject *obj,
-                     const NMDBusInterfaceInfoExtended *interface_info,
-                     const NMDBusMethodInfoExtended *method_info,
-                     GDBusConnection *connection,
-                     const char *sender,
-                     GDBusMethodInvocation *invocation,
-                     GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_enable (NMManager *self,
+                     GDBusMethodInvocation *context,
+                     gboolean enable)
+{
+	NMManagerPrivate *priv;
 	NMAuthChain *chain;
 	GError *error = NULL;
-	gboolean enable;
 
-	g_variant_get (parameters, "(b)", &enable);
+	g_return_if_fail (NM_IS_MANAGER (self));
+
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (priv->net_enabled == enable) {
 		error = g_error_new (NM_MANAGER_ERROR,
@@ -5308,7 +5197,7 @@ impl_manager_enable (NMDBusObject *obj,
 		goto done;
 	}
 
-	chain = nm_auth_chain_new_context (invocation, enable_net_done_cb, self);
+	chain = nm_auth_chain_new_context (context, enable_net_done_cb, self);
 	if (!chain) {
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
@@ -5322,7 +5211,7 @@ impl_manager_enable (NMDBusObject *obj,
 
 done:
 	if (error)
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 }
 
 /* Permissions */
@@ -5389,28 +5278,23 @@ get_permissions_done_cb (NMAuthChain *chain,
 		                                       g_variant_new ("(a{ss})", &results));
 	}
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_get_permissions (NMDBusObject *obj,
-                              const NMDBusInterfaceInfoExtended *interface_info,
-                              const NMDBusMethodInfoExtended *method_info,
-                              GDBusConnection *connection,
-                              const char *sender,
-                              GDBusMethodInvocation *invocation,
-                              GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_get_permissions (NMManager *self,
+                              GDBusMethodInvocation *context)
+{
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
+	GError *error = NULL;
 
-	chain = nm_auth_chain_new_context (invocation, get_permissions_done_cb, self);
+	chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request.");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -5434,123 +5318,83 @@ impl_manager_get_permissions (NMDBusObject *obj,
 }
 
 static void
-impl_manager_state (NMDBusObject *obj,
-                    const NMDBusInterfaceInfoExtended *interface_info,
-                    const NMDBusMethodInfoExtended *method_info,
-                    GDBusConnection *connection,
-                    const char *sender,
-                    GDBusMethodInvocation *invocation,
-                    GVariant *parameters)
+impl_manager_get_state (NMManager *self,
+                        GDBusMethodInvocation *context)
 {
-	NMManager *self = NM_MANAGER (obj);
-
 	nm_manager_update_state (self);
-	g_dbus_method_invocation_return_value (invocation,
+	g_dbus_method_invocation_return_value (context,
 	                                       g_variant_new ("(u)", NM_MANAGER_GET_PRIVATE (self)->state));
 }
 
 static void
-impl_manager_set_logging (NMDBusObject *obj,
-                          const NMDBusInterfaceInfoExtended *interface_info,
-                          const NMDBusMethodInfoExtended *method_info,
-                          GDBusConnection *connection,
-                          const char *sender,
-                          GDBusMethodInvocation *invocation,
-                          GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_set_logging (NMManager *self,
+                          GDBusMethodInvocation *context,
+                          const char *level,
+                          const char *domains)
+{
 	GError *error = NULL;
-	const char *level;
-	const char *domains;
 
 	/* The permission is already enforced by the D-Bus daemon, but we ensure
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                 invocation,
-	                                 G_MAXULONG,
-	                                 NM_MANAGER_ERROR,
-	                                 NM_MANAGER_ERROR_PERMISSION_DENIED))
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_MANAGER_ERROR,
+	                                NM_MANAGER_ERROR_PERMISSION_DENIED))
 		return;
 
-	g_variant_get (parameters, "(&s&s)", &level, &domains);
-
 	if (nm_logging_setup (level, domains, NULL, &error)) {
 		_LOGI (LOGD_CORE, "logging: level '%s' domains '%s'",
 		       nm_logging_level_to_string (), nm_logging_domains_to_string ());
 	}
 
 	if (error)
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 	else
-		g_dbus_method_invocation_return_value (invocation, NULL);
+		g_dbus_method_invocation_return_value (context, NULL);
 }
 
 static void
-impl_manager_get_logging (NMDBusObject *obj,
-                          const NMDBusInterfaceInfoExtended *interface_info,
-                          const NMDBusMethodInfoExtended *method_info,
-                          GDBusConnection *connection,
-                          const char *sender,
-                          GDBusMethodInvocation *invocation,
-                          GVariant *parameters)
-{
-	g_dbus_method_invocation_return_value (invocation,
+impl_manager_get_logging (NMManager *manager,
+                          GDBusMethodInvocation *context)
+{
+	g_dbus_method_invocation_return_value (context,
 	                                       g_variant_new ("(ss)",
 	                                                      nm_logging_level_to_string (),
 	                                                      nm_logging_domains_to_string ()));
 }
 
 typedef struct {
-	NMManager *self;
-	GDBusMethodInvocation *context;
 	guint remaining;
+	GDBusMethodInvocation *context;
+	NMConnectivityState state;
 } ConnectivityCheckData;
 
 static void
-device_connectivity_done (NMDevice *device,
-                          NMDeviceConnectivityHandle *handle,
-                          NMConnectivityState state,
-                          GError *error,
-                          gpointer user_data)
+device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data)
 {
 	ConnectivityCheckData *data = user_data;
-	NMManager *self;
-	NMManagerPrivate *priv;
-
-	nm_assert (data);
-	nm_assert (data->remaining > 0);
-	nm_assert (NM_IS_MANAGER (data->self));
 
 	data->remaining--;
 
-	self = data->self;
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	/* We check if the state is already FULL so that we can provide the
+	 * response without waiting for slower devices that are not going to
+	 * affect the overall state anyway. */
 
-	if (   data->context
-	    && (   data->remaining == 0
-	        || (   state == NM_CONNECTIVITY_FULL
-	            && priv->connectivity_state == NM_CONNECTIVITY_FULL))) {
-		/* despite having a @handle and @state returned by the requests, we always
-		 * return the current connectivity_state. That is, because the connectivity_state
-		 * and the answer to the connectivity check shall agree.
-		 *
-		 * However, if one of the requests (early) returns full connectivity and agrees with
-		 * the accumulated connectivity state, we no longer have to wait. The result is set.
-		 *
-		 * This also works well, because NMDevice first emits change signals to its own
-		 * connectivity state, which is then taken into account for the accumulated global
-		 * state. All this happens, before the callback is invoked. */
-		g_dbus_method_invocation_return_value (g_steal_pointer (&data->context),
-		                                       g_variant_new ("(u)",
-		                                                      (guint) priv->connectivity_state));
+	if (data->state != NM_CONNECTIVITY_FULL) {
+		if (state > data->state)
+			data->state = state;
+
+		if (data->state == NM_CONNECTIVITY_FULL || !data->remaining) {
+			g_dbus_method_invocation_return_value (data->context,
+			                                       g_variant_new ("(u)", data->state));
+		}
 	}
 
-	if (data->remaining == 0) {
-		g_object_unref (self);
+	if (!data->remaining)
 		g_slice_free (ConnectivityCheckData, data);
-	}
 }
 
 static void
@@ -5564,7 +5408,7 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 	GError *error = NULL;
 	NMAuthCallResult result;
 	ConnectivityCheckData *data;
-	NMDevice *device;
+	const GSList *devices;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
@@ -5580,59 +5424,39 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 		error = g_error_new_literal (NM_MANAGER_ERROR,
 		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                             "Not authorized to recheck connectivity");
-	}
-
-	if (error) {
-		g_dbus_method_invocation_take_error (context, error);
-		goto out;
-	}
-
-	data = g_slice_new (ConnectivityCheckData);
-	data->self = g_object_ref (self);
-	data->context = context;
-	data->remaining = 0;
+	} else {
+		/* it's allowed */
+		data = g_slice_new0 (ConnectivityCheckData);
+		data->context = context;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_check_connectivity (device,
-		                                  device_connectivity_done,
-		                                  data))
+		for (devices = priv->devices; devices; devices = devices->next) {
 			data->remaining++;
+			nm_device_check_connectivity (NM_DEVICE (devices->data),
+			                              device_connectivity_done,
+			                              data);
+		}
 	}
 
-	if (data->remaining == 0) {
-		/* call the handler at least once. */
-		data->remaining = 1;
-		device_connectivity_done (NULL,
-		                          NULL,
-		                          NM_CONNECTIVITY_UNKNOWN,
-		                          NULL,
-		                          data);
-		/* @data got destroyed. */
-	}
-
-out:
-	nm_auth_chain_destroy (chain);
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_check_connectivity (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
+impl_manager_check_connectivity (NMManager *self,
+                                 GDBusMethodInvocation *context)
+{
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMAuthChain *chain;
+	GError *error = NULL;
 
-	chain = nm_auth_chain_new_context (invocation, check_connectivity_auth_done_cb, self);
+	/* Validate the request */
+	chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal(invocation,
-		                                              NM_MANAGER_ERROR,
-		                                              NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                              "Unable to authenticate request.");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -5649,14 +5473,15 @@ start_factory (NMDeviceFactory *factory, gpointer user_data)
 void
 nm_manager_write_device_state (NMManager *self)
 {
+	const GSList *devices;
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
 	gs_unref_hashtable GHashTable *seen_ifindexes = NULL;
 	gint nm_owned;
 
-	seen_ifindexes = g_hash_table_new (nm_direct_hash, NULL);
+	seen_ifindexes = g_hash_table_new (NULL, NULL);
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+	for (devices = priv->devices; devices; devices = devices->next) {
+		NMDevice *device = NM_DEVICE (devices->data);
 		int ifindex;
 		gboolean managed;
 		NMConfigDeviceStateManagedType managed_type;
@@ -5797,10 +5622,10 @@ void
 nm_manager_stop (NMManager *self)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
 
-	while ((device = c_list_first_entry (&priv->devices_lst_head, NMDevice, devices_lst)))
-		remove_device (self, device, TRUE, TRUE);
+	/* Remove all devices */
+	while (priv->devices)
+		remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
 
 	_active_connection_cleanup (self);
 
@@ -5812,20 +5637,21 @@ handle_firmware_changed (gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMDevice *device;
+	GSList *iter;
 
 	priv->fw_changed_id = 0;
 
 	/* Try to re-enable devices with missing firmware */
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		NMDeviceState state = nm_device_get_state (device);
+	for (iter = priv->devices; iter; iter = iter->next) {
+		NMDevice *candidate = NM_DEVICE (iter->data);
+		NMDeviceState state = nm_device_get_state (candidate);
 
-		if (   nm_device_get_firmware_missing (device)
+		if (   nm_device_get_firmware_missing (candidate)
 		    && (state == NM_DEVICE_STATE_UNAVAILABLE)) {
-			_LOG2I (LOGD_CORE, device, "firmware may now be available");
+			_LOG2I (LOGD_CORE, candidate, "firmware may now be available");
 
 			/* Re-set unavailable state to try bringing the device up again */
-			nm_device_state_changed (device,
+			nm_device_state_changed (candidate,
 			                         NM_DEVICE_STATE_UNAVAILABLE,
 			                         NM_DEVICE_STATE_REASON_NONE);
 		}
@@ -5944,133 +5770,318 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u
 	}
 }
 
-/*****************************************************************************/
+#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
 
 typedef struct {
 	NMManager *self;
-	NMDBusObject *obj;
-	const NMDBusInterfaceInfoExtended *interface_info;
-	const NMDBusPropertyInfoExtended *property_info;
-	GVariant *value;
-	guint64 export_version_id;
-} DBusSetPropertyHandle;
+	GDBusConnection *connection;
+	GDBusMessage *message;
+	NMAuthSubject *subject;
+	const char *permission;
+	const char *audit_op;
+	char *audit_prop_value;
+	GType interface_type;
+	const char *glib_propname;
+} PropertyFilterData;
 
-#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
+static void
+free_property_filter_data (PropertyFilterData *pfd)
+{
+	g_object_unref (pfd->self);
+	g_object_unref (pfd->connection);
+	g_object_unref (pfd->message);
+	g_clear_object (&pfd->subject);
+	g_free (pfd->audit_prop_value);
+	g_slice_free (PropertyFilterData, pfd);
+}
 
 static void
-_dbus_set_property_auth_cb (NMAuthChain *chain,
-                            GError *error,
-                            GDBusMethodInvocation *invocation,
-                            gpointer user_data)
+prop_set_auth_done_cb (NMAuthChain *chain,
+                       GError *error,
+                       GDBusMethodInvocation *context, /* NULL */
+                       gpointer user_data)
 {
-	DBusSetPropertyHandle *handle_data = user_data;
-	gs_unref_object NMDBusObject *obj = handle_data->obj;
-	const NMDBusInterfaceInfoExtended *interface_info = handle_data->interface_info;
-	const NMDBusPropertyInfoExtended *property_info = handle_data->property_info;
-	gs_unref_variant GVariant *value = handle_data->value;
-	guint64 export_version_id = handle_data->export_version_id;
-	gs_unref_object NMManager *self = handle_data->self;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	PropertyFilterData *pfd = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
 	NMAuthCallResult result;
-	const char *error_name = NULL;
-	const char *error_message = NULL;
-	GValue gvalue;
-
-	g_slice_free (DBusSetPropertyHandle, handle_data);
+	GDBusMessage *reply = NULL;
+	const char *error_message;
+	gs_unref_object NMExportedObject *object = NULL;
+	const NMGlobalDnsConfig *global_dns;
+	gs_unref_variant GVariant *value = NULL;
+	GVariant *args;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
-	result = nm_auth_chain_get_result (chain, property_info->writable.permission);
+	result = nm_auth_chain_get_result (chain, pfd->permission);
+	if (error || (result != NM_AUTH_CALL_RESULT_YES)) {
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Not authorized to perform this operation"));
+		if (error)
+			error_message = error->message;
+		goto done;
+	}
 
-	if (   error
-	    || result != NM_AUTH_CALL_RESULT_YES) {
-		error_name = NM_PERM_DENIED_ERROR;
-		error_message = error ? error->message : "Not authorized to perform this operation";
-		goto out;
+	object = NM_EXPORTED_OBJECT (nm_bus_manager_get_registered_object (priv->dbus_mgr,
+	                                                                   g_dbus_message_get_path (pfd->message)));
+	if (!object) {
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 "org.freedesktop.DBus.Error.UnknownObject",
+		                                                 (error_message = "Object doesn't exist."));
+		goto done;
 	}
 
-	if (export_version_id != nm_dbus_object_get_export_version_id (obj)) {
-		error_name = "org.freedesktop.DBus.Error.UnknownObject";
-		error_message = "Object was deleted while authenticating";
-		goto out;
+	/* do some extra type checking... */
+	if (!nm_exported_object_get_interface_by_type (object, pfd->interface_type)) {
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 "org.freedesktop.DBus.Error.InvalidArgs",
+		                                                 (error_message = "Object is of unexpected type."));
+		goto done;
 	}
 
-	/* Handle some properties specially *sigh* */
-	if (   interface_info == &interface_info_manager
-	    && nm_streq (property_info->property_name, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
-		const NMGlobalDnsConfig *global_dns;
+	args = g_dbus_message_get_body (pfd->message);
+	g_variant_get (args, "(&s&sv)", NULL, NULL, &value);
+	g_assert (pfd->glib_propname);
 
+	if (!strcmp (pfd->glib_propname, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}")));
 		global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
-		if (   global_dns
-		    && !nm_global_dns_config_is_internal (global_dns)) {
-			error_name = NM_PERM_DENIED_ERROR;
-			error_message = "Global DNS configuration already set via configuration file";
-			goto out;
+
+		if (global_dns && !nm_global_dns_config_is_internal (global_dns)) {
+			reply = g_dbus_message_new_method_error_literal (pfd->message,
+			                                                 NM_PERM_DENIED_ERROR,
+			                                                 (error_message = "Global DNS configuration already set via configuration file"));
+			goto done;
 		}
+		/* ... but set the property on the @object itself. It would be correct to set the property
+		 * on the skeleton interface, but as it is now, the result is the same. */
+		g_object_set (object, pfd->glib_propname, value, NULL);
+	} else if (!strcmp (pfd->glib_propname, NM_DEVICE_STATISTICS_REFRESH_RATE_MS)) {
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32));
+		/* the same here */
+		g_object_set (object, pfd->glib_propname, (guint) g_variant_get_uint32 (value), NULL);
+	} else {
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
+		/* the same here */
+		g_object_set (object, pfd->glib_propname, g_variant_get_boolean (value), NULL);
 	}
 
-	g_dbus_gvariant_to_gvalue (value, &gvalue);
-	g_object_set_property (G_OBJECT (obj), property_info->property_name, &gvalue);
-	g_value_unset (&gvalue);
+	reply = g_dbus_message_new_method_reply (pfd->message);
+	g_dbus_message_set_body (reply, g_variant_new_tuple (NULL, 0));
+	error_message = NULL;
+done:
+	nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, !error_message, pfd->subject, error_message);
 
-out:
-	nm_audit_log_control_op (property_info->writable.audit_op,
-	                         property_info->property_name,
-	                         !error_message,
-	                         nm_auth_chain_get_subject (chain),
-	                         error_message);
-	if (error_message)
-		g_dbus_method_invocation_return_dbus_error (invocation, error_name, error_message);
-	else
-		g_dbus_method_invocation_return_value (invocation, NULL);
-	nm_auth_chain_destroy (chain);
+	g_dbus_connection_send_message (pfd->connection, reply,
+	                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+	                                NULL, NULL);
+	g_object_unref (reply);
+	nm_auth_chain_unref (chain);
+
+	free_property_filter_data (pfd);
 }
 
-void
-nm_manager_dbus_set_property_handle (NMDBusObject *obj,
-                                     const NMDBusInterfaceInfoExtended *interface_info,
-                                     const NMDBusPropertyInfoExtended *property_info,
-                                     GDBusConnection *connection,
-                                     const char *sender,
-                                     GDBusMethodInvocation *invocation,
-                                     GVariant *value,
-                                     gpointer user_data)
+static gboolean
+do_set_property_check (gpointer user_data)
 {
-	NMManager *self = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	PropertyFilterData *pfd = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
+	GDBusMessage *reply = NULL;
 	NMAuthChain *chain;
 	const char *error_message = NULL;
-	gs_unref_object NMAuthSubject *subject = NULL;
-	DBusSetPropertyHandle *handle_data;
 
-	subject = nm_auth_subject_new_unix_process_from_context (invocation);
-	if (!subject) {
-		error_message = "Could not determine request UID";
-		goto err;
+	pfd->subject = nm_auth_subject_new_unix_process_from_message (pfd->connection, pfd->message);
+	if (!pfd->subject) {
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Could not determine request UID."));
+		goto out;
 	}
 
-	handle_data = g_slice_new0 (DBusSetPropertyHandle);
-	handle_data->self = g_object_ref (self);
-	handle_data->obj = g_object_ref (obj);
-	handle_data->interface_info = interface_info;
-	handle_data->property_info = property_info;
-	handle_data->value = g_variant_ref (value);
-	handle_data->export_version_id = nm_dbus_object_get_export_version_id (obj);
+	/* Validate the user request */
+	chain = nm_auth_chain_new_subject (pfd->subject, NULL, prop_set_auth_done_cb, pfd);
+	if (!chain) {
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Could not authenticate request."));
+		goto out;
+	}
 
-	chain = nm_auth_chain_new_subject (subject, invocation, _dbus_set_property_auth_cb, handle_data);
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-	nm_auth_chain_add_call (chain, property_info->writable.permission, TRUE);
-	return;
+	nm_auth_chain_add_call (chain, pfd->permission, TRUE);
+
+out:
+	if (reply) {
+		nm_audit_log_control_op (pfd->audit_op, pfd->audit_prop_value, FALSE, pfd->subject, error_message);
+		g_dbus_connection_send_message (pfd->connection, reply,
+		                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+		                                NULL, NULL);
+		g_object_unref (reply);
+		free_property_filter_data (pfd);
+	}
+
+	return FALSE;
+}
+
+static GDBusMessage *
+prop_filter (GDBusConnection *connection,
+             GDBusMessage *message,
+             gboolean incoming,
+             gpointer user_data)
+{
+	gs_unref_object NMManager *self = NULL;
+	GVariant *args;
+	const char *propiface = NULL;
+	const char *propname = NULL;
+	const char *glib_propname = NULL, *permission = NULL;
+	const char *audit_op = NULL;
+	GType interface_type = G_TYPE_INVALID;
+	PropertyFilterData *pfd;
+	const GVariantType *expected_type = G_VARIANT_TYPE_BOOLEAN;
+	gs_unref_variant GVariant *value = NULL;
+
+	self = g_weak_ref_get (user_data);
+	if (!self)
+		return message;
+
+	/* The sole purpose of this function is to validate property accesses on the
+	 * NMManager object since gdbus doesn't give us this functionality.
+	 */
+
+	/* Only filter org.freedesktop.DBus.Properties.Set calls */
+	if (   !incoming
+	    || g_dbus_message_get_message_type (message) != G_DBUS_MESSAGE_TYPE_METHOD_CALL
+	    || g_strcmp0 (g_dbus_message_get_interface (message), DBUS_INTERFACE_PROPERTIES) != 0
+	    || g_strcmp0 (g_dbus_message_get_member (message), "Set") != 0)
+		return message;
+
+	args = g_dbus_message_get_body (message);
+	if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("(ssv)")))
+		return message;
+	g_variant_get (args, "(&s&sv)", &propiface, &propname, &value);
+
+	/* Only filter calls to filtered properties, on existing objects */
+	if (!strcmp (propiface, NM_DBUS_INTERFACE)) {
+		if (!strcmp (propname, "WirelessEnabled")) {
+			glib_propname = NM_MANAGER_WIRELESS_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "WwanEnabled")) {
+			glib_propname = NM_MANAGER_WWAN_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "WimaxEnabled")) {
+			glib_propname = NM_MANAGER_WIMAX_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
+			audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+		} else if (!strcmp (propname, "GlobalDnsConfiguration")) {
+			glib_propname = NM_MANAGER_GLOBAL_DNS_CONFIGURATION;
+			permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS;
+			audit_op = NM_AUDIT_OP_NET_CONTROL;
+			expected_type = G_VARIANT_TYPE ("a{sv}");
+		} else if (!strcmp (propname, "ConnectivityCheckEnabled")) {
+			glib_propname = NM_MANAGER_CONNECTIVITY_CHECK_ENABLED;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK;
+			audit_op = NM_AUDIT_OP_NET_CONTROL;
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_MANAGER_SKELETON;
+	} else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE)) {
+		if (!strcmp (propname, "Autoconnect")) {
+			glib_propname = NM_DEVICE_AUTOCONNECT;
+			permission = NM_AUTH_PERMISSION_NETWORK_CONTROL;
+			audit_op = NM_AUDIT_OP_DEVICE_AUTOCONNECT;
+		} else if (!strcmp (propname, "Managed")) {
+			glib_propname = NM_DEVICE_MANAGED;
+			permission = NM_AUTH_PERMISSION_NETWORK_CONTROL;
+			audit_op = NM_AUDIT_OP_DEVICE_MANAGED;
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_DEVICE_SKELETON;
+	} else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE_STATISTICS)) {
+		if (!strcmp (propname, "RefreshRateMs")) {
+			glib_propname = NM_DEVICE_STATISTICS_REFRESH_RATE_MS;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS;
+			audit_op = NM_AUDIT_OP_STATISTICS;
+			expected_type = G_VARIANT_TYPE ("u");
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_DEVICE_SKELETON;
+	} else
+		return message;
+
+	if (!g_variant_is_of_type (value, expected_type))
+		return message;
+
+	/* This filter function is called from a gdbus worker thread which we can't
+	 * make other D-Bus calls from. In particular, we cannot call
+	 * org.freedesktop.DBus.GetConnectionUnixUser to find the remote UID.
+	 */
+	pfd = g_slice_new0 (PropertyFilterData);
+	pfd->self = self;
+	self = NULL;
+	pfd->connection = g_object_ref (connection);
+	pfd->message = message;
+	pfd->permission = permission;
+	pfd->interface_type = interface_type;
+	pfd->glib_propname = glib_propname;
+	pfd->audit_op = audit_op;
+	if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
+		pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname,
+		                                         g_variant_get_boolean (value));
+	} else
+		pfd->audit_prop_value = g_strdup (pfd->glib_propname);
+
+	g_idle_add (do_set_property_check, pfd);
+
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static int
+_set_prop_filter_free2 (gpointer user_data)
+{
+	g_slice_free (GWeakRef, user_data);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+_set_prop_filter_free (gpointer user_data)
+{
+	g_weak_ref_clear (user_data);
+
+	/* Delay the final deletion of the user_data. There is a race when
+	 * calling g_dbus_connection_remove_filter() that the callback and user_data
+	 * might have been copied and being executed after the destroy function
+	 * runs (bgo #704568).
+	 * This doesn't really fix the race, but it should work well enough. */
+	g_timeout_add_seconds (2, _set_prop_filter_free2, user_data);
+}
+
+static void
+_set_prop_filter (NMManager *self, GDBusConnection *connection)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	nm_assert ((!priv->prop_filter.connection) == (!priv->prop_filter.id));
+
+	if (priv->prop_filter.connection == connection)
+		return;
+
+	if (priv->prop_filter.connection) {
+		g_dbus_connection_remove_filter (priv->prop_filter.connection, priv->prop_filter.id);
+		priv->prop_filter.id = 0;
+		g_clear_object (&priv->prop_filter.connection);
+	}
+	if (connection) {
+		GWeakRef *wptr;
 
-err:
-	nm_audit_log_control_op (property_info->writable.audit_op,
-	                         property_info->property_name,
-	                         FALSE,
-	                         invocation,
-	                         error_message);
-	g_dbus_method_invocation_return_error_literal (invocation,
-	                                               G_DBUS_ERROR,
-	                                               G_DBUS_ERROR_AUTH_FAILED,
-	                                               error_message);
+		wptr = g_slice_new (GWeakRef);
+		g_weak_ref_init  (wptr, self);
+		priv->prop_filter.id = g_dbus_connection_add_filter (connection, prop_filter, wptr, _set_prop_filter_free);
+		priv->prop_filter.connection = g_object_ref (connection);
+	}
 }
 
 /*****************************************************************************/
@@ -6081,7 +6092,7 @@ _checkpoint_mgr_get (NMManager *self, gboolean create_as_needed)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (G_UNLIKELY (!priv->checkpoint_mgr) && create_as_needed)
-		priv->checkpoint_mgr = nm_checkpoint_manager_new (self, obj_properties[PROP_CHECKPOINTS]);
+		priv->checkpoint_mgr = nm_checkpoint_manager_new (self);
 	return priv->checkpoint_mgr;
 }
 
@@ -6100,15 +6111,13 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 	GVariant *variant = NULL;
 	GError *error = NULL;
 	const char *arg = NULL;
-	guint32 add_timeout;
 
 	op = nm_auth_chain_get_data (chain, "audit-op");
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
 
-	if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY,
-	                      NM_AUDIT_OP_CHECKPOINT_ROLLBACK,
-	                      NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT))
+	if (   nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)
+	    || nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK))
 		arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path");
 
 	if (auth_error) {
@@ -6132,7 +6141,7 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 			                                           (NMCheckpointCreateFlags) flags,
 			                                           &error);
 			if (checkpoint) {
-				arg = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint));
+				arg = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
 				variant = g_variant_new ("(o)", arg);
 			}
 		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)) {
@@ -6141,10 +6150,6 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
 			nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
 			                                checkpoint_path, &variant, &error);
-		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) {
-			add_timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add_timeout"));
-			nm_checkpoint_manager_adjust_rollback_timeout (_checkpoint_mgr_get (self, TRUE),
-			                                               checkpoint_path, add_timeout, &error);
 		} else
 			g_return_if_reached ();
 	}
@@ -6157,71 +6162,63 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, variant);
 
-	nm_auth_chain_destroy (chain);
+
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_manager_checkpoint_create (NMDBusObject *obj,
-                                const NMDBusInterfaceInfoExtended *interface_info,
-                                const NMDBusMethodInfoExtended *method_info,
-                                GDBusConnection *connection,
-                                const char *sender,
-                                GDBusMethodInvocation *invocation,
-                                GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_checkpoint_create (NMManager *self,
+                                GDBusMethodInvocation *context,
+                                const char *const *devices,
+                                guint32 rollback_timeout,
+                                guint32 flags)
+{
+	NMManagerPrivate *priv;
 	NMAuthChain *chain;
-	char **devices;
-	guint32 rollback_timeout;
-	guint32 flags;
+	GError *error = NULL;
 
 	G_STATIC_ASSERT_EXPR (sizeof (flags) <= sizeof (NMCheckpointCreateFlags));
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request.");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
-	g_variant_get (parameters, "(^aouu)", &devices, &rollback_timeout, &flags);
-
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL);
-	nm_auth_chain_set_data (chain, "devices", devices, (GDestroyNotify) g_strfreev);
+	nm_auth_chain_set_data (chain, "devices", g_strdupv ((char **) devices), (GDestroyNotify) g_strfreev);
 	nm_auth_chain_set_data (chain, "flags",  GUINT_TO_POINTER (flags), NULL);
 	nm_auth_chain_set_data (chain, "timeout", GUINT_TO_POINTER (rollback_timeout), NULL);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
 }
 
 static void
-impl_manager_checkpoint_destroy (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_checkpoint_destroy (NMManager *self,
+                                 GDBusMethodInvocation *context,
+                                 const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
 	NMAuthChain *chain;
-	const char *checkpoint_path;
 
-	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request.");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
-	g_variant_get (parameters, "(&o)", &checkpoint_path);
-
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
 	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL);
 	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
@@ -6229,66 +6226,29 @@ impl_manager_checkpoint_destroy (NMDBusObject *obj,
 }
 
 static void
-impl_manager_checkpoint_rollback (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const NMDBusMethodInfoExtended *method_info,
-                                  GDBusConnection *connection,
-                                  const char *sender,
-                                  GDBusMethodInvocation *invocation,
-                                  GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+impl_manager_checkpoint_rollback (NMManager *self,
+                                  GDBusMethodInvocation *context,
+                                  const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
 	NMAuthChain *chain;
-	const char *checkpoint_path;
-
-	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
-	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request.");
-		return;
-	}
-
-	g_variant_get (parameters, "(&o)", &checkpoint_path);
-
-	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL);
-	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
-	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
-}
 
-static void
-impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj,
-                                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                                 const NMDBusMethodInfoExtended *method_info,
-                                                 GDBusConnection *connection,
-                                                 const char *sender,
-                                                 GDBusMethodInvocation *invocation,
-                                                 GVariant *parameters)
-{
-	NMManager *self = NM_MANAGER (obj);
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMAuthChain *chain;
-	const char *checkpoint_path;
-	guint32 add_timeout;
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
-	chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_MANAGER_ERROR,
-		                                               NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate request.");
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
-	g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout);
-
 	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
-	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL);
+	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL);
 	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
-	nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
 }
 
@@ -6298,10 +6258,7 @@ static void
 auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data)
 {
 	/* Let clients know they should re-check their authorization */
-	nm_dbus_object_emit_signal (user_data,
-	                            &interface_info_manager,
-	                            &signal_info_check_permissions,
-	                            "()");
+	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
 }
 
 #define KERN_RFKILL_OP_CHANGE_ALL 3
@@ -6426,6 +6383,14 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 	return G_SOURCE_CONTINUE;
 }
 
+static void
+dbus_connection_changed_cb (NMBusManager *dbus_mgr,
+                            GDBusConnection *connection,
+                            gpointer user_data)
+{
+	_set_prop_filter (NM_MANAGER (user_data), connection);
+}
+
 /*****************************************************************************/
 
 void
@@ -6492,7 +6457,8 @@ nm_manager_setup (void)
 	nm_singleton_instance_register ();
 	_LOGD (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance);
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (self));
+	nm_exported_object_export ((NMExportedObject *) self);
+
 	return self;
 }
 
@@ -6505,9 +6471,11 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
 
+	_set_prop_filter (self, nm_bus_manager_get_connection (priv->dbus_mgr));
+
 	priv->settings = nm_settings_new ();
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (priv->settings));
+	nm_exported_object_export (NM_EXPORTED_OBJECT (priv->settings));
 
 	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
 	                  G_CALLBACK (settings_startup_complete_changed), self);
@@ -6575,7 +6543,6 @@ nm_manager_init (NMManager *self)
 	GFile *file;
 
 	c_list_init (&priv->link_cb_lst);
-	c_list_init (&priv->devices_lst_head);
 	c_list_init (&priv->active_connections_lst_head);
 	c_list_init (&priv->delete_volatile_connection_lst_head);
 
@@ -6607,6 +6574,12 @@ nm_manager_init (NMManager *self)
 	priv->state = NM_STATE_DISCONNECTED;
 	priv->startup = TRUE;
 
+	priv->dbus_mgr = g_object_ref (nm_bus_manager_get ());
+	g_signal_connect (priv->dbus_mgr,
+	                  NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                  G_CALLBACK (dbus_connection_changed_cb),
+	                  self);
+
 	/* sleep/wake handling */
 	priv->sleep_monitor = nm_sleep_monitor_new ();
 	g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING,
@@ -6641,7 +6614,13 @@ nm_manager_init (NMManager *self)
 	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, self);
 
 	priv->metered = NM_METERED_UNKNOWN;
-	priv->sleep_devices = g_hash_table_new (nm_direct_hash, NULL);
+	priv->sleep_devices = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static gboolean
+device_is_real (GObject *device, gpointer user_data)
+{
+	return nm_device_is_real (NM_DEVICE (device));
 }
 
 static void
@@ -6656,6 +6635,7 @@ get_property (GObject *object, guint prop_id,
 	const char *path;
 	NMActiveConnection *ac;
 	GPtrArray *ptrarr;
+	gboolean vbool;
 
 	switch (prop_id) {
 	case PROP_VERSION:
@@ -6697,7 +6677,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ACTIVE_CONNECTIONS:
 		ptrarr = g_ptr_array_new ();
 		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
-			path = nm_dbus_object_get_path (NM_DBUS_OBJECT (ac));
+			path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac));
 			if (path)
 				g_ptr_array_add (ptrarr, g_strdup (path));
 		}
@@ -6712,10 +6692,15 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, nm_config_data_get_connectivity_uri (config_data) != NULL);
 		break;
 	case PROP_CONNECTIVITY_CHECK_ENABLED:
-		g_value_set_boolean (value, concheck_enabled (self, NULL));
+#if WITH_CONCHECK
+		vbool = nm_connectivity_check_enabled (nm_connectivity_get ());
+#else
+		vbool = FALSE;
+#endif
+		g_value_set_boolean (value, vbool);
 		break;
 	case PROP_PRIMARY_CONNECTION:
-		nm_dbus_utils_g_value_set_object_path (value, priv->primary_connection);
+		nm_utils_g_value_set_object_path (value, priv->primary_connection);
 		break;
 	case PROP_PRIMARY_CONNECTION_TYPE:
 		type = NULL;
@@ -6729,15 +6714,13 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_string (value, type ? type : "");
 		break;
 	case PROP_ACTIVATING_CONNECTION:
-		nm_dbus_utils_g_value_set_object_path (value, priv->activating_connection);
+		nm_utils_g_value_set_object_path (value, priv->activating_connection);
 		break;
 	case PROP_SLEEPING:
 		g_value_set_boolean (value, priv->sleeping);
 		break;
 	case PROP_DEVICES:
-		g_value_take_boxed (value,
-		                    nm_utils_strv_make_deep_copied (_get_devices_paths (self,
-		                                                                        FALSE)));
+		nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL);
 		break;
 	case PROP_METERED:
 		g_value_set_uint (value, priv->metered);
@@ -6748,16 +6731,7 @@ get_property (GObject *object, guint prop_id,
 		nm_global_dns_config_to_dbus (dns_config, value);
 		break;
 	case PROP_ALL_DEVICES:
-		g_value_take_boxed (value,
-		                    nm_utils_strv_make_deep_copied (_get_devices_paths (self,
-		                                                                        TRUE)));
-		break;
-	case PROP_CHECKPOINTS:
-		g_value_take_boxed (value,
-		                    priv->checkpoint_mgr
-		                    ? nm_utils_strv_make_deep_copied (nm_checkpoint_manager_get_checkpoint_paths (priv->checkpoint_mgr,
-		                                                                                                  NULL))
-		                    : NULL);
+		nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -6824,6 +6798,11 @@ dispose (GObject *object)
 	CList *iter, *iter_safe;
 	NMActiveConnection *ac, *ac_safe;
 
+	nm_clear_g_source (&priv->delete_volatile_connection_idle_id);
+	_delete_volatile_connection_all (self, FALSE);
+	nm_assert (!priv->delete_volatile_connection_idle_id);
+	nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head));
+
 	g_signal_handlers_disconnect_by_func (priv->platform,
 	                                      G_CALLBACK (platform_link_cb),
 	                                      self);
@@ -6835,18 +6814,14 @@ dispose (GObject *object)
 		g_slice_free (PlatformLinkCbData, data);
 	}
 
-	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_destroy);
+	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
 
 	nm_clear_g_source (&priv->devices_inited_id);
 
-	g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_free);
-
-	if (priv->concheck_mgr) {
-		g_signal_handlers_disconnect_by_func (priv->concheck_mgr,
-		                                      G_CALLBACK (concheck_config_changed_cb),
-		                                      self);
-		g_clear_object (&priv->concheck_mgr);
+	if (priv->checkpoint_mgr) {
+		nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL);
+		g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref);
 	}
 
 	if (priv->auth_mgr) {
@@ -6856,7 +6831,7 @@ dispose (GObject *object)
 		g_clear_object (&priv->auth_mgr);
 	}
 
-	nm_assert (c_list_is_empty (&priv->devices_lst_head));
+	g_assert (priv->devices == NULL);
 
 	nm_clear_g_source (&priv->ac_cleanup_id);
 
@@ -6893,6 +6868,13 @@ dispose (GObject *object)
 
 	g_clear_object (&priv->vpn_manager);
 
+	/* Unregister property filter */
+	if (priv->dbus_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
+		g_clear_object (&priv->dbus_mgr);
+	}
+	_set_prop_filter (self, NULL);
+
 	sleep_devices_clear (self);
 	g_clear_pointer (&priv->sleep_devices, g_hash_table_unref);
 
@@ -6915,11 +6897,6 @@ dispose (GObject *object)
 		g_clear_object (&priv->rfkill_mgr);
 	}
 
-	nm_clear_g_source (&priv->delete_volatile_connection_idle_id);
-	_delete_volatile_connection_all (self, FALSE);
-	nm_assert (!priv->delete_volatile_connection_idle_id);
-	nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head));
-
 	nm_device_factory_manager_for_each_factory (_deinit_device_factory, self);
 
 	nm_clear_g_source (&priv->timestamp_update_id);
@@ -6941,274 +6918,22 @@ finalize (GObject *object)
 	g_object_unref (priv->platform);
 }
 
-static const GDBusSignalInfo signal_info_check_permissions = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"CheckPermissions",
-);
-
-static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"StateChanged",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("state", "u"),
-	),
-);
-
-static const GDBusSignalInfo signal_info_device_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"DeviceAdded",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"),
-	),
-);
-
-static const GDBusSignalInfo signal_info_device_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"DeviceRemoved",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("device_path", "o"),
-	),
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_manager = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Reload",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"),
-					),
-				),
-				.handle = impl_manager_reload,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetDevices",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"),
-					),
-				),
-				.handle = impl_manager_get_devices,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetAllDevices",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("devices", "ao"),
-					),
-				),
-				.handle = impl_manager_get_all_devices,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetDeviceByIpIface",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("iface", "s"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("device", "o"),
-					),
-				),
-				.handle = impl_manager_get_device_by_ip_iface,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"ActivateConnection",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection",      "o"),
-						NM_DEFINE_GDBUS_ARG_INFO ("device",          "o"),
-						NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
-					),
-				),
-				.handle = impl_manager_activate_connection,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"AddAndActivateConnection",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection",      "a{sa{sv}}"),
-						NM_DEFINE_GDBUS_ARG_INFO ("device",          "o"),
-						NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("path",              "o"),
-						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
-					),
-				),
-				.handle = impl_manager_add_and_activate_connection,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"DeactivateConnection",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
-					),
-				),
-				.handle = impl_manager_deactivate_connection,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Sleep",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("sleep", "b"),
-					),
-				),
-				.handle = impl_manager_sleep,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Enable",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("enable", "b"),
-					),
-				),
-				.handle = impl_manager_enable,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetPermissions",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("permissions", "a{ss}"),
-					),
-				),
-				.handle = impl_manager_get_permissions,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SetLogging",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("level",   "s"),
-						NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"),
-					),
-				),
-				.handle = impl_manager_set_logging,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetLogging",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("level",   "s"),
-						NM_DEFINE_GDBUS_ARG_INFO ("domains", "s"),
-					),
-				),
-				.handle = impl_manager_get_logging,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"CheckConnectivity",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connectivity", "u"),
-					),
-				),
-				.handle = impl_manager_check_connectivity,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"state",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("state", "u"),
-					),
-				),
-				.handle = impl_manager_state,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"CheckpointCreate",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("devices",          "ao"),
-						NM_DEFINE_GDBUS_ARG_INFO ("rollback_timeout", "u"),
-						NM_DEFINE_GDBUS_ARG_INFO ("flags",            "u"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
-					),
-				),
-				.handle = impl_manager_checkpoint_create,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"CheckpointDestroy",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
-					),
-				),
-				.handle = impl_manager_checkpoint_destroy,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"CheckpointRollback",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("result", "a{su}"),
-					),
-				),
-				.handle = impl_manager_checkpoint_rollback,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"CheckpointAdjustRollbackTimeout",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
-						NM_DEFINE_GDBUS_ARG_INFO ("add_timeout", "u"),
-					),
-				),
-				.handle = impl_manager_checkpoint_adjust_rollback_timeout,
-			),
-		),
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&signal_info_check_permissions,
-			&signal_info_state_changed,
-			&signal_info_device_added,
-			&signal_info_device_removed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Devices",                    "ao",    NM_MANAGER_DEVICES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("AllDevices",                 "ao",    NM_MANAGER_ALL_DEVICES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Checkpoints",                "ao",    NM_MANAGER_CHECKPOINTS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("NetworkingEnabled",          "b",     NM_MANAGER_NETWORKING_ENABLED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WirelessEnabled",            "b",     NM_MANAGER_WIRELESS_ENABLED,              NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI,               NM_AUDIT_OP_RADIO_CONTROL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WirelessHardwareEnabled",    "b",     NM_MANAGER_WIRELESS_HARDWARE_ENABLED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WwanEnabled",                "b",     NM_MANAGER_WWAN_ENABLED,                  NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN,               NM_AUDIT_OP_RADIO_CONTROL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WwanHardwareEnabled",        "b",     NM_MANAGER_WWAN_HARDWARE_ENABLED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("WimaxEnabled",               "b",     NM_MANAGER_WIMAX_ENABLED,                 NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX,              NM_AUDIT_OP_RADIO_CONTROL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WimaxHardwareEnabled",       "b",     NM_MANAGER_WIMAX_HARDWARE_ENABLED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActiveConnections",          "ao",    NM_MANAGER_ACTIVE_CONNECTIONS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimaryConnection",          "o",     NM_MANAGER_PRIMARY_CONNECTION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimartConnectionType",      "s",     NM_MANAGER_PRIMARY_CONNECTION_TYPE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Metered",                    "u",     NM_MANAGER_METERED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActivatingConnection",       "o",     NM_MANAGER_ACTIVATING_CONNECTION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Startup",                    "b",     NM_MANAGER_STARTUP),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Version",                    "s",     NM_MANAGER_VERSION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Capabilities",               "u",     NM_MANAGER_CAPABILITIES),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("State",                      "u",     NM_MANAGER_STATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Connectivity",               "u",     NM_MANAGER_CONNECTIVITY),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ConnectivityCheckAvailable", "b",     NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("ConnectivityCheckEnabled",   "b",     NM_MANAGER_CONNECTIVITY_CHECK_ENABLED,    NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, NM_AUDIT_OP_NET_CONTROL),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("GlobalDnsConfiguration",     "a{sv}", NM_MANAGER_GLOBAL_DNS_CONFIGURATION,      NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS,        NM_AUDIT_OP_NET_CONTROL),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_manager_class_init (NMManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (manager_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH);
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_manager);
+	exported_object_class->export_path = NM_DBUS_PATH;
 
+	/* virtual methods */
 	object_class->constructed = constructed;
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
+	/* properties */
 	obj_properties[PROP_VERSION] =
 	    g_param_spec_string (NM_MANAGER_VERSION, "", "",
 	                         NULL,
@@ -7216,11 +6941,11 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                         G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_CAPABILITIES] =
-	    g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "",
-	                          G_VARIANT_TYPE ("au"),
-	                          NULL,
-	                          G_PARAM_READABLE |
-	                          G_PARAM_STATIC_STRINGS);
+		g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "",
+		                      G_VARIANT_TYPE ("au"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_STATE] =
 	    g_param_spec_uint (NM_MANAGER_STATE, "", "",
@@ -7371,17 +7096,11 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_CHECKPOINTS] =
-	    g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "",
-	                        G_TYPE_STRV,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	/* signals */
 
-	/* emitted only for realized devices */
+	/* D-Bus exported; emitted only for realized devices */
 	signals[DEVICE_ADDED] =
 	    g_signal_new (NM_MANAGER_DEVICE_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -7397,7 +7116,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	/* emitted only for realized devices when a device
+	/* D-Bus exported; emitted only for realized devices when a device
 	 * becomes unrealized or removed */
 	signals[DEVICE_REMOVED] =
 	    g_signal_new (NM_MANAGER_DEVICE_REMOVED,
@@ -7414,6 +7133,20 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
+	signals[STATE_CHANGED] =
+	    g_signal_new (NM_MANAGER_STATE_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1, G_TYPE_UINT);
+
+	signals[CHECK_PERMISSIONS] =
+	    g_signal_new (NM_MANAGER_CHECK_PERMISSIONS,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
+
 	signals[ACTIVE_CONNECTION_ADDED] =
 	    g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -7434,4 +7167,25 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class),
+	                                        NMDBUS_TYPE_MANAGER_SKELETON,
+	                                        "Reload", impl_manager_reload,
+	                                        "GetDevices", impl_manager_get_devices,
+	                                        "GetAllDevices", impl_manager_get_all_devices,
+	                                        "GetDeviceByIpIface", impl_manager_get_device_by_ip_iface,
+	                                        "ActivateConnection", impl_manager_activate_connection,
+	                                        "AddAndActivateConnection", impl_manager_add_and_activate_connection,
+	                                        "DeactivateConnection", impl_manager_deactivate_connection,
+	                                        "Sleep", impl_manager_sleep,
+	                                        "Enable", impl_manager_enable,
+	                                        "GetPermissions", impl_manager_get_permissions,
+	                                        "SetLogging", impl_manager_set_logging,
+	                                        "GetLogging", impl_manager_get_logging,
+	                                        "CheckConnectivity", impl_manager_check_connectivity,
+	                                        "state", impl_manager_get_state,
+	                                        "CheckpointCreate", impl_manager_checkpoint_create,
+	                                        "CheckpointDestroy", impl_manager_checkpoint_destroy,
+	                                        "CheckpointRollback", impl_manager_checkpoint_rollback,
+	                                        NULL);
 }
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 54444532..da838532 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -22,9 +22,9 @@
 #ifndef __NETWORKMANAGER_MANAGER_H__
 #define __NETWORKMANAGER_MANAGER_H__
 
+#include "nm-exported-object.h"
 #include "settings/nm-settings-connection.h"
-#include "c-list/src/c-list.h"
-#include "nm-dbus-manager.h"
+#include "nm-utils/c-list.h"
 
 #define NM_TYPE_MANAGER            (nm_manager_get_type ())
 #define NM_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager))
@@ -55,16 +55,18 @@
 #define NM_MANAGER_METERED "metered"
 #define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration"
 #define NM_MANAGER_ALL_DEVICES "all-devices"
-#define NM_MANAGER_CHECKPOINTS "checkpoints"
 
 /* Not exported */
 #define NM_MANAGER_SLEEPING "sleeping"
 
-/* Signals */
+/* signals */
+#define NM_MANAGER_CHECK_PERMISSIONS         "check-permissions"
 #define NM_MANAGER_DEVICE_ADDED              "device-added"
 #define NM_MANAGER_DEVICE_REMOVED            "device-removed"
+#define NM_MANAGER_STATE_CHANGED             "state-changed"
 #define NM_MANAGER_USER_PERMISSIONS_CHANGED  "user-permissions-changed"
 
+/* Internal signals */
 #define NM_MANAGER_ACTIVE_CONNECTION_ADDED   "active-connection-added"
 #define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
 #define NM_MANAGER_CONFIGURE_QUIT            "configure-quit"
@@ -83,14 +85,13 @@ gboolean      nm_manager_start                         (NMManager *manager,
                                                         GError **error);
 void          nm_manager_stop                          (NMManager *manager);
 NMState       nm_manager_get_state                     (NMManager *manager);
-
 const CList * nm_manager_get_active_connections        (NMManager *manager);
 
 #define nm_manager_for_each_active_connection(manager, iter, tmp_list) \
 	for (tmp_list = nm_manager_get_active_connections (manager), \
 	     iter = c_list_entry (tmp_list->next, NMActiveConnection, active_connections_lst); \
 	     ({ \
-	         const gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
+	         gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
 	         \
 	         if (!_has_next) \
 	             iter = NULL; \
@@ -106,19 +107,7 @@ void          nm_manager_write_device_state (NMManager *manager);
 
 /* Device handling */
 
-const CList *       nm_manager_get_devices             (NMManager *manager);
-
-#define nm_manager_for_each_device(manager, iter, tmp_list) \
-	for (tmp_list = nm_manager_get_devices (manager), \
-	     iter = c_list_entry (tmp_list->next, NMDevice, devices_lst); \
-	     ({ \
-	         const gboolean _has_next = (&iter->devices_lst != tmp_list); \
-	         \
-	         if (!_has_next) \
-	             iter = NULL; \
-	         _has_next; \
-	    }); \
-	    iter = c_list_entry (iter->devices_lst.next, NMDevice, devices_lst))
+const GSList *      nm_manager_get_devices             (NMManager *manager);
 
 NMDevice *          nm_manager_get_device_by_ifindex   (NMManager *manager,
                                                         int ifindex);
@@ -164,13 +153,4 @@ gboolean            nm_manager_remove_device (NMManager *self,
                                               const char *ifname,
                                               NMDeviceType device_type);
 
-void nm_manager_dbus_set_property_handle (NMDBusObject *obj,
-                                          const NMDBusInterfaceInfoExtended *interface_info,
-                                          const NMDBusPropertyInfoExtended *property_info,
-                                          GDBusConnection *connection,
-                                          const char *sender,
-                                          GDBusMethodInvocation *invocation,
-                                          GVariant *value,
-                                          gpointer user_data);
-
 #endif /* __NETWORKMANAGER_MANAGER_H__ */
diff --git a/src/nm-netns.c b/src/nm-netns.c
index 5952a490..96ab2b35 100644
--- a/src/nm-netns.c
+++ b/src/nm-netns.c
@@ -38,6 +38,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 typedef struct {
 	NMPlatform *platform;
 	NMPNetns *platform_netns;
+	bool log_with_ptr;
 } NMNetnsPrivate;
 
 struct _NMNetns {
@@ -112,10 +113,13 @@ constructed (GObject *object)
 {
 	NMNetns *self = NM_NETNS (object);
 	NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self);
+	gboolean log_with_ptr;
 
 	if (!priv->platform)
 		g_return_if_reached ();
 
+	log_with_ptr = nm_platform_get_log_with_ptr (priv->platform);
+
 	priv->platform_netns = nm_platform_netns_get (priv->platform);
 
 	G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object);
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
index caae8b20..36f517a2 100644
--- a/src/nm-pacrunner-manager.c
+++ b/src/nm-pacrunner-manager.c
@@ -27,7 +27,7 @@
 #include "nm-proxy-config.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 
 #define PACRUNNER_DBUS_SERVICE "org.pacrunner"
 #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
@@ -466,18 +466,19 @@ static void
 pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data)
 {
 	Config *config = user_data;
+	NMPacrunnerManager *self;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *ret = NULL;
 
 	ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
-	if (!ret) {
-		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			goto out;
-		_LOG2D (config, "remove failed: %s", error->message);
+	if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		goto out;
-	}
 
-	_LOG2D (config, "removed");
+	self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling);
+	if (!ret)
+		_LOG2D (config, "remove failed: %s", error->message);
+	else
+		_LOG2D (config, "removed");
 
 out:
 	config_unref (config);
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 55b6caf6..2bf25d50 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -337,7 +337,7 @@ device_ip6_prefix_delegated (NMDevice *device,
 		/* Allocate a delegation delegation for new prefix. */
 		g_array_set_size (priv->ip6_prefix_delegations, i + 1);
 		delegation = &g_array_index (priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
-		delegation->subnets = g_hash_table_new (nm_direct_hash, NULL);
+		delegation->subnets = g_hash_table_new (NULL, NULL);
 		delegation->next_subnet = 0;
 	}
 
@@ -385,8 +385,7 @@ get_best_ip_device (NMPolicy *self,
                     gboolean fully_activated)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const CList *tmp_lst;
-	NMDevice *device;
+	const GSList *iter;
 	NMDevice *best_device;
 	NMDevice *prev_device;
 	guint32 best_metric = G_MAXUINT32;
@@ -401,7 +400,8 @@ get_best_ip_device (NMPolicy *self,
 	              ? (fully_activated ? priv->default_device4 : priv->activating_device4)
 	              : (fully_activated ? priv->default_device6 : priv->activating_device6);
 
-	nm_manager_for_each_device (priv->manager, device, tmp_lst) {
+	for (iter = nm_manager_get_devices (priv->manager); iter; iter = iter->next) {
+		NMDevice *device = NM_DEVICE (iter->data);
 		NMDeviceState state;
 		const NMPObject *r;
 		NMConnection *connection;
@@ -418,7 +418,7 @@ get_best_ip_device (NMPolicy *self,
 
 		r = nm_device_get_best_default_route (device, addr_family);
 		if (r) {
-			/* NOTE: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN,
+			/* XXX: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN,
 			 * which means it was injected by a VPN, not added by device.
 			 *
 			 * In this case, is it really the best device? Why do we even need the best
@@ -461,15 +461,15 @@ static gboolean
 all_devices_not_active (NMPolicy *self)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const CList *tmp_lst;
-	NMDevice *device;
+	const GSList *iter = nm_manager_get_devices (priv->manager);
 
-	nm_manager_for_each_device (priv->manager, device, tmp_lst) {
+	while (iter != NULL) {
 		NMDeviceState state;
 
-		state = nm_device_get_state (device);
+		state = nm_device_get_state (NM_DEVICE (iter->data));
 		if (   state <= NM_DEVICE_STATE_DISCONNECTED
 		    || state >= NM_DEVICE_STATE_DEACTIVATING) {
+			iter = g_slist_next (iter);
 			continue;
 		}
 		return FALSE;
@@ -1065,19 +1065,22 @@ update_ip_dns (NMPolicy *self, int addr_family)
 	gpointer ip_config;
 	const char *ip_iface = NULL;
 	NMVpnConnection *vpn = NULL;
+	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
 
 	nm_assert_addr_family (addr_family);
 
 	ip_config = get_best_ip_config (self, addr_family, &ip_iface, NULL, NULL, &vpn);
 	if (ip_config) {
+		if (vpn)
+			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
+
 		/* Tell the DNS manager this config is preferred by re-adding it with
 		 * a different IP config type.
 		 */
-		nm_dns_manager_set_ip_config (NM_POLICY_GET_PRIVATE (self)->dns_manager,
+		nm_dns_manager_add_ip_config (NM_POLICY_GET_PRIVATE (self)->dns_manager,
+		                              ip_iface,
 		                              ip_config,
-		                              vpn
-		                                ? NM_DNS_IP_CONFIG_TYPE_VPN
-		                                : NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE);
+		                              dns_type);
 	}
 
 	if (addr_family == AF_INET6)
@@ -1192,7 +1195,7 @@ auto_activate_device (NMPolicy *self,
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i, len;
 	gs_free_error GError *error = NULL;
-	gs_unref_object NMAuthSubject *subject = NULL;
+	NMAuthSubject *subject;
 	NMActiveConnection *ac;
 
 	nm_assert (NM_IS_POLICY (self));
@@ -1269,11 +1272,13 @@ auto_activate_device (NMPolicy *self,
 	 * activation fails in early stages without changing device
 	 * state.
 	 */
-	if (g_hash_table_add (priv->pending_active_connections, ac)) {
+	if (nm_g_hash_table_add (priv->pending_active_connections, ac)) {
 		g_signal_connect (ac, NM_ACTIVE_CONNECTION_STATE_CHANGED,
 		                  G_CALLBACK (pending_ac_state_changed), g_object_ref (self));
 		g_object_weak_ref (G_OBJECT (ac), (GWeakNotify) pending_ac_gone, self);
 	}
+
+	g_object_unref (subject);
 }
 
 static gboolean
@@ -1357,7 +1362,7 @@ process_secondaries (NMPolicy *self,
 			if (connected) {
 				_LOGD (LOGD_DEVICE, "secondary connection '%s' succeeded; active path '%s'",
 				       nm_active_connection_get_settings_connection_id (active),
-				       nm_dbus_object_get_path (NM_DBUS_OBJECT (active)));
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
 
 				/* Secondary connection activated */
 				secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, secondary_active);
@@ -1373,7 +1378,7 @@ process_secondaries (NMPolicy *self,
 			} else {
 				_LOGD (LOGD_DEVICE, "secondary connection '%s' failed; active path '%s'",
 				       nm_active_connection_get_settings_connection_id (active),
-				       nm_dbus_object_get_path (NM_DBUS_OBJECT (active)));
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
 
 				/* Secondary connection failed -> do not watch other connections */
 				priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
@@ -1669,7 +1674,7 @@ activate_secondary_connections (NMPolicy *self,
 		ac = nm_manager_activate_connection (priv->manager,
 		                                     settings_con,
 		                                     NULL,
-		                                     nm_dbus_object_get_path (NM_DBUS_OBJECT (req)),
+		                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (req)),
 		                                     device,
 		                                     nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)),
 		                                     NM_ACTIVATION_TYPE_MANAGED,
@@ -1708,6 +1713,7 @@ device_state_changed (NMDevice *device,
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 	NMActiveConnection *ac;
 	NMSettingsConnection *connection = nm_device_get_settings_connection (device);
+	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
 	NMSettingConnection *s_con = NULL;
@@ -1804,10 +1810,10 @@ device_state_changed (NMDevice *device,
 
 		ip4_config = nm_device_get_ip4_config (device);
 		if (ip4_config)
-			nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip4_config), NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		ip6_config = nm_device_get_ip6_config (device);
 		if (ip6_config)
-			nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip6_config), NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 
 		update_routing_and_dns (self, FALSE);
 
@@ -1900,24 +1906,50 @@ device_state_changed (NMDevice *device,
 }
 
 static void
-device_ip_config_changed (NMDevice *device,
-                          NMIPConfig *new_config,
-                          NMIPConfig *old_config,
-                          gpointer user_data)
+device_ip4_config_changed (NMDevice *device,
+                           NMIP4Config *new_config,
+                           NMIP4Config *old_config,
+                           gpointer user_data)
 {
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
-	int addr_family;
+	const char *ip_iface = nm_device_get_ip_iface (device);
 
-	nm_assert (new_config || old_config);
-	nm_assert (!new_config || NM_IS_IP_CONFIG (new_config));
-	nm_assert (!old_config || NM_IS_IP_CONFIG (old_config));
+	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
-	if (new_config) {
-		addr_family = nm_ip_config_get_addr_family (new_config);
-		nm_assert (!old_config || addr_family == nm_ip_config_get_addr_family (old_config));
-	} else
-		addr_family = nm_ip_config_get_addr_family (old_config);
+	/* We catch already all the IP events registering on the device state changes but
+	 * the ones where the IP changes but the device state keep stable (i.e., activated):
+	 * ignore IP config changes but when the device is in activated state.
+	 * Prevents unecessary changes to DNS information.
+	 */
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
+		if (old_config != new_config) {
+			if (old_config)
+				nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
+			if (new_config)
+				nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+		}
+		update_ip_dns (self, AF_INET);
+		update_ip4_routing (self, TRUE);
+		update_system_hostname (self, "ip4 conf");
+	} else {
+		/* Old configs get removed immediately */
+		if (old_config)
+			nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
+	}
+
+	nm_dns_manager_end_updates (priv->dns_manager, __func__);
+}
+
+static void
+device_ip6_config_changed (NMDevice *device,
+                           NMIP6Config *new_config,
+                           NMIP6Config *old_config,
+                           gpointer user_data)
+{
+	NMPolicyPrivate *priv = user_data;
+	NMPolicy *self = _PRIV_TO_SELF (priv);
+	const char *ip_iface = nm_device_get_ip_iface (device);
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
@@ -1928,24 +1960,18 @@ device_ip_config_changed (NMDevice *device,
 	 */
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
 		if (old_config != new_config) {
-			if (new_config)
-				nm_dns_manager_set_ip_config (priv->dns_manager, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 			if (old_config)
-				nm_dns_manager_set_ip_config (priv->dns_manager, old_config, NM_DNS_IP_CONFIG_TYPE_REMOVED);
+				nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
+			if (new_config)
+				nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		}
-		update_ip_dns (self, addr_family);
-		if (addr_family == AF_INET)
-			update_ip4_routing (self, TRUE);
-		else
-			update_ip6_routing (self, TRUE);
-		update_system_hostname (self,
-		                        addr_family == AF_INET
-		                          ? "ip4 conf"
-		                          : "ip6 conf");
+		update_ip_dns (self, AF_INET6);
+		update_ip6_routing (self, TRUE);
+		update_system_hostname (self, "ip6 conf");
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
-			nm_dns_manager_set_ip_config (priv->dns_manager, old_config, NM_DNS_IP_CONFIG_TYPE_REMOVED);
+			nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
 	}
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
@@ -1988,8 +2014,8 @@ devices_list_register (NMPolicy *self, NMDevice *device)
 
 	/* Connect state-changed with _after, so that the handler is invoked after other handlers. */
 	g_signal_connect_after (device, NM_DEVICE_STATE_CHANGED,          (GCallback) device_state_changed, priv);
-	g_signal_connect       (device, NM_DEVICE_IP4_CONFIG_CHANGED,     (GCallback) device_ip_config_changed, priv);
-	g_signal_connect       (device, NM_DEVICE_IP6_CONFIG_CHANGED,     (GCallback) device_ip_config_changed, priv);
+	g_signal_connect       (device, NM_DEVICE_IP4_CONFIG_CHANGED,     (GCallback) device_ip4_config_changed, priv);
+	g_signal_connect       (device, NM_DEVICE_IP6_CONFIG_CHANGED,     (GCallback) device_ip6_config_changed, priv);
 	g_signal_connect       (device, NM_DEVICE_IP6_PREFIX_DELEGATED,   (GCallback) device_ip6_prefix_delegated, priv);
 	g_signal_connect       (device, NM_DEVICE_IP6_SUBNET_NEEDED,      (GCallback) device_ip6_subnet_needed, priv);
 	g_signal_connect       (device, "notify::" NM_DEVICE_AUTOCONNECT, (GCallback) device_autoconnect_changed, priv);
@@ -2006,7 +2032,7 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data)
 
 	priv = NM_POLICY_GET_PRIVATE (self);
 
-	if (!g_hash_table_add (priv->devices, device))
+	if (!nm_g_hash_table_add (priv->devices, device))
 		g_return_if_reached ();
 
 	devices_list_register (self, device);
@@ -2044,16 +2070,21 @@ vpn_connection_activated (NMPolicy *self, NMVpnConnection *vpn)
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
+	const char *ip_iface;
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
+	ip_iface = nm_vpn_connection_get_ip_iface (vpn, TRUE);
+
+	/* Add the VPN connection's IP configs from DNS */
+
 	ip4_config = nm_vpn_connection_get_ip4_config (vpn);
 	if (ip4_config)
-		nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip4_config), NM_DNS_IP_CONFIG_TYPE_VPN);
+		nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_VPN);
 
 	ip6_config = nm_vpn_connection_get_ip6_config (vpn);
 	if (ip6_config)
-		nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip6_config), NM_DNS_IP_CONFIG_TYPE_VPN);
+		nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN);
 
 	update_routing_and_dns (self, TRUE);
 
@@ -2070,12 +2101,16 @@ vpn_connection_deactivated (NMPolicy *self, NMVpnConnection *vpn)
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
 	ip4_config = nm_vpn_connection_get_ip4_config (vpn);
-	if (ip4_config)
-		nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip4_config), NM_DNS_IP_CONFIG_TYPE_REMOVED);
+	if (ip4_config) {
+		/* Remove the VPN connection's IP4 config from DNS */
+		nm_dns_manager_remove_ip_config (priv->dns_manager, ip4_config);
+	}
 
 	ip6_config = nm_vpn_connection_get_ip6_config (vpn);
-	if (ip6_config)
-		nm_dns_manager_set_ip_config (priv->dns_manager, NM_IP_CONFIG_CAST (ip6_config), NM_DNS_IP_CONFIG_TYPE_REMOVED);
+	if (ip6_config) {
+		/* Remove the VPN connection's IP6 config from DNS */
+		nm_dns_manager_remove_ip_config (priv->dns_manager, ip6_config);
+	}
 
 	update_routing_and_dns (self, TRUE);
 
@@ -2185,13 +2220,12 @@ schedule_activate_all_cb (gpointer user_data)
 {
 	NMPolicy *self = user_data;
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const CList *tmp_lst;
-	NMDevice *device;
+	const GSList *iter;
 
 	priv->schedule_activate_all_id = 0;
 
-	nm_manager_for_each_device (priv->manager, device, tmp_lst)
-		schedule_activate_check (self, device);
+	for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter))
+		schedule_activate_check (self, iter->data);
 
 	return G_SOURCE_REMOVE;
 }
@@ -2225,8 +2259,7 @@ firewall_state_changed (NMFirewallManager *manager,
 {
 	NMPolicy *self = (NMPolicy *) user_data;
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const CList *tmp_lst;
-	NMDevice *device;
+	const GSList *iter;
 
 	if (initialized_now) {
 		/* the firewall manager was initializing, but all requests
@@ -2239,8 +2272,8 @@ firewall_state_changed (NMFirewallManager *manager,
 		return;
 
 	/* add interface of each device to correct zone */
-	nm_manager_for_each_device (priv->manager, device, tmp_lst)
-		nm_device_update_firewall_zone (device);
+	for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter))
+		nm_device_update_firewall_zone (iter->data);
 }
 
 static void
@@ -2285,13 +2318,14 @@ connection_updated (NMSettings *settings,
 {
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
-	const CList *tmp_lst;
+	const GSList *iter;
 	NMDevice *device = NULL;
-	NMDevice *dev;
 
 	if (by_user) {
 		/* find device with given connection */
-		nm_manager_for_each_device (priv->manager, dev, tmp_lst) {
+		for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter)) {
+			NMDevice *dev = NM_DEVICE (iter->data);
+
 			if (nm_device_get_settings_connection (dev) == connection) {
 				device = dev;
 				break;
@@ -2319,9 +2353,10 @@ _deactivate_if_active (NMPolicy *self, NMSettingsConnection *connection)
 	nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
 
 	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		NMActiveConnectionState state = nm_active_connection_get_state (ac);
 
 		if (   nm_active_connection_get_settings_connection (ac) == connection
-		    && (nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
+		    && (state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
 			if (!nm_manager_deactivate_connection (priv->manager,
 			                                       ac,
 			                                       NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
@@ -2356,7 +2391,7 @@ connection_flags_changed (NMSettings *settings,
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 
 	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
-	                  NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) {
+	                  NM_SETTINGS_CONNECTION_FLAGS_VISIBLE)) {
 		if (!nm_settings_connection_autoconnect_is_blocked (connection))
 			schedule_activate_all (self);
 	} else
@@ -2488,8 +2523,8 @@ nm_policy_init (NMPolicy *self)
 	else /* default - full mode */
 		priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL;
 
-	priv->devices = g_hash_table_new (nm_direct_hash, NULL);
-	priv->pending_active_connections = g_hash_table_new (nm_direct_hash, NULL);
+	priv->devices = g_hash_table_new (NULL, NULL);
+	priv->pending_active_connections = g_hash_table_new (NULL, NULL);
 	priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation));
 	g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation);
 }
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c
index 13ccbd48..e7d1d742 100644
--- a/src/nm-session-monitor.c
+++ b/src/nm-session-monitor.c
@@ -261,7 +261,7 @@ ck_init (NMSessionMonitor *monitor)
 
 	if (g_file_query_exists (file, NULL)) {
 		if ((monitor->ck.monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error))) {
-			monitor->ck.cache = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free);
+			monitor->ck.cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
 			g_signal_connect (monitor->ck.monitor,
 			                  "changed",
 			                  G_CALLBACK (ck_changed),
diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h
index da24992e..58beadcd 100644
--- a/src/nm-test-utils-core.h
+++ b/src/nm-test-utils-core.h
@@ -30,14 +30,6 @@
 
 /*****************************************************************************/
 
-#define NMTST_EXPECT_NM(level, msg)     NMTST_EXPECT ("NetworkManager", level, msg)
-
-#define NMTST_EXPECT_NM_ERROR(msg)      NMTST_EXPECT_NM (G_LOG_LEVEL_MESSAGE, "*<error> [*] "msg)
-#define NMTST_EXPECT_NM_WARN(msg)       NMTST_EXPECT_NM (G_LOG_LEVEL_MESSAGE, "*<warn>  [*] "msg)
-#define NMTST_EXPECT_NM_INFO(msg)       NMTST_EXPECT_NM (G_LOG_LEVEL_INFO,    "*<info>  [*] "msg)
-#define NMTST_EXPECT_NM_DEBUG(msg)      NMTST_EXPECT_NM (G_LOG_LEVEL_DEBUG,   "*<debug> [*] "msg)
-#define NMTST_EXPECT_NM_TRACE(msg)      NMTST_EXPECT_NM (G_LOG_LEVEL_DEBUG,   "*<trace> [*] "msg)
-
 static inline void
 nmtst_init_with_logging (int *argc, char ***argv, const char *log_level, const char *log_domains)
 {
@@ -321,6 +313,18 @@ nmtst_ip4_config_new (int ifindex)
 	return nm_ip4_config_new (multi_idx, ifindex);
 }
 
+static inline NMIP4Config *
+nmtst_ip4_config_clone (NMIP4Config *config)
+{
+	NMIP4Config *copy;
+
+	g_assert (config);
+	copy = nm_ip4_config_new (nm_ip4_config_get_multi_idx (config), -1);
+	g_assert (copy);
+	nm_ip4_config_replace (copy, config, NULL);
+	return copy;
+}
+
 #endif
 
 
@@ -336,6 +340,18 @@ nmtst_ip6_config_new (int ifindex)
 	return nm_ip6_config_new (multi_idx, ifindex);
 }
 
+static inline NMIP6Config *
+nmtst_ip6_config_clone (NMIP6Config *config)
+{
+	NMIP6Config *copy;
+
+	g_assert (config);
+	copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (config), -1);
+	g_assert (copy);
+	nm_ip6_config_replace (copy, config, NULL);
+	return copy;
+}
+
 #endif
 
 #endif /* __NM_TEST_UTILS_CORE_H__ */
diff --git a/src/nm-types.h b/src/nm-types.h
index e1991c28..794b0a1c 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -21,23 +21,24 @@
 #ifndef __NETWORKMANAGER_TYPES_H__
 #define __NETWORKMANAGER_TYPES_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_DAEMON)
-#error Cannot use this header.
+#ifdef __NM_UTILS_PRIVATE_H__
+#error "nm-utils-private.h" must not be used outside of libnm-core/. Do you want "nm-core-internal.h"?
 #endif
 
 #define _NM_SD_MAX_CLIENT_ID_LEN (sizeof (guint32) + 128)
 
 /* core */
-typedef struct _NMDBusObject         NMDBusObject;
+typedef struct _NMExportedObject     NMExportedObject;
 typedef struct _NMActiveConnection   NMActiveConnection;
 typedef struct _NMAuditManager       NMAuditManager;
 typedef struct _NMVpnConnection      NMVpnConnection;
 typedef struct _NMActRequest         NMActRequest;
 typedef struct _NMAuthSubject        NMAuthSubject;
-typedef struct _NMDBusManager        NMDBusManager;
+typedef struct _NMBusManager         NMBusManager;
 typedef struct _NMConfig             NMConfig;
 typedef struct _NMConfigData         NMConfigData;
-typedef struct _NMAcdManager         NMAcdManager;
+typedef struct _NMArpingManager      NMArpingManager;
+typedef struct _NMConnectionProvider NMConnectionProvider;
 typedef struct _NMConnectivity       NMConnectivity;
 typedef struct _NMDevice             NMDevice;
 typedef struct _NMDhcp4Config        NMDhcp4Config;
@@ -101,7 +102,6 @@ typedef enum {
 	NM_IP_CONFIG_SOURCE_KERNEL,
 	NM_IP_CONFIG_SOURCE_SHARED,
 	NM_IP_CONFIG_SOURCE_IP4LL,
-	NM_IP_CONFIG_SOURCE_IP6LL,
 	NM_IP_CONFIG_SOURCE_PPP,
 	NM_IP_CONFIG_SOURCE_WWAN,
 	NM_IP_CONFIG_SOURCE_VPN,
@@ -164,6 +164,7 @@ typedef enum {
 	NM_LINK_TYPE_OPENVSWITCH,
 	NM_LINK_TYPE_PPP,
 	NM_LINK_TYPE_SIT,
+	NM_LINK_TYPE_TAP,
 	NM_LINK_TYPE_TUN,
 	NM_LINK_TYPE_VETH,
 	NM_LINK_TYPE_VLAN,
@@ -197,7 +198,6 @@ typedef enum {
 	NMP_OBJECT_TYPE_LNK_MACVLAN,
 	NMP_OBJECT_TYPE_LNK_MACVTAP,
 	NMP_OBJECT_TYPE_LNK_SIT,
-	NMP_OBJECT_TYPE_LNK_TUN,
 	NMP_OBJECT_TYPE_LNK_VLAN,
 	NMP_OBJECT_TYPE_LNK_VXLAN,
 
@@ -238,6 +238,4 @@ typedef struct _NMSettingsConnection NMSettingsConnection;
 /* utils */
 typedef struct _NMUtilsIPv6IfaceId   NMUtilsIPv6IfaceId;
 
-#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) -42)
-
 #endif  /* NM_TYPES_H */
diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf
index fa74b280..6be1feb6 100644
--- a/src/org.freedesktop.NetworkManager.conf
+++ b/src/org.freedesktop.NetworkManager.conf
@@ -30,7 +30,7 @@
                 <allow send_destination="org.fedoraproject.FirewallD1"/>
 
                 <!-- Allow the custom name for the dnsmasq instance spawned by NM
-                     from the dns dnsmasq plugin to own its dbus name, and for
+                     from the dns dnsmasq plugin to own it's dbus name, and for
                      messages to be sent to it.
                  -->
                 <allow own="org.freedesktop.NetworkManager.dnsmasq"/>
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 06dd7e13..be430152 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -938,6 +938,7 @@ wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
 static void
 wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
 {
+	;
 }
 
 static guint32
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 0ed8fa06..e5961c7e 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -21,7 +21,6 @@
 
 #include "nm-linux-platform.h"
 
-#include <poll.h>
 #include <endian.h>
 #include <errno.h>
 #include <unistd.h>
@@ -37,14 +36,14 @@
 #include <linux/if_link.h>
 #include <linux/if_tun.h>
 #include <linux/if_tunnel.h>
-#include <linux/ip6_tunnel.h>
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
 #include <libudev.h>
 
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-setting-vlan.h"
 
-#include "nm-netlink.h"
 #include "nm-core-utils.h"
 #include "nmp-object.h"
 #include "nmp-netns.h"
@@ -80,6 +79,10 @@ enum {
 
 #define VLAN_FLAG_MVRP 0x8
 
+/* nm-internal error codes for libnl. Make sure they don't overlap. */
+#define _NLE_NM_NOBUFS 500
+#define _NLE_MSG_TRUNC 501
+
 /*****************************************************************************/
 
 #define IFQDISCSIZ                      32
@@ -124,18 +127,6 @@ enum {
 #define IFLA_IPTUN_MAX                  (__IFLA_IPTUN_MAX - 1)
 #endif
 
-#define IFLA_TUN_UNSPEC                 0
-#define IFLA_TUN_OWNER                  1
-#define IFLA_TUN_GROUP                  2
-#define IFLA_TUN_TYPE                   3
-#define IFLA_TUN_PI                     4
-#define IFLA_TUN_VNET_HDR               5
-#define IFLA_TUN_PERSIST                6
-#define IFLA_TUN_MULTI_QUEUE            7
-#define IFLA_TUN_NUM_QUEUES             8
-#define IFLA_TUN_NUM_DISABLED_QUEUES    9
-#define __IFLA_TUN_MAX                  10
-#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1)
 
 static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */);
 
@@ -292,7 +283,7 @@ typedef enum {
 
 #define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \
 	for ((iflags) = (DelayedActionType) 0x1LL; (iflags) <= DELAYED_ACTION_TYPE_MAX; (iflags) <<= 1) \
-		if (NM_FLAGS_ANY (flags_all, iflags))
+		if (NM_FLAGS_HAS (flags_all, iflags))
 
 typedef enum {
 	/* Negative values are errors from kernel. Add dummy member to
@@ -306,9 +297,13 @@ typedef enum {
 	WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL,
 	WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT,
 	WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING,
-	WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS,
 } WaitForNlResponseResult;
 
+typedef void (*WaitForNlResponseCallback) (NMPlatform *platform,
+                                           guint32 seq_number,
+                                           WaitForNlResponseResult seq_result,
+                                           gpointer user_data);
+
 static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data);
 static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink);
 static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name);
@@ -333,9 +328,7 @@ wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result)
 }
 
 static const char *
-wait_for_nl_response_to_string (WaitForNlResponseResult seq_result,
-                                const char *errmsg,
-                                char *buf, gsize buf_size)
+wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size)
 {
 	char *buf0 = buf;
 
@@ -350,13 +343,8 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result,
 		nm_utils_strbuf_append_str (&buf, &buf_size, "failure");
 		break;
 	default:
-		if (seq_result < 0) {
-			nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s%s%s)",
-			                        -((int) seq_result),
-			                        g_strerror (-((int) seq_result)),
-			                        errmsg ? " - " : "",
-			                        errmsg ?: "");
-		}
+		if (seq_result < 0)
+			nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s)", -((int) seq_result), g_strerror (-((int) seq_result)));
 		else
 			nm_utils_strbuf_append (&buf, &buf_size, "internal failure %d", (int) seq_result);
 		break;
@@ -550,7 +538,8 @@ static const LinkDesc linktypes[] = {
 	{ NM_LINK_TYPE_OPENVSWITCH,   "openvswitch", "openvswitch", NULL },
 	{ NM_LINK_TYPE_PPP,           "ppp",         NULL,          "ppp" },
 	{ NM_LINK_TYPE_SIT,           "sit",         "sit",         NULL },
-	{ NM_LINK_TYPE_TUN,           "tun",         "tun",         NULL },
+	{ NM_LINK_TYPE_TAP,           "tap",         NULL,          NULL },
+	{ NM_LINK_TYPE_TUN,           "tun",         NULL,          NULL },
 	{ NM_LINK_TYPE_VETH,          "veth",        "veth",        NULL },
 	{ NM_LINK_TYPE_VLAN,          "vlan",        "vlan",        "vlan" },
 	{ NM_LINK_TYPE_VXLAN,         "vxlan",       "vxlan",       "vxlan" },
@@ -811,25 +800,36 @@ _linktype_get_type (NMPlatform *platform,
 		    && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE)
 		    && nm_streq (ifname, obj->link.name)
 		    && (   !kind
-		        || nm_streq0 (kind, obj->link.kind))) {
+		        || !g_strcmp0 (kind, obj->link.kind))) {
 			nm_assert (obj->link.kind == g_intern_string (obj->link.kind));
 			*out_kind = obj->link.kind;
 			return obj->link.type;
 		}
 	}
 
-	/* we intern kind to not require us to keep the pointer alive. Essentially
-	 * leaking it in a global cache. That should be safe enough, because the
-	 * kind comes only from kernel messages, which depend on the number of
-	 * available drivers. So, there is not the danger that we leak uncontrolled
-	 * many kinds. */
 	*out_kind = g_intern_string (kind);
 
 	if (kind) {
 		for (i = 0; i < G_N_ELEMENTS (linktypes); i++) {
-			if (nm_streq0 (kind, linktypes[i].rtnl_type)) {
+			if (g_strcmp0 (kind, linktypes[i].rtnl_type) == 0)
 				return linktypes[i].nm_type;
+		}
+
+		if (!strcmp (kind, "tun")) {
+			NMPlatformTunProperties props;
+
+			if (   platform
+			    && nm_platform_link_tun_get_properties (platform, ifindex, &props)) {
+				if (!g_strcmp0 (props.mode, "tap"))
+					return NM_LINK_TYPE_TAP;
+				if (!g_strcmp0 (props.mode, "tun"))
+					return NM_LINK_TYPE_TUN;
 			}
+
+			/* try guessing the type using the link flags instead... */
+			if (flags & IFF_POINTOPOINT)
+				return NM_LINK_TYPE_TUN;
+			return NM_LINK_TYPE_TAP;
 		}
 	}
 
@@ -951,6 +951,137 @@ _nl_addattr_l (struct nlmsghdr *n,
 	return TRUE;
 }
 
+static void
+_nm_auto_nl_msg_cleanup (void *ptr)
+{
+	nlmsg_free (*((struct nl_msg **) ptr));
+}
+#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup)
+
+static const char *
+_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
+{
+	const char *b;
+	const char *s;
+	guint flags, flags_before;
+	const char *prefix;
+
+	nm_utils_to_string_buffer_init (&buf, &len);
+	b = buf;
+
+	switch (hdr->nlmsg_type) {
+	case RTM_NEWLINK:    s = "RTM_NEWLINK";  break;
+	case RTM_DELLINK:    s = "RTM_DELLINK";  break;
+	case RTM_NEWADDR:    s = "RTM_NEWADDR";  break;
+	case RTM_DELADDR:    s = "RTM_DELADDR";  break;
+	case RTM_NEWROUTE:   s = "RTM_NEWROUTE"; break;
+	case RTM_DELROUTE:   s = "RTM_DELROUTE"; break;
+	case RTM_NEWQDISC:   s = "RTM_NEWQDISC"; break;
+	case RTM_DELQDISC:   s = "RTM_DELQDISC"; break;
+	case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break;
+	case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break;
+	case NLMSG_NOOP:     s = "NLMSG_NOOP"; break;
+	case NLMSG_ERROR:    s = "NLMSG_ERROR"; break;
+	case NLMSG_DONE:     s = "NLMSG_DONE"; break;
+	case NLMSG_OVERRUN:  s = "NLMSG_OVERRUN"; break;
+	default:             s = NULL;       break;
+	}
+
+	if (s)
+		nm_utils_strbuf_append_str (&buf, &len, s);
+	else
+		nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type);
+
+	flags = hdr->nlmsg_flags;
+
+	if (!flags) {
+		nm_utils_strbuf_append_str (&buf, &len, ", flags 0");
+		goto flags_done;
+	}
+
+#define _F(f, n) \
+	G_STMT_START { \
+		if (NM_FLAGS_ALL (flags, f)) { \
+			flags &= ~(f); \
+			nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \
+			if (!flags) \
+				goto flags_done; \
+			prefix = ","; \
+		} \
+	} G_STMT_END
+
+	prefix = ", flags ";
+	flags_before = flags;
+	_F (NLM_F_REQUEST, "request");
+	_F (NLM_F_MULTI, "multi");
+	_F (NLM_F_ACK, "ack");
+	_F (NLM_F_ECHO, "echo");
+	_F (NLM_F_DUMP_INTR, "dump_intr");
+	_F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered");
+
+	if (flags_before != flags)
+		prefix = ";";
+
+	switch (hdr->nlmsg_type) {
+	case RTM_NEWLINK:
+	case RTM_NEWADDR:
+	case RTM_NEWROUTE:
+	case RTM_NEWQDISC:
+	case RTM_NEWTFILTER:
+		_F (NLM_F_REPLACE, "replace");
+		_F (NLM_F_EXCL, "excl");
+		_F (NLM_F_CREATE, "create");
+		_F (NLM_F_APPEND, "append");
+		break;
+	case RTM_GETLINK:
+	case RTM_GETADDR:
+	case RTM_GETROUTE:
+	case RTM_DELQDISC:
+	case RTM_DELTFILTER:
+		_F (NLM_F_DUMP, "dump");
+		_F (NLM_F_ROOT, "root");
+		_F (NLM_F_MATCH, "match");
+		_F (NLM_F_ATOMIC, "atomic");
+		break;
+	}
+
+#undef _F
+
+	if (flags_before != flags)
+		prefix = ";";
+	nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags);
+
+flags_done:
+
+	nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq);
+
+	return b;
+}
+
+static int
+_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
+               const struct nla_policy *policy)
+{
+	return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy);
+}
+#define nla_parse(...) _nl_nla_parse(__VA_ARGS__)
+
+static int
+_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
+                 int maxtype, const struct nla_policy *policy)
+{
+	return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy);
+}
+#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__)
+
+static int
+_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla,
+                      const struct nla_policy *policy)
+{
+	return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy);
+}
+#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__)
+
 /******************************************************************
  * NMPObject/netlink functions
  ******************************************************************/
@@ -1161,7 +1292,6 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
 		[IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 },
 		[IFLA_IPTUN_FLOWINFO]    = { .type = NLA_U32 },
 		[IFLA_IPTUN_PROTO]       = { .type = NLA_U8 },
-		[IFLA_IPTUN_FLAGS]       = { .type = NLA_U32 },
 	};
 	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
 	int err;
@@ -1196,8 +1326,6 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data)
 	}
 	if (tb[IFLA_IPTUN_PROTO])
 		props->proto = nla_get_u8 (tb[IFLA_IPTUN_PROTO]);
-	if (tb[IFLA_IPTUN_FLAGS])
-		props->flags = nla_get_u32 (tb[IFLA_IPTUN_FLAGS]);
 
 	return obj;
 }
@@ -1377,60 +1505,6 @@ _parse_lnk_sit (const char *kind, struct nlattr *info_data)
 
 /*****************************************************************************/
 
-static NMPObject *
-_parse_lnk_tun (const char *kind, struct nlattr *info_data)
-{
-	static const struct nla_policy policy[IFLA_TUN_MAX + 1] = {
-		[IFLA_TUN_OWNER]               = { .type = NLA_U32 },
-		[IFLA_TUN_GROUP]               = { .type = NLA_U32 },
-		[IFLA_TUN_TYPE]                = { .type = NLA_U8 },
-		[IFLA_TUN_PI]                  = { .type = NLA_U8 },
-		[IFLA_TUN_VNET_HDR]            = { .type = NLA_U8 },
-		[IFLA_TUN_PERSIST]             = { .type = NLA_U8 },
-		[IFLA_TUN_MULTI_QUEUE]         = { .type = NLA_U8 },
-		[IFLA_TUN_NUM_QUEUES]          = { .type = NLA_U32 },
-		[IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NLA_U32 },
-	};
-	struct nlattr *tb[IFLA_TUN_MAX + 1];
-	int err;
-	NMPObject *obj;
-	NMPlatformLnkTun *props;
-
-	if (!info_data || !nm_streq0 (kind, "tun"))
-		return NULL;
-
-	err = nla_parse_nested (tb, IFLA_TUN_MAX, info_data, policy);
-	if (err < 0)
-		return NULL;
-
-	if (!tb[IFLA_TUN_TYPE]) {
-		/* we require at least a type. */
-		return NULL;
-	}
-
-	obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_TUN, NULL);
-	props = &obj->lnk_tun;
-
-	props->type = nla_get_u8 (tb[IFLA_TUN_TYPE]);
-
-	props->pi = !!nla_get_u8_cond (tb, IFLA_TUN_PI, FALSE);
-	props->vnet_hdr = !!nla_get_u8_cond (tb, IFLA_TUN_VNET_HDR, FALSE);
-	props->multi_queue = !!nla_get_u8_cond (tb, IFLA_TUN_MULTI_QUEUE, FALSE);
-	props->persist = !!nla_get_u8_cond (tb, IFLA_TUN_PERSIST, FALSE);
-
-	if (tb[IFLA_TUN_OWNER]) {
-		props->owner_valid = TRUE;
-		props->owner = nla_get_u32 (tb[IFLA_TUN_OWNER]);
-	}
-	if (tb[IFLA_TUN_GROUP]) {
-		props->group_valid = TRUE;
-		props->group = nla_get_u32 (tb[IFLA_TUN_GROUP]);
-	}
-	return obj;
-}
-
-/*****************************************************************************/
-
 static gboolean
 _vlan_qos_mapping_from_nla (struct nlattr *nlattr,
                             const NMVlanQosMapping **out_map,
@@ -1862,9 +1936,6 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 	case NM_LINK_TYPE_SIT:
 		lnk_data = _parse_lnk_sit (nl_info_kind, nl_info_data);
 		break;
-	case NM_LINK_TYPE_TUN:
-		lnk_data = _parse_lnk_tun (nl_info_kind, nl_info_data);
-		break;
 	case NM_LINK_TYPE_VLAN:
 		lnk_data = _parse_lnk_vlan (nl_info_kind, nl_info_data);
 		break;
@@ -2594,7 +2665,8 @@ _nl_msg_new_link (int nlmsg_type,
 
 	nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK));
 
-	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!(msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags)))
+		g_return_val_if_reached (NULL);
 
 	if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -2636,6 +2708,8 @@ _nl_msg_new_address (int nlmsg_type,
 	nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR));
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!msg)
+		g_return_val_if_reached (NULL);
 
 	if (scope == -1) {
 		/* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */
@@ -2748,6 +2822,8 @@ _nl_msg_new_route (int nlmsg_type,
 	nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE));
 
 	msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags);
+	if (!msg)
+		g_return_val_if_reached (NULL);
 
 	if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -2845,6 +2921,8 @@ _nl_msg_new_qdisc (int nlmsg_type,
 	};
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!msg)
+		return NULL;
 
 	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -2919,6 +2997,8 @@ _nl_msg_new_tfilter (int nlmsg_type,
 	};
 
 	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!msg)
+		return NULL;
 
 	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
 		goto nla_put_failure;
@@ -2960,7 +3040,6 @@ typedef struct {
 	DelayedActionWaitForNlResponseType response_type;
 	gint64 timeout_abs_ns;
 	WaitForNlResponseResult *out_seq_result;
-	char **out_errmsg;
 	union {
 		gint *out_refresh_all_in_progess;
 		NMPObject **out_route_get;
@@ -3421,7 +3500,7 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data
 			                        (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND,
 			                        (int) data->response_type,
 			                        data->seq_result ? ", " : "",
-			                        data->seq_result ? wait_for_nl_response_to_string (data->seq_result, NULL, b, sizeof (b)) : "");
+			                        data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : "");
 		} else
 			nm_utils_strbuf_append_str (&buf, &buf_size, " (any)");
 		break;
@@ -3503,58 +3582,27 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform,
 }
 
 static void
-delayed_action_wait_for_nl_response_complete_check (NMPlatform *platform,
-                                                    WaitForNlResponseResult force_result,
-                                                    guint32 *out_next_seq_number,
-                                                    gint64 *out_next_timeout_abs_ns,
-                                                    gint64 *p_now_ns)
+delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform,
+                                                  WaitForNlResponseResult fallback_result)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
-	guint i;
-	guint32 next_seq_number = 0;
-	gint64 next_timeout_abs_ns = 0;
-	gint now_ns = 0;
-
-	for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) {
-		const DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i);
-
-		if (data->seq_result)
-			delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result);
-		else if (   p_now_ns
-		         && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) >= data->timeout_abs_ns)) {
-			/* the caller can optionally check for timeout by providing a p_now_ns argument. */
-			delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT);
-		} else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN)
-			delayed_action_wait_for_nl_response_complete (platform, i, force_result);
-		else {
-			if (   next_seq_number == 0
-			    || next_timeout_abs_ns > data->timeout_abs_ns) {
-				next_seq_number = data->seq_number;
-				next_timeout_abs_ns = data->timeout_abs_ns;
-			}
-			i++;
-		}
-	}
 
-	if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) {
-		nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
-		nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0);
-	}
+	if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
+		while (priv->delayed_action.list_wait_for_nl_response->len > 0) {
+			const DelayedActionWaitForNlResponseData *data;
+			guint idx = priv->delayed_action.list_wait_for_nl_response->len - 1;
+			WaitForNlResponseResult r;
 
-	NM_SET_OUT (out_next_seq_number, next_seq_number);
-	NM_SET_OUT (out_next_timeout_abs_ns, next_timeout_abs_ns);
-	NM_SET_OUT (p_now_ns, now_ns);
-}
+			data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx);
 
-static void
-delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform,
-                                                  WaitForNlResponseResult fallback_result)
-{
-	delayed_action_wait_for_nl_response_complete_check (platform,
-	                                                    fallback_result,
-	                                                    NULL,
-	                                                    NULL,
-	                                                    NULL);
+			/* prefer the result that we already have. */
+			r = data->seq_result ? : fallback_result;
+
+			delayed_action_wait_for_nl_response_complete (platform, idx, r);
+		}
+	}
+	nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
+	nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0);
 }
 
 /*****************************************************************************/
@@ -3737,7 +3785,6 @@ static void
 delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform,
                                               guint32 seq_number,
                                               WaitForNlResponseResult *out_seq_result,
-                                              char **out_errmsg,
                                               DelayedActionWaitForNlResponseType response_type,
                                               gpointer response_out_data)
 {
@@ -3745,7 +3792,6 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform,
 		.seq_number = seq_number,
 		.timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)),
 		.out_seq_result = out_seq_result,
-		.out_errmsg = out_errmsg,
 		.response_type = response_type,
 		.response.out_data = response_out_data,
 	};
@@ -3930,46 +3976,40 @@ cache_on_change (NMPlatform *platform,
 		    && (obj_new && obj_new->_link.netlink.is_in_netlink)
 		    && (!obj_old || !obj_old->_link.netlink.is_in_netlink))
 		{
-			gboolean re_request_link = FALSE;
-			const NMPlatformLnkTun *lnk_tun;
-
-			if (   !obj_new->_link.netlink.lnk
-			    && NM_IN_SET (obj_new->link.type, NM_LINK_TYPE_GRE,
-			                                      NM_LINK_TYPE_IP6TNL,
-			                                      NM_LINK_TYPE_INFINIBAND,
-			                                      NM_LINK_TYPE_MACVLAN,
-			                                      NM_LINK_TYPE_MACVLAN,
-			                                      NM_LINK_TYPE_SIT,
-			                                      NM_LINK_TYPE_TUN,
-			                                      NM_LINK_TYPE_VLAN,
-			                                      NM_LINK_TYPE_VXLAN)) {
+			if (!obj_new->_link.netlink.lnk) {
 				/* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that
 				 * kernel didn't send this notification, thus when we first learn about a link
 				 * that lacks an lnk_data we re-request it again.
 				 *
 				 * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */
-				re_request_link = TRUE;
-			} else if (   obj_new->link.type == NM_LINK_TYPE_TUN
-			           && obj_new->_link.netlink.lnk
-			           && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun)
-			           && !lnk_tun->persist
-			           && lnk_tun->pi
-			           && !lnk_tun->vnet_hdr
-			           && !lnk_tun->multi_queue
-			           && !lnk_tun->owner_valid
-			           && !lnk_tun->group_valid) {
-				/* kernel has/had a know issue that the first notification for TUN device would
-				 * be sent with invalid parameters. The message looks like that kind, so refetch
-				 * it. */
-				re_request_link = TRUE;
-			} else if (   obj_new->link.type == NM_LINK_TYPE_VETH
-			           && obj_new->link.parent == 0) {
+				switch (obj_new->link.type) {
+				case NM_LINK_TYPE_GRE:
+				case NM_LINK_TYPE_IP6TNL:
+				case NM_LINK_TYPE_INFINIBAND:
+				case NM_LINK_TYPE_MACVLAN:
+				case NM_LINK_TYPE_MACVTAP:
+				case NM_LINK_TYPE_SIT:
+				case NM_LINK_TYPE_VLAN:
+				case NM_LINK_TYPE_VXLAN:
+					delayed_action_schedule (platform,
+					                         DELAYED_ACTION_TYPE_REFRESH_LINK,
+					                         GINT_TO_POINTER (obj_new->link.ifindex));
+					break;
+				default:
+					break;
+				}
+			}
+			if (   obj_new->link.type == NM_LINK_TYPE_VETH
+			    && obj_new->link.parent == 0) {
 				/* the initial notification when adding a veth pair can lack the parent/IFLA_LINK
 				 * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827).
 				 * Request it again. */
-				re_request_link = TRUE;
-			} else if (   obj_new->link.type == NM_LINK_TYPE_ETHERNET
-			           && obj_new->link.addr.len == 0) {
+				delayed_action_schedule (platform,
+				                         DELAYED_ACTION_TYPE_REFRESH_LINK,
+				                         GINT_TO_POINTER (obj_new->link.ifindex));
+			}
+			if (   obj_new->link.type == NM_LINK_TYPE_ETHERNET
+			    && obj_new->link.addr.len == 0) {
 				/* Due to a kernel bug, we sometimes receive spurious NEWLINK
 				 * messages after a wifi interface has disappeared. Since the
 				 * link is not present anymore we can't determine its type and
@@ -3977,9 +4017,6 @@ cache_on_change (NMPlatform *platform,
 				 * specified.  Request the link again to check if it really
 				 * exists.  https://bugzilla.redhat.com/show_bug.cgi?id=1302037
 				 */
-				re_request_link = TRUE;
-			}
-			if (re_request_link) {
 				delayed_action_schedule (platform,
 				                         DELAYED_ACTION_TYPE_REFRESH_LINK,
 				                         GINT_TO_POINTER (obj_new->link.ifindex));
@@ -4030,10 +4067,8 @@ cache_on_change (NMPlatform *platform,
 static guint32
 _nlh_seq_next_get (NMLinuxPlatformPrivate *priv)
 {
-	/* generate a new sequence number, but never return zero.
-	 * Wrapping numbers are not a problem, because we don't rely
-	 * on strictly increasing sequence numbers. */
-	return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next);
+	/* generate a new sequence number, but skip zero. */
+	return priv->nlh_seq_next++ ?: priv->nlh_seq_next++;
 }
 
 /**
@@ -4044,13 +4079,12 @@ _nlh_seq_next_get (NMLinuxPlatformPrivate *priv)
  * @response_type:
  * @response_out_data:
  *
- * Returns: 0 on success or a negative errno.
+ * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror.
  */
 static int
 _nl_send_nlmsghdr (NMPlatform *platform,
                    struct nlmsghdr *nlhdr,
                    WaitForNlResponseResult *out_seq_result,
-                   char **out_errmsg,
                    DelayedActionWaitForNlResponseType response_type,
                    gpointer response_out_data)
 {
@@ -4095,7 +4129,7 @@ again:
 		}
 	}
 
-	delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg,
+	delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result,
 	                                              response_type, response_out_data);
 	return 0;
 }
@@ -4114,7 +4148,6 @@ static int
 _nl_send_nlmsg (NMPlatform *platform,
                 struct nl_msg *nlmsg,
                 WaitForNlResponseResult *out_seq_result,
-                char **out_errmsg,
                 DelayedActionWaitForNlResponseType response_type,
                 gpointer response_out_data)
 {
@@ -4133,7 +4166,7 @@ _nl_send_nlmsg (NMPlatform *platform,
 		return nle;
 	}
 
-	delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_errmsg,
+	delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result,
 	                                              response_type, response_out_data);
 	return 0;
 }
@@ -4171,7 +4204,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha
 	                          0,
 	                          0);
 	if (nlmsg) {
-		nle = _nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+		nle = _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 		if (nle < 0) {
 			_LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)",
 			       ifindex, name ?: "",
@@ -4230,6 +4263,8 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 		 * because we need the sequence number.
 		 */
 		nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP);
+		if (!nlmsg)
+			continue;
 
 		if (   klass->obj_type == NMP_OBJECT_TYPE_QDISC
 		    || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) {
@@ -4246,7 +4281,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 		if (nle < 0)
 			continue;
 
-		if (_nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) {
+		if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) {
 			nm_assert (*out_refresh_all_in_progess > 0);
 			*out_refresh_all_in_progess -= 1;
 		}
@@ -4290,7 +4325,7 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number)
 }
 
 static void
-event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result, const char *msg)
+event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult seq_result)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	DelayedActionWaitForNlResponseData *data;
@@ -4309,13 +4344,11 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu
 				/* We potentially receive many parts partial responses for the same sequence number.
 				 * Thus, we only remember the result, and collect it later. */
 				if (data->seq_result < 0) {
-					/* we already saw an error for this sequence number.
+					/* we already saw an error for this seqence number.
 					 * Preserve it. */
 				} else if (   seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN
 				           || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN)
 					data->seq_result = seq_result;
-				if (data->out_errmsg && !*data->out_errmsg)
-					*data->out_errmsg = g_strdup (msg);
 				return;
 			}
 		}
@@ -4358,7 +4391,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	obj = nmp_object_new_from_nl (platform, cache, msg, id_only);
 	if (!obj) {
 		_LOGT ("event-notification: %s: ignore",
-		       nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)));
+		       _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)));
 		return;
 	}
 
@@ -4376,7 +4409,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	}
 
 	_LOGT ("event-notification: %s%s: %s",
-	       nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)),
+	       _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)),
 	       is_dump ? ", in-dump" : "",
 	       nmp_object_to_string (obj,
 	                             id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC,
@@ -4514,14 +4547,13 @@ do_add_link_with_lookup (NMPlatform *platform,
 {
 	const NMPObject *obj = NULL;
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	int nle;
 	char s_buf[256];
 	NMPCache *cache = nm_platform_get_cache (platform);
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)",
 		       name,
@@ -4541,7 +4573,7 @@ do_add_link_with_lookup (NMPlatform *platform,
 	        "do-add-link[%s/%s]: %s",
 	        name,
 	        nm_link_type_to_string (link_type),
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)));
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
 
 	if (out_link) {
 		obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL);
@@ -4558,7 +4590,6 @@ do_add_addrroute (NMPlatform *platform,
                   gboolean suppress_netlink_failure)
 {
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	int nle;
 	char s_buf[256];
 
@@ -4568,7 +4599,7 @@ do_add_addrroute (NMPlatform *platform,
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)",
 		       NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
@@ -4589,7 +4620,7 @@ do_add_addrroute (NMPlatform *platform,
 	        "do-add-%s[%s]: %s",
 	        NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
 	        nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)));
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
 
 	if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) {
 		/* In rare cases, the object is not yet ready as we received the ACK from
@@ -4610,7 +4641,6 @@ static gboolean
 do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg)
 {
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	int nle;
 	char s_buf[256];
 	gboolean success;
@@ -4618,7 +4648,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		_LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)",
 		       NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
@@ -4650,7 +4680,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *
 	        "do-delete-%s[%s]: %s%s",
 	        NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
 	        nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)),
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)),
 	        log_detail);
 
 	if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id),
@@ -4681,7 +4711,6 @@ do_change_link (NMPlatform *platform,
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	int nle;
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	char s_buf[256];
 	NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS;
 	NMLogLevel log_level = LOGL_DEBUG;
@@ -4697,7 +4726,7 @@ do_change_link (NMPlatform *platform,
 	}
 
 retry:
-	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		log_level = LOGL_ERR;
 		log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)",
@@ -4754,7 +4783,7 @@ out:
 	        "do-change-link[%d]: %s changing link: %s%s",
 	        ifindex,
 	        log_result,
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)),
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)),
 	        log_detail);
 	return result;
 }
@@ -4831,12 +4860,6 @@ link_refresh (NMPlatform *platform, int ifindex)
 	return !!nm_platform_link_get_obj (platform, ifindex, TRUE);
 }
 
-static void
-refresh_all (NMPlatform *platform, NMPObjectType obj_type)
-{
-	do_request_one_type (platform, obj_type);
-}
-
 static gboolean
 link_set_netns (NMPlatform *platform,
                 int ifindex,
@@ -5369,7 +5392,6 @@ link_ip6tnl_add (NMPlatform *platform,
 	            & IP6_FLOWINFO_TCLASS_MASK;
 	NLA_PUT_U32 (nlmsg, IFLA_IPTUN_FLOWINFO, htonl (flowinfo));
 	NLA_PUT_U8 (nlmsg, IFLA_IPTUN_PROTO, props->proto);
-	NLA_PUT_U32 (nlmsg, IFLA_IPTUN_FLAGS, props->flags);
 
 	nla_nest_end (nlmsg, data);
 	nla_nest_end (nlmsg, info);
@@ -5593,62 +5615,6 @@ nla_put_failure:
 }
 
 static gboolean
-link_tun_add (NMPlatform *platform,
-              const char *name,
-              const NMPlatformLnkTun *props,
-              const NMPlatformLink **out_link,
-              int *out_fd)
-{
-	const NMPObject *obj;
-	struct ifreq ifr = { };
-	nm_auto_close int fd = -1;
-
-	nm_assert (NM_IN_SET (props->type, IFF_TAP, IFF_TUN));
-	nm_assert (props->persist || out_fd);
-
-	fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
-	if (fd < 0)
-		return FALSE;
-
-	nm_utils_ifname_cpy (ifr.ifr_name, name);
-	ifr.ifr_flags =   ((short) props->type)
-	                | ((short) IFF_TUN_EXCL)
-	                | (!props->pi          ? (short) IFF_NO_PI          : (short) 0)
-	                | ( props->vnet_hdr    ? (short) IFF_VNET_HDR       : (short) 0)
-	                | ( props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0);
-	if (ioctl (fd, TUNSETIFF, &ifr))
-		return FALSE;
-
-	if (props->owner_valid) {
-		if (ioctl (fd, TUNSETOWNER, (uid_t) props->owner))
-			return FALSE;
-	}
-
-	if (props->group_valid) {
-		if (ioctl (fd, TUNSETGROUP, (gid_t) props->group))
-			return FALSE;
-	}
-
-	if (props->persist) {
-		if (ioctl (fd, TUNSETPERSIST, 1))
-			return FALSE;
-	}
-
-	do_request_link (platform, 0, name);
-	obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform),
-	                                  0, name, FALSE,
-	                                  NM_LINK_TYPE_TUN,
-	                                  NULL, NULL);
-
-	if (!obj)
-		return FALSE;
-
-	NM_SET_OUT (out_link, &obj->link);
-	NM_SET_OUT (out_fd, nm_steal_fd (&fd));
-	return TRUE;
-}
-
-static gboolean
 link_vxlan_add (NMPlatform *platform,
                 const char *name,
                 const NMPlatformLnkVxlan *props,
@@ -5888,6 +5854,64 @@ link_vlan_change (NMPlatform *platform,
 	return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS;
 }
 
+static int
+tun_add (NMPlatform *platform, const char *name, gboolean tap,
+         gint64 owner, gint64 group, gboolean pi, gboolean vnet_hdr,
+         gboolean multi_queue, const NMPlatformLink **out_link)
+{
+	const NMPObject *obj;
+	struct ifreq ifr = { };
+	int fd;
+
+	fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return FALSE;
+
+	nm_utils_ifname_cpy (ifr.ifr_name, name);
+	ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN;
+
+	if (!pi)
+		ifr.ifr_flags |= IFF_NO_PI;
+	if (vnet_hdr)
+		ifr.ifr_flags |= IFF_VNET_HDR;
+	if (multi_queue)
+		ifr.ifr_flags |= NM_IFF_MULTI_QUEUE;
+
+	if (ioctl (fd, TUNSETIFF, &ifr)) {
+		nm_close (fd);
+		return FALSE;
+	}
+
+	if (owner >= 0 && owner < G_MAXINT32) {
+		if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) {
+			nm_close (fd);
+			return FALSE;
+		}
+	}
+
+	if (group >= 0 && group < G_MAXINT32) {
+		if (ioctl (fd, TUNSETGROUP, (gid_t) group)) {
+			nm_close (fd);
+			return FALSE;
+		}
+	}
+
+	if (ioctl (fd, TUNSETPERSIST, 1)) {
+		nm_close (fd);
+		return FALSE;
+	}
+	do_request_link (platform, 0, name);
+	obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform),
+	                                  0, name, FALSE,
+	                                  tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN,
+	                                  NULL, NULL);
+	if (out_link)
+		*out_link = obj ? &obj->link : NULL;
+
+	nm_close (fd);
+	return !!obj;
+}
+
 static gboolean
 link_enslave (NMPlatform *platform, int master, int slave)
 {
@@ -6429,7 +6453,7 @@ ip_route_get (NMPlatform *platform,
 		}
 
 		seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-		nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, NULL, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route);
+		nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route);
 		if (nle < 0) {
 			_LOGE ("get-route: failure sending netlink request \"%s\" (%d)",
 			       g_strerror (-nle), -nle);
@@ -6468,7 +6492,6 @@ qdisc_add (NMPlatform *platform,
            const NMPlatformQdisc *qdisc)
 {
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	int nle;
 	char s_buf[256];
 	nm_auto_nlmsg struct nl_msg *msg = NULL;
@@ -6477,7 +6500,7 @@ qdisc_add (NMPlatform *platform,
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-qdisc: failed sending netlink request \"%s\" (%d)",
 		      nl_geterror (nle), -nle);
@@ -6492,7 +6515,7 @@ qdisc_add (NMPlatform *platform,
 	            ? LOGL_DEBUG
 	            : LOGL_WARN,
 	        "do-add-qdisc: %s",
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)));
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
 
 	if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
 		return NM_PLATFORM_ERROR_SUCCESS;
@@ -6508,7 +6531,6 @@ tfilter_add (NMPlatform *platform,
              const NMPlatformTfilter *tfilter)
 {
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
-	gs_free char *errmsg = NULL;
 	int nle;
 	char s_buf[256];
 	nm_auto_nlmsg struct nl_msg *msg = NULL;
@@ -6517,7 +6539,7 @@ tfilter_add (NMPlatform *platform,
 
 	event_handler_read_netlink (platform, FALSE);
 
-	nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
 	if (nle < 0) {
 		_LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)",
 		      nl_geterror (nle), -nle);
@@ -6532,7 +6554,7 @@ tfilter_add (NMPlatform *platform,
 	            ? LOGL_DEBUG
 	            : LOGL_WARN,
 	        "do-add-tfilter: %s",
-	        wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf)));
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
 
 	if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
 		return NM_PLATFORM_ERROR_SUCCESS;
@@ -6563,12 +6585,15 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	struct nl_sock *sk = priv->nlh;
-	int n;
-	int err = 0;
-	gboolean multipart = 0;
-	gboolean interrupted = FALSE;
+	int n, err = 0, multipart = 0, interrupted = 0;
 	struct nlmsghdr *hdr;
 	WaitForNlResponseResult seq_result;
+
+	/*
+	nla is passed on to not only to nl_recv() but may also be passed
+	to a function pointer provided by the caller which may or may not
+	initialize the variable. Thomas Graf.
+	*/
 	struct sockaddr_nl nla = {0};
 	nm_auto_free struct ucred *creds = NULL;
 	nm_auto_free unsigned char *buf = NULL;
@@ -6576,28 +6601,56 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events)
 continue_reading:
 	g_clear_pointer (&buf, free);
 	g_clear_pointer (&creds, free);
+	errno = 0;
 	n = nl_recv (sk, &nla, &buf, &creds);
 
 	if (n <= 0) {
-
-		if (n == -NLE_MSG_TRUNC) {
-			int buf_size;
-
-			/* the message receive buffer was too small. We lost one message, which
-			 * is unfortunate. Try to double the buffer size for the next time. */
-			buf_size = nl_socket_get_msg_buf_size (sk);
-			if (buf_size < 512*1024) {
-				buf_size *= 2;
-				_LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size);
-				if (nl_socket_set_msg_buf_size (sk, buf_size) < 0)
-					nm_assert_not_reached ();
-				if (!handle_events)
-					goto continue_reading;
-			}
+		/* workaround libnl3 <= 3.2.15 returning danling pointers in case nl_recv()
+		 * fails. Fixed by libnl3 69468517d0de1675d80f24661ff57a5dbac7275c. */
+		buf = NULL;
+		creds = NULL;
+	}
+
+	switch (n) {
+	case 0:
+		/* Work around a libnl bug fixed in 3.2.22 (375a6294) */
+		if (errno == EAGAIN) {
+			/* EAGAIN is equal to EWOULDBLOCK. If it would not be, we'd have to
+			 * workaround libnl3 mapping EWOULDBLOCK to -NLE_FAILURE. */
+			G_STATIC_ASSERT (EAGAIN == EWOULDBLOCK);
+			n = -NLE_AGAIN;
+		}
+		break;
+	case -NLE_MSG_TRUNC: {
+		int buf_size;
+
+		/* the message receive buffer was too small. We lost one message, which
+		 * is unfortunate. Try to double the buffer size for the next time. */
+		buf_size = nl_socket_get_msg_buf_size (sk);
+		if (buf_size < 512*1024) {
+			buf_size *= 2;
+			_LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size);
+			if (nl_socket_set_msg_buf_size (sk, buf_size) < 0)
+				nm_assert_not_reached ();
+			if (!handle_events)
+				goto continue_reading;
 		}
+		n = -_NLE_MSG_TRUNC;
+		break;
+	}
+	case -NLE_NOMEM:
+		if (errno == ENOBUFS) {
+			/* we are very much interested in a overrun of the receive buffer.
+			 * nl_recv() maps all kinds of errors to NLE_NOMEM, so check also
+			 * for errno explicitly. And if so, hack our own return code to signal
+			 * the overrun. */
+			n = -_NLE_NM_NOBUFS;
+		}
+		break;
+	}
 
+	if (n <= 0)
 		return n;
-	}
 
 	hdr = (struct nlmsghdr *) buf;
 	while (nlmsg_ok (hdr, n)) {
@@ -6606,9 +6659,12 @@ continue_reading:
 		gboolean process_valid_msg = FALSE;
 		guint32 seq_number;
 		char buf_nlmsghdr[400];
-		const char *extack_msg = NULL;
 
-		msg = nlmsg_alloc_convert (hdr);
+		msg = nlmsg_convert (hdr);
+		if (!msg) {
+			err = -NLE_NOMEM;
+			goto out;
+		}
 
 		nlmsg_set_proto (msg, NETLINK_ROUTE);
 		nlmsg_set_src (msg, &nla);
@@ -6623,13 +6679,13 @@ continue_reading:
 		}
 
 		_LOGt ("netlink: recvmsg: new message %s",
-		       nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)));
+		       _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)));
 
 		if (creds)
 			nlmsg_set_creds (msg, creds);
 
 		if (hdr->nlmsg_flags & NLM_F_MULTI)
-			multipart = TRUE;
+			multipart = 1;
 
 		if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
 			/*
@@ -6637,7 +6693,7 @@ continue_reading:
 			 * all messages until a NLMSG_DONE is
 			 * received and report the inconsistency.
 			 */
-			interrupted = TRUE;
+			interrupted = 1;
 		}
 
 		/* Other side wishes to see an ack for this message */
@@ -6652,7 +6708,7 @@ continue_reading:
 			 * usually the end of a message and therefore we slip
 			 * out of the loop by default. the user may overrule
 			 * this action by skipping this packet. */
-			multipart = FALSE;
+			multipart = 0;
 			seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
 		} else if (hdr->nlmsg_type == NLMSG_NOOP) {
 			/* Message to be ignored, the default action is to
@@ -6679,28 +6735,10 @@ continue_reading:
 			} else if (e->error) {
 				int errsv = e->error > 0 ? e->error : -e->error;
 
-				if (   NM_FLAGS_HAS (hdr->nlmsg_flags, NLM_F_ACK_TLVS)
-				    && hdr->nlmsg_len >= sizeof (*e) + e->msg.nlmsg_len) {
-					static const struct nla_policy policy[NLMSGERR_ATTR_MAX + 1] = {
-						[NLMSGERR_ATTR_MSG]     = { .type = NLA_STRING },
-						[NLMSGERR_ATTR_OFFS]    = { .type = NLA_U32 },
-					};
-					struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
-					struct nlattr *tlvs;
-
-					tlvs = (struct nlattr *) ((char *) e + sizeof (*e) + e->msg.nlmsg_len - NLMSG_HDRLEN);
-					if (!nla_parse (tb, NLMSGERR_ATTR_MAX, tlvs,
-					                hdr->nlmsg_len - sizeof (*e) - e->msg.nlmsg_len, policy)) {
-						if (tb[NLMSGERR_ATTR_MSG])
-							extack_msg = nla_get_string (tb[NLMSGERR_ATTR_MSG]);
-					}
-				}
-
 				/* Error message reported back from kernel. */
-				_LOGD ("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
+				_LOGD ("netlink: recvmsg: error message from kernel: %s (%d) for request %d",
 				       strerror (errsv),
 				       errsv,
-				       NM_PRINT_FMT_QUOTED (extack_msg, " \"", extack_msg, "\"", ""),
 				       nlmsg_hdr (msg)->nlmsg_seq);
 				seq_result = -errsv;
 			} else
@@ -6729,7 +6767,7 @@ continue_reading:
 			seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
 		}
 
-		event_seq_check (platform, seq_number, seq_result, extack_msg);
+		event_seq_check (platform, seq_number, seq_result);
 
 		if (abort_parsing)
 			goto stop;
@@ -6749,9 +6787,9 @@ stop:
 		 * Repeat reading. */
 		goto continue_reading;
 	}
-
+out:
 	if (interrupted)
-		return -NLE_DUMP_INTR;
+		err = -NLE_DUMP_INTR;
 	return err;
 }
 
@@ -6762,50 +6800,46 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
-	int r;
+	int r, nle;
 	struct pollfd pfd;
 	gboolean any = FALSE;
+	gint64 now_ns;
 	int timeout_ms;
+	guint i;
 	struct {
 		guint32 seq_number;
 		gint64 timeout_abs_ns;
-		gint64 now_ns;
-	} next;
+	} data_next;
 
-	if (!nm_platform_netns_push (platform, &netns)) {
-		delayed_action_wait_for_nl_response_complete_all (platform,
-		                                                  WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS);
+	if (!nm_platform_netns_push (platform, &netns))
 		return FALSE;
-	}
 
-	for (;;) {
-		for (;;) {
-			int nle;
+	while (TRUE) {
+
+		while (TRUE) {
 
 			nle = event_handler_recvmsgs (platform, TRUE);
 
 			if (nle < 0) {
 				switch (nle) {
-				case -EAGAIN:
+				case -NLE_AGAIN:
 					goto after_read;
 				case -NLE_DUMP_INTR:
 					_LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
 					break;
-				case -NLE_MSG_TRUNC:
-				case -ENOBUFS:
+				case -_NLE_MSG_TRUNC:
+				case -_NLE_NM_NOBUFS:
 					_LOGI ("netlink: read: %s. Need to resynchronize platform cache",
 					       ({
 					            const char *_reason = "unknown";
 					            switch (nle) {
-					            case -NLE_MSG_TRUNC: _reason = "message truncated";       break;
-					            case -ENOBUFS:       _reason = "too many netlink events"; break;
+					            case -_NLE_MSG_TRUNC: _reason = "message truncated";       break;
+					            case -_NLE_NM_NOBUFS: _reason = "too many netlink events"; break;
 					            }
 					            _reason;
 					       }));
 					event_handler_recvmsgs (platform, FALSE);
-					delayed_action_wait_for_nl_response_complete_all (platform,
-					                                                  WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC);
-
+					delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC);
 					delayed_action_schedule (platform,
 					                         DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS |
 					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
@@ -6829,23 +6863,39 @@ after_read:
 		if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
 			return any;
 
-		delayed_action_wait_for_nl_response_complete_check (platform,
-		                                                    WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN,
-		                                                    &next.seq_number,
-		                                                    &next.timeout_abs_ns,
-		                                                    &next.now_ns);
+		now_ns = 0;
+		data_next.seq_number = 0;
+		data_next.timeout_abs_ns = 0;
+
+		for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) {
+			DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i);
+
+			if (data->seq_result)
+				delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result);
+			else if ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) > data->timeout_abs_ns)
+				delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT);
+			else {
+				i++;
+
+				if (   data_next.seq_number == 0
+				    || data_next.timeout_abs_ns > data->timeout_abs_ns) {
+					data_next.seq_number = data->seq_number;
+					data_next.timeout_abs_ns = data->timeout_abs_ns;
+				}
+			}
+		}
 
 		if (   !wait_for_acks
 		    || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
 			return any;
 
-		nm_assert (next.seq_number);
-		nm_assert (next.now_ns > 0);
-		nm_assert (next.timeout_abs_ns > next.now_ns);
+		nm_assert (data_next.seq_number);
+		nm_assert (data_next.timeout_abs_ns > 0);
+		nm_assert (now_ns > 0);
 
-		_LOGT ("netlink: read: wait for ACK for sequence number %u...", next.seq_number);
+		_LOGT ("netlink: read: wait for ACK for sequence number %u...", data_next.seq_number);
 
-		timeout_ms = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NS_PER_SECOND / 1000);
+		timeout_ms = (data_next.timeout_abs_ns - now_ns) / (NM_UTILS_NS_PER_SECOND / 1000);
 
 		memset (&pfd, 0, sizeof (pfd));
 		pfd.fd = nl_socket_get_fd (priv->nlh);
@@ -6856,7 +6906,6 @@ after_read:
 			/* timeout and there is nothing to read. */
 			goto after_read;
 		}
-
 		if (r < 0) {
 			int errsv = errno;
 
@@ -6998,10 +7047,11 @@ nm_linux_platform_init (NMLinuxPlatform *self)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self);
 
+	priv->nlh_seq_next = 1;
 	priv->delayed_action.list_master_connected = g_ptr_array_new ();
 	priv->delayed_action.list_refresh_link = g_ptr_array_new ();
 	priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
-	priv->wifi_data = g_hash_table_new_full (nm_direct_hash, NULL, NULL, (GDestroyNotify) wifi_utils_unref);
+	priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
 }
 
 static void
@@ -7047,10 +7097,6 @@ constructed (GObject *_object)
 	nle = nl_socket_set_buffer_size (priv->nlh, 8*1024*1024, 0);
 	g_assert (!nle);
 
-	nle = nl_socket_set_ext_ack (priv->nlh, TRUE);
-	if (nle)
-		_LOGD ("could not enable extended acks on netlink socket");
-
 	/* explicitly set the msg buffer size and disable MSG_PEEK.
 	 * If we later encounter NLE_MSG_TRUNC, we will adjust the buffer size. */
 	nl_socket_disable_msg_peek (priv->nlh);
@@ -7130,8 +7176,7 @@ dispose (GObject *object)
 
 	_LOGD ("dispose");
 
-	delayed_action_wait_for_nl_response_complete_all (platform,
-	                                                  WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING);
+	delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING);
 
 	priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE;
 	g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0);
@@ -7181,7 +7226,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_add = link_add;
 	platform_class->link_delete = link_delete;
 
-	platform_class->refresh_all = refresh_all;
 	platform_class->link_refresh = link_refresh;
 
 	platform_class->link_set_netns = link_set_netns;
@@ -7220,6 +7264,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_vlan_change = link_vlan_change;
 	platform_class->link_vxlan_add = link_vxlan_add;
 
+	platform_class->tun_add = tun_add;
+
 	platform_class->infiniband_partition_add = infiniband_partition_add;
 	platform_class->infiniband_partition_delete = infiniband_partition_delete;
 
@@ -7244,7 +7290,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_macvlan_add = link_macvlan_add;
 	platform_class->link_ipip_add = link_ipip_add;
 	platform_class->link_sit_add = link_sit_add;
-	platform_class->link_tun_add = link_tun_add;
 
 	platform_class->object_delete = object_delete;
 	platform_class->ip4_address_add = ip4_address_add;
diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c
deleted file mode 100644
index 4cb19780..00000000
--- a/src/platform/nm-netlink.c
+++ /dev/null
@@ -1,1491 +0,0 @@
-/* -*- 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) 2018 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-netlink.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-
-/*****************************************************************************/
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-/*****************************************************************************/
-
-#define NL_SOCK_PASSCRED        (1<<1)
-#define NL_MSG_PEEK             (1<<3)
-#define NL_MSG_PEEK_EXPLICIT    (1<<4)
-#define NL_NO_AUTO_ACK          (1<<5)
-
-#ifndef NETLINK_EXT_ACK
-#define NETLINK_EXT_ACK         11
-#endif
-
-#define NL_MSG_CRED_PRESENT 1
-
-struct nl_msg {
-	int                     nm_protocol;
-	int                     nm_flags;
-	struct sockaddr_nl      nm_src;
-	struct sockaddr_nl      nm_dst;
-	struct ucred            nm_creds;
-	struct nlmsghdr *       nm_nlh;
-	size_t                  nm_size;
-	int                     nm_refcnt;
-};
-
-struct nl_sock {
-	struct sockaddr_nl      s_local;
-	struct sockaddr_nl      s_peer;
-	int                     s_fd;
-	int                     s_proto;
-	unsigned int            s_seq_next;
-	unsigned int            s_seq_expect;
-	int                     s_flags;
-	size_t                  s_bufsize;
-};
-
-/*****************************************************************************/
-
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int,
-	NM_UTILS_LOOKUP_DEFAULT (NULL),
-	NM_UTILS_LOOKUP_ITEM (NLE_UNSPEC,          "NLE_UNSPEC"),
-	NM_UTILS_LOOKUP_ITEM (NLE_BUG,             "NLE_BUG"),
-	NM_UTILS_LOOKUP_ITEM (NLE_NATIVE_ERRNO,    "NLE_NATIVE_ERRNO"),
-
-	NM_UTILS_LOOKUP_ITEM (NLE_ATTRSIZE,        "NLE_ATTRSIZE"),
-	NM_UTILS_LOOKUP_ITEM (NLE_BAD_SOCK,        "NLE_BAD_SOCK"),
-	NM_UTILS_LOOKUP_ITEM (NLE_DUMP_INTR,       "NLE_DUMP_INTR"),
-	NM_UTILS_LOOKUP_ITEM (NLE_MSG_OVERFLOW,    "NLE_MSG_OVERFLOW"),
-	NM_UTILS_LOOKUP_ITEM (NLE_MSG_TOOSHORT,    "NLE_MSG_TOOSHORT"),
-	NM_UTILS_LOOKUP_ITEM (NLE_MSG_TRUNC,       "NLE_MSG_TRUNC"),
-	NM_UTILS_LOOKUP_ITEM (NLE_SEQ_MISMATCH,    "NLE_SEQ_MISMATCH"),
-)
-
-const char *
-nl_geterror (int err)
-{
-	const char *s;
-
-	err = nl_errno (err);
-
-	if (err >= _NLE_BASE) {
-		s = _geterror (err);
-		if (s)
-			return s;
-	}
-	return g_strerror (err);
-}
-
-/*****************************************************************************/
-
-NM_UTILS_ENUM2STR_DEFINE (nl_nlmsgtype2str, int,
-	NM_UTILS_ENUM2STR (NLMSG_NOOP,    "NOOP"),
-	NM_UTILS_ENUM2STR (NLMSG_ERROR,   "ERROR"),
-	NM_UTILS_ENUM2STR (NLMSG_DONE,    "DONE"),
-	NM_UTILS_ENUM2STR (NLMSG_OVERRUN, "OVERRUN"),
-);
-
-NM_UTILS_FLAGS2STR_DEFINE (nl_nlmsg_flags2str, int,
-	NM_UTILS_FLAGS2STR (NLM_F_REQUEST, "REQUEST"),
-	NM_UTILS_FLAGS2STR (NLM_F_MULTI,   "MULTI"),
-	NM_UTILS_FLAGS2STR (NLM_F_ACK,     "ACK"),
-	NM_UTILS_FLAGS2STR (NLM_F_ECHO,    "ECHO"),
-	NM_UTILS_FLAGS2STR (NLM_F_ROOT,    "ROOT"),
-	NM_UTILS_FLAGS2STR (NLM_F_MATCH,   "MATCH"),
-	NM_UTILS_FLAGS2STR (NLM_F_ATOMIC,  "ATOMIC"),
-	NM_UTILS_FLAGS2STR (NLM_F_REPLACE, "REPLACE"),
-	NM_UTILS_FLAGS2STR (NLM_F_EXCL,    "EXCL"),
-	NM_UTILS_FLAGS2STR (NLM_F_CREATE,  "CREATE"),
-	NM_UTILS_FLAGS2STR (NLM_F_APPEND,  "APPEND"),
-);
-
-/*****************************************************************************/
-
-const char *
-nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
-{
-	const char *b;
-	const char *s;
-	guint flags, flags_before;
-	const char *prefix;
-
-	if (!nm_utils_to_string_buffer_init_null (hdr, &buf, &len))
-		return buf;
-
-	b = buf;
-
-	switch (hdr->nlmsg_type) {
-	case RTM_NEWLINK:    s = "RTM_NEWLINK";  break;
-	case RTM_DELLINK:    s = "RTM_DELLINK";  break;
-	case RTM_NEWADDR:    s = "RTM_NEWADDR";  break;
-	case RTM_DELADDR:    s = "RTM_DELADDR";  break;
-	case RTM_NEWROUTE:   s = "RTM_NEWROUTE"; break;
-	case RTM_DELROUTE:   s = "RTM_DELROUTE"; break;
-	case RTM_NEWQDISC:   s = "RTM_NEWQDISC"; break;
-	case RTM_DELQDISC:   s = "RTM_DELQDISC"; break;
-	case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break;
-	case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break;
-	case NLMSG_NOOP:     s = "NLMSG_NOOP"; break;
-	case NLMSG_ERROR:    s = "NLMSG_ERROR"; break;
-	case NLMSG_DONE:     s = "NLMSG_DONE"; break;
-	case NLMSG_OVERRUN:  s = "NLMSG_OVERRUN"; break;
-	default:             s = NULL;       break;
-	}
-
-	if (s)
-		nm_utils_strbuf_append_str (&buf, &len, s);
-	else
-		nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type);
-
-	flags = hdr->nlmsg_flags;
-
-	if (!flags) {
-		nm_utils_strbuf_append_str (&buf, &len, ", flags 0");
-		goto flags_done;
-	}
-
-#define _F(f, n) \
-	G_STMT_START { \
-		if (NM_FLAGS_ALL (flags, f)) { \
-			flags &= ~(f); \
-			nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \
-			if (!flags) \
-				goto flags_done; \
-			prefix = ","; \
-		} \
-	} G_STMT_END
-
-	prefix = ", flags ";
-	flags_before = flags;
-	_F (NLM_F_REQUEST, "request");
-	_F (NLM_F_MULTI, "multi");
-	_F (NLM_F_ACK, "ack");
-	_F (NLM_F_ECHO, "echo");
-	_F (NLM_F_DUMP_INTR, "dump_intr");
-	_F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered");
-
-	if (flags_before != flags)
-		prefix = ";";
-
-	switch (hdr->nlmsg_type) {
-	case RTM_NEWLINK:
-	case RTM_NEWADDR:
-	case RTM_NEWROUTE:
-	case RTM_NEWQDISC:
-	case RTM_NEWTFILTER:
-		_F (NLM_F_REPLACE, "replace");
-		_F (NLM_F_EXCL, "excl");
-		_F (NLM_F_CREATE, "create");
-		_F (NLM_F_APPEND, "append");
-		break;
-	case RTM_GETLINK:
-	case RTM_GETADDR:
-	case RTM_GETROUTE:
-	case RTM_DELQDISC:
-	case RTM_DELTFILTER:
-		_F (NLM_F_DUMP, "dump");
-		_F (NLM_F_ROOT, "root");
-		_F (NLM_F_MATCH, "match");
-		_F (NLM_F_ATOMIC, "atomic");
-		break;
-	}
-
-#undef _F
-
-	if (flags_before != flags)
-		prefix = ";";
-	nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags);
-
-flags_done:
-
-	nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq);
-
-	return b;
-}
-
-/*****************************************************************************/
-
-struct nlmsghdr *
-nlmsg_hdr (struct nl_msg *n)
-{
-	return n->nm_nlh;
-}
-
-void *
-nlmsg_reserve (struct nl_msg *n, size_t len, int pad)
-{
-	char *buf = (char *) n->nm_nlh;
-	size_t nlmsg_len = n->nm_nlh->nlmsg_len;
-	size_t tlen;
-
-	if (len > n->nm_size)
-		return NULL;
-
-	tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
-
-	if ((tlen + nlmsg_len) > n->nm_size)
-		return NULL;
-
-	buf += nlmsg_len;
-	n->nm_nlh->nlmsg_len += tlen;
-
-	if (tlen > len)
-		memset(buf + len, 0, tlen - len);
-
-	return buf;
-}
-
-/*****************************************************************************/
-
-static int
- get_default_page_size (void)
-{
-	static int val = 0;
-	int v;
-
-	if (G_UNLIKELY (val == 0)) {
-		v = getpagesize ();
-		g_assert (v > 0);
-		val = v;
-	}
-	return val;
-}
-
-struct nlattr *
-nla_reserve (struct nl_msg *msg, int attrtype, int attrlen)
-{
-	struct nlattr *nla;
-	int tlen;
-
-	if (attrlen < 0)
-		return NULL;
-
-	tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
-
-	if (tlen > msg->nm_size)
-		return NULL;
-
-	nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
-	nla->nla_type = attrtype;
-	nla->nla_len = nla_attr_size(attrlen);
-
-	if (attrlen)
-		memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
-	msg->nm_nlh->nlmsg_len = tlen;
-
-	return nla;
-}
-
-struct nl_msg *
-nlmsg_alloc_size (size_t len)
-{
-	struct nl_msg *nm;
-
-	if (len < sizeof (struct nlmsghdr))
-		len = sizeof (struct nlmsghdr);
-
-	nm = g_slice_new0 (struct nl_msg);
-
-	nm->nm_refcnt = 1;
-	nm->nm_protocol = -1;
-	nm->nm_size = len;
-	nm->nm_nlh = g_malloc0 (len);
-	nm->nm_nlh->nlmsg_len = nlmsg_total_size (0);
-	return nm;
-}
-
-/**
- * Allocate a new netlink message with the default maximum payload size.
- *
- * Allocates a new netlink message without any further payload. The
- * maximum payload size defaults to PAGESIZE or as otherwise specified
- * with nlmsg_set_default_size().
- *
- * @return Newly allocated netlink message or NULL.
- */
-struct nl_msg *
-nlmsg_alloc (void)
-{
-	return nlmsg_alloc_size (get_default_page_size ());
-}
-
-/**
- * Allocate a new netlink message with maximum payload size specified.
- */
-struct nl_msg *
-nlmsg_alloc_inherit (struct nlmsghdr *hdr)
-{
-	struct nl_msg *nm;
-
-	nm = nlmsg_alloc ();
-	if (hdr) {
-		struct nlmsghdr *new = nm->nm_nlh;
-
-		new->nlmsg_type = hdr->nlmsg_type;
-		new->nlmsg_flags = hdr->nlmsg_flags;
-		new->nlmsg_seq = hdr->nlmsg_seq;
-		new->nlmsg_pid = hdr->nlmsg_pid;
-	}
-
-	return nm;
-}
-
-struct nl_msg *
-nlmsg_alloc_convert (struct nlmsghdr *hdr)
-{
-	struct nl_msg *nm;
-
-	nm = nlmsg_alloc_size (NLMSG_ALIGN (hdr->nlmsg_len));
-	memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len);
-	return nm;
-}
-
-struct nl_msg *
-nlmsg_alloc_simple (int nlmsgtype, int flags)
-{
-	struct nlmsghdr nlh = {
-		.nlmsg_type = nlmsgtype,
-		.nlmsg_flags = flags,
-	};
-
-	return nlmsg_alloc_inherit (&nlh);
-}
-
-int
-nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad)
-{
-	void *tmp;
-
-	tmp = nlmsg_reserve (n, len, pad);
-	if (tmp == NULL)
-		return -ENOMEM;
-
-	memcpy(tmp, data, len);
-	return 0;
-}
-
-int
-nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-             int maxtype, const struct nla_policy *policy)
-{
-	if (!nlmsg_valid_hdr(nlh, hdrlen))
-		return -NLE_MSG_TOOSHORT;
-
-	return nla_parse (tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
-	                  nlmsg_attrlen(nlh, hdrlen), policy);
-}
-
-struct nlmsghdr *
-nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq,
-           int type, int payload, int flags)
-{
-	struct nlmsghdr *nlh;
-
-	if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN)
-		g_return_val_if_reached (NULL);
-
-	nlh = (struct nlmsghdr *) n->nm_nlh;
-	nlh->nlmsg_type = type;
-	nlh->nlmsg_flags = flags;
-	nlh->nlmsg_pid = pid;
-	nlh->nlmsg_seq = seq;
-
-	if (payload > 0 &&
-	    nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL)
-		return NULL;
-
-	return nlh;
-}
-
-uint64_t
-nla_get_u64 (const struct nlattr *nla)
-{
-	uint64_t tmp = 0;
-
-	if (nla && nla_len(nla) >= sizeof (tmp))
-		memcpy(&tmp, nla_data(nla), sizeof (tmp));
-
-	return tmp;
-}
-
-size_t
-nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize)
-{
-	size_t srclen = nla_len(nla);
-	const char *src = nla_data(nla);
-
-	if (srclen > 0 && src[srclen - 1] == '\0')
-		srclen--;
-
-	if (dstsize > 0) {
-		size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
-
-		memset(dst, 0, dstsize);
-		memcpy(dst, src, len);
-	}
-
-	return srclen;
-}
-
-int
-nla_memcpy (void *dest, const struct nlattr *src, int count)
-{
-	int minlen;
-
-	if (!src)
-		return 0;
-
-	minlen = NM_MIN (count, (int) nla_len (src));
-	memcpy(dest, nla_data(src), minlen);
-
-	return minlen;
-}
-
-int
-nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data)
-{
-	struct nlattr *nla;
-
-	nla = nla_reserve(msg, attrtype, datalen);
-	if (!nla) {
-		if (datalen < 0)
-			g_return_val_if_reached (-NLE_BUG);
-
-		return -ENOMEM;
-	}
-
-	if (datalen > 0)
-		memcpy (nla_data(nla), data, datalen);
-
-	return 0;
-}
-
-struct nlattr *
-nla_find (const struct nlattr *head, int len, int attrtype)
-{
-	const struct nlattr *nla;
-	int rem;
-
-	nla_for_each_attr (nla, head, len, rem) {
-		if (nla_type (nla) == attrtype)
-			return (struct nlattr*)nla;
-	}
-
-	return NULL;
-}
-
-void
-nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr)
-{
-	ssize_t len;
-
-	len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr;
-	if (len < 0)
-		g_return_if_reached ();
-	else if (len > 0) {
-		msg->nm_nlh->nlmsg_len -= len;
-		memset(nlmsg_tail(msg->nm_nlh), 0, len);
-	}
-}
-
-struct nlattr *
-nla_nest_start (struct nl_msg *msg, int attrtype)
-{
-	struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
-
-	if (nla_put(msg, attrtype, 0, NULL) < 0)
-		return NULL;
-
-	return start;
-}
-
-static int
-_nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty)
-{
-	size_t pad, len;
-
-	len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start;
-
-	if (   len > USHRT_MAX
-	    || (!keep_empty && len == NLA_HDRLEN)) {
-		/*
-		 * Max nlattr size exceeded or empty nested attribute, trim the
-		 * attribute header again
-		 */
-		nla_nest_cancel(msg, start);
-
-		/* Return error only if nlattr size was exceeded */
-		return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE;
-	}
-
-	start->nla_len = len;
-
-	pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len;
-	if (pad > 0) {
-		/*
-		 * Data inside attribute does not end at a alignment boundry.
-		 * Pad accordingly and accoun for the additional space in
-		 * the message. nlmsg_reserve() may never fail in this situation,
-		 * the allocate message buffer must be a multiple of NLMSG_ALIGNTO.
-		 */
-		if (!nlmsg_reserve(msg, pad, 0))
-			g_return_val_if_reached (-NLE_BUG);
-	}
-
-	return 0;
-}
-
-int
-nla_nest_end (struct nl_msg *msg, struct nlattr *start)
-{
-	return _nest_end (msg, start, 0);
-}
-
-static const uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
-	[NLA_U8]        = sizeof (uint8_t),
-	[NLA_U16]       = sizeof (uint16_t),
-	[NLA_U32]       = sizeof (uint32_t),
-	[NLA_U64]       = sizeof (uint64_t),
-	[NLA_STRING]    = 1,
-	[NLA_FLAG]      = 0,
-};
-
-static int
-validate_nla (const struct nlattr *nla, int maxtype,
-              const struct nla_policy *policy)
-{
-	const struct nla_policy *pt;
-	unsigned int minlen = 0;
-	int type = nla_type(nla);
-
-	if (type < 0 || type > maxtype)
-		return 0;
-
-	pt = &policy[type];
-
-	if (pt->type > NLA_TYPE_MAX)
-		g_return_val_if_reached (-NLE_BUG);
-
-	if (pt->minlen)
-		minlen = pt->minlen;
-	else if (pt->type != NLA_UNSPEC)
-		minlen = nla_attr_minlen[pt->type];
-
-	if (nla_len(nla) < minlen)
-		return -NLE_UNSPEC;
-
-	if (pt->maxlen && nla_len(nla) > pt->maxlen)
-		return -NLE_UNSPEC;
-
-	if (pt->type == NLA_STRING) {
-		const char *data = nla_data(nla);
-		if (data[nla_len(nla) - 1] != '\0')
-			return -NLE_UNSPEC;
-	}
-
-	return 0;
-}
-
-int
-nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
-           const struct nla_policy *policy)
-{
-	struct nlattr *nla;
-	int rem, err;
-
-	memset(tb, 0, sizeof (struct nlattr *) * (maxtype + 1));
-
-	nla_for_each_attr(nla, head, len, rem) {
-		int type = nla_type(nla);
-
-		if (type > maxtype)
-			continue;
-
-		if (policy) {
-			err = validate_nla(nla, maxtype, policy);
-			if (err < 0)
-				goto errout;
-		}
-
-		tb[type] = nla;
-	}
-
-	err = 0;
-errout:
-	return err;
-}
-
-/*****************************************************************************/
-
-void nlmsg_free (struct nl_msg *msg)
-{
-	if (!msg)
-		return;
-
-	if (msg->nm_refcnt < 1)
-		g_return_if_reached ();
-
-	msg->nm_refcnt--;
-
-	if (msg->nm_refcnt <= 0) {
-		g_free (msg->nm_nlh);
-		g_slice_free (struct nl_msg, msg);
-	}
-}
-
-int
-nlmsg_get_proto (struct nl_msg *msg)
-{
-	return msg->nm_protocol;
-}
-
-void
-nlmsg_set_proto (struct nl_msg *msg, int protocol)
-{
-	msg->nm_protocol = protocol;
-}
-
-void
-nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr)
-{
-	memcpy (&msg->nm_src, addr, sizeof (*addr));
-}
-
-struct ucred *
-nlmsg_get_creds (struct nl_msg *msg)
-{
-	if (msg->nm_flags & NL_MSG_CRED_PRESENT)
-		return &msg->nm_creds;
-	return NULL;
-}
-
-void
-nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds)
-{
-	memcpy (&msg->nm_creds, creds, sizeof (*creds));
-	msg->nm_flags |= NL_MSG_CRED_PRESENT;
-}
-
-/*****************************************************************************/
-
-void *
-genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
-             int hdrlen, int flags, uint8_t cmd, uint8_t version)
-{
-	struct nlmsghdr *nlh;
-	struct genlmsghdr hdr = {
-		.cmd = cmd,
-		.version = version,
-	};
-
-	nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags);
-	if (nlh == NULL)
-		return NULL;
-
-	memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr));
-
-	return (char *) nlmsg_data (nlh) + GENL_HDRLEN;
-}
-
-void *
-genlmsg_data (const struct genlmsghdr *gnlh)
-{
-	return ((unsigned char *) gnlh + GENL_HDRLEN);
-}
-
-void *
-genlmsg_user_hdr (const struct genlmsghdr *gnlh)
-{
-	return genlmsg_data (gnlh);
-}
-
-struct genlmsghdr *
-genlmsg_hdr (struct nlmsghdr *nlh)
-{
-	return nlmsg_data (nlh);
-}
-
-void *
-genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen)
-{
-	return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen);
-}
-
-struct nlattr *
-genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen)
-{
-	return genlmsg_user_data (gnlh, hdrlen);
-}
-
-int
-genlmsg_len (const struct genlmsghdr *gnlh)
-{
-	const struct nlmsghdr *nlh;
-
-	nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN);
-	return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
-}
-
-int
-genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen)
-{
-	return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen);
-}
-
-int
-genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen)
-{
-	struct genlmsghdr *ghdr;
-
-	if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN))
-		return 0;
-
-	ghdr = nlmsg_data (nlh);
-	if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen))
-		return 0;
-
-	return 1;
-}
-
-int
-genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-               int maxtype, const struct nla_policy *policy)
-{
-	struct genlmsghdr *ghdr;
-
-	if (!genlmsg_valid_hdr (nlh, hdrlen))
-		return -NLE_MSG_TOOSHORT;
-
-	ghdr = nlmsg_data (nlh);
-	return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen),
-	                  genlmsg_attrlen (ghdr, hdrlen), policy);
-}
-
-static int
-_genl_parse_getfamily (struct nl_msg *msg, void *arg)
-{
-	static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
-		[CTRL_ATTR_FAMILY_ID]    = { .type = NLA_U16 },
-		[CTRL_ATTR_FAMILY_NAME]  = { .type = NLA_STRING,
-		                            .maxlen = GENL_NAMSIZ },
-		[CTRL_ATTR_VERSION]      = { .type = NLA_U32 },
-		[CTRL_ATTR_HDRSIZE]      = { .type = NLA_U32 },
-		[CTRL_ATTR_MAXATTR]      = { .type = NLA_U32 },
-		[CTRL_ATTR_OPS]          = { .type = NLA_NESTED },
-		[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
-	};
-	struct nlattr *tb[CTRL_ATTR_MAX+1];
-	struct nlmsghdr *nlh = nlmsg_hdr (msg);
-	gint32 *response_data = arg;
-
-	if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy))
-		return NL_SKIP;
-
-	if (tb[CTRL_ATTR_FAMILY_ID])
-		*response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]);
-
-	return NL_STOP;
-}
-
-int
-genl_ctrl_resolve (struct nl_sock *sk, const char *name)
-{
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
-	int result = -ENOMEM;
-	gint32 response_data = -1;
-	const struct nl_cb cb = {
-		.valid_cb = _genl_parse_getfamily,
-		.valid_arg = &response_data,
-	};
-
-	msg = nlmsg_alloc ();
-
-	if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
-	                  0, 0, CTRL_CMD_GETFAMILY, 1))
-		goto out;
-
-	if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0)
-		goto out;
-
-	result = nl_send_auto (sk, msg);
-	if (result < 0)
-		goto out;
-
-	result = nl_recvmsgs (sk, &cb);
-	if (result < 0)
-		goto out;
-
-	/* If search was successful, request may be ACKed after data */
-	result = nl_wait_for_ack (sk, NULL);
-	if (result < 0)
-		goto out;
-
-	if (response_data > 0)
-		result = response_data;
-	else
-		result = -ENOENT;
-
-out:
-	return result;
-}
-
-/*****************************************************************************/
-
-struct nl_sock *
-nl_socket_alloc (void)
-{
-	struct nl_sock *sk;
-
-	sk = g_slice_new0 (struct nl_sock);
-
-	sk->s_fd = -1;
-	sk->s_local.nl_family = AF_NETLINK;
-	sk->s_peer.nl_family = AF_NETLINK;
-	sk->s_seq_expect = sk->s_seq_next = time(NULL);
-
-	return sk;
-}
-
-void
-nl_socket_free (struct nl_sock *sk)
-{
-	if (!sk)
-		return;
-
-	if (sk->s_fd >= 0)
-		nm_close (sk->s_fd);
-	g_slice_free (struct nl_sock, sk);
-}
-
-int
-nl_socket_get_fd (const struct nl_sock *sk)
-{
-	return sk->s_fd;
-}
-
-uint32_t
-nl_socket_get_local_port (const struct nl_sock *sk)
-{
-	return sk->s_local.nl_pid;
-}
-
-size_t
-nl_socket_get_msg_buf_size (struct nl_sock *sk)
-{
-	return sk->s_bufsize;
-}
-
-int
-nl_socket_set_passcred (struct nl_sock *sk, int state)
-{
-	int err;
-
-	if (sk->s_fd == -1)
-		return -NLE_BAD_SOCK;
-
-	err = setsockopt (sk->s_fd, SOL_SOCKET, SO_PASSCRED,
-	                  &state, sizeof (state));
-	if (err < 0)
-		return -nl_syserr2nlerr (errno);
-
-	if (state)
-		sk->s_flags |= NL_SOCK_PASSCRED;
-	else
-		sk->s_flags &= ~NL_SOCK_PASSCRED;
-
-	return 0;
-}
-
-int
-nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize)
-{
-	sk->s_bufsize = bufsize;
-
-	return 0;
-}
-
-struct sockaddr_nl *
-nlmsg_get_dst (struct nl_msg *msg)
-{
-	return &msg->nm_dst;
-}
-
-int
-nl_socket_set_nonblocking (const struct nl_sock *sk)
-{
-	if (sk->s_fd == -1)
-		return -NLE_BAD_SOCK;
-
-	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
-		return -nl_syserr2nlerr (errno);
-
-	return 0;
-}
-
-int
-nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf)
-{
-	int err;
-
-	if (rxbuf <= 0)
-		rxbuf = 32768;
-
-	if (txbuf <= 0)
-		txbuf = 32768;
-
-	if (sk->s_fd == -1)
-		return -NLE_BAD_SOCK;
-
-	err = setsockopt (sk->s_fd, SOL_SOCKET, SO_SNDBUF,
-	                  &txbuf, sizeof (txbuf));
-	if (err < 0) {
-		return -nl_syserr2nlerr (errno);
-	}
-
-	err = setsockopt (sk->s_fd, SOL_SOCKET, SO_RCVBUF,
-	                  &rxbuf, sizeof (rxbuf));
-	if (err < 0) {
-		return -nl_syserr2nlerr (errno);
-	}
-
-	return 0;
-}
-
-int
-nl_socket_add_memberships (struct nl_sock *sk, int group, ...)
-{
-	int err;
-	va_list ap;
-
-	if (sk->s_fd == -1)
-		return -NLE_BAD_SOCK;
-
-	va_start(ap, group);
-
-	while (group != 0) {
-		if (group < 0) {
-			va_end(ap);
-			g_return_val_if_reached (-NLE_BUG);
-		}
-
-		err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
-		                  &group, sizeof (group));
-		if (err < 0) {
-			va_end(ap);
-			return -nl_syserr2nlerr (errno);
-		}
-
-		group = va_arg(ap, int);
-	}
-
-	va_end(ap);
-
-	return 0;
-}
-
-int
-nl_socket_set_ext_ack (struct nl_sock *sk, gboolean enable)
-{
-	int err, val;
-
-	if (sk->s_fd == -1)
-		return -NLE_BAD_SOCK;
-
-	val = !!enable;
-	err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof (val));
-	if (err < 0)
-		return -nl_syserr2nlerr (errno);
-
-	return 0;
-}
-
-void nl_socket_disable_msg_peek (struct nl_sock *sk)
-{
-	sk->s_flags |= NL_MSG_PEEK_EXPLICIT;
-	sk->s_flags &= ~NL_MSG_PEEK;
-}
-
-int
-nl_connect (struct nl_sock *sk, int protocol)
-{
-	int err;
-	socklen_t addrlen;
-	struct sockaddr_nl local = { 0 };
-
-	if (sk->s_fd != -1)
-		return -NLE_BAD_SOCK;
-
-	sk->s_fd = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);
-	if (sk->s_fd < 0) {
-		err = -nl_syserr2nlerr (errno);
-		goto errout;
-	}
-
-	err = nl_socket_set_buffer_size(sk, 0, 0);
-	if (err < 0)
-		goto errout;
-
-	nm_assert (sk->s_local.nl_pid == 0);
-
-	err = bind (sk->s_fd, (struct sockaddr*) &sk->s_local,
-	            sizeof (sk->s_local));
-	if (err != 0) {
-		err = -nl_syserr2nlerr (errno);
-		goto errout;
-	}
-
-	addrlen = sizeof (local);
-	err = getsockname (sk->s_fd, (struct sockaddr *) &local,
-	                   &addrlen);
-	if (err < 0) {
-		err = -nl_syserr2nlerr (errno);
-		goto errout;
-	}
-
-	if (addrlen != sizeof (local)) {
-		err = -NLE_UNSPEC;
-		goto errout;
-	}
-
-	if (local.nl_family != AF_NETLINK) {
-		err = -NLE_UNSPEC;
-		goto errout;
-	}
-
-	sk->s_local = local;
-	sk->s_proto = protocol;
-
-	return 0;
-
-errout:
-	if (sk->s_fd != -1) {
-		close(sk->s_fd);
-		sk->s_fd = -1;
-	}
-	return err;
-}
-
-/*****************************************************************************/
-
-static void
-_cb_init (struct nl_cb *dst, const struct nl_cb *src)
-{
-	nm_assert (dst);
-
-	if (src)
-		*dst = *src;
-	else
-		memset (dst, 0, sizeof (*dst));
-}
-
-static int ack_wait_handler(struct nl_msg *msg, void *arg)
-{
-	return NL_STOP;
-}
-
-int
-nl_wait_for_ack (struct nl_sock *sk,
-                 const struct nl_cb *cb)
-{
-	struct nl_cb cb2;
-
-	_cb_init (&cb2, cb);
-	cb2.ack_cb = ack_wait_handler;
-	return nl_recvmsgs (sk, &cb2);
-}
-
-#define NL_CB_CALL(cb, type, msg) \
-do { \
-	const struct nl_cb *_cb = (cb); \
-	\
-	if (_cb->type##_cb) { \
-		err = _cb->type##_cb ((msg), _cb->type##_arg); \
-		switch (err) { \
-		case NL_OK: \
-			err = 0; \
-			break; \
-		case NL_SKIP: \
-			goto skip; \
-		case NL_STOP: \
-			goto stop; \
-		default: \
-			goto out; \
-		} \
-	} \
-} while (0)
-
-int
-nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb)
-{
-	int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0;
-	gs_free unsigned char *buf = NULL;
-	struct nlmsghdr *hdr;
-	struct sockaddr_nl nla = { 0 };
-	gs_free struct ucred *creds = NULL;
-
-continue_reading:
-	n = nl_recv (sk, &nla, &buf, &creds);
-	if (n <= 0)
-		return n;
-
-	hdr = (struct nlmsghdr *) buf;
-	while (nlmsg_ok (hdr, n)) {
-		nm_auto_nlmsg struct nl_msg *msg = NULL;
-
-		msg = nlmsg_alloc_convert (hdr);
-
-		nlmsg_set_proto (msg, sk->s_proto);
-		nlmsg_set_src (msg, &nla);
-		if (creds)
-			nlmsg_set_creds (msg, creds);
-
-		nrecv++;
-
-		/* Only do sequence checking if auto-ack mode is enabled */
-		if (!(sk->s_flags & NL_NO_AUTO_ACK)) {
-			if (hdr->nlmsg_seq != sk->s_seq_expect) {
-				err = -NLE_SEQ_MISMATCH;
-				goto out;
-			}
-		}
-
-		if (hdr->nlmsg_type == NLMSG_DONE ||
-		    hdr->nlmsg_type == NLMSG_ERROR ||
-		    hdr->nlmsg_type == NLMSG_NOOP ||
-		    hdr->nlmsg_type == NLMSG_OVERRUN) {
-			/* We can't check for !NLM_F_MULTI since some netlink
-			 * users in the kernel are broken. */
-			sk->s_seq_expect++;
-		}
-
-		if (hdr->nlmsg_flags & NLM_F_MULTI)
-			multipart = 1;
-
-		if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
-			/*
-			 * We have to continue reading to clear
-			 * all messages until a NLMSG_DONE is
-			 * received and report the inconsistency.
-			 */
-			interrupted = 1;
-		}
-
-		/* messages terminates a multipart message, this is
-		 * usually the end of a message and therefore we slip
-		 * out of the loop by default. the user may overrule
-		 * this action by skipping this packet. */
-		if (hdr->nlmsg_type == NLMSG_DONE) {
-			multipart = 0;
-			NL_CB_CALL(cb, finish, msg);
-		}
-
-		/* Message to be ignored, the default action is to
-		 * skip this message if no callback is specified. The
-		 * user may overrule this action by returning
-		 * NL_PROCEED. */
-		else if (hdr->nlmsg_type == NLMSG_NOOP)
-			goto skip;
-
-		/* Data got lost, report back to user. The default action is to
-		 * quit parsing. The user may overrule this action by retuning
-		 * NL_SKIP or NL_PROCEED (dangerous) */
-		else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
-			err = -NLE_MSG_OVERFLOW;
-			goto out;
-		}
-
-		/* Message carries a nlmsgerr */
-		else if (hdr->nlmsg_type == NLMSG_ERROR) {
-			struct nlmsgerr *e = nlmsg_data(hdr);
-
-			if (hdr->nlmsg_len < nlmsg_size(sizeof (*e))) {
-				/* Truncated error message, the default action
-				 * is to stop parsing. The user may overrule
-				 * this action by returning NL_SKIP or
-				 * NL_PROCEED (dangerous) */
-				err = -NLE_MSG_TRUNC;
-				goto out;
-			}
-			if (e->error) {
-				/* Error message reported back from kernel. */
-				if (cb->err_cb) {
-					err = cb->err_cb (&nla, e,
-					                  cb->err_arg);
-					if (err < 0)
-						goto out;
-					else if (err == NL_SKIP)
-						goto skip;
-					else if (err == NL_STOP) {
-						err = -e->error;
-						goto out;
-					}
-				} else {
-					err = -e->error;
-					goto out;
-				}
-			} else
-				NL_CB_CALL(cb, ack, msg);
-		} else {
-			/* Valid message (not checking for MULTIPART bit to
-			 * get along with broken kernels. NL_SKIP has no
-			 * effect on this.  */
-			NL_CB_CALL(cb, valid, msg);
-		}
-skip:
-		err = 0;
-		hdr = nlmsg_next(hdr, &n);
-	}
-
-	if (multipart) {
-		/* Multipart message not yet complete, continue reading */
-		nm_clear_g_free (&creds);
-		nm_clear_g_free (&buf);
-
-		goto continue_reading;
-	}
-
-stop:
-	err = 0;
-
-out:
-	if (interrupted)
-		err = -NLE_DUMP_INTR;
-
-	return err ?: nrecv;
-}
-
-int
-nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
-{
-	int ret;
-
-	if (sk->s_fd < 0)
-		return -NLE_BAD_SOCK;
-
-	nlmsg_set_src (msg, &sk->s_local);
-
-	ret = sendmsg(sk->s_fd, hdr, 0);
-	if (ret < 0)
-		return -nl_syserr2nlerr (errno);
-
-	return ret;
-}
-
-int
-nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen)
-{
-	struct sockaddr_nl *dst;
-	struct ucred *creds;
-	struct msghdr hdr = {
-		.msg_name = (void *) &sk->s_peer,
-		.msg_namelen = sizeof (struct sockaddr_nl),
-		.msg_iov = iov,
-		.msg_iovlen = iovlen,
-	};
-	char buf[CMSG_SPACE(sizeof (struct ucred))];
-
-	/* Overwrite destination if specified in the message itself, defaults
-	 * to the peer address of the socket.
-	 */
-	dst = nlmsg_get_dst(msg);
-	if (dst->nl_family == AF_NETLINK)
-		hdr.msg_name = dst;
-
-	/* Add credentials if present. */
-	creds = nlmsg_get_creds(msg);
-	if (creds != NULL) {
-		struct cmsghdr *cmsg;
-
-		hdr.msg_control = buf;
-		hdr.msg_controllen = sizeof (buf);
-
-		cmsg = CMSG_FIRSTHDR(&hdr);
-		cmsg->cmsg_level = SOL_SOCKET;
-		cmsg->cmsg_type = SCM_CREDENTIALS;
-		cmsg->cmsg_len = CMSG_LEN(sizeof (struct ucred));
-		memcpy(CMSG_DATA(cmsg), creds, sizeof (struct ucred));
-	}
-
-	return nl_sendmsg(sk, msg, &hdr);
-}
-
-void
-nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg)
-{
-	struct nlmsghdr *nlh;
-
-	nlh = nlmsg_hdr(msg);
-	if (nlh->nlmsg_pid == NL_AUTO_PORT)
-		nlh->nlmsg_pid = nl_socket_get_local_port(sk);
-
-	if (nlh->nlmsg_seq == NL_AUTO_SEQ)
-		nlh->nlmsg_seq = sk->s_seq_next++;
-
-	if (msg->nm_protocol == -1)
-		msg->nm_protocol = sk->s_proto;
-
-	nlh->nlmsg_flags |= NLM_F_REQUEST;
-
-	if (!(sk->s_flags & NL_NO_AUTO_ACK))
-		nlh->nlmsg_flags |= NLM_F_ACK;
-}
-
-int
-nl_send (struct nl_sock *sk, struct nl_msg *msg)
-{
-	struct iovec iov = {
-		.iov_base = (void *) nlmsg_hdr(msg),
-		.iov_len = nlmsg_hdr(msg)->nlmsg_len,
-	};
-
-	return nl_send_iovec(sk, msg, &iov, 1);
-}
-
-int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
-{
-	nl_complete_msg(sk, msg);
-
-	return nl_send(sk, msg);
-}
-
-int
-nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla,
-         unsigned char **buf, struct ucred **creds)
-{
-	ssize_t n;
-	int flags = 0;
-	static int page_size = 0;
-	struct iovec iov;
-	struct msghdr msg = {
-		.msg_name = (void *) nla,
-		.msg_namelen = sizeof (struct sockaddr_nl),
-		.msg_iov = &iov,
-		.msg_iovlen = 1,
-	};
-	gs_free struct ucred* tmpcreds = NULL;
-	int retval;
-
-	nm_assert (nla);
-	nm_assert (buf && !*buf);
-	nm_assert (!creds || !*creds);
-
-	if (   (sk->s_flags & NL_MSG_PEEK)
-	    || (   !(sk->s_flags & NL_MSG_PEEK_EXPLICIT)
-	        && sk->s_bufsize == 0))
-		flags |= MSG_PEEK | MSG_TRUNC;
-
-	if (page_size == 0)
-		page_size = getpagesize() * 4;
-
-	iov.iov_len = sk->s_bufsize ? : page_size;
-	iov.iov_base = g_malloc (iov.iov_len);
-
-	if (   creds
-	    && (sk->s_flags & NL_SOCK_PASSCRED)) {
-		msg.msg_controllen = CMSG_SPACE (sizeof (struct ucred));
-		msg.msg_control = g_malloc (msg.msg_controllen);
-	}
-
-retry:
-	n = recvmsg(sk->s_fd, &msg, flags);
-	if (!n) {
-		retval = 0;
-		goto abort;
-	}
-
-	if (n < 0) {
-		if (errno == EINTR)
-			goto retry;
-
-		retval = -nl_syserr2nlerr (errno);
-		goto abort;
-	}
-
-	if (msg.msg_flags & MSG_CTRUNC) {
-		if (msg.msg_controllen == 0) {
-			retval = -NLE_MSG_TRUNC;
-			goto abort;
-		}
-
-		msg.msg_controllen *= 2;
-		msg.msg_control = g_realloc (msg.msg_control, msg.msg_controllen);
-		goto retry;
-	}
-
-	if (   iov.iov_len < n
-	    || (msg.msg_flags & MSG_TRUNC)) {
-		/* respond with error to an incomplete message */
-		if (flags == 0) {
-			retval = -NLE_MSG_TRUNC;
-			goto abort;
-		}
-
-		/* Provided buffer is not long enough, enlarge it
-		 * to size of n (which should be total length of the message)
-		 * and try again. */
-		iov.iov_base = g_realloc (iov.iov_base, n);
-		iov.iov_len = n;
-		flags = 0;
-		goto retry;
-	}
-
-	if (flags != 0) {
-		/* Buffer is big enough, do the actual reading */
-		flags = 0;
-		goto retry;
-	}
-
-	if (msg.msg_namelen != sizeof (struct sockaddr_nl)) {
-		retval =  -NLE_UNSPEC;
-		goto abort;
-	}
-
-	if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) {
-		struct cmsghdr *cmsg;
-
-		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-			if (cmsg->cmsg_level != SOL_SOCKET)
-				continue;
-			if (cmsg->cmsg_type != SCM_CREDENTIALS)
-				continue;
-			tmpcreds = g_memdup (CMSG_DATA(cmsg), sizeof (*tmpcreds));
-			break;
-		}
-	}
-
-	retval = n;
-
-abort:
-	g_free (msg.msg_control);
-
-	if (retval <= 0) {
-		g_free (iov.iov_base);
-		return retval;
-	}
-
-	*buf = iov.iov_base;
-	NM_SET_OUT (creds, g_steal_pointer (&tmpcreds));
-	return retval;
-}
diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h
deleted file mode 100644
index c0dc09c4..00000000
--- a/src/platform/nm-netlink.h
+++ /dev/null
@@ -1,511 +0,0 @@
-/* -*- 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) 2018 Red Hat, Inc.
- */
-
-#ifndef __NM_NETLINK_H__
-#define __NM_NETLINK_H__
-
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/genetlink.h>
-
-/*****************************************************************************/
-#define _NLE_BASE               100000
-#define NLE_UNSPEC              (_NLE_BASE +  0)
-#define NLE_BUG                 (_NLE_BASE +  1)
-#define NLE_NATIVE_ERRNO        (_NLE_BASE +  2)
-#define NLE_SEQ_MISMATCH        (_NLE_BASE +  3)
-#define NLE_MSG_TRUNC           (_NLE_BASE +  4)
-#define NLE_MSG_TOOSHORT        (_NLE_BASE +  5)
-#define NLE_DUMP_INTR           (_NLE_BASE +  6)
-#define NLE_ATTRSIZE            (_NLE_BASE +  7)
-#define NLE_BAD_SOCK            (_NLE_BASE +  8)
-#define NLE_NOADDR              (_NLE_BASE +  9)
-#define NLE_MSG_OVERFLOW        (_NLE_BASE + 10)
-
-#define _NLE_BASE_END           (_NLE_BASE + 11)
-
-#define NLMSGERR_ATTR_UNUSED            0
-#define NLMSGERR_ATTR_MSG               1
-#define NLMSGERR_ATTR_OFFS              2
-#define NLMSGERR_ATTR_COOKIE            3
-#define NLMSGERR_ATTR_MAX               3
-
-#ifndef NLM_F_ACK_TLVS
-#define NLM_F_ACK_TLVS                  0x200
-#endif
-
-static inline int
-nl_errno (int err)
-{
-	/* the error codes from our netlink implementation are plain errno
-	 * extended with our own error in a particular range starting from
-	 * _NLE_BASE.
-	 *
-	 * However, often we encode errors as negative values. This function
-	 * normalizes the error and returns its positive value. */
-	return err >= 0
-	       ? err
-	       : ((err == G_MININT) ? NLE_BUG : -errno);
-}
-
-static inline int
-nl_syserr2nlerr (int err)
-{
-	if (err == G_MININT)
-		return NLE_NATIVE_ERRNO;
-	if (err < 0)
-		err = -err;
-	return (err >= _NLE_BASE && err < _NLE_BASE_END)
-	       ? NLE_NATIVE_ERRNO
-	       : err;
-}
-
-const char *nl_geterror (int err);
-
-/*****************************************************************************/
-
-/* Basic attribute data types */
-enum {
-	NLA_UNSPEC,     /* Unspecified type, binary data chunk */
-	NLA_U8,         /* 8 bit integer */
-	NLA_U16,        /* 16 bit integer */
-	NLA_U32,        /* 32 bit integer */
-	NLA_U64,        /* 64 bit integer */
-	NLA_STRING,     /* NUL terminated character string */
-	NLA_FLAG,       /* Flag */
-	NLA_MSECS,      /* Micro seconds (64bit) */
-	NLA_NESTED,     /* Nested attributes */
-	NLA_NESTED_COMPAT,
-	NLA_NUL_STRING,
-	NLA_BINARY,
-	NLA_S8,
-	NLA_S16,
-	NLA_S32,
-	NLA_S64,
-	__NLA_TYPE_MAX,
-};
-
-#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
-
-struct nl_msg;
-
-/*****************************************************************************/
-
-const char *nl_nlmsgtype2str (int type, char *buf, size_t size);
-
-const char *nl_nlmsg_flags2str (int flags, char *buf, size_t len);
-
-const char *nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len);
-
-/*****************************************************************************/
-
-struct nla_policy {
-	/* Type of attribute or NLA_UNSPEC */
-	uint16_t type;
-
-	/* Minimal length of payload required */
-	uint16_t minlen;
-
-	/* Maximal length of payload allowed */
-	uint16_t maxlen;
-};
-
-/*****************************************************************************/
-
-static inline int
-nla_attr_size(int payload)
-{
-	nm_assert (payload >= 0);
-
-	return NLA_HDRLEN + payload;
-}
-
-static inline int
-nla_total_size (int payload)
-{
-	return NLA_ALIGN (nla_attr_size (payload));
-}
-
-static inline int
-nla_padlen (int payload)
-{
-	return nla_total_size(payload) - nla_attr_size(payload);
-}
-
-struct nlattr *nla_reserve (struct nl_msg *msg, int attrtype, int attrlen);
-
-static inline int
-nla_len (const struct nlattr *nla)
-{
-	return nla->nla_len - NLA_HDRLEN;
-}
-
-static inline int
-nla_type (const struct nlattr *nla)
-{
-	return nla->nla_type & NLA_TYPE_MASK;
-}
-
-static inline void *
-nla_data (const struct nlattr *nla)
-{
-	nm_assert (nla);
-	return (char *) nla + NLA_HDRLEN;
-}
-
-static inline uint8_t
-nla_get_u8 (const struct nlattr *nla)
-{
-	return *(const uint8_t *) nla_data (nla);
-}
-
-static inline uint8_t
-nla_get_u8_cond (/*const*/ struct nlattr *const*tb, int attr, uint8_t default_val)
-{
-	nm_assert (tb);
-	nm_assert (attr >= 0);
-
-	return tb[attr] ? nla_get_u8 (tb[attr]) : default_val;
-}
-
-static inline uint16_t
-nla_get_u16 (const struct nlattr *nla)
-{
-	return *(const uint16_t *) nla_data (nla);
-}
-
-static inline uint32_t
-nla_get_u32(const struct nlattr *nla)
-{
-	return *(const uint32_t *) nla_data (nla);
-}
-
-uint64_t nla_get_u64 (const struct nlattr *nla);
-
-static inline char *
-nla_get_string (const struct nlattr *nla)
-{
-	return (char *) nla_data (nla);
-}
-
-size_t nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize);
-
-int nla_memcpy (void *dest, const struct nlattr *src, int count);
-
-int nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data);
-
-static inline int
-nla_put_string (struct nl_msg *msg, int attrtype, const char *str)
-{
-	return nla_put(msg, attrtype, strlen(str) + 1, str);
-}
-
-#define NLA_PUT(msg, attrtype, attrlen, data) \
-	do { \
-		if (nla_put(msg, attrtype, attrlen, data) < 0) \
-			goto nla_put_failure; \
-	} while(0)
-
-#define NLA_PUT_TYPE(msg, type, attrtype, value) \
-	do { \
-		type __tmp = value; \
-		NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \
-	} while(0)
-
-#define NLA_PUT_U8(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
-
-#define NLA_PUT_U16(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
-
-#define NLA_PUT_U32(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
-
-#define NLA_PUT_U64(msg, attrtype, value) \
-	NLA_PUT_TYPE(msg, uint64_t, attrtype, value)
-
-#define NLA_PUT_STRING(msg, attrtype, value) \
-	NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value)
-
-struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype);
-
-static inline int
-nla_ok (const struct nlattr *nla, int remaining)
-{
-	return remaining >= sizeof(*nla) &&
-	       nla->nla_len >= sizeof(*nla) &&
-	       nla->nla_len <= remaining;
-}
-
-static inline struct nlattr *
-nla_next(const struct nlattr *nla, int *remaining)
-{
-	int totlen = NLA_ALIGN(nla->nla_len);
-
-	*remaining -= totlen;
-	return (struct nlattr *) ((char *) nla + totlen);
-}
-
-#define nla_for_each_attr(pos, head, len, rem) \
-	for (pos = head, rem = len; \
-	     nla_ok(pos, rem); \
-	     pos = nla_next(pos, &(rem)))
-
-#define nla_for_each_nested(pos, nla, rem) \
-	for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \
-	     nla_ok(pos, rem); \
-	     pos = nla_next(pos, &(rem)))
-
-void nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr);
-struct nlattr *nla_nest_start (struct nl_msg *msg, int attrtype);
-int nla_nest_end (struct nl_msg *msg, struct nlattr *start);
-
-int nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
-               const struct nla_policy *policy);
-
-static inline int
-nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla,
-                  const struct nla_policy *policy)
-{
-	return nla_parse (tb, maxtype, nla_data(nla), nla_len(nla), policy);
-}
-
-/*****************************************************************************/
-
-struct nl_msg *nlmsg_alloc (void);
-
-struct nl_msg *nlmsg_alloc_size (size_t max);
-
-struct nl_msg *nlmsg_alloc_inherit (struct nlmsghdr *hdr);
-
-struct nl_msg *nlmsg_alloc_convert (struct nlmsghdr *hdr);
-
-struct nl_msg *nlmsg_alloc_simple (int nlmsgtype, int flags);
-
-void *nlmsg_reserve (struct nl_msg *n, size_t len, int pad);
-
-int nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad);
-
-void nlmsg_free (struct nl_msg *msg);
-
-static inline int
-nlmsg_size (int payload)
-{
-	nm_assert (payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4);
-	return NLMSG_HDRLEN + payload;
-}
-
-static inline int
-nlmsg_total_size (int payload)
-{
-	return NLMSG_ALIGN (nlmsg_size (payload));
-}
-
-static inline int
-nlmsg_ok (const struct nlmsghdr *nlh, int remaining)
-{
-	return (remaining >= (int)sizeof(struct nlmsghdr) &&
-	       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
-	       nlh->nlmsg_len <= remaining);
-}
-
-static inline struct nlmsghdr *
-nlmsg_next (struct nlmsghdr *nlh, int *remaining)
-{
-	int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
-
-	*remaining -= totlen;
-
-	return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
-}
-
-int nlmsg_get_proto (struct nl_msg *msg);
-void nlmsg_set_proto (struct nl_msg *msg, int protocol);
-
-void nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr);
-
-struct ucred *nlmsg_get_creds (struct nl_msg *msg);
-void nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds);
-
-static inline void
-_nm_auto_nl_msg_cleanup (struct nl_msg **ptr)
-{
-	nlmsg_free (*ptr);
-}
-#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup)
-
-static inline void *
-nlmsg_data (const struct nlmsghdr *nlh)
-{
-	return (unsigned char *) nlh + NLMSG_HDRLEN;
-}
-
-static inline void *
-nlmsg_tail (const struct nlmsghdr *nlh)
-{
-	return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
-}
-
-struct nlmsghdr *nlmsg_hdr (struct nl_msg *n);
-
-static inline int
-nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
-{
-	if (nlh->nlmsg_len < nlmsg_size (hdrlen))
-		return 0;
-
-	return 1;
-}
-
-static inline int
-nlmsg_datalen (const struct nlmsghdr *nlh)
-{
-	return nlh->nlmsg_len - NLMSG_HDRLEN;
-}
-
-static inline int
-nlmsg_attrlen (const struct nlmsghdr *nlh, int hdrlen)
-{
-	return NM_MAX ((int) (nlmsg_datalen (nlh) - NLMSG_ALIGN (hdrlen)), 0);
-}
-
-static inline struct nlattr *
-nlmsg_attrdata (const struct nlmsghdr *nlh, int hdrlen)
-{
-	unsigned char *data = nlmsg_data(nlh);
-	return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
-}
-
-static inline struct nlattr *
-nlmsg_find_attr (struct nlmsghdr *nlh, int hdrlen, int attrtype)
-{
-	return nla_find (nlmsg_attrdata (nlh, hdrlen),
-	                 nlmsg_attrlen (nlh, hdrlen),
-	                 attrtype);
-}
-
-int nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-                 int maxtype, const struct nla_policy *policy);
-
-struct nlmsghdr *nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq,
-                            int type, int payload, int flags);
-
-/*****************************************************************************/
-
-#define NL_AUTO_PORT 0
-#define NL_AUTO_SEQ  0
-
-struct nl_sock;
-
-struct nl_sock *nl_socket_alloc (void);
-
-void nl_socket_free (struct nl_sock *sk);
-
-int nl_socket_get_fd (const struct nl_sock *sk);
-
-struct sockaddr_nl *nlmsg_get_dst (struct nl_msg *msg);
-
-size_t nl_socket_get_msg_buf_size (struct nl_sock *sk);
-int nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize);
-
-int nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf);
-
-int nl_socket_set_passcred (struct nl_sock *sk, int state);
-
-int nl_socket_set_nonblocking (const struct nl_sock *sk);
-
-void nl_socket_disable_msg_peek (struct nl_sock *sk);
-
-uint32_t nl_socket_get_local_port (const struct nl_sock *sk);
-
-int nl_socket_add_memberships (struct nl_sock *sk, int group, ...);
-
-int nl_connect (struct nl_sock *sk, int protocol);
-
-int nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla,
-             unsigned char **buf, struct ucred **creds);
-
-int nl_send (struct nl_sock *sk, struct nl_msg *msg);
-
-int nl_send_auto (struct nl_sock *sk, struct nl_msg *msg);
-
-/*****************************************************************************/
-
-enum nl_cb_action {
-	/* Proceed with wathever would come next */
-	NL_OK,
-	/* Skip this message */
-	NL_SKIP,
-	/* Stop parsing altogether and discard remaining messages */
-	NL_STOP,
-};
-
-typedef int (*nl_recvmsg_msg_cb_t) (struct nl_msg *msg, void *arg);
-
-typedef int (*nl_recvmsg_err_cb_t) (struct sockaddr_nl *nla,
-                                    struct nlmsgerr *nlerr, void *arg);
-
-struct nl_cb {
-	nl_recvmsg_msg_cb_t     valid_cb;
-	void *                  valid_arg;
-
-	nl_recvmsg_msg_cb_t     finish_cb;
-	void *                  finish_arg;
-
-	nl_recvmsg_msg_cb_t     ack_cb;
-	void *                  ack_arg;
-
-	nl_recvmsg_err_cb_t     err_cb;
-	void *                  err_arg;
-};
-
-int nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr);
-
-int nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen);
-
-void nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg);
-
-int nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb);
-
-int nl_wait_for_ack (struct nl_sock *sk,
-                     const struct nl_cb *cb);
-
-int nl_socket_set_ext_ack (struct nl_sock *sk, gboolean enable);
-
-/*****************************************************************************/
-
-void *genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
-                   int hdrlen, int flags, uint8_t cmd, uint8_t version);
-void *genlmsg_data (const struct genlmsghdr *gnlh);
-void *genlmsg_user_hdr (const struct genlmsghdr *gnlh);
-struct genlmsghdr *genlmsg_hdr (struct nlmsghdr *nlh);
-void *genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen);
-struct nlattr *genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen);
-int genlmsg_len (const struct genlmsghdr *gnlh);
-int genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen);
-int genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen);
-int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
-                   int maxtype, const struct nla_policy *policy);
-
-int genl_ctrl_resolve (struct nl_sock *sk, const char *name);
-
-/*****************************************************************************/
-
-#endif /* __NM_NETLINK_H__ */
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index 114bf4b3..b664e8a9 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -589,8 +589,6 @@ nmp_utils_ip_config_source_coerce_to_rtprot (NMIPConfigSource source)
 	switch (source) {
 	case NM_IP_CONFIG_SOURCE_KERNEL:
 		return RTPROT_KERNEL;
-	case NM_IP_CONFIG_SOURCE_IP6LL:
-		return RTPROT_KERNEL;
 	case NM_IP_CONFIG_SOURCE_DHCP:
 		return RTPROT_DHCP;
 	case NM_IP_CONFIG_SOURCE_NDISC:
@@ -658,7 +656,6 @@ nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize
 	case NM_IP_CONFIG_SOURCE_KERNEL:          s = "kernel"; break;
 	case NM_IP_CONFIG_SOURCE_SHARED:          s = "shared"; break;
 	case NM_IP_CONFIG_SOURCE_IP4LL:           s = "ipv4ll"; break;
-	case NM_IP_CONFIG_SOURCE_IP6LL:           s = "ipv6ll"; break;
 	case NM_IP_CONFIG_SOURCE_PPP:             s = "ppp"; break;
 	case NM_IP_CONFIG_SOURCE_WWAN:            s = "wwan"; break;
 	case NM_IP_CONFIG_SOURCE_VPN:             s = "vpn"; break;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 84d862d2..c7ed90e3 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -353,45 +353,6 @@ nm_platform_process_events (NMPlatform *self)
 		klass->process_events (self);
 }
 
-const NMPlatformLink *
-nm_platform_process_events_ensure_link (NMPlatform *self,
-                                        int ifindex,
-                                        const char *ifname)
-{
-	const NMPObject *obj;
-	gboolean refreshed = FALSE;
-
-	g_return_val_if_fail (NM_IS_PLATFORM (self), NULL);
-
-	if (ifindex <= 0 && !ifname)
-		return NULL;
-
-	/* we look into the cache, whether a link for given ifindex/ifname
-	 * exits. If not, we poll the netlink socket, maybe the event
-	 * with the link is waiting.
-	 *
-	 * Then we try again to find the object.
-	 *
-	 * If the link is already cached the first time, we avoid polling
-	 * the netlink socket. */
-again:
-	obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self),
-	                                  ifindex,
-	                                  ifname,
-	                                  FALSE, /* also invisible. We don't care here whether udev is ready */
-	                                  NM_LINK_TYPE_NONE,
-	                                  NULL, NULL);
-	if (obj)
-		return NMP_OBJECT_CAST_LINK (obj);
-	if (!refreshed) {
-		refreshed = TRUE;
-		nm_platform_process_events (self);
-		goto again;
-	}
-
-	return NULL;
-}
-
 /*****************************************************************************/
 
 /**
@@ -636,11 +597,11 @@ nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name)
 	 * further by moving children/slaves to the end. */
 	g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name));
 
-	unseen = g_hash_table_new (nm_direct_hash, NULL);
+	unseen = g_hash_table_new (g_direct_hash, g_direct_equal);
 	for (i = 0; i < links->len; i++) {
 		item = NMP_OBJECT_CAST_LINK (links->pdata[i]);
 		nm_assert (item->ifindex > 0);
-		if (!g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL))
+		if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL))
 			nm_assert_not_reached ();
 	}
 
@@ -1157,21 +1118,6 @@ nm_platform_link_supports_slaves (NMPlatform *self, int ifindex)
 }
 
 /**
- * nm_platform_refresh_all:
- * @self: platform instance
- * @obj_type: The object type to request.
- *
- * Resync and re-request all objects from kernel of a certain @obj_type.
- */
-void
-nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type)
-{
-	_CHECK_SELF_VOID (self, klass);
-
-	klass->refresh_all (self, obj_type);
-}
-
-/**
  * nm_platform_link_refresh:
  * @self: platform instance
  * @ifindex: Interface index
@@ -1379,26 +1325,30 @@ gconstpointer
 nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length)
 {
 	const NMPlatformLink *pllink;
+	gconstpointer a = NULL;
+	guint8 l = 0;
 
 	_CHECK_SELF (self, klass, NULL);
 
+	if (length)
+		*length = 0;
+
 	g_return_val_if_fail (ifindex > 0, NULL);
 
 	pllink = nm_platform_link_get (self, ifindex);
-
-	if (   !pllink
-	    || pllink->addr.len <= 0) {
-		NM_SET_OUT (length, 0);
-		return NULL;
-	}
-
-	if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) {
-		NM_SET_OUT (length, 0);
-		g_return_val_if_reached (NULL);
+	if (pllink && pllink->addr.len > 0) {
+		if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) {
+			if (length)
+				*length = 0;
+			g_return_val_if_reached (NULL);
+		}
+		a = pllink->addr.data;
+		l = pllink->addr.len;
 	}
 
-	NM_SET_OUT (length, pllink->addr.len);
-	return pllink->addr.data;
+	if (length)
+		*length = l;
+	return a;
 }
 
 /**
@@ -1894,12 +1844,6 @@ nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLin
 	return _link_get_lnk (self, ifindex, NM_LINK_TYPE_SIT, out_link);
 }
 
-const NMPlatformLnkTun *
-nm_platform_link_get_lnk_tun (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
-{
-	return _link_get_lnk (self, ifindex, NM_LINK_TYPE_TUN, out_link);
-}
-
 const NMPlatformLnkVlan *
 nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
 {
@@ -2046,43 +1990,33 @@ nm_platform_link_vxlan_add (NMPlatform *self,
  * @vnet_hdr: whether to set the IFF_VNET_HDR flag
  * @multi_queue: whether to set the IFF_MULTI_QUEUE flag
  * @out_link: on success, the link object
- * @out_fd: (allow-none): if give, return the file descriptor for the
- *   created device. Note that when creating a non-persistent device,
- *   this argument is mandatory, otherwise it makes no sense
- *   to create such an interface.
- *   The caller is responsible for closing this file descriptor.
  *
  * Create a TUN or TAP interface.
  */
 NMPlatformError
 nm_platform_link_tun_add (NMPlatform *self,
                           const char *name,
-                          const NMPlatformLnkTun *props,
-                          const NMPlatformLink **out_link,
-                          int *out_fd)
+                          gboolean tap,
+                          gint64 owner,
+                          gint64 group,
+                          gboolean pi,
+                          gboolean vnet_hdr,
+                          gboolean multi_queue,
+                          const NMPlatformLink **out_link)
 {
-	char b[255];
 	NMPlatformError plerr;
 
 	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
 
 	g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
-	g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
-	g_return_val_if_fail (NM_IN_SET (props->type, IFF_TUN, IFF_TAP), NM_PLATFORM_ERROR_BUG);
-
-	/* creating a non-persistant device requires that the caller handles
-	 * the file descriptor. */
-	g_return_val_if_fail (props->persist || out_fd, NM_PLATFORM_ERROR_BUG);
 
-	NM_SET_OUT (out_fd, -1);
-
-	plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_TUN, out_link);
+	plerr = _link_add_check_existing (self, name, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, out_link);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS)
 		return plerr;
 
-	_LOGD ("link: adding tun '%s' %s",
-	       name, nm_platform_lnk_tun_to_string (props, b, sizeof (b)));
-	if (!klass->link_tun_add (self, name, props, out_link, out_fd))
+	_LOGD ("link: adding %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT,
+	       tap ? "tap" : "tun", name, owner, group);
+	if (!klass->tun_add (self, name, tap, owner, group, pi, vnet_hdr, multi_queue, out_link))
 		return NM_PLATFORM_ERROR_UNSPECIFIED;
 	return NM_PLATFORM_ERROR_SUCCESS;
 }
@@ -2686,100 +2620,44 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe
 	return TRUE;
 }
 
-/**
- * nm_platform_link_tun_get_properties:
- * @self: the #NMPlatform instance
- * @ifindex: the ifindex to look up
- * @out_properties: (out): (allow-none): return the read properties
- *
- * Only recent versions of kernel export tun properties via netlink.
- * So, if that's the case, then we have the NMPlatformLnkTun instance
- * in the platform cache ready to return. Otherwise, this function
- * falls back reading sysctl to obtain the tun properties. That
- * is racy, because querying sysctl means that the object might
- * be already removed from cache (while NM didn't yet process the
- * netlink message).
- *
- * Hence, to lookup the tun properties, you always need to use this
- * function, and use it with care knowing that it might obtain its
- * data by reading sysctl. Note that we don't want to add this workaround
- * to the platform cache itself, because the cache should (mainly)
- * contain data from netlink. To access the sysctl side channel, the
- * user needs to do explicitly.
- *
- * Returns: #TRUE, if the properties could be read. */
 gboolean
-nm_platform_link_tun_get_properties (NMPlatform *self,
-                                     int ifindex,
-                                     NMPlatformLnkTun *out_properties)
+nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props)
 {
-	const NMPObject *plobj;
-	const NMPObject *pllnk;
+	nm_auto_close int dirfd = -1;
 	char ifname[IFNAMSIZ];
-	gint64 owner;
-	gint64 group;
 	gint64 flags;
-
+	gboolean success = TRUE;
 	_CHECK_SELF (self, klass, FALSE);
 
 	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (props, FALSE);
 
-	/* we consider also invisible links (those that are not yet in udev). */
-	plobj = nm_platform_link_get_obj (self, ifindex, FALSE);
-	if (!plobj)
-		return FALSE;
-	if (NMP_OBJECT_CAST_LINK (plobj)->type != NM_LINK_TYPE_TUN)
-		return FALSE;
-
-	pllnk = plobj->_link.netlink.lnk;
-	if (pllnk) {
-		nm_assert (NMP_OBJECT_GET_TYPE (pllnk) == NMP_OBJECT_TYPE_LNK_TUN);
-		nm_assert (NMP_OBJECT_GET_CLASS (pllnk)->lnk_link_type == NM_LINK_TYPE_TUN);
+	memset (props, 0, sizeof (*props));
+	props->owner = -1;
+	props->group = -1;
 
-		/* recent kernels expose tun properties via netlink and thus we have them
-		 * in the platform cache. */
-		NM_SET_OUT (out_properties, pllnk->lnk_tun);
-		return TRUE;
-	}
-
-	/* fallback to reading sysctl. */
-	{
-		nm_auto_close int dirfd = -1;
+	dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname);
+	if (dirfd < 0)
+		return FALSE;
 
-		dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname);
-		if (dirfd < 0)
-			return FALSE;
+	props->owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXINT64, -1);
+	if (errno)
+		success = FALSE;
 
-		owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXUINT32, -2);
-		if (owner == -2)
-			return FALSE;
+	props->group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXINT64, -1);
+	if (errno)
+		success = FALSE;
 
-		group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXUINT32, -2);
-		if (group == -2)
-			return FALSE;
-
-		flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1);
-		if (flags == -1)
-			return FALSE;
-	}
+	flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1);
+	if (flags >= 0) {
+		props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap";
+		props->no_pi = !!(flags & IFF_NO_PI);
+		props->vnet_hdr = !!(flags & IFF_VNET_HDR);
+		props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
+	} else
+		success = FALSE;
 
-	if (out_properties) {
-		memset (out_properties, 0, sizeof (*out_properties));
-		if (owner != -1) {
-			out_properties->owner_valid = TRUE;
-			out_properties->owner = owner;
-		}
-		if (group != -1) {
-			out_properties->group_valid = TRUE;
-			out_properties->group = group;
-		}
-		out_properties->type = (flags & TUN_TYPE_MASK);
-		out_properties->pi = !(flags & IFF_NO_PI);
-		out_properties->vnet_hdr = !!(flags & IFF_VNET_HDR);
-		out_properties->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
-		out_properties->persist = !!(flags & IFF_PERSIST);
-	}
-	return TRUE;
+	return success;
 }
 
 gboolean
@@ -3054,7 +2932,7 @@ nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *ob
  * @user_data: user data for @predicate
  *
  * Returns the result of lookup in a GPtrArray. The result array contains
- * references objects from the cache, its destroy function will unref them.
+ * references objects from the cache, it's destroy function will unref them.
  *
  * The user must unref the GPtrArray, which will also unref the NMPObject
  * elements.
@@ -3233,68 +3111,24 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr
 }
 
 static gboolean
-_addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint32 now, GHashTable **idx)
+array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now)
 {
+	guint len = addresses ? addresses->len : 0;
 	guint i;
-	gboolean any_addrs = FALSE;
-
-	nm_assert_addr_family (addr_family);
-	nm_assert (ifindex > 0);
-	nm_assert (now > 0);
 
-	if (!array)
-		return FALSE;
-
-	/* remove all addresses that are already expired. */
-	for (i = 0; i < array->len; i++) {
-		const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS (array->pdata[i]);
-
-#if NM_MORE_ASSERTS > 10
-		nm_assert (a);
-		nm_assert (a->ifindex == ifindex);
-		{
-			const NMPObject *o = NMP_OBJECT_UP_CAST (a);
-			guint j;
-
-			nm_assert (NMP_OBJECT_GET_CLASS (o)->addr_family == addr_family);
-			for (j = i + 1; j < array->len; j++) {
-				const NMPObject *o2 = array->pdata[j];
-
-				nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_GET_TYPE (o2));
-				nm_assert (!nmp_object_id_equal (o, o2));
-			}
-		}
-#endif
-
-		if (   addr_family == AF_INET6
-		    && NM_FLAGS_HAS (a->n_ifa_flags, IFA_F_TEMPORARY)) {
-			/* temporary addresses are never added explicitly by NetworkManager but
-			 * kernel adds them via mngtempaddr flag.
-			 *
-			 * We drop them from this list. */
-			goto clear_and_next;
-		}
+	for (i = 0; i < len; i++) {
+		NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]);
 
-		if (!nm_utils_lifetime_get (a->timestamp, a->lifetime, a->preferred,
-		                            now, NULL))
-			goto clear_and_next;
+		if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) {
+			guint32 lifetime, preferred;
 
-		if (idx) {
-			if (G_UNLIKELY (!*idx)) {
-				*idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
-				                         (GEqualFunc) nmp_object_id_equal);
-			}
-			if (!g_hash_table_add (*idx, (gpointer) NMP_OBJECT_UP_CAST (a)))
-				nm_assert_not_reached ();
+			if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
+			                           now, &lifetime, &preferred))
+				return TRUE;
 		}
-		any_addrs = TRUE;
-		continue;
-
-clear_and_next:
-		nmp_object_unref (g_steal_pointer (&array->pdata[i]));
 	}
 
-	return any_addrs;
+	return FALSE;
 }
 
 static gboolean
@@ -3345,7 +3179,7 @@ ip4_addr_subnets_build_index (const GPtrArray *addresses,
 
 	nm_assert (addresses && addresses->len);
 
-	subnets = g_hash_table_new (nm_direct_hash, NULL);
+	subnets = g_hash_table_new (NULL, NULL);
 
 	/* Build a hash table of all addresses per subnet */
 	for (i = 0; i < addresses->len; i++) {
@@ -3478,8 +3312,39 @@ nm_platform_ip4_address_sync (NMPlatform *self,
 
 	_CHECK_SELF (self, klass, FALSE);
 
-	if (!_addr_array_clean_expired (AF_INET, ifindex, known_addresses, now, &known_addresses_idx))
-		known_addresses = NULL;
+	if (known_addresses) {
+		/* remove all addresses that are already expired. */
+		for (i = 0; i < known_addresses->len; i++) {
+			const NMPObject *o;
+
+			o = known_addresses->pdata[i];
+			nm_assert (o);
+
+			known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]);
+
+			if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+			                            now, &lifetime, &preferred))
+				goto delete_and_next;
+
+			if (G_UNLIKELY (!known_addresses_idx)) {
+				known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
+				                                        (GEqualFunc) nmp_object_id_equal);
+			}
+			if (!nm_g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) {
+				/* duplicate? Keep only the first instance. */
+				goto delete_and_next;
+			}
+
+			continue;
+delete_and_next:
+			nmp_object_unref (o);
+			known_addresses->pdata[i] = NULL;
+		}
+
+		if (   !known_addresses_idx
+		    || g_hash_table_size (known_addresses_idx) == 0)
+			known_addresses = NULL;
+	}
 
 	plat_addresses = nm_platform_lookup_clone (self,
 	                                           nmp_lookup_init_object (&lookup,
@@ -3576,9 +3441,8 @@ nm_platform_ip4_address_sync (NMPlatform *self,
 
 		known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o);
 
-		lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
-		                                  now, &preferred);
-		if (!lifetime)
+		if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+		                            now, &lifetime, &preferred))
 			goto delete_and_next2;
 
 		if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen,
@@ -3600,15 +3464,9 @@ delete_and_next2:
  * nm_platform_ip6_address_sync:
  * @self: platform instance
  * @ifindex: Interface index
- * @known_addresses: List of addresses. The list will be modified and only
- *   addresses that were successfully added will be kept in the list.
- *   That means, expired addresses and addresses that could not be added
- *   will be dropped.
- *   Hence, the input argument @known_addresses is also an output argument
- *   telling which addresses were succesfully added.
- *   Addresses are removed by unrefing the instance via nmp_object_unref()
- *   and leaving a NULL tombstone.
- * @full_sync: Also remove link-local and temporary addresses.
+ * @known_addresses: List of IPv6 addresses, as NMPObject. The list
+ *   is not modified.
+ * @keep_link_local: Don't remove link-local address
  *
  * A convenience function to synchronize addresses for a specific interface
  * with the least possible disturbance. It simply removes addresses that are
@@ -3619,117 +3477,32 @@ delete_and_next2:
 gboolean
 nm_platform_ip6_address_sync (NMPlatform *self,
                               int ifindex,
-                              GPtrArray *known_addresses,
-                              gboolean full_sync)
+                              const GPtrArray *known_addresses,
+                              gboolean keep_link_local)
 {
 	gs_unref_ptrarray GPtrArray *plat_addresses = NULL;
+	NMPlatformIP6Address *address;
 	gint32 now = nm_utils_get_monotonic_timestamp_s ();
-	guint i_plat, i_know;
-	gs_unref_hashtable GHashTable *known_addresses_idx = NULL;
+	guint i;
 	NMPLookup lookup;
 	guint32 ifa_flags;
 
-	if (!_addr_array_clean_expired (AF_INET6, ifindex, known_addresses, now, &known_addresses_idx))
-		known_addresses = NULL;
-
-	/* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to
-	 * @known_addresses which is in increasing priority order (lowest priority addresses first). */
+	/* Delete unknown addresses */
 	plat_addresses = nm_platform_lookup_clone (self,
 	                                           nmp_lookup_init_object (&lookup,
 	                                                                   NMP_OBJECT_TYPE_IP6_ADDRESS,
 	                                                                   ifindex),
 	                                           NULL, NULL);
-
 	if (plat_addresses) {
-		guint known_addresses_len;
-
-		known_addresses_len = known_addresses ? known_addresses->len : 0;
-
-		/* First, compare every address whether it is still a "known address", that is, whether
-		 * to keep it or to delete it.
-		 *
-		 * If we don't find a matching valid address in @known_addresses, we will delete
-		 * plat_addr.
-		 *
-		 * Certain addresses, like temporary addresses, are ignored by this function
-		 * if not run with full_sync. These addresses are usually not managed by NetworkManager
-		 * directly, or at least, they are not managed via nm_platform_ip6_address_sync().
-		 * Only in full_sync mode, we really want to get rid of them (usually, when we take
-		 * the interface down).
-		 *
-		 * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */
-		for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) {
-			const NMPObject *plat_obj = plat_addresses->pdata[i_plat];
-			const NMPObject *know_obj;
-			const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj);
-
-			if (NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_TEMPORARY)) {
-				if (!full_sync) {
-					/* just mark as handled, without actually deleting the address. */
-					goto clear_and_next;
-				}
-			} else if (known_addresses_idx) {
-				know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj);
-				if (   know_obj
-				    && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) {
-					/* technically, plen is not part of the ID for IPv6 addresses and thus
-					 * @plat_addr is essentially the same address as @know_addr (regrading
-					 * its identity, not its other attributes).
-					 * However, we cannot modify an existing addresses' plen without
-					 * removing and readding it. Thus, only keep plat_addr, if the plen
-					 * matches.
-					 *
-					 * keep this one, and continue */
-					continue;
-				}
-			}
-
-			nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
-clear_and_next:
-			nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat]));
-		}
+		for (i = 0; i < plat_addresses->len; i++) {
+			address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]);
 
-		/* Next, we must preserve the priority of the routes. That is, source address
-		 * selection will choose addresses in the order as they are reported by kernel.
-		 * Note that the order in @plat_addresses of the remaining matches is highest
-		 * priority first.
-		 * We need to compare this to the order in @known_addresses (which has lowest
-		 * priority first).
-		 *
-		 * If we find a first discrepancy, we need to delete all remaining addresses
-		 * from that point on, because below we must re-add all the addresses in the
-		 * right order to get their priority right. */
-		i_plat = plat_addresses->len;
-		i_know = 0;
-		while (i_plat > 0) {
-			const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]);
-
-			if (!plat_addr)
+			/* Leave link local address management to the kernel */
+			if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address))
 				continue;
 
-			for (; i_know < known_addresses_len; i_know++) {
-				const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
-
-				if (!know_addr)
-					continue;
-
-				if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) {
-					/* we have a match. Mark address as handled. */
-					i_know++;
-					goto next_plat;
-				}
-
-				/* all remainging addresses need to be removed as well, so that we can
-				 * re-add them in the correct order. Signal that, by setting @i_know
-				 * so that the next @i_plat iteration, we won't enter the loop and
-				 * delete the address right away */
-				i_know = known_addresses_len;
-				break;
-			}
-
-			nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
-next_plat:
-			;
+			if (!array_contains_ip6_address (known_addresses, address, now))
+				nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen);
 		}
 	}
 
@@ -3740,18 +3513,19 @@ next_plat:
 	            ? IFA_F_NOPREFIXROUTE
 	            : 0;
 
-	/* Add missing addresses. New addresses are added by kernel with top
-	 * priority.
-	 */
-	for (i_know = 0; i_know < known_addresses->len; i_know++) {
-		const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
+	/* Add missing addresses */
+	for (i = 0; i < known_addresses->len; i++) {
+		const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]);
 		guint32 lifetime, preferred;
 
-		if (!known_address)
+		if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_TEMPORARY)) {
+			/* Kernel manages these */
 			continue;
+		}
 
-		lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
-		                                  now, &preferred);
+		if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+		                            now, &lifetime, &preferred))
+			continue;
 
 		if (!nm_platform_ip6_address_add (self, ifindex, known_address->address,
 		                                  known_address->plen, known_address->peer_address,
@@ -3779,7 +3553,7 @@ nm_platform_ip_address_flush (NMPlatform *self,
 	if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET))
 		success &= nm_platform_ip4_address_sync (self, ifindex, NULL);
 	if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6))
-		success &= nm_platform_ip6_address_sync (self, ifindex, NULL, TRUE);
+		success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE);
 	return success;
 }
 
@@ -3918,8 +3692,7 @@ nm_platform_ip_route_sync (NMPlatform *self,
 
 	for (i_type = 0; routes && i_type < 2; i_type++) {
 		for (i = 0; i < routes->len; i++) {
-			NMPlatformError plerr, plerr2;
-			gboolean gateway_route_added = FALSE;
+			NMPlatformError plerr;
 
 			conf_o = routes->pdata[i];
 
@@ -3939,7 +3712,7 @@ nm_platform_ip_route_sync (NMPlatform *self,
 				routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
 				                               (GEqualFunc) nmp_object_id_equal);
 			}
-			if (!g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) {
+			if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) {
 				_LOGD ("route-sync: skip adding duplicate route %s",
 				       nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)));
 				continue;
@@ -3965,7 +3738,6 @@ nm_platform_ip_route_sync (NMPlatform *self,
 				}
 			}
 
-sync_route_add:
 			plerr = nm_platform_ip_route_add (self,
 			                                    NMP_NLM_FLAG_APPEND
 			                                  | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE,
@@ -3990,11 +3762,6 @@ sync_route_add:
 							       nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2)));
 						}
 					}
-				} else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) {
-					_LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s",
-					       vt->is_ip4 ? '4' : '6',
-					       nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)),
-					       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)));
 				} else if (   -((int) plerr) == EINVAL
 				           && out_temporary_not_available
 				           && _err_inval_due_to_ipv6_tentative_pref_src (self, conf_o)) {
@@ -4004,66 +3771,25 @@ sync_route_add:
 					if (!*out_temporary_not_available)
 						*out_temporary_not_available = g_ptr_array_new_full (0, (GDestroyNotify) nmp_object_unref);
 					g_ptr_array_add (*out_temporary_not_available, (gpointer) nmp_object_ref (conf_o));
-				} else if (   !gateway_route_added
-				           && (   (   -((int) plerr) == ENETUNREACH
-				                   && vt->is_ip4
-				                   && !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway)
-				               || (   -((int) plerr) == EHOSTUNREACH
-				                   && !vt->is_ip4
-				                   && !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway)))) {
-					NMPObject oo;
-
-					if (vt->is_ip4) {
-						const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (conf_o);
-
-						nmp_object_stackinit (&oo,
-						                      NMP_OBJECT_TYPE_IP4_ROUTE,
-						                      &((NMPlatformIP4Route) {
-						                          .network = r->gateway,
-						                          .plen = 32,
-						                          .metric = r->metric,
-						                          .rt_source = r->rt_source,
-						                          .table_coerced = r->table_coerced,
-						                      }));
-					} else {
-						const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (conf_o);
-
-						nmp_object_stackinit (&oo,
-						                      NMP_OBJECT_TYPE_IP6_ROUTE,
-						                      &((NMPlatformIP6Route) {
-						                          .network = r->gateway,
-						                          .plen = 128,
-						                          .metric = r->metric,
-						                          .rt_source = r->rt_source,
-						                          .table_coerced = r->table_coerced,
-						                      }));
-					}
-
-					_LOGD ("route-sync: failure to add IPv%c route: %s: %s; try adding direct route to gateway %s",
+				} else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) {
+					_LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s",
 					       vt->is_ip4 ? '4' : '6',
 					       nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)),
-					       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)),
-					       nmp_object_to_string (&oo, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2)));
-
-					plerr2 = nm_platform_ip_route_add (self,
-					                                     NMP_NLM_FLAG_APPEND
-					                                   | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE,
-					                                   &oo);
-
-					if (plerr2 != NM_PLATFORM_ERROR_SUCCESS) {
-						_LOGD ("route-sync: failure to add gateway IPv%c route: %s: %s",
-						       vt->is_ip4 ? '4' : '6',
-						       nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)),
-						       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)));
-					}
-
-					gateway_route_added = TRUE;
-					goto sync_route_add;
+					       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)));
 				} else {
-					_LOGW ("route-sync: failure to add IPv%c route: %s: %s",
+					const char *reason = "";
+
+					if (   -((int) plerr) == ENETUNREACH
+					    && (  vt->is_ip4
+					        ? !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway
+					        : !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway)))
+						reason = "; is the gateway directly reachable?";
+
+					_LOGW ("route-sync: failure to add IPv%c route: %s: %s%s",
 					       vt->is_ip4 ? '4' : '6',
 					       nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)),
-					       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)));
+					       nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)),
+					       reason);
 					success = FALSE;
 				}
 			}
@@ -4987,7 +4713,6 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi
 	            "%s" /* encap limit */
 	            "%s" /* flow label */
 	            "%s" /* proto */
-	            " flags 0x%x"
 	            "",
 	            nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)),
 	            nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)),
@@ -4996,8 +4721,7 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi
 	            lnk->tclass == 1 ? " tclass inherit" : nm_sprintf_buf (str_tclass, " tclass 0x%x", lnk->tclass),
 	            nm_sprintf_buf (str_encap, " encap-limit %u", lnk->encap_limit),
 	            nm_sprintf_buf (str_flow, " flow-label 0x05%x", lnk->flow_label),
-	            nm_sprintf_buf (str_proto, " proto %u", lnk->proto),
-	            (guint) lnk->flags);
+	            nm_sprintf_buf (str_proto, " proto %u", lnk->proto));
 	return buf;
 }
 
@@ -5118,43 +4842,6 @@ nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len
 }
 
 const char *
-nm_platform_lnk_tun_to_string (const NMPlatformLnkTun *lnk, char *buf, gsize len)
-{
-	char str_owner[50];
-	char str_group[50];
-	char str_type[50];
-	const char *type;
-
-	if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
-		return buf;
-
-	if (lnk->type == IFF_TUN)
-		type = "tun";
-	else if (lnk->type == IFF_TAP)
-		type = "tap";
-	else
-		type = nm_sprintf_buf (str_type, "tun type %u", (guint) lnk->type);
-
-	g_snprintf (buf, len,
-	            "%s" /* type */
-	            "%s" /* pi */
-	            "%s" /* vnet_hdr */
-	            "%s" /* multi_queue */
-	            "%s" /* persist */
-	            "%s" /* owner */
-	            "%s" /* group */
-	            "",
-	            type,
-	            lnk->pi ? " pi" : "",
-	            lnk->vnet_hdr ? " vnet_hdr" : "",
-	            lnk->multi_queue ? " multi_queue" : "",
-	            lnk->persist ? " persist" : "",
-	            lnk->owner_valid ? nm_sprintf_buf (str_owner, " owner %u", (guint) lnk->owner) : "",
-	            lnk->group_valid ? nm_sprintf_buf (str_group, " group %u", (guint) lnk->group) : "");
-	return buf;
-}
-
-const char *
 nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len)
 {
 	char *b;
@@ -5356,10 +5043,10 @@ NM_UTILS_FLAGS2STR_DEFINE (nm_platform_addr_flags2str, unsigned,
 	NM_UTILS_FLAGS2STR (IFA_F_OPTIMISTIC, "optimistic"),
 	NM_UTILS_FLAGS2STR (IFA_F_HOMEADDRESS, "homeaddress"),
 	NM_UTILS_FLAGS2STR (IFA_F_DEPRECATED, "deprecated"),
+	NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"),
 	NM_UTILS_FLAGS2STR (IFA_F_PERMANENT, "permanent"),
 	NM_UTILS_FLAGS2STR (IFA_F_MANAGETEMPADDR, "mngtmpaddr"),
 	NM_UTILS_FLAGS2STR (IFA_F_NOPREFIXROUTE, "noprefixroute"),
-	NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"),
 );
 
 NM_UTILS_ENUM2STR_DEFINE (nm_platform_route_scope2str, int,
@@ -5850,8 +5537,7 @@ nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState
 	                     obj->tclass,
 	                     obj->encap_limit,
 	                     obj->proto,
-	                     obj->flow_label,
-	                     obj->flags);
+	                     obj->flow_label);
 }
 
 int
@@ -5866,7 +5552,6 @@ nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6
 	NM_CMP_FIELD (a, b, encap_limit);
 	NM_CMP_FIELD (a, b, flow_label);
 	NM_CMP_FIELD (a, b, proto);
-	NM_CMP_FIELD (a, b, flags);
 	return 0;
 }
 
@@ -5985,38 +5670,6 @@ nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b)
 }
 
 void
-nm_platform_lnk_tun_hash_update (const NMPlatformLnkTun *obj, NMHashState *h)
-{
-	nm_hash_update_vals (h,
-	                     obj->type,
-	                     obj->owner,
-	                     obj->group,
-	                     NM_HASH_COMBINE_BOOLS (guint8,
-	                                            obj->owner_valid,
-	                                            obj->group_valid,
-	                                            obj->pi,
-	                                            obj->vnet_hdr,
-	                                            obj->multi_queue,
-	                                            obj->persist));
-}
-
-int
-nm_platform_lnk_tun_cmp (const NMPlatformLnkTun *a, const NMPlatformLnkTun *b)
-{
-	NM_CMP_SELF (a, b);
-	NM_CMP_FIELD (a, b, type);
-	NM_CMP_FIELD (a, b, owner);
-	NM_CMP_FIELD (a, b, group);
-	NM_CMP_FIELD_BOOL (a, b, owner_valid);
-	NM_CMP_FIELD_BOOL (a, b, group_valid);
-	NM_CMP_FIELD_BOOL (a, b, pi);
-	NM_CMP_FIELD_BOOL (a, b, vnet_hdr);
-	NM_CMP_FIELD_BOOL (a, b, multi_queue);
-	NM_CMP_FIELD_BOOL (a, b, persist);
-	return 0;
-}
-
-void
 nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h)
 {
 	nm_hash_update_vals (h,
@@ -6607,13 +6260,14 @@ nm_platform_cache_update_emit_signal (NMPlatform *self,
 	const NMPObject *o;
 	const NMPClass *klass;
 
-	nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, NM_PLATFORM_SIGNAL_NONE,
-	                                                             NM_PLATFORM_SIGNAL_ADDED,
-	                                                             NM_PLATFORM_SIGNAL_CHANGED,
-	                                                             NM_PLATFORM_SIGNAL_REMOVED));
+	nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED));
 
 	ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new);
 
+	nm_assert (NM_IN_SET (nm_platform_netns_get (self),
+	                      NULL,
+	                      nmp_netns_get_current ()));
+
 	NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self);
 
 	switch (cache_op) {
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index e6cef63b..f6bf02bf 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -25,7 +25,6 @@
 #include <linux/if.h>
 #include <linux/if_addr.h>
 #include <linux/if_link.h>
-#include <linux/ip6_tunnel.h>
 
 #include "nm-dbus-interface.h"
 #include "nm-core-types-internal.h"
@@ -33,7 +32,6 @@
 #include "nm-core-utils.h"
 #include "nm-setting-vlan.h"
 #include "nm-setting-wired.h"
-#include "nm-setting-ip-tunnel.h"
 
 #define NM_TYPE_PLATFORM            (nm_platform_get_type ())
 #define NM_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform))
@@ -171,34 +169,6 @@ typedef enum { /*< skip >*/
 	NM_PLATFORM_ERROR_CANT_SET_MTU,
 } NMPlatformError;
 
-typedef enum {
-
-	/* match-flags are strictly inclusive. That means,
-	 * by default nothing is matched, but if you enable a particular
-	 * flag, a candidate that matches passes the check.
-	 *
-	 * In other words: adding more flags can only extend the result
-	 * set of matching objects.
-	 *
-	 * Also, the flags form partitions. Like, an address can be either of
-	 * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for
-	 * the ADDRSTATE match types.
-	 */
-	NM_PLATFORM_MATCH_WITH_NONE                                 = 0,
-
-	NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL                      = (1LL <<  0),
-	NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL                   = (1LL <<  1),
-	NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY                        =   NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL
-	                                                              | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL,
-
-	NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL                     = (1LL <<  2),
-	NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE                  = (1LL <<  3),
-	NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED                  = (1LL <<  4),
-	NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY                       =   NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL
-	                                                              | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE
-	                                                              | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED,
-} NMPlatformMatchFlags;
-
 #define NM_PLATFORM_LINK_OTHER_NETNS    (-1)
 
 #define __NMPlatformObject_COMMON \
@@ -284,8 +254,6 @@ struct _NMPlatformObject {
 	__NMPlatformObject_COMMON;
 };
 
-#define NM_PLATFORM_IP_ADDRESS_CAST(address) \
-	NM_CONSTCAST (NMPlatformIPAddress, (address), NMPlatformIPXAddress, NMPlatformIP4Address, NMPlatformIP6Address)
 
 #define __NMPlatformIPAddress_COMMON \
 	__NMPlatformObject_COMMON; \
@@ -434,7 +402,7 @@ typedef union {
 	 * do not exist from the point-of-view of platform users.
 	 * Such a route is not alive, according to nmp_object_is_alive().
 	 *
-	 * NOTE: currently we ignore all flags except RTM_F_CLONED
+	 * XXX: currently we ignore all flags except RTM_F_CLONED
 	 * and RTNH_F_ONLINK for IPv4.
 	 * We also may not properly consider the flags as part of the ID
 	 * in route-cmp. */ \
@@ -636,7 +604,6 @@ typedef struct {
 	guint8 encap_limit;
 	guint8 proto;
 	guint flow_label;
-	guint32 flags;
 } NMPlatformLnkIp6Tnl;
 
 typedef struct {
@@ -684,21 +651,6 @@ typedef struct {
 } NMPlatformLnkSit;
 
 typedef struct {
-	guint32 owner;
-	guint32 group;
-
-	guint8 type;
-
-	bool owner_valid:1;
-	bool group_valid:1;
-
-	bool pi:1;
-	bool vnet_hdr:1;
-	bool multi_queue:1;
-	bool persist:1;
-} NMPlatformLnkTun;
-
-typedef struct {
 	/* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */
 	guint16 id;
 	NMVlanFlags flags;
@@ -725,6 +677,15 @@ typedef struct {
 	bool l3miss:1;
 } NMPlatformLnkVxlan;
 
+typedef struct {
+	gint64 owner;
+	gint64 group;
+	const char *mode;
+	bool no_pi:1;
+	bool vnet_hdr:1;
+	bool multi_queue:1;
+} NMPlatformTunProperties;
+
 typedef enum {
 	NM_PLATFORM_LINK_DUPLEX_UNKNOWN,
 	NM_PLATFORM_LINK_DUPLEX_HALF,
@@ -753,8 +714,6 @@ typedef struct {
 	gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value);
 	char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path);
 
-	void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type);
-
 	gboolean (*link_add) (NMPlatform *,
 	                      const char *name,
 	                      NMLinkType type,
@@ -850,15 +809,12 @@ typedef struct {
 	                          const NMPlatformLnkSit *props,
 	                          const NMPlatformLink **out_link);
 
-	gboolean (*link_tun_add) (NMPlatform *platform,
-	                          const char *name,
-	                          const NMPlatformLnkTun *props,
-	                          const NMPlatformLink **out_link,
-	                          int *out_fd);
-
 	gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link);
 	gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key);
 
+	gboolean (*tun_add) (NMPlatform *platform, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi,
+	                     gboolean vnet_hdr, gboolean multi_queue, const NMPlatformLink **out_link);
+
 	gboolean    (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps);
 	gboolean    (*wifi_get_bssid)        (NMPlatform *, int ifindex, guint8 *bssid);
 	GByteArray *(*wifi_get_ssid)         (NMPlatform *, int ifindex);
@@ -1072,8 +1028,6 @@ gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char
 const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */);
 int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname);
 
-void nm_platform_refresh_all (NMPlatform *self, NMPObjectType obj_type);
-
 const NMPObject *nm_platform_link_get_obj (NMPlatform *self,
                                            int ifindex,
                                            gboolean visible_only);
@@ -1117,21 +1071,7 @@ gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex);
 gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex);
 guint32 nm_platform_link_get_mtu (NMPlatform *self, int ifindex);
 gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex);
-
 gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length);
-
-static inline GBytes *
-nm_platform_link_get_address_as_bytes (NMPlatform *self, int ifindex)
-{
-	gconstpointer p;
-	gsize l;
-
-	p = nm_platform_link_get_address (self, ifindex, &l);
-	return p
-	       ? g_bytes_new (p, l)
-	       : NULL;
-}
-
 int nm_platform_link_get_master (NMPlatform *self, int slave);
 
 gboolean nm_platform_link_can_assume (NMPlatform *self, int ifindex);
@@ -1143,10 +1083,6 @@ const char *nm_platform_link_get_type_name (NMPlatform *self, int ifindex);
 gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex);
 void nm_platform_process_events (NMPlatform *self);
 
-const NMPlatformLink *nm_platform_process_events_ensure_link (NMPlatform *self,
-                                                              int ifindex,
-                                                              const char *ifname);
-
 gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware);
 gboolean nm_platform_link_set_down (NMPlatform *self, int ifindex);
 gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex);
@@ -1196,7 +1132,6 @@ const NMPlatformLnkMacsec *nm_platform_link_get_lnk_macsec (NMPlatform *self, in
 const NMPlatformLnkMacvlan *nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkMacvtap *nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkSit *nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
-const NMPlatformLnkTun *nm_platform_link_get_lnk_tun (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
 
@@ -1224,6 +1159,16 @@ NMPlatformError nm_platform_link_vxlan_add (NMPlatform *self,
                                             const NMPlatformLnkVxlan *props,
                                             const NMPlatformLink **out_link);
 
+NMPlatformError nm_platform_link_tun_add (NMPlatform *self,
+                                          const char *name,
+                                          gboolean tap,
+                                          gint64 owner,
+                                          gint64 group,
+                                          gboolean pi,
+                                          gboolean vnet_hdr,
+                                          gboolean multi_queue,
+                                          const NMPlatformLink **out_link);
+
 NMPlatformError nm_platform_link_infiniband_add (NMPlatform *self,
                                                  int parent,
                                                  int p_key,
@@ -1234,9 +1179,7 @@ NMPlatformError nm_platform_link_infiniband_delete (NMPlatform *self,
 gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode);
 
 gboolean nm_platform_link_veth_get_properties   (NMPlatform *self, int ifindex, int *out_peer_ifindex);
-gboolean nm_platform_link_tun_get_properties    (NMPlatform *self,
-                                                 int ifindex,
-                                                 NMPlatformLnkTun *out_properties);
+gboolean nm_platform_link_tun_get_properties    (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties);
 
 gboolean    nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps);
 gboolean    nm_platform_wifi_get_bssid        (NMPlatform *self, int ifindex, guint8 *bssid);
@@ -1284,11 +1227,6 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self,
                                           const char *name,
                                           const NMPlatformLnkSit *props,
                                           const NMPlatformLink **out_link);
-NMPlatformError nm_platform_link_tun_add (NMPlatform *self,
-                                          const char *name,
-                                          const NMPlatformLnkTun *props,
-                                          const NMPlatformLink **out_link,
-                                          int *out_fd);
 
 const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address);
 
@@ -1313,8 +1251,8 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self,
                                       guint32 flags);
 gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address);
 gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen);
-gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses);
-gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync);
+gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse);
+gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local);
 gboolean nm_platform_ip_address_flush (NMPlatform *self,
                                        int addr_family,
                                        int ifindex);
@@ -1372,7 +1310,6 @@ const char *nm_platform_lnk_ipip_to_string (const NMPlatformLnkIpIp *lnk, char *
 const char *nm_platform_lnk_macsec_to_string (const NMPlatformLnkMacsec *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len);
-const char *nm_platform_lnk_tun_to_string (const NMPlatformLnkTun *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len);
 const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len);
@@ -1396,7 +1333,6 @@ int nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpI
 int nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b);
 int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b);
 int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b);
-int nm_platform_lnk_tun_cmp (const NMPlatformLnkTun *a, const NMPlatformLnkTun *b);
 int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b);
 int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
 int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b);
@@ -1432,7 +1368,6 @@ void nm_platform_lnk_ipip_hash_update (const NMPlatformLnkIpIp *obj, NMHashState
 void nm_platform_lnk_macsec_hash_update (const NMPlatformLnkMacsec *obj, NMHashState *h);
 void nm_platform_lnk_macvlan_hash_update (const NMPlatformLnkMacvlan *obj, NMHashState *h);
 void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *h);
-void nm_platform_lnk_tun_hash_update (const NMPlatformLnkTun *obj, NMHashState *h);
 void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h);
 void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h);
 
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index f1092fe9..d8561aef 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -200,8 +200,8 @@ _stack_current_ns_types (NMPNetns *netns, int ns_types)
 		}
 
 		for (i = 0; i < G_N_ELEMENTS (ns_types_check); i++) {
-			if (   NM_FLAGS_ANY (ns_types, ns_types_check[i])
-			    && NM_FLAGS_ANY (info->ns_types, ns_types_check[i])) {
+			if (   NM_FLAGS_HAS (ns_types, ns_types_check[i])
+			    && NM_FLAGS_HAS (info->ns_types, ns_types_check[i])) {
 				res = NM_FLAGS_SET (res, ns_types_check[i]);
 				ns_types = NM_FLAGS_UNSET (ns_types, ns_types_check[i]);
 			}
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 29bb999b..1a9b9325 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -325,7 +325,7 @@ _vlan_xgress_qos_mappings_cmp (guint n_map,
 
 static void
 _vlan_xgress_qos_mappings_cpy (guint *dst_n_map,
-                               NMVlanQosMapping **dst_map,
+                               const NMVlanQosMapping **dst_map,
                                guint src_n_map,
                                const NMVlanQosMapping *src_map)
 {
@@ -388,7 +388,7 @@ _nmp_object_fixup_link_udev_fields (NMPObject **obj_new, NMPObject *obj_orig, gb
 	/* The link contains internal fields that are combined by
 	 * properties from netlink and udev. Update those properties */
 
-	/* When a link is not in netlink, its udev fields don't matter. */
+	/* When a link is not in netlink, it's udev fields don't matter. */
 	if (obj->_link.netlink.is_in_netlink) {
 		driver = _link_get_driver (obj->_link.udev.device,
 		                           obj->link.kind,
@@ -532,7 +532,7 @@ _nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type)
 }
 
 const NMPObject *
-nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj)
+nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj)
 {
 	const NMPClass *klass = nmp_class_from_type (obj_type);
 
@@ -865,6 +865,12 @@ _vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2)
 	return c;
 }
 
+gboolean
+nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2)
+{
+	return nmp_object_cmp (obj1, obj2) == 0;
+}
+
 /* @src is a const object, which is not entirely correct for link types, where
  * we increase the ref count for src->_link.udev.device.
  * Hence, nmp_object_copy() can violate the const promise of @src.
@@ -916,11 +922,11 @@ _vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src)
 {
 	dst->lnk_vlan = src->lnk_vlan;
 	_vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_ingress_qos_map,
-	                               NM_UNCONST_PPTR (NMVlanQosMapping, &dst->_lnk_vlan.ingress_qos_map),
+	                               &dst->_lnk_vlan.ingress_qos_map,
 	                               src->_lnk_vlan.n_ingress_qos_map,
 	                               src->_lnk_vlan.ingress_qos_map);
 	_vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_egress_qos_map,
-	                               NM_UNCONST_PPTR (NMVlanQosMapping, &dst->_lnk_vlan.egress_qos_map),
+	                               &dst->_lnk_vlan.egress_qos_map,
 	                               src->_lnk_vlan.n_egress_qos_map,
 	                               src->_lnk_vlan.egress_qos_map);
 }
@@ -1082,7 +1088,7 @@ nmp_object_id_hash (const NMPObject *obj)
 	NMHashState h;
 
 	if (!obj)
-		return nm_hash_static (914932607u);
+		return 0;
 
 	nm_hash_init (&h, 914932607u);
 	nmp_object_id_hash_update (obj, &h);
@@ -1526,6 +1532,8 @@ const NMPLookup *
 nmp_lookup_init_obj_type (NMPLookup *lookup,
                           NMPObjectType obj_type)
 {
+	NMPObject *o;
+
 	nm_assert (lookup);
 
 	switch (obj_type) {
@@ -1536,7 +1544,7 @@ nmp_lookup_init_obj_type (NMPLookup *lookup,
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
 	case NMP_OBJECT_TYPE_QDISC:
 	case NMP_OBJECT_TYPE_TFILTER:
-		_nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type);
+		o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type);
 		lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE;
 		return _L (lookup);
 	default:
@@ -1768,54 +1776,6 @@ nmp_cache_lookup_link_full (const NMPCache *cache,
 
 /*****************************************************************************/
 
-static NMDedupMultiIdxMode
-_obj_get_add_mode (const NMPObject *obj)
-{
-	/* new objects are usually appended to the list. Except for
-	 * addresses, which are prepended during `ip address add`.
-	 *
-	 * Actually, for routes it is more complicated, because depending on
-	 * `ip route append`, `ip route replace`, `ip route prepend`, the object
-	 * will be added at the tail, at the front, or even replace an element
-	 * in the list. However, that is handled separately by nmp_cache_update_netlink_route()
-	 * and of no concern here. */
-	if (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj),
-	               NMP_OBJECT_TYPE_IP4_ADDRESS,
-	               NMP_OBJECT_TYPE_IP6_ADDRESS))
-		return NM_DEDUP_MULTI_IDX_MODE_PREPEND;
-	return NM_DEDUP_MULTI_IDX_MODE_APPEND;
-}
-
-static void
-_idxcache_update_order_for_dump (NMPCache *cache,
-                                 const NMDedupMultiEntry *entry)
-{
-	const NMPClass *klass;
-	const guint8 *i_idx_type;
-	const NMDedupMultiEntry *entry2;
-
-	nm_dedup_multi_entry_reorder (entry, NULL, TRUE);
-
-	klass = NMP_OBJECT_GET_CLASS (entry->obj);
-	for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) {
-		NMPCacheIdType id_type = *i_idx_type;
-
-		if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE)
-			continue;
-
-		entry2 = nm_dedup_multi_index_lookup_obj (cache->multi_idx,
-		                                          _idx_type_get (cache, id_type),
-		                                          entry->obj);
-		if (!entry2)
-			continue;
-
-		nm_assert (entry2 != entry);
-		nm_assert (entry2->obj == entry->obj);
-
-		nm_dedup_multi_entry_reorder (entry2, NULL, TRUE);
-	}
-}
-
 static void
 _idxcache_update_other_cache_ids (NMPCache *cache,
                                   NMPCacheIdType cache_id_type,
@@ -1875,7 +1835,7 @@ _idxcache_update_other_cache_ids (NMPCache *cache,
 		                               obj_new,
 		                               is_dump
 		                                 ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
-		                                 : _obj_get_add_mode (obj_new),
+		                                 : NM_DEDUP_MULTI_IDX_MODE_APPEND,
 		                               is_dump
 		                                 ? NULL
 		                                 : entry_order,
@@ -1953,7 +1913,7 @@ _idxcache_update (NMPCache *cache,
 		                               obj_new,
 		                               is_dump
 		                                 ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
-		                                 : _obj_get_add_mode (obj_new),
+		                                 : NM_DEDUP_MULTI_IDX_MODE_APPEND,
 		                               NULL,
 		                               entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
 		                               NULL,
@@ -2210,8 +2170,6 @@ nmp_cache_update_netlink (NMPCache *cache,
 	}
 
 	if (nmp_object_equal (obj_old, obj_hand_over)) {
-		if (is_dump)
-			_idxcache_update_order_for_dump (cache, entry_old);
 		nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
 		NM_SET_OUT (out_obj_new, nmp_object_ref (obj_old));
 		return NMP_CACHE_OPS_UNCHANGED;
@@ -2285,8 +2243,6 @@ nmp_cache_update_netlink_route (NMPCache *cache,
 	}
 
 	if (nmp_object_equal (entry_old->obj, obj_hand_over)) {
-		if (is_dump)
-			_idxcache_update_order_for_dump (cache, entry_old);
 		nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
 		goto update_done;
 	}
@@ -2310,8 +2266,9 @@ update_done:
 	 * properly find @obj_replaced. */
 	resync_required = FALSE;
 	entry_replace = NULL;
-	if (is_dump)
+	if (is_dump) {
 		goto out;
+	}
 
 	if (!entry_new) {
 		if (   NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE)
@@ -2326,8 +2283,6 @@ update_done:
 		goto out;
 	}
 
-	/* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID
-	 * index. For all other indexes their order becomes messed up. */
 	entry_cur = _lookup_entry_with_idx_type (cache,
 	                                         NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
 	                                         entry_new->obj);
@@ -2780,17 +2735,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
 		.cmd_plobj_hash_update              = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_sit_hash_update,
 		.cmd_plobj_cmp                      = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_sit_cmp,
 	},
-	[NMP_OBJECT_TYPE_LNK_TUN - 1] = {
-		.parent                             = DEDUP_MULTI_OBJ_CLASS_INIT(),
-		.obj_type                           = NMP_OBJECT_TYPE_LNK_TUN,
-		.sizeof_data                        = sizeof (NMPObjectLnkTun),
-		.sizeof_public                      = sizeof (NMPlatformLnkTun),
-		.obj_type_name                      = "tun",
-		.lnk_link_type                      = NM_LINK_TYPE_TUN,
-		.cmd_plobj_to_string                = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_tun_to_string,
-		.cmd_plobj_hash_update              = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_tun_hash_update,
-		.cmd_plobj_cmp                      = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_tun_cmp,
-	},
 	[NMP_OBJECT_TYPE_LNK_VLAN - 1] = {
 		.parent                             = DEDUP_MULTI_OBJ_CLASS_INIT(),
 		.obj_type                           = NMP_OBJECT_TYPE_LNK_VLAN,
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index f473f462..e17b17b0 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -88,7 +88,7 @@ typedef enum { /*< skip >*/
 	/* Consider all the destination fields of a route, that is, the ID without the ifindex
 	 * and gateway (meaning: network/plen,metric).
 	 * The reason for this is that `ip route change` can replace an existing route
-	 * and modify its ifindex/gateway. Effectively, that means it deletes an existing
+	 * and modify it's ifindex/gateway. Effectively, that means it deletes an existing
 	 * route and adds a different one (as the ID of the route changes). However, it only
 	 * sends one RTM_NEWADDR notification without notifying about the deletion. We detect
 	 * that by having this index to contain overlapping routes which require special
@@ -197,10 +197,6 @@ typedef struct {
 } NMPObjectLnkSit;
 
 typedef struct {
-	NMPlatformLnkTun _public;
-} NMPObjectLnkTun;
-
-typedef struct {
 	NMPlatformLnkVlan _public;
 
 	guint n_ingress_qos_map;
@@ -269,9 +265,6 @@ struct _NMPObject {
 		NMPlatformLnkSit        lnk_sit;
 		NMPObjectLnkSit         _lnk_sit;
 
-		NMPlatformLnkTun        lnk_tun;
-		NMPObjectLnkTun         _lnk_tun;
-
 		NMPlatformLnkVlan       lnk_vlan;
 		NMPObjectLnkVlan        _lnk_vlan;
 
@@ -467,8 +460,6 @@ nmp_object_ref (const NMPObject *obj)
 static inline void
 nmp_object_unref (const NMPObject *obj)
 {
-	nm_assert (!obj || NMP_OBJECT_IS_VALID (obj));
-
 	nm_dedup_multi_obj_unref ((const NMDedupMultiObj *) obj);
 }
 
@@ -490,7 +481,7 @@ nmp_object_unref (const NMPObject *obj)
 NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob);
 NMPObject *nmp_object_new_link (int ifindex);
 
-const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj);
+const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj);
 
 static inline NMPObject *
 nmp_object_stackinit_obj (NMPObject *obj, const NMPObject *src)
@@ -508,13 +499,7 @@ const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifinde
 const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size);
 void nmp_object_hash_update (const NMPObject *obj, NMHashState *h);
 int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2);
-
-static inline gboolean
-nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2)
-{
-	return nmp_object_cmp (obj1, obj2) == 0;
-}
-
+gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2);
 void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only);
 NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only);
 
@@ -739,16 +724,6 @@ const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform,
                                                    NMPCacheIdType cache_id_type,
                                                    const NMPObject *obj);
 
-static inline const NMPObject *
-nm_platform_lookup_obj (NMPlatform *platform,
-                        NMPCacheIdType cache_id_type,
-                        const NMPObject *obj)
-{
-	return nm_dedup_multi_entry_get_obj (nm_platform_lookup_entry (platform,
-	                                                               cache_id_type,
-	                                                               obj));
-}
-
 static inline const NMDedupMultiHeadEntry *
 nm_platform_lookup_obj_type (NMPlatform *platform,
                              NMPObjectType obj_type)
diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build
deleted file mode 100644
index 0571efac..00000000
--- a/src/platform/tests/meson.build
+++ /dev/null
@@ -1,37 +0,0 @@
-test_units = [
-  ['test-link-fake', 'test-link.c', 60],
-  ['test-link-linux', 'test-link.c', 60],
-  ['test-address-fake', 'test-address.c'],
-  ['test-address-linux', 'test-address.c'],
-  ['test-general', 'test-general.c'],
-  ['test-nmp-object', 'test-nmp-object.c'],
-  ['test-route-fake', 'test-route.c'],
-  ['test-route-linux', 'test-route.c'],
-  ['test-cleanup-fake', 'test-cleanup.c'],
-  ['test-cleanup-linux', 'test-cleanup.c'],
-]
-
-foreach test_unit: test_units
-  exe = executable(
-    'platform-' + test_unit[0],
-    test_unit[1],
-    dependencies: test_nm_dep,
-    c_args: test_cflags_platform
-  )
-
-  test(
-    'platform/' + test_unit[0],
-    test_script,
-    timeout: test_unit.length() > 2 ? test_unit[2] : 30,
-    args: test_args + [exe.full_path()]
-  )
-endforeach
-
-test = 'monitor'
-
-executable(
-  test,
-  test + '.c',
-  dependencies: test_nm_dep,
-  c_args: test_cflags_platform
-)
diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c
index f0e3e6cf..e1220052 100644
--- a/src/platform/tests/monitor.c
+++ b/src/platform/tests/monitor.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
index ddef8853..93851ff7 100644
--- a/src/platform/tests/test-address.c
+++ b/src/platform/tests/test-address.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
@@ -148,7 +149,7 @@ test_ip6_address_general (void)
 
 	/* Add address again (aka update) */
 	nmtstp_ip6_address_add (NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags);
-	accept_signals (address_changed, 0, 2);
+	accept_signals (address_changed, 0, 1);
 
 	/* Test address listing */
 	addresses = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index a213b31f..937cd12c 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 7885c083..d56e681e 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
@@ -23,7 +24,6 @@
 #include <sched.h>
 #include <sys/wait.h>
 #include <fcntl.h>
-#include <linux/if_tun.h>
 
 #include "test-common.h"
 
@@ -628,10 +628,7 @@ nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms)
 const NMPlatformLink *
 nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 timeout_ms)
 {
-	return nmtstp_wait_for_link_until (platform, ifname, expected_link_type,
-	                                   timeout_ms
-	                                     ? nm_utils_get_monotonic_timestamp_ms () + timeout_ms
-	                                     : 0);
+	return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
 
 const NMPlatformLink *
@@ -639,7 +636,6 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType
 {
 	const NMPlatformLink *plink;
 	gint64 now;
-	gboolean waited_once = FALSE;
 
 	_init_platform (&platform, FALSE);
 
@@ -651,24 +647,29 @@ nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType
 		    && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type))
 			return plink;
 
-		if (until_ms == 0) {
-			/* don't wait, don't even poll the socket. */
-			return NULL;
-		}
-
-		if (   waited_once
-		    && until_ms < now) {
-			/* timeout reached (+ we already waited for a signal at least once). */
+		if (until_ms < now)
 			return NULL;
-		}
 
-		waited_once = TRUE;
-		/* regardless of whether timeout is already reached, we poll the netlink
-		 * socket a bit. */
 		nmtstp_wait_for_signal (platform, until_ms - now);
 	}
 }
 
+const NMPlatformLink *
+nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+{
+	return nmtstp_assert_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
+}
+
+const NMPlatformLink *
+nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
+{
+	const NMPlatformLink *plink;
+
+	plink = nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms);
+	g_assert (plink);
+	return plink;
+}
+
 /*****************************************************************************/
 
 int
@@ -1283,10 +1284,6 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform,
 	const NMPlatformLink *pllink = NULL;
 	gboolean success;
 	char buffer[INET6_ADDRSTRLEN];
-	char encap[20];
-	char tclass[20];
-	gboolean encap_ignore;
-	gboolean tclass_inherit;
 
 	g_assert (nm_utils_is_valid_iface_name (name, NULL));
 
@@ -1312,18 +1309,15 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform,
 			g_assert_not_reached ();
 		}
 
-		encap_ignore = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_IGN_ENCAP_LIMIT);
-		tclass_inherit = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS);
-
-		success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %s encaplimit %s flowlabel %x",
+		success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %02x encaplimit %u flowlabel %x",
 		                                name,
 		                                mode,
 		                                dev,
 		                                nm_utils_inet6_ntop (&lnk->local, NULL),
 		                                nm_utils_inet6_ntop (&lnk->remote, buffer),
 		                                lnk->ttl,
-		                                tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass),
-		                                encap_ignore ? "none" : nm_sprintf_buf (encap, "%u", lnk->encap_limit),
+		                                lnk->tclass,
+		                                lnk->encap_limit,
 		                                lnk->flow_label);
 		if (success)
 			pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_IP6TNL, 100);
@@ -1469,70 +1463,6 @@ nmtstp_link_sit_add (NMPlatform *platform,
 }
 
 const NMPlatformLink *
-nmtstp_link_tun_add (NMPlatform *platform,
-                     gboolean external_command,
-                     const char *name,
-                     const NMPlatformLnkTun *lnk,
-                     int *out_fd)
-{
-	const NMPlatformLink *pllink = NULL;
-	NMPlatformError plerr;
-	int err;
-
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
-	g_assert (lnk);
-	g_assert (NM_IN_SET (lnk->type, IFF_TUN, IFF_TAP));
-	g_assert (!out_fd || *out_fd == -1);
-
-	if (!lnk->persist) {
-		/* ip tuntap does not support non-persistent devices.
-		 *
-		 * Add this device only via NMPlatform. */
-		if (external_command == -1)
-			external_command = FALSE;
-	}
-
-	external_command = nmtstp_run_command_check_external (external_command);
-
-	_init_platform (&platform, external_command);
-
-	if (external_command) {
-		g_assert (lnk->persist);
-
-		err = nmtstp_run_command ("ip tuntap add"
-		                          " mode %s"
-		                          "%s" /* user */
-		                          "%s" /* group */
-		                          "%s" /* pi */
-		                          "%s" /* vnet_hdr */
-		                          "%s" /* multi_queue */
-		                          " name %s",
-		                          lnk->type == IFF_TUN ? "tun" : "tap",
-		                          lnk->owner_valid ? nm_sprintf_bufa (100, " user %u", (guint) lnk->owner) : "",
-		                          lnk->group_valid ? nm_sprintf_bufa (100, " group %u", (guint) lnk->group) : "",
-		                          lnk->pi ? " pi" : "",
-		                          lnk->vnet_hdr ? " vnet_hdr" : "",
-		                          lnk->multi_queue ? " multi_queue" : "",
-		                          name);
-		/* Older versions of iproute2 don't support adding  devices.
-		 * On failure, fallback to using platform code. */
-		if (err == 0)
-			pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_TUN, 100);
-		else
-			g_error ("failure to add tun/tap device via ip-route");
-	} else {
-		g_assert (lnk->persist || out_fd);
-		plerr = nm_platform_link_tun_add (platform, name, lnk, &pllink, out_fd);
-		g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS);
-	}
-
-	g_assert (pllink);
-	g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_TUN);
-	g_assert_cmpstr (pllink->name, ==, name);
-	return pllink;
-}
-
-const NMPlatformLink *
 nmtstp_link_vxlan_add (NMPlatform *platform,
                        gboolean external_command,
                        const char *name,
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index bd02b0d7..fb406a3f 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -1,21 +1,3 @@
-/*
- * 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 2016 - 2017 Red Hat, Inc.
- */
-
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
@@ -129,11 +111,8 @@ const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const ch
 			g_assert_not_reached (); \
 	} G_STMT_END
 
-#define nmtstp_assert_wait_for_link(platform, ifname, expected_link_type, timeout_ms) \
-	nmtst_assert_nonnull (nmtstp_wait_for_link (platform, ifname, expected_link_type, timeout_ms))
-
-#define nmtstp_assert_wait_for_link_until(platform, ifname, expected_link_type, until_ms) \
-	nmtst_assert_nonnull (nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms))
+const NMPlatformLink *nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
+const NMPlatformLink *nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
 
 /*****************************************************************************/
 
@@ -310,11 +289,6 @@ const NMPlatformLink *nmtstp_link_sit_add (NMPlatform *platform,
                                            gboolean external_command,
                                            const char *name,
                                            const NMPlatformLnkSit *lnk);
-const NMPlatformLink *nmtstp_link_tun_add (NMPlatform *platform,
-                                           gboolean external_command,
-                                           const char *name,
-                                           const NMPlatformLnkTun *lnk,
-                                           int *out_fd);
 const NMPlatformLink *nmtstp_link_vxlan_add (NMPlatform *platform,
                                              gboolean external_command,
                                              const char *name,
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index dcd600ee..ef78cc24 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
@@ -23,7 +24,6 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <linux/if_tun.h>
 
 #include "platform/nmp-object.h"
 #include "platform/nmp-netns.h"
@@ -698,8 +698,6 @@ test_software_detect (gconstpointer user_data)
 	const NMPObject *lnk;
 	guint i_step;
 	const gboolean ext = test_data->external_command;
-	NMPlatformLnkTun lnk_tun;
-	nm_auto_close int tun_fd = -1;
 
 	nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
 	ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
@@ -763,27 +761,13 @@ test_software_detect (gconstpointer user_data)
 			gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 0;
 		}
 
-		switch (test_data->test_mode) {
-		case 0:
-			lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15");
-			lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16");
-			lnk_ip6tnl.parent_ifindex = ifindex_parent;
-			lnk_ip6tnl.tclass = 20;
-			lnk_ip6tnl.encap_limit = 6;
-			lnk_ip6tnl.flow_label = 1337;
-			lnk_ip6tnl.proto = IPPROTO_IPV6;
-			break;
-		case 1:
-			lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::17");
-			lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::18");
-			lnk_ip6tnl.parent_ifindex = ifindex_parent;
-			lnk_ip6tnl.tclass = 0;
-			lnk_ip6tnl.encap_limit = 0;
-			lnk_ip6tnl.flow_label = 1338;
-			lnk_ip6tnl.proto = IPPROTO_IPV6;
-			lnk_ip6tnl.flags = IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS;
-			break;
-		}
+		lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15");
+		lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16");
+		lnk_ip6tnl.parent_ifindex = ifindex_parent;
+		lnk_ip6tnl.tclass = 20;
+		lnk_ip6tnl.encap_limit = 6;
+		lnk_ip6tnl.flow_label = 1337;
+		lnk_ip6tnl.proto = IPPROTO_IPV6;
 
 		if (!nmtstp_link_ip6tnl_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) {
 			if (gracefully_skip) {
@@ -895,38 +879,6 @@ test_software_detect (gconstpointer user_data)
 		g_assert (nmtstp_link_vxlan_add (NULL, ext, DEVICE_NAME, &lnk_vxlan));
 		break;
 	}
-	case NM_LINK_TYPE_TUN: {
-		gboolean owner_valid = nmtst_get_rand_bool ();
-		gboolean group_valid = nmtst_get_rand_bool ();
-
-		switch (test_data->test_mode) {
-		case 0:
-			lnk_tun = (NMPlatformLnkTun) {
-				.type = nmtst_get_rand_bool () ? IFF_TUN : IFF_TAP,
-				.owner = owner_valid ? getuid () : 0,
-				.owner_valid = owner_valid,
-				.group = group_valid ? getgid () : 0,
-				.group_valid = group_valid,
-				.pi = nmtst_get_rand_bool (),
-				.vnet_hdr = nmtst_get_rand_bool (),
-				.multi_queue = nmtst_get_rand_bool (),
-
-				/* if we add the device via iproute2 (external), we can only
-				 * create persistent devices. */
-				.persist = (ext == 1) ? TRUE : nmtst_get_rand_bool (),
-			};
-			break;
-		default:
-			g_assert_not_reached ();
-			break;
-		}
-
-		g_assert (nmtstp_link_tun_add (NULL, ext, DEVICE_NAME, &lnk_tun,
-		                               (!lnk_tun.persist || nmtst_get_rand_bool ())
-		                                 ? &tun_fd
-		                                 : NULL));
-		break;
-	}
 	default:
 		g_assert_not_reached ();
 	}
@@ -957,13 +909,7 @@ test_software_detect (gconstpointer user_data)
 		lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink);
 		g_assert (plink);
 		g_assert_cmpint (plink->ifindex, ==, ifindex);
-
-		if (   !lnk
-		    && test_data->link_type == NM_LINK_TYPE_TUN) {
-			/* this is ok. Kernel apparently does not support tun properties via netlink. We
-			 * fetch them from sysfs below. */
-		} else
-			g_assert (lnk);
+		g_assert (lnk);
 
 		switch (test_data->link_type) {
 		case NM_LINK_TYPE_GRE: {
@@ -985,31 +931,15 @@ test_software_detect (gconstpointer user_data)
 		case NM_LINK_TYPE_IP6TNL: {
 			const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl;
 
-			switch (test_data->test_mode) {
-			case 0:
-				g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL));
-				g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
-				nmtst_assert_ip6_address (&plnk->local, "fd01::15");
-				nmtst_assert_ip6_address (&plnk->remote, "fd01::16");
-				g_assert_cmpint (plnk->ttl, ==, 0);
-				g_assert_cmpint (plnk->tclass, ==, 20);
-				g_assert_cmpint (plnk->encap_limit, ==, 6);
-				g_assert_cmpint (plnk->flow_label, ==, 1337);
-				g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6);
-				break;
-			case 1:
-				g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL));
-				g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
-				nmtst_assert_ip6_address (&plnk->local, "fd01::17");
-				nmtst_assert_ip6_address (&plnk->remote, "fd01::18");
-				g_assert_cmpint (plnk->ttl, ==, 0);
-				g_assert_cmpint (plnk->flow_label, ==, 1338);
-				g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6);
-				g_assert_cmpint (plnk->flags & 0xFFFF, /* ignore kernel internal flags */
-				                 ==,
-				                 IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS);
-				break;
-			}
+			g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL));
+			g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent);
+			nmtst_assert_ip6_address (&plnk->local, "fd01::15");
+			nmtst_assert_ip6_address (&plnk->remote, "fd01::16");
+			g_assert_cmpint (plnk->ttl, ==, 0);
+			g_assert_cmpint (plnk->tclass, ==, 20);
+			g_assert_cmpint (plnk->encap_limit, ==, 6);
+			g_assert_cmpint (plnk->flow_label, ==, 1337);
+			g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6);
 			break;
 		}
 		case NM_LINK_TYPE_IPIP: {
@@ -1052,27 +982,6 @@ test_software_detect (gconstpointer user_data)
 			g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE);
 			break;
 		}
-		case NM_LINK_TYPE_TUN: {
-			const NMPlatformLnkTun *plnk;
-			NMPlatformLnkTun lnk_tun2;
-
-			g_assert ((lnk ? &lnk->lnk_tun : NULL) == nm_platform_link_get_lnk_tun (NM_PLATFORM_GET, ifindex, NULL));
-
-			/* kernel might not expose tun options via netlink. Either way, try
-			 * to read them (either from platform cache, or fallback to sysfs).
-			 * See also: rh#1547213. */
-			if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET,
-			                                          ifindex,
-			                                          &lnk_tun2))
-				g_assert_not_reached ();
-
-			plnk = lnk ? &lnk->lnk_tun : &lnk_tun2;
-			if (lnk)
-				g_assert (memcmp (plnk, &lnk_tun2, sizeof (NMPlatformLnkTun)) == 0);
-
-			g_assert (nm_platform_lnk_tun_cmp (plnk, &lnk_tun) == 0);
-			break;
-		}
 		case NM_LINK_TYPE_VLAN: {
 			const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan;
 
@@ -2339,7 +2248,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data)
 		p = pl_base;
 		for (j = nstack; j >= 1; ) {
 			j--;
-			if (NM_FLAGS_ANY (stack[j].ns_types, ns_type)) {
+			if (NM_FLAGS_HAS (stack[j].ns_types, ns_type)) {
 				p = stack[j].pl;
 				break;
 			}
@@ -2637,13 +2546,11 @@ _nmtstp_setup_tests (void)
 		g_test_add_func ("/link/external", test_external);
 
 		test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0);
-		test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0);
-		test_software_detect_add ("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1);
+		test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0);
 		test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0);
 		test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0);
 		test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
 		test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0);
-		test_software_detect_add ("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0);
 		test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0);
 		test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0);
 		test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1);
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 047d7a85..3228de83 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -54,20 +54,27 @@ test_obj_base (void)
 	gs_unref_object GCancellable *obj_cancellable = g_cancellable_new ();
 	nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10);
 
-	g_assert (&g->g_type_instance              == (void *) &o->_class);
-	g_assert (&g->g_type_instance.g_class      == (void *) &o->_class);
+#define STATIC_ASSERT(cond) \
+	G_STMT_START { \
+		G_STATIC_ASSERT (cond); \
+		G_STATIC_ASSERT_EXPR (cond); \
+		g_assert (cond); \
+	} G_STMT_END
 
-	g_assert (sizeof (o->parent.parent)        == sizeof (GTypeInstance));
+	STATIC_ASSERT (&g->g_type_instance              == (void *) &o->_class);
+	STATIC_ASSERT (&g->g_type_instance.g_class      == (void *) &o->_class);
 
-	g_assert (&c->parent                       == (void *) c);
-	g_assert (&c->parent.parent.g_type_class   == (void *) c);
-	g_assert (&c->parent.parent.g_type         == (void *) c);
-	g_assert (&c->parent.parent.g_type         == &k->g_type);
+	STATIC_ASSERT (sizeof (o->parent.parent)        == sizeof (GTypeInstance));
 
-	g_assert (sizeof (c->parent.parent)        == sizeof (GTypeClass));
+	STATIC_ASSERT (&c->parent                       == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type_class   == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type         == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type         == &k->g_type);
 
-	g_assert (&o->parent                       == (void *) o);
-	g_assert (&o->parent.klass                 == (void *) &o->_class);
+	STATIC_ASSERT (sizeof (c->parent.parent)        == sizeof (GTypeClass));
+
+	STATIC_ASSERT (&o->parent                       == (void *) o);
+	STATIC_ASSERT (&o->parent.klass                 == (void *) &o->_class);
 
 	obj = (NMObjBaseInst *) obj_cancellable;
 	g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass));
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 85b14b57..13648f16 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -1,5 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
+/* NetworkManager audit support
+ *
  * 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
diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c
index db187a1f..a5f25b02 100644
--- a/src/platform/wifi/wifi-utils-nl80211.c
+++ b/src/platform/wifi/wifi-utils-nl80211.c
@@ -22,17 +22,17 @@
 
 #include "nm-default.h"
 
-#include "wifi-utils-nl80211.h"
-
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
 #include <linux/nl80211.h>
 
-#include "platform/nm-netlink.h"
 #include "wifi-utils-private.h"
+#include "wifi-utils-nl80211.h"
 #include "platform/nm-platform.h"
 #include "platform/nm-platform-utils.h"
 #include "nm-utils.h"
@@ -46,14 +46,223 @@
 		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
 	} G_STMT_END
 
+/*****************************************************************************/
+
+static int
+_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
+               const struct nla_policy *policy)
+{
+	return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy);
+}
+#define nla_parse(...) _nl_nla_parse(__VA_ARGS__)
+
+static int
+_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla,
+                      const struct nla_policy *policy)
+{
+	return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy);
+}
+#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__)
+
+/*****************************************************************************
+ * Copied from libnl3/genl:
+ *****************************************************************************/
+
+static void *
+genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
+             int hdrlen, int flags, uint8_t cmd, uint8_t version)
+{
+	struct nlmsghdr *nlh;
+	struct genlmsghdr hdr = {
+		.cmd = cmd,
+		.version = version,
+	};
+
+	nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags);
+	if (nlh == NULL)
+		return NULL;
+
+	memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr));
+
+	return (char *) nlmsg_data (nlh) + GENL_HDRLEN;
+}
+
+static void *
+genlmsg_data (const struct genlmsghdr *gnlh)
+{
+	return ((unsigned char *) gnlh + GENL_HDRLEN);
+}
+
+static void *
+genlmsg_user_hdr (const struct genlmsghdr *gnlh)
+{
+	return genlmsg_data (gnlh);
+}
+
+static struct genlmsghdr *
+genlmsg_hdr (struct nlmsghdr *nlh)
+{
+	return nlmsg_data (nlh);
+}
+
+static void *
+genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen)
+{
+	return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen);
+}
+
+static struct nlattr *
+genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen)
+{
+	return genlmsg_user_data (gnlh, hdrlen);
+}
+
+static int
+genlmsg_len (const struct genlmsghdr *gnlh)
+{
+	const struct nlmsghdr *nlh;
+
+	nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN);
+	return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
+}
+
+static int
+genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen)
+{
+	return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen);
+}
+
+static int
+genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen)
+{
+	struct genlmsghdr *ghdr;
+
+	if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN))
+		return 0;
+
+	ghdr = nlmsg_data (nlh);
+	if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen))
+		return 0;
+
+	return 1;
+}
+
+static int
+genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
+               int maxtype, const struct nla_policy *policy)
+{
+	struct genlmsghdr *ghdr;
+
+	if (!genlmsg_valid_hdr (nlh, hdrlen))
+		return -NLE_MSG_TOOSHORT;
+
+	ghdr = nlmsg_data (nlh);
+	return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen),
+	                  genlmsg_attrlen (ghdr, hdrlen), policy);
+}
+
+/*****************************************************************************
+ * Reimplementation of libnl3/genl functions:
+ *****************************************************************************/
+
+static int
+probe_response (struct nl_msg *msg, void *arg)
+{
+	static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
+		[CTRL_ATTR_FAMILY_ID]    = { .type = NLA_U16 },
+		[CTRL_ATTR_FAMILY_NAME]  = { .type = NLA_STRING,
+		                            .maxlen = GENL_NAMSIZ },
+		[CTRL_ATTR_VERSION]      = { .type = NLA_U32 },
+		[CTRL_ATTR_HDRSIZE]      = { .type = NLA_U32 },
+		[CTRL_ATTR_MAXATTR]      = { .type = NLA_U32 },
+		[CTRL_ATTR_OPS]          = { .type = NLA_NESTED },
+		[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
+	};
+	struct nlattr *tb[CTRL_ATTR_MAX+1];
+	struct nlmsghdr *nlh = nlmsg_hdr (msg);
+	gint32 *response_data = arg;
+
+	if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy))
+		return NL_SKIP;
+
+	if (tb[CTRL_ATTR_FAMILY_ID])
+		*response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]);
+
+	return NL_STOP;
+}
+
+static int
+genl_ctrl_resolve (struct nl_sock *sk, const char *name)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb, *orig;
+	int rc;
+	int result = -NLE_OBJ_NOTFOUND;
+	gint32 response_data = -1;
+
+	if (!(orig = nl_socket_get_cb (sk)))
+		goto out;
+
+	cb = nl_cb_clone (orig);
+	nl_cb_put (orig);
+	if (!cb)
+		goto out;
+
+	msg = nlmsg_alloc ();
+	if (!msg)
+		goto out_cb_free;
+
+	if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
+	                  0, 0, CTRL_CMD_GETFAMILY, 1))
+		goto out_msg_free;
+
+	if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0)
+		goto out_msg_free;
+
+	rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data);
+	if (rc < 0)
+		goto out_msg_free;
+
+	rc = nl_send_auto_complete (sk, msg);
+	if (rc < 0)
+		goto out_msg_free;
+
+	rc = nl_recvmsgs (sk, cb);
+	if (rc < 0)
+		goto out_msg_free;
+
+	/* If search was successful, request may be ACKed after data */
+	rc = nl_wait_for_ack (sk);
+	if (rc < 0)
+		goto out_msg_free;
+
+	if (response_data > 0)
+		result = response_data;
+
+out_msg_free:
+	nlmsg_free (msg);
+out_cb_free:
+	nl_cb_put (cb);
+out:
+	if (result >= 0)
+		_LOGD (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result);
+	else
+		_LOGE (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name);
+	return result;
+}
+
+/*****************************************************************************
+ * </libn-genl-3>
+ *****************************************************************************/
+
 typedef struct {
 	WifiData parent;
 	struct nl_sock *nl_sock;
-	guint32 *freqs;
 	int id;
+	struct nl_cb *nl_cb;
+	guint32 *freqs;
 	int num_freqs;
 	int phy;
-	bool can_wowlan:1;
 } WifiDataNl80211;
 
 static int
@@ -83,16 +292,19 @@ error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
 static struct nl_msg *
 _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags)
 {
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 
 	msg = nlmsg_alloc ();
-	genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0);
-	NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex);
-	if (phy != -1)
-		NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy);
-	return g_steal_pointer (&msg);
+	if (msg) {
+		genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0);
+		NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex);
+		if (phy != -1)
+			NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy);
+	}
+	return msg;
 
-nla_put_failure:
+ nla_put_failure:
+	nlmsg_free (msg);
 	return NULL;
 }
 
@@ -102,37 +314,42 @@ nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags)
 	return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags);
 }
 
+/* NOTE: this function consumes 'msg' */
 static int
 _nl80211_send_and_recv (struct nl_sock *nl_sock,
+                        struct nl_cb *nl_cb,
                         struct nl_msg *msg,
                         int (*valid_handler) (struct nl_msg *, void *),
                         void *valid_data)
 {
-	int err;
-	int done = 0;
-	const struct nl_cb cb = {
-		.err_cb     = error_handler,
-		.err_arg    = &done,
-		.finish_cb  = finish_handler,
-		.finish_arg = &done,
-		.ack_cb     = ack_handler,
-		.ack_arg    = &done,
-		.valid_cb   = valid_handler,
-		.valid_arg  = valid_data,
-	};
+	struct nl_cb *cb;
+	int err, done;
 
 	g_return_val_if_fail (msg != NULL, -ENOMEM);
 
-	err = nl_send_auto (nl_sock, msg);
+	cb = nl_cb_clone (nl_cb);
+	if (!cb) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	err = nl_send_auto_complete (nl_sock, msg);
 	if (err < 0)
-		return err;
+		goto out;
+
+	done = 0;
+	nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done);
+	nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done);
+	nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done);
+	if (valid_handler)
+		nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data);
 
 	/* Loop until one of our NL callbacks says we're done; on success
 	 * done will be 1, on error it will be < 0.
 	 */
 	while (!done) {
-		err = nl_recvmsgs (nl_sock, &cb);
-		if (err < 0 && err != -EAGAIN) {
+		err = nl_recvmsgs (nl_sock, cb);
+		if (err && err != -NLE_AGAIN) {
 			/* Kernel scan list can change while we are dumping it, as new scan
 			 * results from H/W can arrive. BSS info is assured to be consistent
 			 * and we don't need consistent view of whole scan list. Hence do
@@ -147,9 +364,12 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock,
 			break;
 		}
 	}
-
-	if (err >= 0 && done < 0)
+	if (err == 0 && done < 0)
 		err = done;
+
+ out:
+	nl_cb_put (cb);
+	nlmsg_free (msg);
 	return err;
 }
 
@@ -159,7 +379,7 @@ nl80211_send_and_recv (WifiDataNl80211 *nl80211,
                        int (*valid_handler) (struct nl_msg *, void *),
                        void *valid_data)
 {
-	return _nl80211_send_and_recv (nl80211->nl_sock, msg,
+	return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg,
 	                               valid_handler, valid_data);
 }
 
@@ -170,6 +390,8 @@ wifi_nl80211_deinit (WifiData *parent)
 
 	if (nl80211->nl_sock)
 		nl_socket_free (nl80211->nl_sock);
+	if (nl80211->nl_cb)
+		nl_cb_put (nl80211->nl_cb);
 	g_free (nl80211->freqs);
 }
 
@@ -213,12 +435,12 @@ wifi_nl80211_get_mode (WifiData *data)
 	struct nl80211_iface_info iface_info = {
 		.mode = NM_802_11_MODE_UNKNOWN,
 	};
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0);
 
 	if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler,
-	                           &iface_info) < 0)
+				   &iface_info) < 0)
 		return NM_802_11_MODE_UNKNOWN;
 
 	return iface_info.mode;
@@ -228,7 +450,7 @@ static gboolean
 wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_INTERFACE, 0);
@@ -248,7 +470,7 @@ wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode)
 	}
 
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err >= 0;
+	return err ? FALSE : TRUE;
 
  nla_put_failure:
 	nlmsg_free (msg);
@@ -259,14 +481,14 @@ static gboolean
 wifi_nl80211_set_powersave (WifiData *data, guint32 powersave)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0);
 	NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE,
 	             powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err >= 0;
+	return err ? FALSE : TRUE;
 
 nla_put_failure:
 	nlmsg_free (msg);
@@ -296,7 +518,7 @@ struct nl80211_bss_info {
 	gboolean valid;
 };
 
-#define WLAN_EID_SSID 0
+#define WLAN_EID_SSID	0
 
 static void
 find_ssid (guint8 *ies, guint32 ies_len,
@@ -396,7 +618,7 @@ static void
 nl80211_get_bss_info (WifiDataNl80211 *nl80211,
                       struct nl80211_bss_info *bss_info)
 {
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 
 	memset (bss_info, 0, sizeof (*bss_info));
 
@@ -520,7 +742,7 @@ static void
 nl80211_get_ap_info (WifiDataNl80211 *nl80211,
                      struct nl80211_station_info *sta_info)
 {
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	struct nl80211_bss_info bss_info;
 
 	memset (sta_info, 0, sizeof (*sta_info));
@@ -568,38 +790,35 @@ wifi_nl80211_get_qual (WifiData *data)
 	return sta_info.signal;
 }
 
+#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
 static gboolean
 wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	int err;
 
 	msg = nl80211_alloc_msg (nl80211,
-	                         running
-	                           ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */
-	                           : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */,
+	                         running ? NL80211_CMD_CRIT_PROTOCOL_START :
+	                         NL80211_CMD_CRIT_PROTOCOL_STOP,
 	                         0);
 	/* Despite the DHCP name, we're using this for any type of IP addressing,
 	 * DHCPv4, DHCPv6, and IPv6 SLAAC.
 	 */
-	NLA_PUT_U16 (msg,
-	             179 /* NL80211_ATTR_CRIT_PROT_ID */,
-	             1 /* NL80211_CRIT_PROTO_DHCP */);
+	NLA_PUT_U16 (msg, NL80211_ATTR_CRIT_PROT_ID, NL80211_CRIT_PROTO_DHCP);
 	if (running) {
 		/* Give DHCP 5 seconds to complete */
-		NLA_PUT_U16 (msg,
-		             180 /* NL80211_ATTR_MAX_CRIT_PROT_DURATION */,
-		             5000);
+		NLA_PUT_U16 (msg, NL80211_ATTR_MAX_CRIT_PROT_DURATION, 5000);
 	}
 
 	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
-	return err >= 0;
+	return err ? FALSE : TRUE;
 
 nla_put_failure:
 	nlmsg_free (msg);
 	return FALSE;
 }
+#endif
 
 struct nl80211_wowlan_info {
 	gboolean enabled;
@@ -628,14 +847,12 @@ static gboolean
 wifi_nl80211_get_wowlan (WifiData *data)
 {
 	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	struct nl80211_wowlan_info info;
 
-	if (!nl80211->can_wowlan)
-		return FALSE;
-
 	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0);
 	nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info);
+
 	return info.enabled;
 }
 
@@ -845,22 +1062,8 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 WifiData *
 wifi_nl80211_init (int ifindex)
 {
-	static const WifiDataClass klass = {
-		.struct_size = sizeof (WifiDataNl80211),
-		.get_mode = wifi_nl80211_get_mode,
-		.set_mode = wifi_nl80211_set_mode,
-		.set_powersave = wifi_nl80211_set_powersave,
-		.get_freq = wifi_nl80211_get_freq,
-		.find_freq = wifi_nl80211_find_freq,
-		.get_bssid = wifi_nl80211_get_bssid,
-		.get_rate = wifi_nl80211_get_rate,
-		.get_qual = wifi_nl80211_get_qual,
-		.get_wowlan = wifi_nl80211_get_wowlan,
-		.indicate_addressing_running = wifi_nl80211_indicate_addressing_running,
-		.deinit = wifi_nl80211_deinit,
-	};
 	WifiDataNl80211 *nl80211;
-	nm_auto_nlmsg struct nl_msg *msg = NULL;
+	struct nl_msg *msg;
 	struct nl80211_device_info device_info = {};
 	char ifname[IFNAMSIZ];
 
@@ -870,7 +1073,19 @@ wifi_nl80211_init (int ifindex)
 		nm_sprintf_buf (ifname, "if %d", ifindex);
 	}
 
-	nl80211 = wifi_data_new (&klass, ifindex);
+	nl80211 = wifi_data_new (ifindex, sizeof (*nl80211));
+	nl80211->parent.get_mode = wifi_nl80211_get_mode;
+	nl80211->parent.set_mode = wifi_nl80211_set_mode;
+	nl80211->parent.set_powersave = wifi_nl80211_set_powersave;
+	nl80211->parent.get_freq = wifi_nl80211_get_freq;
+	nl80211->parent.find_freq = wifi_nl80211_find_freq;
+	nl80211->parent.get_bssid = wifi_nl80211_get_bssid;
+	nl80211->parent.get_rate = wifi_nl80211_get_rate;
+	nl80211->parent.get_qual = wifi_nl80211_get_qual;
+#if HAVE_NL80211_CRITICAL_PROTOCOL_CMDS
+	nl80211->parent.indicate_addressing_running = wifi_nl80211_indicate_addressing_running;
+#endif
+	nl80211->parent.deinit = wifi_nl80211_deinit;
 
 	nl80211->nl_sock = nl_socket_alloc ();
 	if (nl80211->nl_sock == NULL)
@@ -880,10 +1095,12 @@ wifi_nl80211_init (int ifindex)
 		goto error;
 
 	nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211");
-	if (nl80211->id < 0) {
-		_LOGD (LOGD_WIFI, "genl_ctrl_resolve: failed to resolve \"nl80211\"");
+	if (nl80211->id < 0)
+		goto error;
+
+	nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT);
+	if (nl80211->nl_cb == NULL)
 		goto error;
-	}
 
 	nl80211->phy = -1;
 
@@ -936,15 +1153,18 @@ wifi_nl80211_init (int ifindex)
 	nl80211->freqs = device_info.freqs;
 	nl80211->num_freqs = device_info.num_freqs;
 	nl80211->parent.caps = device_info.caps;
-	nl80211->can_wowlan = device_info.can_wowlan;
+
+	if (device_info.can_wowlan)
+		nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan;
 
 	_LOGI (LOGD_PLATFORM | LOGD_WIFI,
 	       "(%s): using nl80211 for WiFi device control",
 	       ifname);
+
 	return (WifiData *) nl80211;
 
 error:
-	wifi_utils_unref ((WifiData *) nl80211);
+	wifi_utils_deinit ((WifiData *) nl80211);
 	return NULL;
 }
 
diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h
index 59386514..11a0f060 100644
--- a/src/platform/wifi/wifi-utils-private.h
+++ b/src/platform/wifi/wifi-utils-private.h
@@ -24,8 +24,9 @@
 #include "nm-dbus-interface.h"
 #include "wifi-utils.h"
 
-typedef struct {
-	gsize struct_size;
+struct WifiData {
+	int ifindex;
+	NMDeviceWifiCapabilities caps;
 
 	NM80211Mode (*get_mode) (WifiData *data);
 
@@ -65,14 +66,9 @@ typedef struct {
 	gboolean (*set_mesh_ssid) (WifiData *data, const guint8 *ssid, gsize len);
 
 	gboolean (*indicate_addressing_running) (WifiData *data, gboolean running);
-} WifiDataClass;
-
-struct WifiData {
-	const WifiDataClass *klass;
-	int ifindex;
-	NMDeviceWifiCapabilities caps;
 };
 
-gpointer wifi_data_new (const WifiDataClass *klass, int ifindex);
+gpointer wifi_data_new (int ifindex, gsize len);
+void wifi_data_free (WifiData *data);
 
 #endif  /* __WIFI_UTILS_PRIVATE_H__ */
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index c8744f79..c4d3c999 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -21,14 +21,17 @@
 
 #include "nm-default.h"
 
-#include "wifi-utils-wext.h"
-
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
 
+#include "wifi-utils-private.h"
+#include "wifi-utils-wext.h"
+#include "nm-utils.h"
+#include "platform/nm-platform-utils.h"
+
 /* Hacks necessary to #include wireless.h; yay for WEXT */
 #ifndef __user
 #define __user
@@ -38,10 +41,6 @@
 #include <sys/socket.h>
 #include <linux/wireless.h>
 
-#include "wifi-utils-private.h"
-#include "nm-utils.h"
-#include "platform/nm-platform-utils.h"
-
 typedef struct {
 	WifiData parent;
 	int fd;
@@ -629,21 +628,6 @@ wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range)
 WifiData *
 wifi_wext_init (int ifindex, gboolean check_scan)
 {
-	static const WifiDataClass klass = {
-		.struct_size = sizeof (WifiDataWext),
-		.get_mode = wifi_wext_get_mode,
-		.set_mode = wifi_wext_set_mode,
-		.set_powersave = wifi_wext_set_powersave,
-		.get_freq = wifi_wext_get_freq,
-		.find_freq = wifi_wext_find_freq,
-		.get_bssid = wifi_wext_get_bssid,
-		.get_rate = wifi_wext_get_rate,
-		.get_qual = wifi_wext_get_qual,
-		.deinit = wifi_wext_deinit,
-		.get_mesh_channel = wifi_wext_get_mesh_channel,
-		.set_mesh_channel = wifi_wext_set_mesh_channel,
-		.set_mesh_ssid = wifi_wext_set_mesh_ssid,
-	};
 	WifiDataWext *wext;
 	struct iw_range range;
 	guint32 response_len = 0;
@@ -658,7 +642,19 @@ wifi_wext_init (int ifindex, gboolean check_scan)
 		return NULL;
 	}
 
-	wext = wifi_data_new (&klass, ifindex);
+	wext = wifi_data_new (ifindex, sizeof (*wext));
+	wext->parent.get_mode = wifi_wext_get_mode;
+	wext->parent.set_mode = wifi_wext_set_mode;
+	wext->parent.set_powersave = wifi_wext_set_powersave;
+	wext->parent.get_freq = wifi_wext_get_freq;
+	wext->parent.find_freq = wifi_wext_find_freq;
+	wext->parent.get_bssid = wifi_wext_get_bssid;
+	wext->parent.get_rate = wifi_wext_get_rate;
+	wext->parent.get_qual = wifi_wext_get_qual;
+	wext->parent.deinit = wifi_wext_deinit;
+	wext->parent.get_mesh_channel = wifi_wext_get_mesh_channel;
+	wext->parent.set_mesh_channel = wifi_wext_set_mesh_channel;
+	wext->parent.set_mesh_ssid = wifi_wext_set_mesh_ssid;
 
 	wext->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 	if (wext->fd < 0)
@@ -734,7 +730,7 @@ wifi_wext_init (int ifindex, gboolean check_scan)
 	return (WifiData *) wext;
 
 error:
-	wifi_utils_unref ((WifiData *) wext);
+	wifi_utils_deinit ((WifiData *) wext);
 	return NULL;
 }
 
diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c
index 8818dc9d..d0052121 100644
--- a/src/platform/wifi/wifi-utils.c
+++ b/src/platform/wifi/wifi-utils.c
@@ -38,19 +38,22 @@
 #include "platform/nm-platform-utils.h"
 
 gpointer
-wifi_data_new (const WifiDataClass *klass, int ifindex)
+wifi_data_new (int ifindex, gsize len)
 {
 	WifiData *data;
 
-	nm_assert (klass);
-	nm_assert (klass->struct_size > sizeof (WifiData));
-
-	data = g_malloc0 (klass->struct_size);
-	data->klass = klass;
+	data = g_malloc0 (len);
 	data->ifindex = ifindex;
 	return data;
 }
 
+void
+wifi_data_free (WifiData *data)
+{
+	memset (data, 0, sizeof (*data));
+	g_free (data);
+}
+
 /*****************************************************************************/
 
 WifiData *
@@ -82,14 +85,14 @@ wifi_utils_get_caps (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE);
 
-	return data->caps;
+	return data->caps;	
 }
 
 NM80211Mode
 wifi_utils_get_mode (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN);
-	return data->klass->get_mode (data);
+	return data->get_mode (data);
 }
 
 gboolean
@@ -101,7 +104,7 @@ wifi_utils_set_mode (WifiData *data, const NM80211Mode mode)
 	                      || (mode == NM_802_11_MODE_ADHOC), FALSE);
 
 	/* nl80211 probably doesn't need this */
-	return data->klass->set_mode ? data->klass->set_mode (data, mode) : TRUE;
+	return data->set_mode ? data->set_mode (data, mode) : TRUE;
 }
 
 gboolean
@@ -109,14 +112,14 @@ wifi_utils_set_powersave (WifiData *data, guint32 powersave)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
 
-	return data->klass->set_powersave ? data->klass->set_powersave (data, powersave) : TRUE;
+	return data->set_powersave ? data->set_powersave (data, powersave) : TRUE;
 }
 
 guint32
 wifi_utils_get_freq (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->klass->get_freq (data);
+	return data->get_freq (data);
 }
 
 guint32
@@ -124,7 +127,7 @@ wifi_utils_find_freq (WifiData *data, const guint32 *freqs)
 {
 	g_return_val_if_fail (data != NULL, 0);
 	g_return_val_if_fail (freqs != NULL, 0);
-	return data->klass->find_freq (data, freqs);
+	return data->find_freq (data, freqs);
 }
 
 gboolean
@@ -134,40 +137,38 @@ wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid)
 	g_return_val_if_fail (out_bssid != NULL, FALSE);
 
 	memset (out_bssid, 0, ETH_ALEN);
-	return data->klass->get_bssid (data, out_bssid);
+	return data->get_bssid (data, out_bssid);
 }
 
 guint32
 wifi_utils_get_rate (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->klass->get_rate (data);
+	return data->get_rate (data);
 }
 
 int
 wifi_utils_get_qual (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-	return data->klass->get_qual (data);
+	return data->get_qual (data);
 }
 
 gboolean
 wifi_utils_get_wowlan (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, 0);
-
-	if (!data->klass->get_wowlan)
+	if (!data->get_wowlan)
 		return FALSE;
-	return data->klass->get_wowlan (data);
+	return data->get_wowlan (data);
 }
 
 void
-wifi_utils_unref (WifiData *data)
+wifi_utils_deinit (WifiData *data)
 {
 	g_return_if_fail (data != NULL);
-
-	data->klass->deinit (data);
-	g_free (data);
+	data->deinit (data);
+	wifi_data_free (data);
 }
 
 gboolean
@@ -190,8 +191,8 @@ guint32
 wifi_utils_get_mesh_channel (WifiData *data)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (data->klass->get_mesh_channel != NULL, FALSE);
-	return data->klass->get_mesh_channel (data);
+	g_return_val_if_fail (data->get_mesh_channel != NULL, FALSE);
+	return data->get_mesh_channel (data);
 }
 
 gboolean
@@ -199,24 +200,24 @@ wifi_utils_set_mesh_channel (WifiData *data, guint32 channel)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
 	g_return_val_if_fail (channel <= 13, FALSE);
-	g_return_val_if_fail (data->klass->set_mesh_channel != NULL, FALSE);
-	return data->klass->set_mesh_channel (data, channel);
+	g_return_val_if_fail (data->set_mesh_channel != NULL, FALSE);
+	return data->set_mesh_channel (data, channel);
 }
 
 gboolean
 wifi_utils_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	g_return_val_if_fail (data->klass->set_mesh_ssid != NULL, FALSE);
-	return data->klass->set_mesh_ssid (data, ssid, len);
+	g_return_val_if_fail (data->set_mesh_ssid != NULL, FALSE);
+	return data->set_mesh_ssid (data, ssid, len);
 }
 
 gboolean
 wifi_utils_indicate_addressing_running (WifiData *data, gboolean running)
 {
 	g_return_val_if_fail (data != NULL, FALSE);
-	if (data->klass->indicate_addressing_running)
-		return data->klass->indicate_addressing_running (data, running);
+	if (data->indicate_addressing_running)
+		return data->indicate_addressing_running (data, running);
 	return FALSE;
 }
 
diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h
index 2633e965..705717b0 100644
--- a/src/platform/wifi/wifi-utils.h
+++ b/src/platform/wifi/wifi-utils.h
@@ -34,7 +34,7 @@ WifiData *wifi_utils_init (int ifindex, gboolean check_scan);
 
 int wifi_utils_get_ifindex (WifiData *data);
 
-void wifi_utils_unref (WifiData *data);
+void wifi_utils_deinit (WifiData *data);
 
 NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data);
 
diff --git a/src/ppp/meson.build b/src/ppp/meson.build
deleted file mode 100644
index 20edb9d0..00000000
--- a/src/ppp/meson.build
+++ /dev/null
@@ -1,41 +0,0 @@
-name = 'nm-pppd-plugin'
-
-deps = [
-  dl_dep,
-  nm_core_dep
-]
-
-nm_pppd_plugin = shared_module(
-  name,
-  name_prefix: '',
-  sources: name + '.c',
-  include_directories: src_inc,
-  dependencies: deps,
-  c_args: [
-    '-DG_LOG_DOMAIN="@0@"'.format(name),
-    '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
-  ],
-  install: true,
-  install_dir: pppd_plugin_dir
-)
-
-name = 'nm-ppp-plugin'
-
-deps = [
-  nm_dep
-]
-
-linker_script = join_paths(meson.current_source_dir(), 'nm-ppp-plugin.ver')
-
-core_plugins += shared_module(
-  name,
-  sources: 'nm-ppp-manager.c',
-  dependencies: deps,
-  c_args: '-DPPPD_PLUGIN_DIR="@0@"'.format(pppd_plugin_dir),
-  link_args: [
-    '-Wl,--version-script,@0@'.format(linker_script),
-  ],
-  link_depends: linker_script,
-  install: true,
-  install_dir: nm_pkglibdir
-)
diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c
index ed70f687..ad3307a9 100644
--- a/src/ppp/nm-ppp-manager-call.c
+++ b/src/ppp/nm-ppp-manager-call.c
@@ -44,9 +44,11 @@ nm_ppp_manager_create (const char *iface, GError **error)
 	GError *error_local = NULL;
 	NMPPPOps *ops;
 	struct stat st;
+	int errsv;
 
 	if (G_UNLIKELY (!ppp_ops)) {
 		if (stat (PPP_PLUGIN_PATH, &st) != 0) {
+			errsv = errno;
 			g_set_error_literal (error,
 			                     NM_MANAGER_ERROR, NM_MANAGER_ERROR_MISSING_PLUGIN,
 			                     "the PPP plugin " PPP_PLUGIN_PATH " is not installed");
diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c
index 7d1eb408..743f80a2 100644
--- a/src/ppp/nm-ppp-manager.c
+++ b/src/ppp/nm-ppp-manager.c
@@ -50,12 +50,13 @@
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-dbus-object.h"
 
 #include "nm-pppd-plugin.h"
 #include "nm-ppp-plugin-api.h"
 #include "nm-ppp-status.h"
 
+#include "introspection/org.freedesktop.NetworkManager.PPP.h"
+
 #define NM_PPPD_PLUGIN PPPD_PLUGIN_DIR "/nm-pppd-plugin.so"
 
 static NM_CACHED_QUARK_FCN ("ppp-manager-secret-tries", ppp_manager_secret_tries_quark)
@@ -75,7 +76,6 @@ GType nm_ppp_manager_get_type (void);
 
 enum {
 	STATE_CHANGED,
-	IFINDEX_SET,
 	IP4_CONFIG,
 	IP6_CONFIG,
 	STATS,
@@ -93,8 +93,6 @@ typedef struct {
 	GPid pid;
 
 	char *parent_iface;
-	char *ip_iface;
-	int ifindex;
 
 	NMActRequest *act_req;
 	GDBusMethodInvocation *pending_secrets_context;
@@ -105,6 +103,7 @@ typedef struct {
 	guint ppp_timeout_handler;
 
 	/* Monitoring */
+	char *ip_iface;
 	int monitor_fd;
 	guint monitor_id;
 
@@ -115,17 +114,17 @@ typedef struct {
 } NMPPPManagerPrivate;
 
 struct _NMPPPManager {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMPPPManagerPrivate _priv;
 };
 
 typedef struct {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 } NMPPPManagerClass;
 
-G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_PPP_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMPPPManager, NM_IS_PPP_MANAGER, NMDBusObject)
+#define NM_PPP_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMPPPManager, NM_IS_PPP_MANAGER)
 
 /*****************************************************************************/
 
@@ -175,28 +174,24 @@ monitor_cb (gpointer user_data)
 {
 	NMPPPManager *manager = NM_PPP_MANAGER (user_data);
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
-	const char *ifname;
+	struct ifreq req;
+	struct ppp_stats stats;
 
-	ifname = nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex);
+	memset (&req, 0, sizeof (req));
+	memset (&stats, 0, sizeof (stats));
+	req.ifr_data = (caddr_t) &stats;
 
-	if (ifname) {
-		struct ppp_stats stats = { };
-		struct ifreq req = {
-			.ifr_data = (caddr_t) &stats,
-		};
-
-		nm_utils_ifname_cpy (req.ifr_name, ifname);
-		if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) {
-			if (errno != ENODEV)
-				_LOGW ("could not read ppp stats: %s", strerror (errno));
-		} else {
-			g_signal_emit (manager, signals[STATS], 0,
-			               (guint) stats.p.ppp_ibytes,
-			               (guint) stats.p.ppp_obytes);
-		}
+	strncpy (req.ifr_name, priv->ip_iface, sizeof (req.ifr_name));
+	if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) {
+		if (errno != ENODEV)
+			_LOGW ("could not read ppp stats: %s", strerror (errno));
+	} else {
+		g_signal_emit (manager, signals[STATS], 0,
+		               (guint) stats.p.ppp_ibytes,
+		               (guint) stats.p.ppp_obytes);
 	}
 
-	return G_SOURCE_CONTINUE;
+	return TRUE;
 }
 
 static void
@@ -331,27 +326,20 @@ ppp_secrets_cb (NMActRequest *req,
 	 * against libnm just to parse this. So instead, let's just send what
 	 * it needs.
 	 */
-	g_dbus_method_invocation_return_value (priv->pending_secrets_context,
-	                                       g_variant_new ("(ss)",
-	                                                      username ?: "",
-	                                                      password ?: ""));
+	g_dbus_method_invocation_return_value (
+		priv->pending_secrets_context,
+		g_variant_new ("(ss)", username ? username : "", password ? password : ""));
 
-out:
+ out:
 	priv->pending_secrets_context = NULL;
 	priv->secrets_id = NULL;
 	priv->secrets_setting_name = NULL;
 }
 
 static void
-impl_ppp_manager_need_secrets (NMDBusObject *obj,
-                               const NMDBusInterfaceInfoExtended *interface_info,
-                               const NMDBusMethodInfoExtended *method_info,
-                               GDBusConnection *connection,
-                               const char *sender,
-                               GDBusMethodInvocation *invocation,
-                               GVariant *parameters)
+impl_ppp_manager_need_secrets (NMPPPManager *manager,
+                               GDBusMethodInvocation *context)
 {
-	NMPPPManager *manager = NM_PPP_MANAGER (obj);
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 	NMConnection *applied_connection;
 	const char *username = NULL;
@@ -370,7 +358,7 @@ impl_ppp_manager_need_secrets (NMDBusObject *obj,
 		/* Use existing secrets from the connection */
 		if (extract_details_from_connection (applied_connection, NULL, &username, &password, &error)) {
 			/* Send existing secrets to the PPP plugin */
-			priv->pending_secrets_context = invocation;
+			priv->pending_secrets_context = context;
 			ppp_secrets_cb (priv->act_req, priv->secrets_id, NULL, NULL, manager);
 		} else {
 			_LOGW ("%s", error->message);
@@ -395,85 +383,39 @@ impl_ppp_manager_need_secrets (NMDBusObject *obj,
 	                                               ppp_secrets_cb,
 	                                               manager);
 	g_object_set_qdata (G_OBJECT (applied_connection), ppp_manager_secret_tries_quark (), GUINT_TO_POINTER (++tries));
-	priv->pending_secrets_context = invocation;
+	priv->pending_secrets_context = context;
 
 	if (hints)
 		g_ptr_array_free (hints, TRUE);
 }
 
 static void
-impl_ppp_manager_set_state (NMDBusObject *obj,
-                            const NMDBusInterfaceInfoExtended *interface_info,
-                            const NMDBusMethodInfoExtended *method_info,
-                            GDBusConnection *connection,
-                            const char *sender,
-                            GDBusMethodInvocation *invocation,
-                            GVariant *parameters)
+impl_ppp_manager_set_state (NMPPPManager *manager,
+                            GDBusMethodInvocation *context,
+                            guint32 state)
 {
-	NMPPPManager *manager = NM_PPP_MANAGER (obj);
-	guint32 state;
-
-	g_variant_get (parameters, "(u)", &state);
 	g_signal_emit (manager, signals[STATE_CHANGED], 0, (guint) state);
-	g_dbus_method_invocation_return_value (invocation, NULL);
-}
 
-static void
-impl_ppp_manager_set_ifindex (NMDBusObject *obj,
-                              const NMDBusInterfaceInfoExtended *interface_info,
-                              const NMDBusMethodInfoExtended *method_info,
-                              GDBusConnection *connection,
-                              const char *sender,
-                              GDBusMethodInvocation *invocation,
-                              GVariant *parameters)
-{
-	NMPPPManager *manager = NM_PPP_MANAGER (obj);
-	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
-	const NMPlatformLink *plink = NULL;
-	nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL;
-	gint32 ifindex;
-
-	g_variant_get (parameters, "(i)", &ifindex);
-
-	_LOGD ("set-ifindex %d", (int) ifindex);
-
-	if (priv->ifindex >= 0) {
-		_LOGW ("can't change the ifindex from %d to %d", priv->ifindex, (int) ifindex);
-		return;
-	}
-
-	if (ifindex > 0) {
-		plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
-		if (!plink) {
-			nm_platform_process_events (NM_PLATFORM_GET);
-			plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
-		}
-	}
-
-	if (!plink) {
-		_LOGW ("unknown interface with ifindex %d", ifindex);
-		ifindex = 0;
-	}
-
-	priv->ifindex = ifindex;
-
-	obj_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink));
-
-	g_signal_emit (manager, signals[IFINDEX_SET], 0, ifindex, plink->name);
-	g_dbus_method_invocation_return_value (invocation, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 }
 
 static gboolean
 set_ip_config_common (NMPPPManager *self,
                       GVariant *config_dict,
+                      const char *iface_prop,
                       guint32 *out_mtu)
 {
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self);
 	NMConnection *applied_connection;
 	NMSettingPpp *s_ppp;
+	const char *iface;
 
-	if (priv->ifindex <= 0)
+	if (!g_variant_lookup (config_dict, iface_prop, "&s", &iface)) {
+		_LOGE ("no interface received!");
 		return FALSE;
+	}
+	if (priv->ip_iface == NULL)
+		priv->ip_iface = g_strdup (iface);
 
 	/* Got successful IP config; obviously the secrets worked */
 	applied_connection = nm_act_request_get_applied_connection (priv->act_req);
@@ -490,32 +432,29 @@ set_ip_config_common (NMPPPManager *self,
 }
 
 static void
-impl_ppp_manager_set_ip4_config (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
+impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
+                                 GDBusMethodInvocation *context,
+                                 GVariant *config_dict)
 {
-	NMPPPManager *manager = NM_PPP_MANAGER (obj);
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 	gs_unref_object NMIP4Config *config = NULL;
 	NMPlatformIP4Address address;
 	guint32 u32, mtu;
 	GVariantIter *iter;
-	gs_unref_variant GVariant *config_dict = NULL;
+	int ifindex;
 
 	_LOGI ("(IPv4 Config Get) reply received.");
 
-	g_variant_get (parameters, "(@a{sv})", &config_dict);
-
 	nm_clear_g_source (&priv->ppp_timeout_handler);
 
-	if (!set_ip_config_common (manager, config_dict, &mtu))
+	if (!set_ip_config_common (manager, config_dict, NM_PPP_IP4_CONFIG_INTERFACE, &mtu))
 		goto out;
 
-	config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), priv->ifindex);
+	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
+	if (ifindex <= 0)
+		goto out;
+
+	config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex);
 
 	if (mtu)
 		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_PPP);
@@ -528,7 +467,7 @@ impl_ppp_manager_set_ip4_config (NMDBusObject *obj,
 
 	if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_GATEWAY, "u", &u32)) {
 		const NMPlatformIP4Route r = {
-			.ifindex   = priv->ifindex,
+			.ifindex   = ifindex,
 			.rt_source = NM_IP_CONFIG_SOURCE_PPP,
 			.gateway   = u32,
 			.table_coerced = nm_platform_route_table_coerce (priv->ip4_route_table),
@@ -564,10 +503,10 @@ impl_ppp_manager_set_ip4_config (NMDBusObject *obj,
 	}
 
 	/* Push the IP4 config up to the device */
-	g_signal_emit (manager, signals[IP4_CONFIG], 0, config);
+	g_signal_emit (manager, signals[IP4_CONFIG], 0, priv->ip_iface, config);
 
 out:
-	g_dbus_method_invocation_return_value (invocation, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 }
 
 /* Converts the named Interface Identifier item to an IPv6 LL address and
@@ -600,40 +539,37 @@ iid_value_to_ll6_addr (GVariant *dict,
 }
 
 static void
-impl_ppp_manager_set_ip6_config (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
+impl_ppp_manager_set_ip6_config (NMPPPManager *manager,
+                                 GDBusMethodInvocation *context,
+                                 GVariant *config_dict)
 {
-	NMPPPManager *manager = NM_PPP_MANAGER (obj);
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 	gs_unref_object NMIP6Config *config = NULL;
 	NMPlatformIP6Address addr;
 	struct in6_addr a;
 	NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
 	gboolean has_peer = FALSE;
-	gs_unref_variant GVariant *config_dict = NULL;
+	int ifindex;
 
 	_LOGI ("(IPv6 Config Get) reply received.");
 
-	g_variant_get (parameters, "(@a{sv})", &config_dict);
-
 	nm_clear_g_source (&priv->ppp_timeout_handler);
 
-	if (!set_ip_config_common (manager, config_dict, NULL))
+	if (!set_ip_config_common (manager, config_dict, NM_PPP_IP6_CONFIG_INTERFACE, NULL))
 		goto out;
 
-	config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), priv->ifindex);
+	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
+	if (ifindex <= 0)
+		goto out;
+
+	config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), ifindex);
 
 	memset (&addr, 0, sizeof (addr));
 	addr.plen = 64;
 
 	if (iid_value_to_ll6_addr (config_dict, NM_PPP_IP6_CONFIG_PEER_IID, &a, NULL)) {
 		const NMPlatformIP6Route r = {
-			.ifindex   = priv->ifindex,
+			.ifindex   = ifindex,
 			.rt_source = NM_IP_CONFIG_SOURCE_PPP,
 			.gateway   = a,
 			.table_coerced = nm_platform_route_table_coerce (priv->ip6_route_table),
@@ -651,12 +587,12 @@ impl_ppp_manager_set_ip6_config (NMDBusObject *obj,
 		nm_ip6_config_add_address (config, &addr);
 
 		/* Push the IPv6 config and interface identifier up to the device */
-		g_signal_emit (manager, signals[IP6_CONFIG], 0, &iid, config);
+		g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config);
 	} else
 		_LOGE ("invalid IPv6 address received!");
 
 out:
-	g_dbus_method_invocation_return_value (invocation, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
 }
 
 /*****************************************************************************/
@@ -963,7 +899,7 @@ create_pppd_cmd_line (NMPPPManager *self,
 	nm_cmd_line_add_int (cmd, 0);
 
 	nm_cmd_line_add_string (cmd, "ipparam");
-	nm_cmd_line_add_string (cmd, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
+	nm_cmd_line_add_string (cmd, nm_exported_object_get_path (NM_EXPORTED_OBJECT (self)));
 
 	nm_cmd_line_add_string (cmd, "plugin");
 	nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN);
@@ -1042,7 +978,7 @@ _ppp_manager_start (NMPPPManager *manager,
 	return FALSE;
 #endif
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (manager));
+	nm_exported_object_export (NM_EXPORTED_OBJECT (manager));
 
 	priv->pid = 0;
 
@@ -1116,7 +1052,7 @@ out:
 		nm_cmd_line_destroy (ppp_cmd);
 
 	if (priv->pid <= 0)
-		nm_dbus_object_unexport (NM_DBUS_OBJECT (manager));
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (manager));
 
 	return priv->pid > 0;
 }
@@ -1220,7 +1156,7 @@ _ppp_manager_stop_async (NMPPPManager *manager,
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 	StopContext *ctx;
 
-	nm_dbus_object_unexport (NM_DBUS_OBJECT (manager));
+	nm_exported_object_unexport (NM_EXPORTED_OBJECT (manager));
 
 	ctx = g_slice_new0 (StopContext);
 	ctx->manager = g_object_ref (manager);
@@ -1257,10 +1193,10 @@ _ppp_manager_stop_async (NMPPPManager *manager,
 static void
 _ppp_manager_stop_sync (NMPPPManager *manager)
 {
-	NMDBusObject *dbus = NM_DBUS_OBJECT (manager);
+	NMExportedObject *exported = NM_EXPORTED_OBJECT (manager);
 
-	if (nm_dbus_object_is_exported (dbus))
-		nm_dbus_object_unexport (dbus);
+	if (nm_exported_object_is_exported (exported))
+		nm_exported_object_unexport (exported);
 
 	_ppp_cleanup (manager);
 	_ppp_kill (manager);
@@ -1308,7 +1244,6 @@ nm_ppp_manager_init (NMPPPManager *manager)
 {
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
 
-	priv->ifindex = -1;
 	priv->monitor_fd = -1;
 	priv->ip4_route_table = RT_TABLE_MAIN;
 	priv->ip4_route_metric = 460;
@@ -1330,11 +1265,11 @@ static void
 dispose (GObject *object)
 {
 	NMPPPManager *self = (NMPPPManager *) object;
-	NMDBusObject *dbus = NM_DBUS_OBJECT (self);
+	NMExportedObject *exported = NM_EXPORTED_OBJECT (self);
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self);
 
-	if (nm_dbus_object_is_exported (dbus))
-		nm_dbus_object_unexport (dbus);
+	if (nm_exported_object_is_exported (exported))
+		nm_exported_object_unexport (exported);
 
 	_ppp_cleanup (self);
 	_ppp_kill (self);
@@ -1349,78 +1284,24 @@ finalize (GObject *object)
 {
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE ((NMPPPManager *) object);
 
+	g_free (priv->ip_iface);
 	g_free (priv->parent_iface);
 
 	G_OBJECT_CLASS (nm_ppp_manager_parent_class)->finalize (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_ppp = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_PPP,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"NeedSecrets",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("username", "s"),
-						NM_DEFINE_GDBUS_ARG_INFO ("password", "s"),
-					),
-				),
-				.handle = impl_ppp_manager_need_secrets,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SetIp4Config",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("config", "a{sv}"),
-					),
-				),
-				.handle = impl_ppp_manager_set_ip4_config,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SetIp6Config",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("config", "a{sv}"),
-					),
-				),
-				.handle = impl_ppp_manager_set_ip6_config,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SetState",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("state", "u"),
-					),
-				),
-				.handle = impl_ppp_manager_set_state,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SetIfindex",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("ifindex", "i"),
-					),
-				),
-				.handle = impl_ppp_manager_set_ifindex,
-			),
-		),
-	),
-};
-
 static void
 nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (manager_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class);
 
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/PPP");
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_ppp);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/PPP");
 
 	obj_properties[PROP_PARENT_IFACE] =
 	     g_param_spec_string (NM_PPP_MANAGER_PARENT_IFACE, "", "",
@@ -1439,23 +1320,14 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
 	                  G_TYPE_NONE, 1,
 	                  G_TYPE_UINT);
 
-	signals[IFINDEX_SET] =
-	    g_signal_new (NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0,
-	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 2,
-	                  G_TYPE_INT,
-	                  G_TYPE_STRING);
-
 	signals[IP4_CONFIG] =
 	    g_signal_new (NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
 	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 1,
+	                  G_TYPE_NONE, 2,
+	                  G_TYPE_STRING,
 	                  G_TYPE_OBJECT);
 
 	signals[IP6_CONFIG] =
@@ -1464,9 +1336,7 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
 	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 2,
-	                  G_TYPE_POINTER,
-	                  G_TYPE_OBJECT);
+	                  G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT);
 
 	signals[STATS] =
 	    g_signal_new (NM_PPP_MANAGER_SIGNAL_STATS,
@@ -1477,6 +1347,14 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
 	                  G_TYPE_NONE, 2,
 	                  G_TYPE_UINT /*guint32 in_bytes*/,
 	                  G_TYPE_UINT /*guint32 out_bytes*/);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class),
+	                                        NMDBUS_TYPE_PPP_MANAGER_SKELETON,
+	                                        "NeedSecrets", impl_ppp_manager_need_secrets,
+	                                        "SetIp4Config", impl_ppp_manager_set_ip4_config,
+	                                        "SetIp6Config", impl_ppp_manager_set_ip6_config,
+	                                        "SetState", impl_ppp_manager_set_state,
+	                                        NULL);
 }
 
 NMPPPOps ppp_ops = {
diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h
index 5457726e..35fb1b60 100644
--- a/src/ppp/nm-ppp-manager.h
+++ b/src/ppp/nm-ppp-manager.h
@@ -25,7 +25,6 @@
 #define NM_PPP_MANAGER_PARENT_IFACE    "parent-iface"
 
 #define NM_PPP_MANAGER_SIGNAL_STATE_CHANGED "state-changed"
-#define NM_PPP_MANAGER_SIGNAL_IFINDEX_SET   "ifindex-set"
 #define NM_PPP_MANAGER_SIGNAL_IP4_CONFIG    "ip4-config"
 #define NM_PPP_MANAGER_SIGNAL_IP6_CONFIG    "ip6-config"
 #define NM_PPP_MANAGER_SIGNAL_STATS         "stats"
diff --git a/src/ppp/nm-pppd-plugin.c b/src/ppp/nm-pppd-plugin.c
index 989f7433..0ac8f907 100644
--- a/src/ppp/nm-pppd-plugin.c
+++ b/src/ppp/nm-pppd-plugin.c
@@ -28,7 +28,6 @@
 #include <pppd/ipcp.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
-#include <net/if.h>
 #include <arpa/inet.h>
 #include <dlfcn.h>
 
@@ -37,9 +36,7 @@
 #include <pppd/ipv6cp.h>
 
 #include "nm-default.h"
-
 #include "nm-dbus-interface.h"
-
 #include "nm-pppd-plugin.h"
 #include "nm-ppp-status.h"
 
@@ -53,9 +50,7 @@ static void
 nm_phasechange (void *data, int arg)
 {
 	NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN;
-	char new_name[IF_NAMESIZE];
 	char *ppp_phase;
-	int index;
 
 	g_return_if_fail (G_IS_DBUS_PROXY (proxy));
 
@@ -131,25 +126,6 @@ nm_phasechange (void *data, int arg)
 		                   NULL,
 		                   NULL, NULL);
 	}
-
-	if (ppp_status == PHASE_RUNNING) {
-		index = if_nametoindex (ifname);
-		/* Make a sync call to ensure that when the call
-		 * terminates the interface already has its final
-		 * name. */
-		g_dbus_proxy_call_sync (proxy,
-		                        "SetIfindex",
-		                        g_variant_new ("(i)", index),
-		                        G_DBUS_CALL_FLAGS_NONE,
-		                        25000,
-		                        NULL, NULL);
-		/* Update the name in pppd if NM changed it */
-		if (   if_indextoname (index, new_name)
-		    && !nm_streq0 (ifname, new_name)) {
-			g_message ("nm-ppp-plugin: interface name changed from '%s' to '%s'", ifname, new_name);
-			strncpy (ifname, new_name, IF_NAMESIZE);
-		}
-	}
 }
 
 static void
@@ -172,9 +148,6 @@ nm_ip_up (void *data, int arg)
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
 
-	/* Keep sending the interface name to be backwards compatible
-	 * with older versions of NM during a package upgrade, where
-	 * NM is not restarted and the pppd plugin was not loaded. */
 	g_variant_builder_add (&builder, "{sv}",
 	                       NM_PPP_IP4_CONFIG_INTERFACE,
 	                       g_variant_new_string (ifname));
@@ -269,9 +242,6 @@ nm_ip6_up (void *data, int arg)
 	g_message ("nm-ppp-plugin: (%s): ip6-up event", __func__);
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-	/* Keep sending the interface name to be backwards compatible
-	 * with older versions of NM during a package upgrade, where
-	 * NM is not restarted and the pppd plugin was not loaded. */
 	g_variant_builder_add (&builder, "{sv}",
 	                       NM_PPP_IP6_CONFIG_INTERFACE,
 	                       g_variant_new_string (ifname));
@@ -391,6 +361,8 @@ plugin_init (void)
 	GDBusConnection *bus;
 	GError *err = NULL;
 
+	nm_g_type_init ();
+
 	g_message ("nm-ppp-plugin: (%s): initializing", __func__);
 
 	bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &err);
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 453136e4..a68db47e 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -31,12 +31,14 @@
 #include "nm-auth-utils.h"
 #include "nm-setting-vpn.h"
 #include "nm-auth-manager.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "nm-session-monitor.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
+
+#include "introspection/org.freedesktop.NetworkManager.AgentManager.h"
 
 /*****************************************************************************/
 
@@ -65,15 +67,15 @@ typedef struct {
 } NMAgentManagerPrivate;
 
 struct _NMAgentManager {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMAgentManagerPrivate _priv;
 };
 
 struct _NMAgentManagerClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, NM_TYPE_DBUS_OBJECT)
+G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_AGENT_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAgentManager, NM_IS_AGENT_MANAGER)
 
@@ -360,7 +362,7 @@ agent_register_permissions_done (NMAuthChain *chain,
 			request_add_agent (c_list_entry (iter, Request, lst_request), agent);
 	}
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static NMSecretAgent *
@@ -390,10 +392,10 @@ agent_disconnected_cb (NMSecretAgent *agent, gpointer user_data)
 }
 
 static void
-agent_manager_register_with_capabilities (NMAgentManager *self,
-                                          GDBusMethodInvocation *context,
-                                          const char *identifier,
-                                          guint32 capabilities)
+impl_agent_manager_register_with_capabilities (NMAgentManager *self,
+                                               GDBusMethodInvocation *context,
+                                               const char *identifier,
+                                               guint32 capabilities)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 	NMAuthSubject *subject;
@@ -458,56 +460,45 @@ done:
 }
 
 static void
-impl_agent_manager_register (NMDBusObject *obj,
-                             const NMDBusInterfaceInfoExtended *interface_info,
-                             const NMDBusMethodInfoExtended *method_info,
-                             GDBusConnection *connection,
-                             const char *sender,
-                             GDBusMethodInvocation *invocation,
-                             GVariant *parameters)
+impl_agent_manager_register (NMAgentManager *self,
+                             GDBusMethodInvocation *context,
+                             const char *identifier)
 {
-	const char *identifier;
-
-	g_variant_get (parameters, "(&s)", &identifier);
-	agent_manager_register_with_capabilities (NM_AGENT_MANAGER (obj), invocation, identifier, 0);
+	impl_agent_manager_register_with_capabilities (self, context, identifier, 0);
 }
 
 static void
-impl_agent_manager_register_with_capabilities (NMDBusObject *obj,
-                                               const NMDBusInterfaceInfoExtended *interface_info,
-                                               const NMDBusMethodInfoExtended *method_info,
-                                               GDBusConnection *connection,
-                                               const char *sender,
-                                               GDBusMethodInvocation *invocation,
-                                               GVariant *parameters)
+impl_agent_manager_unregister (NMAgentManager *self,
+                               GDBusMethodInvocation *context)
 {
-	const char *identifier;
-	guint32 capabilities;
-
-	g_variant_get (parameters, "(&su)", &identifier, &capabilities);
-	agent_manager_register_with_capabilities (NM_AGENT_MANAGER (obj), invocation, identifier, capabilities);
-}
+	GError *error = NULL;
+	char *sender = NULL;
 
-static void
-impl_agent_manager_unregister (NMDBusObject *obj,
-                               const NMDBusInterfaceInfoExtended *interface_info,
-                               const NMDBusMethodInfoExtended *method_info,
-                               GDBusConnection *connection,
-                               const char *sender,
-                               GDBusMethodInvocation *invocation,
-                               GVariant *parameters)
-{
-	NMAgentManager *self = NM_AGENT_MANAGER (obj);
+	if (!nm_bus_manager_get_caller_info (nm_bus_manager_get (),
+	                                     context,
+	                                     &sender,
+	                                     NULL,
+	                                     NULL)) {
+		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
+		                             NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to determine request sender.");
+		goto done;
+	}
 
+	/* Found the agent, unregister and remove it */
 	if (!remove_agent (self, sender)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_AGENT_MANAGER_ERROR,
-		                                               NM_AGENT_MANAGER_ERROR_NOT_REGISTERED,
-		                                               "Caller is not registered as an Agent");
-		return;
+		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
+		                             NM_AGENT_MANAGER_ERROR_NOT_REGISTERED,
+		                             "Caller is not registered as an Agent");
+		goto done;
 	}
 
-	g_dbus_method_invocation_return_value (invocation, NULL);
+	g_dbus_method_invocation_return_value (context, NULL);
+
+done:
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	g_free (sender);
 }
 
 /*****************************************************************************/
@@ -539,7 +530,7 @@ request_free (Request *req)
 		g_object_unref (req->con.connection);
 		g_free (req->con.path);
 		if (req->con.chain)
-			nm_auth_chain_destroy (req->con.chain);
+			nm_auth_chain_unref (req->con.chain);
 		if (req->request_type == REQUEST_TYPE_CON_GET) {
 			g_free (req->con.get.setting_name);
 			g_strfreev (req->con.get.hints);
@@ -810,7 +801,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent)
 		case REQUEST_TYPE_CON_DEL:
 			if (req->con.chain) {
 				/* This cancels the pending authorization requests. */
-				nm_auth_chain_destroy (req->con.chain);
+				nm_auth_chain_unref (req->con.chain);
 				req->con.chain = NULL;
 			}
 			break;
@@ -1047,7 +1038,7 @@ _con_get_request_start_validated (NMAuthChain *chain,
 		_con_get_request_start_proceed (req, req->con.current_has_modify);
 	}
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
@@ -1541,7 +1532,7 @@ agent_permissions_changed_done (NMAuthChain *chain,
 	nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected);
 	nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open);
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
@@ -1595,7 +1586,7 @@ constructed (GObject *object)
 	priv->auth_mgr = g_object_ref (nm_auth_manager_get ());
 	priv->session_monitor = g_object_ref (nm_session_monitor_get ());
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (object));
+	nm_exported_object_export (NM_EXPORTED_OBJECT (object));
 
 	g_signal_connect (priv->auth_mgr,
 	                  NM_AUTH_MANAGER_SIGNAL_CHANGED,
@@ -1616,7 +1607,7 @@ cancel_more:
 		goto cancel_more;
 	}
 
-	g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_destroy);
+	g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->chains = NULL;
 
 	if (priv->agents) {
@@ -1631,64 +1622,20 @@ cancel_more:
 		g_clear_object (&priv->auth_mgr);
 	}
 
-	nm_dbus_object_unexport (NM_DBUS_OBJECT (object));
+	nm_exported_object_unexport (NM_EXPORTED_OBJECT (object));
 
 	g_clear_object (&priv->session_monitor);
 
 	G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object);
 }
 
-static const NMDBusInterfaceInfoExtended interface_info_agent_manager = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_AGENT_MANAGER,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Register",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("identifier", "s"),
-					),
-				),
-				.handle = impl_agent_manager_register,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"RegisterWithCapabilities",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("identifier",   "s"),
-						NM_DEFINE_GDBUS_ARG_INFO ("capabilities", "u"),
-					),
-				),
-				.handle = impl_agent_manager_register_with_capabilities,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"RegisterWithCapabilities",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("identifier",   "s"),
-						NM_DEFINE_GDBUS_ARG_INFO ("capabilities", "u"),
-					),
-				),
-				.handle = impl_agent_manager_register_with_capabilities,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Unregister",
-				),
-				.handle = impl_agent_manager_unregister,
-			),
-		),
-	),
-};
-
 static void
 nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (agent_manager_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (agent_manager_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (agent_manager_class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH_AGENT_MANAGER);
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_agent_manager);
+	exported_object_class->export_path = NM_DBUS_PATH_AGENT_MANAGER;
 
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
@@ -1702,4 +1649,11 @@ nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class)
 	                  g_cclosure_marshal_VOID__OBJECT,
 	                  G_TYPE_NONE, 1,
 	                  G_TYPE_OBJECT);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (agent_manager_class),
+	                                        NMDBUS_TYPE_AGENT_MANAGER_SKELETON,
+	                                        "Register", impl_agent_manager_register,
+	                                        "RegisterWithCapabilities", impl_agent_manager_register_with_capabilities,
+	                                        "Unregister", impl_agent_manager_unregister,
+	                                        NULL);
 }
diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h
index 8c27f26b..f6845818 100644
--- a/src/settings/nm-agent-manager.h
+++ b/src/settings/nm-agent-manager.h
@@ -23,7 +23,7 @@
 
 #include "nm-connection.h"
 
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-secret-agent.h"
 
 #define NM_TYPE_AGENT_MANAGER            (nm_agent_manager_get_type ())
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index af6d7017..192e9877 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -26,12 +26,13 @@
 #include <pwd.h>
 
 #include "nm-dbus-interface.h"
-#include "nm-dbus-manager.h"
-#include "nm-core-internal.h"
+#include "nm-bus-manager.h"
 #include "nm-auth-subject.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
+
+#include "introspection/org.freedesktop.NetworkManager.SecretAgent.h"
 
 /*****************************************************************************/
 
@@ -50,8 +51,8 @@ typedef struct {
 	char *dbus_owner;
 	NMSecretAgentCapabilities capabilities;
 	GSList *permissions;
-	GDBusProxy *proxy;
-	NMDBusManager *bus_mgr;
+	NMDBusSecretAgent *proxy;
+	NMBusManager *bus_mgr;
 	GDBusConnection *connection;
 	CList requests;
 	gulong on_disconnected_id;
@@ -299,7 +300,7 @@ nm_secret_agent_add_permission (NMSecretAgent *agent,
  * @permission: The name of the permission to check for
  *
  * Returns whether or not the agent has the given permission.
- *
+ * 
  * Returns: %TRUE if the agent has the given permission, %FALSE if it does not
  * or if the permission was not previous recorded with
  * nm_secret_agent_add_permission().
@@ -335,17 +336,11 @@ get_callback (GObject *proxy,
 	if (request_check_return (r)) {
 		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
 		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
 		gs_unref_variant GVariant *secrets = NULL;
 
-		ret = _nm_dbus_proxy_call_finish (priv->proxy, result, G_VARIANT_TYPE ("(a{sa{sv}})"), &error);
-		if (!ret)
+		nmdbus_secret_agent_call_get_secrets_finish (priv->proxy, &secrets, result, &error);
+		if (error)
 			g_dbus_error_strip_remote_error (error);
-		else {
-			g_variant_get (ret,
-			               "(@a{sa{sv}})",
-			               &secrets);
-		}
 		r->callback (r->agent, r, secrets, error, r->callback_data);
 	}
 
@@ -363,6 +358,7 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
                              gpointer callback_data)
 {
 	NMSecretAgentPrivate *priv;
+	static const char *no_hints[] = { NULL };
 	GVariant *dict;
 	NMSecretAgentCallId *r;
 
@@ -383,20 +379,16 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
 	r = request_new (self, "GetSecrets", path, setting_name, callback, callback_data);
 	r->is_get_secrets = TRUE;
 
-	g_dbus_proxy_call (priv->proxy,
-	                   "GetSecrets",
-	                   g_variant_new ("(@a{sa{sv}}os^asu)",
-	                                  dict,
-	                                  path,
-	                                  setting_name,
-	                                  hints ?: NM_PTRARRAY_EMPTY (const char *),
-	                                  (guint32) flags),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   120000,
-	                   r->cancellable,
-	                   get_callback,
-	                   r);
-
+	/* Increase the timeout only for this call */
+	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), 120000);
+	nmdbus_secret_agent_call_get_secrets (priv->proxy,
+	                                      dict,
+	                                      path,
+	                                      setting_name,
+	                                      hints ? hints : no_hints,
+	                                      flags,
+	                                      r->cancellable,
+	                                      get_callback, r);
 	g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (priv->proxy), -1);
 
 	return r;
@@ -407,16 +399,17 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
 static void
 cancel_done (GObject *proxy, GAsyncResult *result, gpointer user_data)
 {
-	gs_free char *description = user_data;
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *ret = NULL;
+	char *description = user_data;
+	GError *error = NULL;
 
-	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-	if (!ret) {
+	if (!nmdbus_secret_agent_call_cancel_get_secrets_finish (NMDBUS_SECRET_AGENT (proxy), result, &error)) {
 		nm_log_dbg (LOGD_AGENTS, "%s%s%s: agent failed to cancel secrets: %s",
 		            NM_PRINT_FMT_QUOTED (description, "(", description, ")", "???"),
 		            error->message);
+		g_clear_error (&error);
 	}
+
+	g_free (description);
 }
 
 static void
@@ -433,16 +426,11 @@ do_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *r, gboolean disposi
 	if (   r->is_get_secrets
 	    && priv->proxy) {
 		/* for GetSecrets call, we must cancel the request. */
-		g_dbus_proxy_call (G_DBUS_PROXY (priv->proxy),
-		                   "CancelGetSecrets",
-		                   g_variant_new ("(os)",
-		                                  r->path,
-		                                  r->setting_name),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   NULL,
-		                   cancel_done,
-		                   g_strdup (nm_secret_agent_get_description (self)));
+		nmdbus_secret_agent_call_cancel_get_secrets (priv->proxy,
+		                                             r->path, r->setting_name,
+		                                             NULL,
+		                                             cancel_done,
+		                                             g_strdup (nm_secret_agent_get_description (self)));
 	}
 
 	cancellable = r->cancellable;
@@ -506,11 +494,11 @@ agent_save_cb (GObject *proxy,
 	NMSecretAgentCallId *r = user_data;
 
 	if (request_check_return (r)) {
+		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
 		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
 
-		ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-		if (!ret)
+		nmdbus_secret_agent_call_save_secrets_finish (priv->proxy, result, &error);
+		if (error)
 			g_dbus_error_strip_remote_error (error);
 		r->callback (r->agent, r, NULL, error, r->callback_data);
 	}
@@ -539,16 +527,11 @@ nm_secret_agent_save_secrets (NMSecretAgent *self,
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
 	r = request_new (self, "SaveSecrets", path, NULL, callback, callback_data);
-	g_dbus_proxy_call (priv->proxy,
-	                   "SaveSecrets",
-	                   g_variant_new ("(@a{sa{sv}}o)",
-	                                  dict,
-	                                  path),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   NULL, /* cancelling the request does *not* cancel the D-Bus call. */
-	                   agent_save_cb,
-	                   r);
+	nmdbus_secret_agent_call_save_secrets (priv->proxy,
+	                                       dict,
+	                                       path,
+	                                       NULL, /* cancelling the request does *not* cancel the D-Bus call. */
+	                                       agent_save_cb, r);
 
 	return r;
 }
@@ -563,11 +546,11 @@ agent_delete_cb (GObject *proxy,
 	NMSecretAgentCallId *r = user_data;
 
 	if (request_check_return (r)) {
+		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
 		gs_free_error GError *error = NULL;
-		gs_unref_variant GVariant *ret = NULL;
 
-		ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, G_VARIANT_TYPE ("()"), &error);
-		if (!ret)
+		nmdbus_secret_agent_call_delete_secrets_finish (priv->proxy, result, &error);
+		if (error)
 			g_dbus_error_strip_remote_error (error);
 		r->callback (r->agent, r, NULL, error, r->callback_data);
 	}
@@ -596,16 +579,12 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self,
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
 
 	r = request_new (self, "DeleteSecrets", path, NULL, callback, callback_data);
-	g_dbus_proxy_call (priv->proxy,
-	                   "DeleteSecrets",
-	                   g_variant_new ("(@a{sa{sv}}o)",
-	                                  dict,
-	                                  path),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   NULL, /* cancelling the request does *not* cancel the D-Bus call. */
-	                   agent_delete_cb,
-	                   r);
+	nmdbus_secret_agent_call_delete_secrets (priv->proxy,
+	                                         dict,
+	                                         path,
+	                                         NULL, /* cancelling the request does *not* cancel the D-Bus call. */
+	                                         agent_delete_cb, r);
+
 	return r;
 }
 
@@ -631,7 +610,7 @@ _on_disconnected_cleanup (NMSecretAgentPrivate *priv)
 }
 
 static void
-_on_disconnected_private_connection (NMDBusManager *mgr,
+_on_disconnected_private_connection (NMBusManager *mgr,
                                      GDBusConnection *connection,
                                      NMSecretAgent *self)
 {
@@ -687,6 +666,7 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	NMSecretAgentPrivate *priv;
 	const char *dbus_owner;
 	struct passwd *pw;
+	GDBusProxy *proxy;
 	char *owner_username = NULL;
 	char *description = NULL;
 	char buf_subject[64];
@@ -715,9 +695,9 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 
 	priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 
-	priv->bus_mgr = g_object_ref (nm_dbus_manager_get ());
+	priv->bus_mgr = g_object_ref (nm_bus_manager_get ());
 	priv->connection = g_object_ref (connection);
-	priv->connection_is_private = !!nm_dbus_manager_connection_get_private_name (priv->bus_mgr, connection);
+	priv->connection_is_private = !!nm_bus_manager_connection_get_private_name (priv->bus_mgr, connection);
 
 	_LOGt ("constructed: %s, owner=%s%s%s (%s), private-connection=%d, unique-name=%s%s%s, capabilities=%s",
 	       (description = _create_description (dbus_owner, identifier, uid)),
@@ -734,18 +714,20 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	priv->capabilities = capabilities;
 	priv->subject = g_object_ref (subject);
 
-	priv->proxy = nm_dbus_manager_new_proxy (priv->bus_mgr,
-	                                         priv->connection,
-	                                         G_TYPE_DBUS_PROXY,
-	                                         priv->dbus_owner,
-	                                         NM_DBUS_PATH_SECRET_AGENT,
-	                                         NM_DBUS_INTERFACE_SECRET_AGENT);
+	proxy = nm_bus_manager_new_proxy (priv->bus_mgr,
+	                                  priv->connection,
+	                                  NMDBUS_TYPE_SECRET_AGENT_PROXY,
+	                                  priv->dbus_owner,
+	                                  NM_DBUS_PATH_SECRET_AGENT,
+	                                  NM_DBUS_INTERFACE_SECRET_AGENT);
+	g_assert (proxy);
+	priv->proxy = NMDBUS_SECRET_AGENT (proxy);
 
 	/* we cannot subscribe to notify::g-name-owner because that doesn't work
 	 * for unique names and it doesn't work for private connections. */
 	if (priv->connection_is_private) {
 		priv->on_disconnected_id = g_signal_connect (priv->bus_mgr,
-		                                             NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
+		                                             NM_BUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED,
 		                                             G_CALLBACK (_on_disconnected_private_connection),
 		                                             self);
 	} else {
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 0b998372..58626372 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -25,14 +25,13 @@
 
 #include <string.h>
 
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 
 #include "nm-common-macros.h"
 #include "nm-config.h"
 #include "nm-config-data.h"
 #include "nm-dbus-interface.h"
 #include "nm-session-monitor.h"
-#include "nm-auth-manager.h"
 #include "nm-auth-utils.h"
 #include "nm-auth-subject.h"
 #include "nm-agent-manager.h"
@@ -40,6 +39,8 @@
 #include "nm-core-internal.h"
 #include "nm-audit-manager.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Settings.Connection.h"
+
 #define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
 
@@ -59,9 +60,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
 );
 
 enum {
+	UPDATED,
 	REMOVED,
 	UPDATED_INTERNAL,
-	FLAGS_CHANGED,
 	LAST_SIGNAL
 };
 
@@ -73,7 +74,7 @@ typedef struct _NMSettingsConnectionPrivate {
 	NMSessionMonitor *session_monitor;
 	gulong session_changed_id;
 
-	NMSettingsConnectionIntFlags flags:5;
+	NMSettingsConnectionFlags flags:5;
 
 	bool removed:1;
 	bool ready:1;
@@ -82,8 +83,7 @@ typedef struct _NMSettingsConnectionPrivate {
 
 	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
 
-	/* List of pending authentication requests */
-	CList auth_lst_head;
+	GSList *pending_auths; /* List of pending authentication requests */
 
 	CList call_ids_lst_head; /* in-progress secrets requests */
 
@@ -115,7 +115,7 @@ typedef struct _NMSettingsConnectionPrivate {
 
 } NMSettingsConnectionPrivate;
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT,
+G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_EXPORTED_OBJECT,
                          G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_settings_connection_connection_interface_init)
                          )
 
@@ -146,9 +146,12 @@ G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_D
 
 /*****************************************************************************/
 
-static const GDBusSignalInfo signal_info_updated;
-static const GDBusSignalInfo signal_info_removed;
-static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
+static void
+_emit_updated (NMSettingsConnection *self, gboolean by_user)
+{
+	g_signal_emit (self, signals[UPDATED], 0);
+	g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user);
+}
 
 /*****************************************************************************/
 
@@ -317,7 +320,7 @@ static void
 set_visible (NMSettingsConnection *self, gboolean new_visible)
 {
 	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_VISIBLE,
 	                                  new_visible);
 }
 
@@ -387,7 +390,7 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (self),
-	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE))
+	                   NM_SETTINGS_CONNECTION_FLAGS_VISIBLE))
 		return FALSE;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
@@ -487,30 +490,30 @@ secrets_cleared_cb (NMSettingsConnection *self)
 static void
 set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode)
 {
-	NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
-	const NMSettingsConnectionIntFlags ALL =   NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
-	                                         | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
-	                                         | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+	NMSettingsConnectionFlags flags = NM_SETTINGS_CONNECTION_FLAGS_NONE;
+	const NMSettingsConnectionFlags ALL =   NM_SETTINGS_CONNECTION_FLAGS_UNSAVED
+	                                      | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED
+	                                      | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE;
 
 	switch (persist_mode) {
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
+		flags = NM_SETTINGS_CONNECTION_FLAGS_NONE;
 		break;
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED;
+		flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED;
 		break;
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED:
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY:
-		flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED |
-		        NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
+		flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
+		        NM_SETTINGS_CONNECTION_FLAGS_VOLATILE;
 		break;
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED:
 		/* only set the connection as unsaved, but preserve the nm-generated
 		 * and volatile flag. */
 		nm_settings_connection_set_flags (self,
-		                                  NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
+		                                  NM_SETTINGS_CONNECTION_FLAGS_UNSAVED,
 		                                  TRUE);
 		return;
 	case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
@@ -523,16 +526,6 @@ set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode pe
 }
 
 static void
-_emit_updated (NMSettingsConnection *self, gboolean by_user)
-{
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_settings_connection,
-	                            &signal_info_updated,
-	                            "()");
-	g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user);
-}
-
-static void
 connection_changed_cb (NMSettingsConnection *self, gpointer unused)
 {
 	set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
@@ -578,13 +571,17 @@ _update_prepare (NMSettingsConnection *self,
                  NMConnection *new_connection,
                  GError **error)
 {
+	NMSettingsConnectionPrivate *priv;
+
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 	g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
 
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
 	if (!nm_connection_normalize (new_connection, NULL, NULL, error))
 		return FALSE;
 
-	if (   nm_dbus_object_get_path (NM_DBUS_OBJECT (self))
+	if (   nm_connection_get_path (NM_CONNECTION (self))
 	    && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) {
 		/* Updating the UUID is not allowed once the path is exported. */
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
@@ -664,10 +661,8 @@ nm_settings_connection_update (NMSettingsConnection *self,
 	                               NM_SETTING_COMPARE_FLAG_EXACT)) {
 		gs_unref_object NMConnection *simple = NULL;
 
-		if (log_diff_name) {
-			nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
-			                              nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
-		}
+		if (log_diff_name)
+			nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ");
 
 		/* Make a copy of agent-owned secrets because they won't be present in
 		 * the connection returned by plugins, as plugins return only what was
@@ -684,7 +679,7 @@ nm_settings_connection_update (NMSettingsConnection *self,
 	}
 
 	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 	                                  FALSE);
 
 	if (replaced) {
@@ -803,7 +798,7 @@ nm_settings_connection_delete (NMSettingsConnection *self,
 	for_agents = nm_simple_connection_new_clone (NM_CONNECTION (self));
 	nm_connection_clear_secrets (for_agents);
 	nm_agent_manager_delete_secrets (priv->agent_mgr,
-	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+	                                 nm_connection_get_path (NM_CONNECTION (self)),
 	                                 for_agents);
 	g_object_unref (for_agents);
 
@@ -1300,7 +1295,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr);
 
 	call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr,
-	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+	                                          nm_connection_get_path (NM_CONNECTION (self)),
 	                                          NM_CONNECTION (self),
 	                                          subject,
 	                                          existing_secrets,
@@ -1364,7 +1359,7 @@ nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
 	_get_secrets_cancel (self, call_id, FALSE);
 }
 
-/*****************************************************************************/
+/**** User authorization **************************************/
 
 typedef void (*AuthCallback) (NMSettingsConnection *self,
                               GDBusMethodInvocation *context,
@@ -1372,61 +1367,46 @@ typedef void (*AuthCallback) (NMSettingsConnection *self,
                               GError *error,
                               gpointer data);
 
-typedef struct {
-	CList auth_lst;
-	NMAuthManagerCallId *call_id;
-	NMSettingsConnection *self;
-	AuthCallback callback;
-	gpointer callback_data;
-	GDBusMethodInvocation *invocation;
-	NMAuthSubject *subject;
-} AuthData;
-
 static void
-pk_auth_cb (NMAuthManager *auth_manager,
-            NMAuthManagerCallId *auth_call_id,
-            gboolean is_authorized,
-            gboolean is_challenge,
-            GError *auth_error,
+pk_auth_cb (NMAuthChain *chain,
+            GError *chain_error,
+            GDBusMethodInvocation *context,
             gpointer user_data)
 {
-	AuthData *auth_data = user_data;
-	NMSettingsConnection *self;
-	gs_free_error GError *error = NULL;
-
-	nm_assert (auth_data);
-	nm_assert (NM_IS_SETTINGS_CONNECTION (auth_data->self));
-
-	self = auth_data->self;
+	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	GError *error = NULL;
+	NMAuthCallResult result;
+	const char *perm;
+	AuthCallback callback;
+	gpointer callback_data;
+	NMAuthSubject *subject;
 
-	auth_data->call_id = NULL;
+	priv->pending_auths = g_slist_remove (priv->pending_auths, chain);
 
-	c_list_unlink (&auth_data->auth_lst);
+	perm = nm_auth_chain_get_data (chain, "perm");
+	g_assert (perm);
+	result = nm_auth_chain_get_result (chain, perm);
 
-	if (g_error_matches (auth_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		error = g_error_new (NM_SETTINGS_ERROR,
-		                     NM_SETTINGS_ERROR_FAILED,
-		                     "Error checking authorization: connection was deleted");
-	} else if (auth_error) {
+	/* If our NMSettingsConnection is already gone, do nothing */
+	if (chain_error) {
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_FAILED,
 		                     "Error checking authorization: %s",
-		                     auth_error->message);
-	} else if (nm_auth_call_result_eval (is_authorized, is_challenge, auth_error) != NM_AUTH_CALL_RESULT_YES) {
+		                     chain_error->message ? chain_error->message : "(unknown)");
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Insufficient privileges");
+		                             "Insufficient privileges.");
 	}
 
-	auth_data->callback (self,
-	                     auth_data->invocation,
-	                     auth_data->subject,
-	                     error,
-	                     auth_data->callback_data);
+	callback = nm_auth_chain_get_data (chain, "callback");
+	callback_data = nm_auth_chain_get_data (chain, "callback-data");
+	subject = nm_auth_chain_get_data (chain, "subject");
+	callback (self, context, subject, error, callback_data);
 
-	g_object_unref (auth_data->invocation);
-	g_object_unref (auth_data->subject);
-	g_slice_free (AuthData, auth_data);
+	g_clear_error (&error);
+	nm_auth_chain_unref (chain);
 }
 
 /**
@@ -1454,52 +1434,59 @@ _new_auth_subject (GDBusMethodInvocation *context, GError **error)
 	return subject;
 }
 
-/* may either invoke callback synchronously or asynchronously. */
 static void
 auth_start (NMSettingsConnection *self,
-            GDBusMethodInvocation *invocation,
+            GDBusMethodInvocation *context,
             NMAuthSubject *subject,
             const char *check_permission,
             AuthCallback callback,
             gpointer callback_data)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	AuthData *auth_data;
+	NMAuthChain *chain;
 	GError *error = NULL;
+	char *error_desc = NULL;
 
-	nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (self)));
-	nm_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
-	nm_assert (NM_IS_AUTH_SUBJECT (subject));
+	g_return_if_fail (context != NULL);
+	g_return_if_fail (NM_IS_AUTH_SUBJECT (subject));
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (self),
-	                                          subject,
-	                                          NM_SETTINGS_ERROR,
-	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
-	                                          &error)) {
-		callback (self, invocation, subject, error, callback_data);
+	/* Ensure the caller can view this connection */
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (self),
+	                                subject,
+	                                &error_desc)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
+
+		callback (self, context, subject, error, callback_data);
 		g_clear_error (&error);
 		return;
 	}
 
 	if (!check_permission) {
 		/* Don't need polkit auth, automatic success */
-		callback (self, invocation, subject, NULL, callback_data);
+		callback (self, context, subject, NULL, callback_data);
+		return;
+	}
+
+	chain = nm_auth_chain_new_subject (subject, context, pk_auth_cb, self);
+	if (!chain) {
+		g_set_error_literal (&error,
+		                     NM_SETTINGS_ERROR,
+		                     NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                     "Unable to authenticate the request.");
+		callback (self, context, subject, error, callback_data);
+		g_clear_error (&error);
 		return;
 	}
 
-	auth_data = g_slice_new (AuthData);
-	auth_data->self = self;
-	auth_data->callback = callback;
-	auth_data->callback_data = callback_data;
-	auth_data->invocation = g_object_ref (invocation);
-	auth_data->subject = g_object_ref (subject);
-	c_list_link_tail (&priv->auth_lst_head, &auth_data->auth_lst);
-	auth_data->call_id = nm_auth_manager_check_authorization (nm_auth_manager_get (),
-	                                                          subject,
-	                                                          check_permission,
-	                                                          TRUE,
-	                                                          pk_auth_cb,
-	                                                          auth_data);
+	priv->pending_auths = g_slist_append (priv->pending_auths, chain);
+	nm_auth_chain_set_data (chain, "perm", (gpointer) check_permission, NULL);
+	nm_auth_chain_set_data (chain, "callback", callback, NULL);
+	nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL);
+	nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref);
+	nm_auth_chain_add_call (chain, check_permission, TRUE);
 }
 
 /**** DBus method handlers ************************************/
@@ -1536,7 +1523,7 @@ check_writable (NMConnection *self, GError **error)
 }
 
 static void
-get_settings_auth_cb (NMSettingsConnection *self,
+get_settings_auth_cb (NMSettingsConnection *self, 
                       GDBusMethodInvocation *context,
                       NMAuthSubject *subject,
                       GError *error,
@@ -1590,25 +1577,18 @@ get_settings_auth_cb (NMSettingsConnection *self,
 }
 
 static void
-impl_settings_connection_get_settings (NMDBusObject *obj,
-                                       const NMDBusInterfaceInfoExtended *interface_info,
-                                       const NMDBusMethodInfoExtended *method_info,
-                                       GDBusConnection *connection,
-                                       const char *sender,
-                                       GDBusMethodInvocation *invocation,
-                                       GVariant *parameters)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_object NMAuthSubject *subject = NULL;
+impl_settings_connection_get_settings (NMSettingsConnection *self,
+                                       GDBusMethodInvocation *context)
+{
+	NMAuthSubject *subject;
 	GError *error = NULL;
 
-	subject = _new_auth_subject (invocation, &error);
-	if (!subject) {
-		g_dbus_method_invocation_take_error (invocation, error);
-		return;
-	}
-
-	auth_start (self, invocation, subject, NULL, get_settings_auth_cb, NULL);
+	subject = _new_auth_subject (context, &error);
+	if (subject) {
+		auth_start (self, context, subject, NULL, get_settings_auth_cb, NULL);
+		g_object_unref (subject);
+	} else
+		g_dbus_method_invocation_take_error (context, error);
 }
 
 typedef struct {
@@ -1802,7 +1782,7 @@ update_auth_cb (NMSettingsConnection *self,
 		                                        secrets_filter_cb,
 		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
 		nm_agent_manager_save_secrets (info->agent_mgr,
-		                               nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+		                               nm_connection_get_path (NM_CONNECTION (self)),
 		                               for_agent,
 		                               info->subject);
 	}
@@ -1850,6 +1830,7 @@ settings_connection_update (NMSettingsConnection *self,
 	GError *error = NULL;
 	UpdateInfo *info;
 	const char *permission;
+	char *error_desc = NULL;
 
 	/* If the connection is read-only, that has to be changed at the source of
 	 * the problem (ex a system settings plugin that can't write connections out)
@@ -1886,12 +1867,15 @@ settings_connection_update (NMSettingsConnection *self,
 	 * that's sending the update request.  You can't make a connection
 	 * invisible to yourself.
 	 */
-	if (!nm_auth_is_subject_in_acl_set_error (tmp ? tmp : NM_CONNECTION (self),
-	                                          subject,
-	                                          NM_SETTINGS_ERROR,
-	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
-	                                          &error))
+	if (!nm_auth_is_subject_in_acl (tmp ? tmp : NM_CONNECTION (self),
+	                                subject,
+	                                &error_desc)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
 		goto error;
+	}
 
 	info = g_slice_new0 (UpdateInfo);
 	info->is_update2 = is_update2;
@@ -1917,87 +1901,54 @@ error:
 }
 
 static void
-impl_settings_connection_update (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
+impl_settings_connection_update (NMSettingsConnection *self,
+                                 GDBusMethodInvocation *context,
+                                 GVariant *new_settings)
 {
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_variant GVariant *settings = NULL;
-
-	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
-	settings_connection_update (self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+	settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
 }
 
 static void
-impl_settings_connection_update_unsaved (NMDBusObject *obj,
-                                         const NMDBusInterfaceInfoExtended *interface_info,
-                                         const NMDBusMethodInfoExtended *method_info,
-                                         GDBusConnection *connection,
-                                         const char *sender,
-                                         GDBusMethodInvocation *invocation,
-                                         GVariant *parameters)
+impl_settings_connection_update_unsaved (NMSettingsConnection *self,
+                                         GDBusMethodInvocation *context,
+                                         GVariant *new_settings)
 {
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_variant GVariant *settings = NULL;
-
-	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
-	settings_connection_update (self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY);
+	settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY);
 }
 
 static void
-impl_settings_connection_save (NMDBusObject *obj,
-                               const NMDBusInterfaceInfoExtended *interface_info,
-                               const NMDBusMethodInfoExtended *method_info,
-                               GDBusConnection *connection,
-                               const char *sender,
-                               GDBusMethodInvocation *invocation,
-                               GVariant *parameters)
+impl_settings_connection_save (NMSettingsConnection *self,
+                               GDBusMethodInvocation *context)
 {
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-
-	settings_connection_update (self, FALSE, invocation, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+	settings_connection_update (self, FALSE, context, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
 }
 
 static void
-impl_settings_connection_update2 (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const NMDBusMethodInfoExtended *method_info,
-                                  GDBusConnection *connection,
-                                  const char *sender,
-                                  GDBusMethodInvocation *invocation,
-                                  GVariant *parameters)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_variant GVariant *settings = NULL;
-	gs_unref_variant GVariant *args = NULL;
-	guint32 flags_u;
+impl_settings_connection_update2 (NMSettingsConnection *self,
+                                  GDBusMethodInvocation *context,
+                                  GVariant *settings,
+                                  guint32 flags_u,
+                                  GVariant *args)
+{
 	GError *error = NULL;
 	GVariantIter iter;
 	const char *args_name;
-	NMSettingsUpdate2Flags flags;
+	const NMSettingsUpdate2Flags flags = (NMSettingsUpdate2Flags) flags_u;
 	const NMSettingsUpdate2Flags ALL_PERSIST_MODES =   NM_SETTINGS_UPDATE2_FLAG_TO_DISK
 	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
 	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
 	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY;
 
-	g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
-
 	if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES |
 	                                        NM_SETTINGS_UPDATE2_FLAG_VOLATILE |
 	                                        NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                             "Unknown flags");
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
-	flags = (NMSettingsUpdate2Flags) flags_u;
-
 	if (   (   NM_FLAGS_ANY (flags, ALL_PERSIST_MODES)
 	        && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES))
 	    || (   NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
@@ -2006,7 +1957,7 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                             "Conflicting flags");
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -2014,7 +1965,7 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                             "args is of invalid type");
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
@@ -2023,13 +1974,13 @@ impl_settings_connection_update2 (NMDBusObject *obj,
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
 		                     "Unsupported argument '%s'", args_name);
-		g_dbus_method_invocation_take_error (invocation, error);
+		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
 
 	settings_connection_update (self,
 	                            TRUE,
-	                            invocation,
+	                            context,
 	                            settings,
 	                            flags);
 }
@@ -2074,7 +2025,7 @@ get_modify_permission_basic (NMSettingsConnection *self)
 	 * request affects more than just the caller, require 'modify.system'.
 	 */
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
-	nm_assert (s_con);
+	g_assert (s_con);
 	if (nm_setting_connection_get_num_permissions (s_con) == 1)
 		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
 
@@ -2082,30 +2033,26 @@ get_modify_permission_basic (NMSettingsConnection *self)
 }
 
 static void
-impl_settings_connection_delete (NMDBusObject *obj,
-                                 const NMDBusInterfaceInfoExtended *interface_info,
-                                 const NMDBusMethodInfoExtended *method_info,
-                                 GDBusConnection *connection,
-                                 const char *sender,
-                                 GDBusMethodInvocation *invocation,
-                                 GVariant *parameters)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_object NMAuthSubject *subject = NULL;
+impl_settings_connection_delete (NMSettingsConnection *self,
+                                 GDBusMethodInvocation *context)
+{
+	NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 
 	if (!check_writable (NM_CONNECTION (self), &error))
-		goto err;
+		goto out_err;
 
-	subject = _new_auth_subject (invocation, &error);
-	if (!subject)
-		goto err;
+	subject = _new_auth_subject (context, &error);
+	if (subject) {
+		auth_start (self, context, subject, get_modify_permission_basic (self), delete_auth_cb, NULL);
+		g_object_unref (subject);
+	} else
+		goto out_err;
 
-	auth_start (self, invocation, subject, get_modify_permission_basic (self), delete_auth_cb, NULL);
 	return;
-err:
+out_err:
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject, error->message);
-	g_dbus_method_invocation_take_error (invocation, error);
+	g_dbus_method_invocation_take_error (context, error);
 }
 
 /*****************************************************************************/
@@ -2164,33 +2111,24 @@ dbus_get_secrets_auth_cb (NMSettingsConnection *self,
 }
 
 static void
-impl_settings_connection_get_secrets (NMDBusObject *obj,
-                                      const NMDBusInterfaceInfoExtended *interface_info,
-                                      const NMDBusMethodInfoExtended *method_info,
-                                      GDBusConnection *connection,
-                                      const char *sender,
-                                      GDBusMethodInvocation *invocation,
-                                      GVariant *parameters)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_object NMAuthSubject *subject = NULL;
+impl_settings_connection_get_secrets (NMSettingsConnection *self,
+                                      GDBusMethodInvocation *context,
+                                      const gchar *setting_name)
+{
+	NMAuthSubject *subject;
 	GError *error = NULL;
-	const char *setting_name;
-
-	subject = _new_auth_subject (invocation, &error);
-	if (!subject) {
-		g_dbus_method_invocation_take_error (invocation, error);
-		return;
-	}
-
-	g_variant_get (parameters, "(&s)", &setting_name);
 
-	auth_start (self,
-	            invocation,
-	            subject,
-	            get_modify_permission_basic (self),
-	            dbus_get_secrets_auth_cb,
-	            g_strdup (setting_name));
+	subject = _new_auth_subject (context, &error);
+	if (subject) {
+		auth_start (self,
+		            context,
+		            subject,
+		            get_modify_permission_basic (self),
+		            dbus_get_secrets_auth_cb,
+		            g_strdup (setting_name));
+		g_object_unref (subject);
+	} else
+		g_dbus_method_invocation_take_error (context, error);
 }
 
 static void
@@ -2219,7 +2157,7 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 
 	/* Tell agents to remove secrets for this connection */
 	nm_agent_manager_delete_secrets (priv->agent_mgr,
-	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
+	                                 nm_connection_get_path (NM_CONNECTION (self)),
 	                                 NM_CONNECTION (self));
 
 	nm_settings_connection_update (self,
@@ -2239,31 +2177,26 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 }
 
 static void
-impl_settings_connection_clear_secrets (NMDBusObject *obj,
-                                        const NMDBusInterfaceInfoExtended *interface_info,
-                                        const NMDBusMethodInfoExtended *method_info,
-                                        GDBusConnection *connection,
-                                        const char *sender,
-                                        GDBusMethodInvocation *invocation,
-                                        GVariant *parameters)
-{
-	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (obj);
-	gs_unref_object NMAuthSubject *subject = NULL;
+impl_settings_connection_clear_secrets (NMSettingsConnection *self,
+                                        GDBusMethodInvocation *context)
+{
+	NMAuthSubject *subject;
 	GError *error = NULL;
 
-	subject = _new_auth_subject (invocation, &error);
-	if (!subject) {
+	subject = _new_auth_subject (context, &error);
+	if (subject) {
+		auth_start (self,
+		            context,
+		            subject,
+		            get_modify_permission_basic (self),
+		            dbus_clear_secrets_auth_cb,
+		            NULL);
+		g_object_unref (subject);
+	} else {
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
 		                            FALSE, NULL, NULL, error->message);
-		g_dbus_method_invocation_take_error (invocation, error);
-		return;
+		g_dbus_method_invocation_take_error (context, error);
 	}
-	auth_start (self,
-	            invocation,
-	            subject,
-	            get_modify_permission_basic (self),
-	            dbus_clear_secrets_auth_cb,
-	            NULL);
 }
 
 /*****************************************************************************/
@@ -2283,64 +2216,55 @@ void
 nm_settings_connection_signal_remove (NMSettingsConnection *self)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	AuthData *auth_data;
 
 	if (priv->removed)
 		return;
 	priv->removed = TRUE;
-
-	while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst)))
-		nm_auth_manager_check_authorization_cancel (auth_data->call_id);
-
-	nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-	                            &interface_info_settings_connection,
-	                            &signal_info_removed,
-	                            "()");
-	g_signal_emit (self, signals[REMOVED], 0);
+	g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED);
 }
 
 gboolean
 nm_settings_connection_get_unsaved (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_UNSAVED);
 }
 
 /*****************************************************************************/
 
-NM_UTILS_FLAGS2STR_DEFINE_STATIC (_settings_connection_flags_to_string, NMSettingsConnectionIntFlags,
-	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,          "none"),
-	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,       "unsaved"),
-	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,  "nm-generated"),
-	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE,      "volatile"),
-	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE,       "visible"),
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_settings_connection_flags_to_string, NMSettingsConnectionFlags,
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NONE,          "none"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_UNSAVED,       "unsaved"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED,  "nm-generated"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,      "volatile"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VISIBLE,       "visible"),
 );
 
-NMSettingsConnectionIntFlags
+NMSettingsConnectionFlags
 nm_settings_connection_get_flags (NMSettingsConnection *self)
 {
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
 
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
 }
 
-NMSettingsConnectionIntFlags
-nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set)
+NMSettingsConnectionFlags
+nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set)
 {
 	return nm_settings_connection_set_flags_full (self,
 	                                              flags,
-	                                              set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
+	                                              set ? flags : NM_SETTINGS_CONNECTION_FLAGS_NONE);
 }
 
-NMSettingsConnectionIntFlags
+NMSettingsConnectionFlags
 nm_settings_connection_set_flags_full (NMSettingsConnection *self,
-                                       NMSettingsConnectionIntFlags mask,
-                                       NMSettingsConnectionIntFlags value)
+                                       NMSettingsConnectionFlags mask,
+                                       NMSettingsConnectionFlags value)
 {
 	NMSettingsConnectionPrivate *priv;
-	NMSettingsConnectionIntFlags old_flags;
+	NMSettingsConnectionFlags old_flags;
 
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE);
-	nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL));
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
+	nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_FLAGS_ALL));
 	nm_assert (!NM_FLAGS_ANY (value, ~mask));
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
@@ -2349,7 +2273,6 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self,
 
 	old_flags = priv->flags;
 	if (old_flags != value) {
-		gboolean notify_unsaved = FALSE;
 		char buf1[255], buf2[255];
 
 		_LOGT ("update settings-connection flags to %s (was %s)",
@@ -2357,17 +2280,9 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self,
 		       _settings_connection_flags_to_string (priv->flags, buf2, sizeof (buf2)));
 		priv->flags = value;
 		nm_assert (priv->flags == value);
-
-		if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED) != NM_FLAGS_HAS (value, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED)) {
-			g_object_freeze_notify (G_OBJECT (self));
-			_notify (self, PROP_UNSAVED);
-			notify_unsaved = TRUE;
-		}
 		_notify (self, PROP_FLAGS);
-		if (notify_unsaved)
-			g_object_thaw_notify (G_OBJECT (self));
-
-		g_signal_emit (self, signals[FLAGS_CHANGED], 0);
+		if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (value, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
+			_notify (self, PROP_UNSAVED);
 	}
 	return old_flags;
 }
@@ -2788,7 +2703,7 @@ _autoconnect_retries_set (NMSettingsConnection *self,
 	if (retries)
 		priv->autoconnect_retries_blocked_until = 0;
 	else {
-		/* NOTE: the blocked time must be identical for all connections, otherwise
+		/* XXX: the blocked time must be identical for all connections, otherwise
 		 * the tracking of resetting the retry count in NMPolicy needs adjustment
 		 * in _connection_autoconnect_retries_set() (as it would need to re-evaluate
 		 * the next-timeout everytime a connection gets blocked). */
@@ -2883,7 +2798,7 @@ gboolean
 nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
 {
 	NMSettingsConnectionPrivate *priv;
-	NMSettingsConnectionIntFlags flags;
+	NMSettingsConnectionFlags flags;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), TRUE);
 
@@ -2895,9 +2810,9 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
 		return TRUE;
 
 	flags = priv->flags;
-	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE))
+	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
 		return TRUE;
-	if (!NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE))
+	if (!NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VISIBLE))
 		return TRUE;
 
 	return FALSE;
@@ -2985,11 +2900,8 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionPrivate);
 	self->_priv = priv;
 
-	c_list_init (&self->_connections_lst);
-
 	priv->ready = TRUE;
 	c_list_init (&priv->call_ids_lst_head);
-	c_list_init (&priv->auth_lst_head);
 
 	priv->session_monitor = g_object_ref (nm_session_monitor_get ());
 	priv->session_changed_id = g_signal_connect (priv->session_monitor,
@@ -3025,9 +2937,6 @@ dispose (GObject *object)
 
 	_LOGD ("disposing");
 
-	nm_assert (c_list_is_empty (&self->_connections_lst));
-	nm_assert (c_list_is_empty (&priv->auth_lst_head));
-
 	/* Cancel in-progress secrets requests */
 	if (priv->agent_mgr) {
 		c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
@@ -3045,7 +2954,11 @@ dispose (GObject *object)
 	g_clear_object (&priv->system_secrets);
 	g_clear_object (&priv->agent_secrets);
 
-	g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy);
+	/* Cancel PolicyKit requests */
+	g_slist_free_full (priv->pending_auths, (GDestroyNotify) nm_auth_chain_unref);
+	priv->pending_auths = NULL;
+
+	g_clear_pointer (&priv->seen_bssids, (GDestroyNotify) g_hash_table_destroy);
 
 	set_visible (self, FALSE);
 
@@ -3073,8 +2986,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, nm_settings_connection_get_ready (self));
 		break;
 	case PROP_FLAGS:
-		g_value_set_uint (value,
-		                  nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK);
+		g_value_set_uint (value, nm_settings_connection_get_flags (self));
 		break;
 	case PROP_FILENAME:
 		g_value_set_string (value, nm_settings_connection_get_filename (self));
@@ -3102,120 +3014,22 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
-static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"Updated",
-);
-
-static const GDBusSignalInfo signal_info_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"Removed",
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_settings_connection = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_SETTINGS_CONNECTION,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Update",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sa{sv}}"),
-					),
-				),
-				.handle = impl_settings_connection_update,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"UpdateUnsaved",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("properties", "a{sa{sv}}"),
-					),
-				),
-				.handle = impl_settings_connection_update_unsaved,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Delete",
-				),
-				.handle = impl_settings_connection_delete,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetSettings",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"),
-					),
-				),
-				.handle = impl_settings_connection_get_settings,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetSecrets",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("setting_name", "s"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("secrets", "a{sa{sv}}"),
-					),
-				),
-				.handle = impl_settings_connection_get_secrets,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"ClearSecrets",
-				),
-				.handle = impl_settings_connection_clear_secrets,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Save",
-				),
-				.handle = impl_settings_connection_save,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"Update2",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"),
-						NM_DEFINE_GDBUS_ARG_INFO ("flags",    "u"),
-						NM_DEFINE_GDBUS_ARG_INFO ("args",     "a{sv}"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("result", "a{sv}"),
-					),
-				),
-				.handle = impl_settings_connection_update2,
-			),
-		),
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&signal_info_updated,
-			&signal_info_removed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Unsaved", "b",  NM_SETTINGS_CONNECTION_UNSAVED),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE   ("Flags",   "u",  NM_SETTINGS_CONNECTION_FLAGS),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
-nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
+nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
 
-	g_type_class_add_private (klass, sizeof (NMSettingsConnectionPrivate));
+	g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate));
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS);
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection);
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS);
 
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
-	klass->supports_secrets = supports_secrets;
+	class->supports_secrets = supports_secrets;
 
 	obj_properties[PROP_UNSAVED] =
 	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
@@ -3231,7 +3045,9 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 
 	obj_properties[PROP_FLAGS] =
 	     g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "",
-	                        0, G_MAXUINT32, 0,
+	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
+	                        NM_SETTINGS_CONNECTION_FLAGS_ALL,
+	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
 	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
@@ -3244,10 +3060,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
+
+	signals[UPDATED] =
+	    g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
+	                  G_TYPE_FROM_CLASS (class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,
+	                  NULL, NULL,
+	                  g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
+
 	/* internal signal, with an argument (gboolean by_user). */
 	signals[UPDATED_INTERNAL] =
 	    g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
-	                  G_TYPE_FROM_CLASS (klass),
+	                  G_TYPE_FROM_CLASS (class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL,
 	                  g_cclosure_marshal_VOID__BOOLEAN,
@@ -3255,20 +3081,24 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 
 	signals[REMOVED] =
 	    g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
-	                  G_TYPE_FROM_CLASS (klass),
+	                  G_TYPE_FROM_CLASS (class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
 	                  NULL, NULL,
 	                  g_cclosure_marshal_VOID__VOID,
 	                  G_TYPE_NONE, 0);
 
-	signals[FLAGS_CHANGED] =
-	    g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
-	                  G_TYPE_FROM_CLASS (klass),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
+	                                        NMDBUS_TYPE_SETTINGS_CONNECTION_SKELETON,
+	                                        "Update", impl_settings_connection_update,
+	                                        "UpdateUnsaved", impl_settings_connection_update_unsaved,
+	                                        "Delete", impl_settings_connection_delete,
+	                                        "GetSettings", impl_settings_connection_get_settings,
+	                                        "GetSecrets", impl_settings_connection_get_secrets,
+	                                        "ClearSecrets", impl_settings_connection_clear_secrets,
+	                                        "Save", impl_settings_connection_save,
+	                                        "Update2", impl_settings_connection_update2,
+	                                        NULL);
 }
 
 static void
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 58901699..29ec05dd 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -24,7 +24,7 @@
 
 #include <net/ethernet.h>
 
-#include "nm-dbus-object.h"
+#include "nm-exported-object.h"
 #include "nm-connection.h"
 
 #define NM_TYPE_SETTINGS_CONNECTION            (nm_settings_connection_get_type ())
@@ -34,11 +34,14 @@
 #define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION))
 #define NM_SETTINGS_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
 
+/* Signals */
+#define NM_SETTINGS_CONNECTION_UPDATED "updated"
 #define NM_SETTINGS_CONNECTION_REMOVED "removed"
 #define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets"
 #define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets"
+
+/* Internal signals */
 #define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal"
-#define NM_SETTINGS_CONNECTION_FLAGS_CHANGED    "flags-changed"
 
 /* Properties */
 #define NM_SETTINGS_CONNECTION_UNSAVED  "unsaved"
@@ -50,45 +53,32 @@
 
 
 /**
- * NMSettingsConnectionIntFlags:
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_NONE: no flag set
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED: the connection is not saved to disk.
- *  See also #NM_SETTINGS_CONNECTION_FLAG_UNSAVED.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED: A connection is "nm-generated" if
+ * NMSettingsConnectionFlags:
+ * @NM_SETTINGS_CONNECTION_FLAGS_NONE: no flag set
+ * @NM_SETTINGS_CONNECTION_FLAGS_UNSAVED: the connection is not saved to disk
+ * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED: A connection is "nm-generated" if
  *  it was generated by NetworkManger. If the connection gets modified or saved
  *  by the user, the flag gets cleared. A nm-generated is implicitly unsaved.
- *  See also #NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE: The connection will be deleted
+ * @NM_SETTINGS_CONNECTION_FLAGS_VOLATILE: The connection will be deleted
  *  when it disconnects. That is for in-memory connections (unsaved), which are
  *  currently active but cleanup on disconnect.
- *  See also #NM_SETTINGS_CONNECTION_FLAG_VOLATILE.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE: The connection is visible
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is
- *   internal, however, parts of it is public API as #NMSettingsConnectionFlags.
- *   This mask, are the public flags.
- * @NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags
+ * @NM_SETTINGS_CONNECTION_FLAGS_VISIBLE: The connection is visible
+ * @NM_SETTINGS_CONNECTION_FLAGS_ALL: special mask, for all known flags
  *
  * #NMSettingsConnection flags.
  **/
 typedef enum {
-	NM_SETTINGS_CONNECTION_INT_FLAGS_NONE                   = 0,
-
-	NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED                = NM_SETTINGS_CONNECTION_FLAG_UNSAVED,
-	NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED           = NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED,
-	NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE               = NM_SETTINGS_CONNECTION_FLAG_VOLATILE,
-
-	NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE                = (1LL <<  3),
+	NM_SETTINGS_CONNECTION_FLAGS_NONE                               = 0,
 
-	__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST,
+	NM_SETTINGS_CONNECTION_FLAGS_UNSAVED                            = (1LL <<  0),
+	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED                       = (1LL <<  1),
+	NM_SETTINGS_CONNECTION_FLAGS_VOLATILE                           = (1LL <<  2),
 
-	NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK          = 0
-	                                                          | NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED
-	                                                          | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED
-	                                                          | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
-	                                                          | 0,
+	NM_SETTINGS_CONNECTION_FLAGS_VISIBLE                            = (1LL <<  3),
 
-	NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1,
-} NMSettingsConnectionIntFlags;
+	__NM_SETTINGS_CONNECTION_FLAGS_LAST,
+	NM_SETTINGS_CONNECTION_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_FLAGS_LAST - 1) << 1) - 1,
+} NMSettingsConnectionFlags;
 
 typedef enum {
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE                       = 0,
@@ -116,13 +106,12 @@ typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
 struct _NMSettingsConnectionPrivate;
 
 struct _NMSettingsConnection {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	struct _NMSettingsConnectionPrivate *_priv;
-	CList _connections_lst;
 };
 
 struct _NMSettingsConnectionClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 
 	gboolean (*commit_changes) (NMSettingsConnection *self,
 	                            NMConnection *new_connection,
@@ -210,9 +199,9 @@ void nm_settings_connection_signal_remove (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
 
-NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
-NMSettingsConnectionIntFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set);
-NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value);
+NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
+NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set);
+NMSettingsConnectionFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionFlags mask, NMSettingsConnectionFlags value);
 
 int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab);
 int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 2c6b7101..8e3fc582 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -62,12 +62,10 @@
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 
-#include "nm-utils/nm-c-list.h"
-#include "nm-dbus-object.h"
 #include "devices/nm-device-ethernet.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "nm-auth-utils.h"
 #include "nm-auth-subject.h"
 #include "nm-session-monitor.h"
@@ -79,6 +77,8 @@
 #include "nm-dispatcher.h"
 #include "nm-hostname-manager.h"
 
+#include "introspection/org.freedesktop.NetworkManager.Settings.h"
+
 /*****************************************************************************/
 
 #define EXPORT(sym) void * __export_##sym = &sym;
@@ -107,6 +107,7 @@ enum {
 	CONNECTION_UPDATED,
 	CONNECTION_REMOVED,
 	CONNECTION_FLAGS_CHANGED,
+	NEW_CONNECTION, /* exported, not used internally */
 	LAST_SIGNAL
 };
 
@@ -120,33 +121,29 @@ typedef struct {
 	GSList *auths;
 
 	GSList *plugins;
-
-	CList connections_lst_head;
-
+	gboolean connections_loaded;
+	GHashTable *connections;
 	NMSettingsConnection **connections_cached_list;
 	GSList *unmanaged_specs;
 	GSList *unrecognized_specs;
 
-	NMHostnameManager *hostname_manager;
-
-	guint connections_len;
+	gboolean started;
+	gboolean startup_complete;
 
-	bool started:1;
-	bool startup_complete:1;
-	bool connections_loaded:1;
+	NMHostnameManager *hostname_manager;
 
 } NMSettingsPrivate;
 
 struct _NMSettings {
-	NMDBusObject parent;
+	NMExportedObject parent;
 	NMSettingsPrivate _priv;
 };
 
 struct _NMSettingsClass {
-	NMDBusObjectClass parent;
+	NMExportedObjectClass parent;
 };
 
-G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
+G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT);
 
 #define NM_SETTINGS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettings, NM_IS_SETTINGS)
 
@@ -157,10 +154,6 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT);
 
 /*****************************************************************************/
 
-static const NMDBusInterfaceInfoExtended interface_info_settings;
-static const GDBusSignalInfo signal_info_new_connection;
-static const GDBusSignalInfo signal_info_connection_removed;
-
 static void claim_connection (NMSettings *self,
                               NMSettingsConnection *connection);
 
@@ -171,29 +164,27 @@ static void connection_ready_changed (NMSettingsConnection *conn,
                                       GParamSpec *pspec,
                                       gpointer user_data);
 
-static void default_wired_clear_tag (NMSettings *self,
-                                     NMDevice *device,
-                                     NMSettingsConnection *connection,
-                                     gboolean add_to_no_auto_default);
-
 /*****************************************************************************/
 
 static void
 check_startup_complete (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GHashTableIter iter;
 	NMSettingsConnection *conn;
 
 	if (priv->startup_complete)
 		return;
 
-	c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) {
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn)) {
 		if (!nm_settings_connection_get_ready (conn))
 			return;
 	}
 
 	/* the connection_ready_changed signal handler is no longer needed. */
-	c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst)
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn))
 		g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_ready_changed), self);
 
 	priv->startup_complete = TRUE;
@@ -256,25 +247,43 @@ load_connections (NMSettings *self)
 	unrecognized_specs_changed (NULL, self);
 }
 
+void
+nm_settings_for_each_connection (NMSettings *self,
+                                 NMSettingsForEachFunc for_each_func,
+                                 gpointer user_data)
+{
+	NMSettingsPrivate *priv;
+	GHashTableIter iter;
+	gpointer data;
+
+	g_return_if_fail (NM_IS_SETTINGS (self));
+	g_return_if_fail (for_each_func != NULL);
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data))
+		for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data);
+}
+
 static void
-impl_settings_list_connections (NMDBusObject *obj,
-                                const NMDBusInterfaceInfoExtended *interface_info,
-                                const NMDBusMethodInfoExtended *method_info,
-                                GDBusConnection *dbus_connection,
-                                const char *sender,
-                                GDBusMethodInvocation *invocation,
-                                GVariant *parameters)
+impl_settings_list_connections (NMSettings *self,
+                                GDBusMethodInvocation *context)
 {
-	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	gs_free const char **strv = NULL;
-
-	strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
-	                                          priv->connections_len,
-	                                          G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
-	                                          TRUE);
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(^ao)", strv));
+	GPtrArray *connections;
+	GHashTableIter iter;
+	gpointer key;
+
+	connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, &key, NULL))
+		g_ptr_array_add (connections, key);
+	g_ptr_array_add (connections, NULL);
+
+	g_dbus_method_invocation_return_value (context,
+	                                       g_variant_new ("(^ao)", connections->pdata));
+	g_ptr_array_unref (connections);
 }
 
 NMSettingsConnection *
@@ -282,14 +291,16 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 {
 	NMSettingsPrivate *priv;
 	NMSettingsConnection *candidate;
+	GHashTableIter iter;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
-		if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate)))
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
+		if (g_strcmp0 (uuid, nm_settings_connection_get_uuid (candidate)) == 0)
 			return candidate;
 	}
 
@@ -297,21 +308,14 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
 }
 
 static void
-impl_settings_get_connection_by_uuid (NMDBusObject *obj,
-                                      const NMDBusInterfaceInfoExtended *interface_info,
-                                      const NMDBusMethodInfoExtended *method_info,
-                                      GDBusConnection *dbus_connection,
-                                      const char *sender,
-                                      GDBusMethodInvocation *invocation,
-                                      GVariant *parameters)
+impl_settings_get_connection_by_uuid (NMSettings *self,
+                                      GDBusMethodInvocation *context,
+                                      const char *uuid)
 {
-	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsConnection *connection = NULL;
-	gs_unref_object NMAuthSubject *subject = NULL;
+	NMAuthSubject *subject = NULL;
 	GError *error = NULL;
-	const char *uuid;
-
-	g_variant_get (parameters, "(&s)", &uuid);
+	char *error_desc = NULL;
 
 	connection = nm_settings_get_connection_by_uuid (self, uuid);
 	if (!connection) {
@@ -321,7 +325,7 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj,
 		goto error;
 	}
 
-	subject = nm_auth_subject_new_unix_process_from_context (invocation);
+	subject = nm_auth_subject_new_unix_process_from_context (context);
 	if (!subject) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -329,40 +333,26 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj,
 		goto error;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
-	                                          subject,
-	                                          NM_SETTINGS_ERROR,
-	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
-	                                          &error))
+	if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
+	                                subject,
+	                                &error_desc)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
 		goto error;
+	}
 
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(o)",
-	                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))));
+	g_clear_object (&subject);
+	g_dbus_method_invocation_return_value (
+		context,
+		g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
 	return;
 
 error:
-	g_dbus_method_invocation_take_error (invocation, error);
-}
-
-static void
-_clear_connections_cached_list (NMSettingsPrivate *priv)
-{
-	if (!priv->connections_cached_list)
-		return;
-
-	nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list));
-
-#if NM_MORE_ASSERTS
-	/* set the pointer to a bogus value. This makes it more apparent
-	 * if somebody has a reference to the cached list and still uses
-	 * it. That is a bug, this code just tries to make it blow up
-	 * more eagerly. */
-	memset (priv->connections_cached_list,
-	        0xdeaddead,
-	        sizeof (NMSettingsConnection *) * (priv->connections_len + 1));
-#endif
-	nm_clear_g_free (&priv->connections_cached_list);
+	g_assert (error);
+	g_dbus_method_invocation_take_error (context, error);
+	g_clear_object (&subject);
 }
 
 /**
@@ -380,33 +370,37 @@ _clear_connections_cached_list (NMSettingsPrivate *priv)
 NMSettingsConnection *const*
 nm_settings_get_connections (NMSettings *self, guint *out_len)
 {
+	GHashTableIter iter;
 	NMSettingsPrivate *priv;
+	guint l, i;
 	NMSettingsConnection **v;
 	NMSettingsConnection *con;
-	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head));
+	if (G_LIKELY (priv->connections_cached_list)) {
+		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
+		return priv->connections_cached_list;
+	}
 
-	if (G_UNLIKELY (!priv->connections_cached_list)) {
-		v = g_new (NMSettingsConnection *, priv->connections_len + 1);
+	l = g_hash_table_size (priv->connections);
 
-		i = 0;
-		c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) {
-			nm_assert (i < priv->connections_len);
-			v[i++] = con;
-		}
-		nm_assert (i == priv->connections_len);
-		v[i] = NULL;
+	v = g_new (NMSettingsConnection *, (gsize) l + 1);
 
-		priv->connections_cached_list = v;
+	i = 0;
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) {
+		nm_assert (i < l);
+		v[i++] = con;
 	}
+	nm_assert (i == l);
+	v[i] = NULL;
 
-	NM_SET_OUT (out_len, priv->connections_len);
-	return priv->connections_cached_list;
+	NM_SET_OUT (out_len, l);
+	priv->connections_cached_list = v;
+	return v;
 }
 
 /**
@@ -473,41 +467,28 @@ NMSettingsConnection *
 nm_settings_get_connection_by_path (NMSettings *self, const char *path)
 {
 	NMSettingsPrivate *priv;
-	NMSettingsConnection *connection;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
-	g_return_val_if_fail (path, NULL);
+	g_return_val_if_fail (path != NULL, NULL);
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	connection = (NMSettingsConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                                     path);
-	if (   !connection
-	    || !NM_IS_SETTINGS_CONNECTION (connection))
-		return NULL;
-
-	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
-	return connection;
+	return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path);
 }
 
 gboolean
 nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection)
 {
-	NMSettingsConnection *candidate = NULL;
-	const char *path;
-
-	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer data;
 
-	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
-	if (path)
-		candidate = nm_settings_get_connection_by_path (self, path);
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data))
+		if (data == connection)
+			return TRUE;
 
-	nm_assert (!candidate || candidate == connection);
-	nm_assert (!!candidate == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head,
-	                                                    connection,
-	                                                    _connections_lst));
-	return !!candidate;
+	return FALSE;
 }
 
 const GSList *
@@ -519,7 +500,7 @@ nm_settings_get_unmanaged_specs (NMSettings *self)
 }
 
 static NMSettingsPlugin *
-get_plugin (NMSettings *self, NMSettingsPluginCapabilities capability)
+get_plugin (NMSettings *self, guint32 capability)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
@@ -696,8 +677,8 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) {
-			_LOGW ("skipping deprecated plugin %s", pname);
+		if (!strcmp (pname, "ifcfg-suse")) {
+			_LOGW ("skipping deprecated plugin ifcfg-suse");
 			continue;
 		}
 
@@ -825,6 +806,7 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer
 
 static void
 connection_flags_changed (NMSettingsConnection *connection,
+                          GParamSpec *pspec,
                           gpointer user_data)
 {
 	g_signal_emit (NM_SETTINGS (user_data),
@@ -838,20 +820,11 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 {
 	NMSettings *self = NM_SETTINGS (user_data);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	NMDevice *device;
+	const char *cpath = nm_connection_get_path (NM_CONNECTION (connection));
 
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
-	g_return_if_fail (!c_list_is_empty (&connection->_connections_lst));
-	nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
-
-	/* When the default wired connection is removed (either deleted or saved to
-	 * a new persistent connection by a plugin), write the MAC address of the
-	 * wired device to the config file and don't create a new default wired
-	 * connection for that device again.
-	 */
-	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
-	if (device)
-		default_wired_clear_tag (self, device, connection, TRUE);
+	if (!g_hash_table_lookup (priv->connections, cpath))
+		g_return_if_reached ();
+	g_object_ref (connection);
 
 	/* Disconnect signal handlers, as plugins might still keep references
 	 * to the connection (and thus the signal handlers would still be live)
@@ -863,30 +836,23 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
 	if (!priv->startup_complete)
 		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
+	g_object_unref (self);
 
 	/* Forget about the connection internally */
-	_clear_connections_cached_list (priv);
-	priv->connections_len--;
-	c_list_unlink (&connection->_connections_lst);
-
-	if (priv->connections_loaded) {
-		_notify (self, PROP_CONNECTIONS);
+	g_hash_table_remove (priv->connections, (gpointer) cpath);
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
-		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-		                            &interface_info_settings,
-		                            &signal_info_connection_removed,
-		                            "(o)",
-		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
-	}
+	/* Notify D-Bus */
+	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
 
-	nm_dbus_object_unexport (NM_DBUS_OBJECT (connection));
+	/* Re-emit for listeners like NMPolicy */
+	_notify (self, PROP_CONNECTIONS);
+	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection)))
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection));
 
-	if (priv->connections_loaded)
-		g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
+	check_startup_complete (self);
 
 	g_object_unref (connection);
-
-	check_startup_complete (self);
 }
 
 #define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
@@ -934,16 +900,19 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GError *error = NULL;
+	GHashTableIter iter;
+	gpointer data;
 	const char *path;
 	NMSettingsConnection *existing;
 
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
-	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (connection)));
+	g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
 
-	/* prevent duplicates */
-	if (!c_list_is_empty (&connection->_connections_lst)) {
-		nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
-		return;
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		/* prevent duplicates */
+		if (data == connection)
+			return;
 	}
 
 	if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) {
@@ -975,19 +944,20 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	/* Read seen-bssids from look-aside file and put it into the connection's data */
 	nm_settings_connection_read_and_fill_seen_bssids (connection);
 
-	/* Ensure its initial visibility is up-to-date */
+	/* Ensure it's initial visibility is up-to-date */
 	nm_settings_connection_recheck_visibility (connection);
 
 	/* Evil openconnect migration hack */
 	openconnect_migrate_hack (NM_CONNECTION (connection));
 
+	g_object_ref (self);
 	/* This one unexports the connection, it needs to run late to give the active
 	 * connection a chance to deal with its reference to this settings connection. */
 	g_signal_connect_after (connection, NM_SETTINGS_CONNECTION_REMOVED,
 	                        G_CALLBACK (connection_removed), self);
 	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (connection_updated), self);
-	g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
+	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FLAGS,
 	                  G_CALLBACK (connection_flags_changed),
 	                  self);
 	if (!priv->startup_complete) {
@@ -996,29 +966,28 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 		                  self);
 	}
 
-	_clear_connections_cached_list (priv);
+	/* Export the connection over D-Bus */
+	g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
+	path = nm_exported_object_export (NM_EXPORTED_OBJECT (connection));
+	nm_connection_set_path (NM_CONNECTION (connection), path);
 
-	g_object_ref (connection);
-	priv->connections_len++;
-	c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst);
-
-	path = nm_dbus_object_export (NM_DBUS_OBJECT (connection));
+	g_hash_table_insert (priv->connections,
+	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)),
+	                     g_object_ref (connection));
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
-	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ",
-	                              path);
+	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ");
 
 	/* Only emit the individual connection-added signal after connections
 	 * have been initially loaded.
 	 */
 	if (priv->connections_loaded) {
-		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-		                            &interface_info_settings,
-		                            &signal_info_new_connection,
-		                            "(o)",
-		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
-
+		/* Internal added signal */
 		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
 		_notify (self, PROP_CONNECTIONS);
+
+		/* Exported D-Bus signal */
+		g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
 	}
 
 	nm_settings_connection_added (connection);
@@ -1066,14 +1035,14 @@ nm_settings_add_connection (NMSettings *self,
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 	NMSettingsConnection *added = NULL;
-	NMSettingsConnection *candidate = NULL;
-	const char *uuid;
-
-	uuid = nm_connection_get_uuid (connection);
+	GHashTableIter citer;
+	NMConnection *candidate = NULL;
 
 	/* Make sure a connection with this UUID doesn't already exist */
-	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
-		if (nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate)))) {
+	g_hash_table_iter_init (&citer, priv->connections);
+	while (g_hash_table_iter_next (&citer, NULL, (gpointer *) &candidate)) {
+		if (g_strcmp0 (nm_connection_get_uuid (connection),
+		               nm_connection_get_uuid (candidate)) == 0) {
 			g_set_error_literal (error,
 			                     NM_SETTINGS_ERROR,
 			                     NM_SETTINGS_ERROR_UUID_EXISTS,
@@ -1130,7 +1099,7 @@ send_agent_owned_secrets (NMSettings *self,
                           NMAuthSubject *subject)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	gs_unref_object NMConnection *for_agent = NULL;
+	NMConnection *for_agent;
 
 	/* Dupe the connection so we can clear out non-agent-owned secrets,
 	 * as agent-owned secrets are the only ones we send back to be saved.
@@ -1141,9 +1110,10 @@ send_agent_owned_secrets (NMSettings *self,
 	                                        secrets_filter_cb,
 	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
 	nm_agent_manager_save_secrets (priv->agent_mgr,
-	                               nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
+	                               nm_connection_get_path (NM_CONNECTION (connection)),
 	                               for_agent,
 	                               subject);
+	g_object_unref (for_agent);
 }
 
 static void
@@ -1200,7 +1170,7 @@ pk_add_cb (NMAuthChain *chain,
 		send_agent_owned_secrets (self, added, subject);
 
 	g_clear_error (&error);
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 /* FIXME: remove if/when kernel supports adhoc wpa */
@@ -1248,6 +1218,7 @@ nm_settings_add_connection_dbus (NMSettings *self,
 	NMAuthSubject *subject = NULL;
 	NMAuthChain *chain;
 	GError *error = NULL, *tmp_error = NULL;
+	char *error_desc = NULL;
 	const char *perm;
 
 	g_return_if_fail (connection != NULL);
@@ -1290,12 +1261,18 @@ nm_settings_add_connection_dbus (NMSettings *self,
 		goto done;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (connection,
-	                                          subject,
-	                                          NM_SETTINGS_ERROR,
-	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
-	                                          &error))
+	/* Ensure the caller's username exists in the connection's permissions,
+	 * or that the permissions is empty (ie, visible by everyone).
+	 */
+	if (!nm_auth_is_subject_in_acl (connection,
+	                                subject,
+	                                &error_desc)) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		g_free (error_desc);
 		goto done;
+	}
 
 	/* If the caller is the only user in the connection's permissions, then
 	 * we use the 'modify.own' permission instead of 'modify.system'.  If the
@@ -1335,30 +1312,30 @@ done:
 }
 
 static void
-settings_add_connection_add_cb (NMSettings *self,
-                                NMSettingsConnection *connection,
-                                GError *error,
-                                GDBusMethodInvocation *context,
-                                NMAuthSubject *subject,
-                                gpointer user_data)
+impl_settings_add_connection_add_cb (NMSettings *self,
+                                     NMSettingsConnection *connection,
+                                     GError *error,
+                                     GDBusMethodInvocation *context,
+                                     NMAuthSubject *subject,
+                                     gpointer user_data)
 {
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message);
 	} else {
-		g_dbus_method_invocation_return_value (context,
-		                                       g_variant_new ("(o)",
-		                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))));
+		g_dbus_method_invocation_return_value (
+		    context,
+		    g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL,
 		                            subject, NULL);
 	}
 }
 
 static void
-settings_add_connection_helper (NMSettings *self,
-                                GDBusMethodInvocation *context,
-                                GVariant *settings,
-                                gboolean save_to_disk)
+impl_settings_add_connection_helper (NMSettings *self,
+                                     GDBusMethodInvocation *context,
+                                     GVariant *settings,
+                                     gboolean save_to_disk)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	GError *error = NULL;
@@ -1378,111 +1355,77 @@ settings_add_connection_helper (NMSettings *self,
 	                                 connection,
 	                                 save_to_disk,
 	                                 context,
-	                                 settings_add_connection_add_cb,
+	                                 impl_settings_add_connection_add_cb,
 	                                 NULL);
 }
 
 static void
-impl_settings_add_connection (NMDBusObject *obj,
-                              const NMDBusInterfaceInfoExtended *interface_info,
-                              const NMDBusMethodInfoExtended *method_info,
-                              GDBusConnection *connection,
-                              const char *sender,
-                              GDBusMethodInvocation *invocation,
-                              GVariant *parameters)
+impl_settings_add_connection (NMSettings *self,
+                              GDBusMethodInvocation *context,
+                              GVariant *settings)
 {
-	NMSettings *self = NM_SETTINGS (obj);
-	gs_unref_variant GVariant *settings = NULL;
-
-	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
-	settings_add_connection_helper (self, invocation, settings, TRUE);
+	impl_settings_add_connection_helper (self, context, settings, TRUE);
 }
 
 static void
-impl_settings_add_connection_unsaved (NMDBusObject *obj,
-                                      const NMDBusInterfaceInfoExtended *interface_info,
-                                      const NMDBusMethodInfoExtended *method_info,
-                                      GDBusConnection *connection,
-                                      const char *sender,
-                                      GDBusMethodInvocation *invocation,
-                                      GVariant *parameters)
+impl_settings_add_connection_unsaved (NMSettings *self,
+                                      GDBusMethodInvocation *context,
+                                      GVariant *settings)
 {
-	NMSettings *self = NM_SETTINGS (obj);
-	gs_unref_variant GVariant *settings = NULL;
-
-	g_variant_get (parameters, "(@a{sa{sv}})", &settings);
-	settings_add_connection_helper (self, invocation, settings, FALSE);
+	impl_settings_add_connection_helper (self, context, settings, FALSE);
 }
 
 static void
-impl_settings_load_connections (NMDBusObject *obj,
-                                const NMDBusInterfaceInfoExtended *interface_info,
-                                const NMDBusMethodInfoExtended *method_info,
-                                GDBusConnection *connection,
-                                const char *sender,
-                                GDBusMethodInvocation *invocation,
-                                GVariant *parameters)
+impl_settings_load_connections (NMSettings *self,
+                                GDBusMethodInvocation *context,
+                                char **filenames)
 {
-	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	gs_unref_ptrarray GPtrArray *failures = NULL;
+	GPtrArray *failures;
 	GSList *iter;
-	guint i;
-	gs_free const char **filenames = NULL;
-
-	g_variant_get (parameters, "(^a&s)", &filenames);
+	int i;
 
 	/* The permission is already enforced by the D-Bus daemon, but we ensure
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj),
-	                                 invocation,
-	                                 G_MAXULONG,
-	                                 NM_SETTINGS_ERROR,
-	                                 NM_SETTINGS_ERROR_PERMISSION_DENIED))
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_SETTINGS_ERROR,
+	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
-	if (filenames) {
-		for (i = 0; filenames[i]; i++) {
-			for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
-				NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
+	failures = g_ptr_array_new ();
 
-				if (nm_settings_plugin_load_connection (plugin, filenames[i]))
-					break;
-			}
+	for (i = 0; filenames[i]; i++) {
+		for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+			NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
 
-			if (!iter) {
-				if (!g_path_is_absolute (filenames[i]))
-					_LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
-				if (!failures)
-					failures = g_ptr_array_new ();
-				g_ptr_array_add (failures, (char *) filenames[i]);
-			}
+			if (nm_settings_plugin_load_connection (plugin, filenames[i]))
+				break;
 		}
-	}
 
-	if (failures)
-		g_ptr_array_add (failures, NULL);
+		if (!iter) {
+			if (!g_path_is_absolute (filenames[i]))
+				_LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
+			g_ptr_array_add (failures, (char *) filenames[i]);
+		}
+	}
 
-	g_dbus_method_invocation_return_value (invocation,
-	                                       g_variant_new ("(b^as)",
-	                                                      (gboolean) (!!failures),
-	                                                      failures
-	                                                        ? (const char **) failures->pdata
-	                                                        : NM_PTRARRAY_EMPTY (const char *)));
+	g_ptr_array_add (failures, NULL);
+	g_dbus_method_invocation_return_value (
+		context,
+		g_variant_new ("(b^as)",
+		               failures->len == 1,
+		               failures->pdata));
+	g_ptr_array_unref (failures);
 }
 
 static void
-impl_settings_reload_connections (NMDBusObject *obj,
-                                  const NMDBusInterfaceInfoExtended *interface_info,
-                                  const NMDBusMethodInfoExtended *method_info,
-                                  GDBusConnection *connection,
-                                  const char *sender,
-                                  GDBusMethodInvocation *invocation,
-                                  GVariant *parameters)
+impl_settings_reload_connections (NMSettings *self,
+                                  GDBusMethodInvocation *context)
 {
-	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
@@ -1490,11 +1433,11 @@ impl_settings_reload_connections (NMDBusObject *obj,
 	 * that the caller is still alive so that clients are forced to wait and
 	 * we'll be able to switch to polkit without breaking behavior.
 	 */
-	if (!nm_dbus_manager_ensure_uid (nm_dbus_object_get_manager (obj),
-	                                 invocation,
-	                                 G_MAXULONG,
-	                                 NM_SETTINGS_ERROR,
-	                                 NM_SETTINGS_ERROR_PERMISSION_DENIED))
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_SETTINGS_ERROR,
+	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
@@ -1503,7 +1446,7 @@ impl_settings_reload_connections (NMDBusObject *obj,
 		nm_settings_plugin_reload_connections (plugin);
 	}
 
-	g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
+	g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE));
 }
 
 /*****************************************************************************/
@@ -1551,46 +1494,41 @@ pk_hostname_cb (NMAuthChain *chain,
 	else
 		g_dbus_method_invocation_return_value (context, NULL);
 
-	nm_auth_chain_destroy (chain);
+	nm_auth_chain_unref (chain);
 }
 
 static void
-impl_settings_save_hostname (NMDBusObject *obj,
-                             const NMDBusInterfaceInfoExtended *interface_info,
-                             const NMDBusMethodInfoExtended *method_info,
-                             GDBusConnection *connection,
-                             const char *sender,
-                             GDBusMethodInvocation *invocation,
-                             GVariant *parameters)
+impl_settings_save_hostname (NMSettings *self,
+                             GDBusMethodInvocation *context,
+                             const char *hostname)
 {
-	NMSettings *self = NM_SETTINGS (obj);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	NMAuthChain *chain;
-	const char *hostname;
-
-	g_variant_get (parameters, "(&s)", &hostname);
+	GError *error = NULL;
 
 	/* Minimal validation of the hostname */
 	if (!nm_hostname_manager_validate_hostname (hostname)) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_SETTINGS_ERROR,
-		                                               NM_SETTINGS_ERROR_INVALID_HOSTNAME,
-		                                               "The hostname was too long or contained invalid characters.");
-		return;
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_INVALID_HOSTNAME,
+		                             "The hostname was too long or contained invalid characters.");
+		goto done;
 	}
 
-	chain = nm_auth_chain_new_context (invocation, pk_hostname_cb, self);
+	chain = nm_auth_chain_new_context (context, pk_hostname_cb, self);
 	if (!chain) {
-		g_dbus_method_invocation_return_error_literal (invocation,
-		                                               NM_SETTINGS_ERROR,
-		                                               NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                                               "Unable to authenticate the request.");
-		return;
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate the request.");
+		goto done;
 	}
 
 	priv->auths = g_slist_append (priv->auths, chain);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE);
 	nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free);
+
+done:
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
 }
 
 /*****************************************************************************/
@@ -1599,24 +1537,27 @@ static gboolean
 have_connection_for_device (NMSettings *self, NMDevice *device)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GHashTableIter iter;
+	gpointer data;
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	const char *setting_hwaddr;
 	const char *perm_hw_addr;
-	NMSettingsConnection *connection;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
 
 	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 
 	/* Find a wired connection locked to the given MAC address, if any */
-	c_list_for_each_entry (connection, &priv->connections_lst_head, _connections_lst) {
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		NMConnection *connection = NM_CONNECTION (data);
 		const char *ctype, *iface;
 
-		if (!nm_device_check_connection_compatible (device, NM_CONNECTION (connection)))
+		if (!nm_device_check_connection_compatible (device, connection))
 			continue;
 
-		s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+		s_con = nm_connection_get_setting_connection (connection);
 
 		iface = nm_setting_connection_get_interface_name (s_con);
 		if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
@@ -1627,7 +1568,7 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		    && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
 			continue;
 
-		s_wired = nm_connection_get_setting_wired (NM_CONNECTION (connection));
+		s_wired = nm_connection_get_setting_wired (connection);
 
 		if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
 			/* No wired setting; therefore the PPPoE connection applies to any device */
@@ -1655,6 +1596,26 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	return FALSE;
 }
 
+static void default_wired_clear_tag (NMSettings *self,
+                                     NMDevice *device,
+                                     NMSettingsConnection *connection,
+                                     gboolean add_to_no_auto_default);
+
+static void
+default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
+{
+	NMDevice *device;
+
+	/* When the default wired connection is removed (either deleted or saved to
+	 * a new persistent connection by a plugin), write the MAC address of the
+	 * wired device to the config file and don't create a new default wired
+	 * connection for that device again.
+	 */
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
+	if (device)
+		default_wired_clear_tag (self, device, connection, TRUE);
+}
+
 static void
 default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self)
 {
@@ -1687,6 +1648,7 @@ default_wired_clear_tag (NMSettings *self,
 	g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
 	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
 
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
 
 	if (add_to_no_auto_default)
@@ -1738,6 +1700,8 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 
 	g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
+	g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
+	                  G_CALLBACK (default_wired_connection_removed_cb), self);
 
 	_LOGI ("(%s): created default wired connection '%s'",
 	       nm_device_get_iface (device),
@@ -1810,8 +1774,10 @@ nm_settings_start (NMSettings *self, GError **error)
 	/* Load the plugins; fail if a plugin is not found. */
 	plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE);
 
-	if (!load_plugins (self, (const char **) plugins, error))
+	if (!load_plugins (self, (const char **) plugins, error)) {
+		g_object_unref (self);
 		return FALSE;
+	}
 
 	load_connections (self);
 	check_startup_complete (self);
@@ -1836,19 +1802,18 @@ get_property (GObject *object, guint prop_id,
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	const GSList *specs, *iter;
-	guint i;
-	char **strvs;
-	const char **strv;
+	GHashTableIter citer;
+	GPtrArray *array;
+	const char *path;
 
 	switch (prop_id) {
 	case PROP_UNMANAGED_SPECS:
+		array = g_ptr_array_new ();
 		specs = nm_settings_get_unmanaged_specs (self);
-		strvs = g_new (char *, g_slist_length ((GSList *) specs) + 1);
-		i = 0;
-		for (iter = specs; iter; iter = iter->next)
-			strvs[i++] = g_strdup (iter->data);
-		strvs[i] = NULL;
-		g_value_take_boxed (value, strvs);
+		for (iter = specs; iter; iter = g_slist_next (iter))
+			g_ptr_array_add (array, g_strdup (iter->data));
+		g_ptr_array_add (array, NULL);
+		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
 		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value,
@@ -1860,14 +1825,12 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS));
 		break;
 	case PROP_CONNECTIONS:
-		if (priv->connections_loaded) {
-			strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head,
-			                                          priv->connections_len,
-			                                          G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst),
-			                                          TRUE);
-			g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
-		} else
-			g_value_set_boxed (value, NULL);
+		array = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
+		g_hash_table_iter_init (&citer, priv->connections);
+		while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL))
+			g_ptr_array_add (array, g_strdup (path));
+		g_ptr_array_add (array, NULL);
+		g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
 		break;
 	case PROP_STARTUP_COMPLETE:
 		g_value_set_boolean (value, nm_settings_get_startup_complete (self));
@@ -1885,7 +1848,7 @@ nm_settings_init (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	c_list_init (&priv->connections_lst_head);
+	priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
 
 	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
 	priv->config = g_object_ref (nm_config_get ());
@@ -1903,9 +1866,11 @@ dispose (GObject *object)
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy);
+	g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auths = NULL;
 
+	g_object_unref (priv->agent_mgr);
+
 	if (priv->hostname_manager) {
 		g_signal_handlers_disconnect_by_func (priv->hostname_manager,
 		                                      G_CALLBACK (_hostname_changed_cb),
@@ -1922,139 +1887,26 @@ finalize (GObject *object)
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	_clear_connections_cached_list (priv);
-
-	nm_assert (c_list_is_empty (&priv->connections_lst_head));
+	g_hash_table_destroy (priv->connections);
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
 	g_slist_free_full (priv->unmanaged_specs, g_free);
 	g_slist_free_full (priv->unrecognized_specs, g_free);
 
 	g_slist_free_full (priv->plugins, g_object_unref);
 
-	g_clear_object (&priv->agent_mgr);
-
 	g_clear_object (&priv->config);
 
 	G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
 }
 
-static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"NewConnection",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
-	),
-);
-
-static const GDBusSignalInfo signal_info_connection_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"ConnectionRemoved",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
-	),
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_settings = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_SETTINGS,
-		.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"ListConnections",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connections", "ao"),
-					),
-				),
-				.handle = impl_settings_list_connections,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"GetConnectionByUuid",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection", "o"),
-					),
-				),
-				.handle = impl_settings_get_connection_by_uuid,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"AddConnection",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
-					),
-				),
-				.handle = impl_settings_add_connection,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"AddConnectionUnsaved",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
-					),
-				),
-				.handle = impl_settings_add_connection_unsaved,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"LoadConnections",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("filenames", "as"),
-					),
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("status", "b"),
-						NM_DEFINE_GDBUS_ARG_INFO ("failures", "as"),
-					),
-				),
-				.handle = impl_settings_load_connections,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"ReloadConnections",
-					.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("status", "b"),
-					),
-				),
-				.handle = impl_settings_reload_connections,
-			),
-			NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
-				NM_DEFINE_GDBUS_METHOD_INFO_INIT (
-					"SaveHostname",
-					.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-						NM_DEFINE_GDBUS_ARG_INFO ("hostname", "s"),
-					),
-				),
-				.handle = impl_settings_save_hostname,
-			),
-		),
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&signal_info_new_connection,
-			&signal_info_connection_removed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connections", "ao", NM_SETTINGS_CONNECTIONS),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Hostname",    "s",  NM_SETTINGS_HOSTNAME),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CanModify",   "b",  NM_SETTINGS_CAN_MODIFY),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_settings_class_init (NMSettingsClass *class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
 
-	dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC (NM_DBUS_PATH_SETTINGS);
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings);
+	exported_object_class->export_path = NM_DBUS_PATH_SETTINGS;
 
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
@@ -2123,4 +1975,23 @@ nm_settings_class_init (NMSettingsClass *class)
 	                  0, NULL, NULL,
 	                  g_cclosure_marshal_VOID__OBJECT,
 	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
+
+	signals[NEW_CONNECTION] =
+	    g_signal_new ("new-connection",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
+	                  g_cclosure_marshal_VOID__OBJECT,
+	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
+	                                        NMDBUS_TYPE_SETTINGS_SKELETON,
+	                                        "ListConnections", impl_settings_list_connections,
+	                                        "GetConnectionByUuid", impl_settings_get_connection_by_uuid,
+	                                        "AddConnection", impl_settings_add_connection,
+	                                        "AddConnectionUnsaved", impl_settings_add_connection_unsaved,
+	                                        "LoadConnections", impl_settings_load_connections,
+	                                        "ReloadConnections", impl_settings_reload_connections,
+	                                        "SaveHostname", impl_settings_save_hostname,
+	                                        NULL);
 }
+
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 7d56f7b4..0ecffb70 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -28,6 +28,8 @@
 
 #include "nm-connection.h"
 
+#include "nm-exported-object.h"
+
 #define NM_TYPE_SETTINGS            (nm_settings_get_type ())
 #define NM_SETTINGS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings))
 #define NM_SETTINGS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SETTINGS, NMSettingsClass))
@@ -70,6 +72,14 @@ NMSettings *nm_settings_get (void);
 NMSettings *nm_settings_new (void);
 gboolean nm_settings_start (NMSettings *self, GError **error);
 
+typedef void (*NMSettingsForEachFunc) (NMSettings *settings,
+                                       NMSettingsConnection *connection,
+                                       gpointer user_data);
+
+void nm_settings_for_each_connection (NMSettings *settings,
+                                      NMSettingsForEachFunc for_each_func,
+                                      gpointer user_data);
+
 typedef void (*NMSettingsAddCallback) (NMSettings *settings,
                                        NMSettingsConnection *connection,
                                        GError *error,
diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build
deleted file mode 100644
index da9f5566..00000000
--- a/src/settings/plugins/ibft/meson.build
+++ /dev/null
@@ -1,48 +0,0 @@
-name = 'nm-settings-plugin-ibft'
-
-cflags = [
-  '-DSBINDIR="@0@"'.format(nm_sbindir),
-  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-]
-
-libnms_ibft_core = static_library(
-  'nms-ibft-core',
-  'nms-ibft-reader.c',
-  dependencies: nm_dep,
-  c_args: cflags
-)
-
-sources = files(
-  'nms-ibft-connection.c',
-  'nms-ibft-plugin.c'
-)
-
-libnm_settings_plugin_ibft = shared_module(
-  name,
-  sources: sources,
-  dependencies: nm_dep,
-  c_args: cflags,
-  link_with: libnms_ibft_core,
-  link_args: ldflags_linker_script_settings,
-  link_depends: linker_script_settings,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_settings_plugin_ibft
-
-# FIXME: check_so_symbols replacement
-'''
-run_target(
-  'check-local-symbols-settings-ibft',
-  command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()],
-  depends: libnm_settings_plugin_ibft
-)
-
-check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la
-  $(call check_so_symbols,$(builddir)/src/settings/plugins/ibft/.libs/libnm-settings-plugin-ibft.so)
-'''
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build
deleted file mode 100644
index 7a9445dd..00000000
--- a/src/settings/plugins/ibft/tests/meson.build
+++ /dev/null
@@ -1,22 +0,0 @@
-test_unit = 'test-ibft'
-
-test_ibft_dir = meson.current_source_dir()
-
-cflags = [
-  '-DTEST_IBFT_DIR="@0@"'.format(test_ibft_dir),
-  '-DTEST_SCRATCH_DIR="@0@"'.format(test_ibft_dir)
-]
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: cflags,
-  link_with: libnms_ibft_core
-)
-
-test(
-  'ibft/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c
index fd2ec61b..72d1a7db 100644
--- a/src/settings/plugins/ibft/tests/test-ibft.c
+++ b/src/settings/plugins/ibft/tests/test-ibft.c
@@ -181,7 +181,7 @@ test_read_ibft_malformed (gconstpointer user_data)
 
 	g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS));
 
-	NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*malformed iscsiadm record*");
 
 	success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error);
 	g_assert_no_error (error);
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
deleted file mode 100644
index fdf308be..00000000
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ /dev/null
@@ -1,77 +0,0 @@
-install_data(
-  'nm-ifcfg-rh.conf',
-  install_dir: dbus_conf_dir
-)
-
-cflags = [
-  '-DSBINDIR="@0@"'.format(nm_sbindir),
-  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-]
-
-name = 'nmdbus-ifcfg-rh'
-
-dbus_sources = gnome.gdbus_codegen(
-  name,
-  'nm-ifcfg-rh.xml',
-  interface_prefix: 'com.redhat',
-  namespace: 'NMDBus'
-)
-
-libnmdbus_ifcfg_rh = static_library(
-  name,
-  sources: dbus_sources,
-  dependencies: glib_dep,
-  c_args: cflags
-)
-
-sources = files(
-  'nm-inotify-helper.c',
-  'nms-ifcfg-rh-reader.c',
-  'nms-ifcfg-rh-utils.c',
-  'nms-ifcfg-rh-writer.c',
-  'shvar.c'
-)
-
-deps = [
-  crypto_dep,
-  nm_dep
-]
-
-libnms_ifcfg_rh_core = static_library(
-  'nms-ifcfg-rh-core',
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags
-)
-
-sources = [dbus_sources] + files('nms-ifcfg-rh-connection.c')
-
-libnm_settings_plugin_ifcfg_rh = shared_module(
-  'nm-settings-plugin-ifcfg-rh',
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags,
-  link_with: [libnms_ifcfg_rh_core],
-  link_args: ldflags_linker_script_settings,
-  link_depends: linker_script_settings,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_settings_plugin_ifcfg_rh
-
-# FIXME: check_so_symbols replacement
-'''
-run_target(
-  'check-local-symbols-settings-ifcfg-rh',
-  command: [check_so_symbols, libnm_settings_plugin_ifcfg_rh.full_path()],
-  depends: libnm_settings_plugin_ifcfg_rh
-)
-
-check-local-symbols-settings-ifcfg-rh: src/settings/plugins/ifcfg-rh/libnm-settings-plugin-ifcfg-rh.la
-  $(call check_so_symbols,$(builddir)/src/settings/plugins/ifcfg-rh/.libs/libnm-settings-plugin-ifcfg-rh.so)
-'''
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
index 2863df64..97417db9 100644
--- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
+++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
@@ -172,7 +172,7 @@ nm_inotify_helper_init (NMInotifyHelper *self)
 {
 	NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self);
 
-	priv->wd_refs = g_hash_table_new (nm_direct_hash, NULL);
+	priv->wd_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index c7207297..0743fc9f 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -37,6 +37,7 @@
 #include "settings/nm-settings-plugin.h"
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
+#include "nm-exported-object.h"
 
 #include "nms-ifcfg-rh-connection.h"
 #include "nms-ifcfg-rh-common.h"
@@ -45,10 +46,10 @@
 #include "nms-ifcfg-rh-utils.h"
 #include "shvar.h"
 
-#define IFCFGRH1_BUS_NAME                               "com.redhat.ifcfgrh1"
-#define IFCFGRH1_OBJECT_PATH                            "/com/redhat/ifcfgrh1"
-#define IFCFGRH1_IFACE1_NAME                            "com.redhat.ifcfgrh1"
-#define IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS        "GetIfcfgDetails"
+#include "settings/plugins/ifcfg-rh/nmdbus-ifcfg-rh.h"
+
+#define IFCFGRH1_DBUS_SERVICE_NAME "com.redhat.ifcfgrh1"
+#define IFCFGRH1_DBUS_OBJECT_PATH "/com/redhat/ifcfgrh1"
 
 /*****************************************************************************/
 
@@ -57,9 +58,9 @@ typedef struct {
 
 	struct {
 		GDBusConnection *connection;
+		GDBusInterfaceSkeleton *interface;
 		GCancellable *cancellable;
 		gulong signal_id;
-		guint regist_id;
 	} dbus;
 
 	GHashTable *connections;  /* uuid::connection */
@@ -326,21 +327,21 @@ update_connection (SettingsPluginIfcfg *self,
 
 			if (new_unmanaged || new_unrecognized) {
 				if (!old_unmanaged && !old_unrecognized) {
-					/* ref connection first, because we put it into priv->connections below.
-					 * Emitting signal-removed might otherwise delete it. */
 					g_object_ref (connection_by_uuid);
-
 					/* Unexport the connection by telling the settings service it's
 					 * been removed.
 					 */
 					nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid));
+					/* Remove the path so that claim_connection() doesn't complain later when
+					 * interface gets managed and connection is re-added. */
+					nm_connection_set_path (NM_CONNECTION (connection_by_uuid), NULL);
 
 					/* signal_remove() will end up removing the connection from our hash,
 					 * so add it back now.
 					 */
 					g_hash_table_insert (priv->connections,
 					                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection_by_uuid))),
-					                     connection_by_uuid /* we took reference above and pass it on */);
+					                     connection_by_uuid);
 				}
 			} else {
 				if (old_unmanaged /* && !new_unmanaged */) {
@@ -372,9 +373,7 @@ update_connection (SettingsPluginIfcfg *self,
 			_LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
 		else
 			_LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-		g_hash_table_insert (priv->connections,
-		                     g_strdup (uuid),
-		                     connection_new /* take reference */);
+		g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new);
 
 		g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED,
 		                  G_CALLBACK (connection_removed_cb),
@@ -517,7 +516,7 @@ read_connections (SettingsPluginIfcfg *plugin)
 		return;
 	}
 
-	alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+	alive_connections = g_hash_table_new (NULL, NULL);
 
 	filenames = g_ptr_array_new_with_free_func (g_free);
 	while ((item = g_dir_read_name (dir))) {
@@ -750,7 +749,7 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
 		return;
 	}
 
-	path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection));
+	path = nm_connection_get_path (NM_CONNECTION (connection));
 	if (!path) {
 		g_dbus_method_invocation_return_error (context,
 		                                       NM_SETTINGS_ERROR,
@@ -769,15 +768,15 @@ static void
 _dbus_clear (SettingsPluginIfcfg *self)
 {
 	SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
-	guint id;
 
 	nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id);
 
 	nm_clear_g_cancellable (&priv->dbus.cancellable);
 
-	if ((id = nm_steal_int (&priv->dbus.regist_id))) {
-		if (!g_dbus_connection_unregister_object (priv->dbus.connection, id))
-			_LOGW ("dbus: unexpected failure to unregister object");
+	if (priv->dbus.interface) {
+		g_dbus_interface_skeleton_unexport (priv->dbus.interface);
+		nm_exported_object_skeleton_release (priv->dbus.interface);
+		priv->dbus.interface = NULL;
 	}
 
 	g_clear_object (&priv->dbus.connection);
@@ -789,56 +788,13 @@ _dbus_connection_closed (GDBusConnection *connection,
                          GError          *error,
                          gpointer         user_data)
 {
-	_LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME);
+	_LOGW ("dbus: %s bus closed", IFCFGRH1_DBUS_SERVICE_NAME);
 	_dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data));
 
 	/* Retry or recover? */
 }
 
 static void
-_method_call (GDBusConnection *connection,
-              const char *sender,
-              const char *object_path,
-              const char *interface_name,
-              const char *method_name,
-              GVariant *parameters,
-              GDBusMethodInvocation *invocation,
-              gpointer user_data)
-{
-	SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data);
-	const char *ifcfg;
-
-	if (   !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)
-	    || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) {
-		g_dbus_method_invocation_return_error (invocation,
-		                                       G_DBUS_ERROR,
-		                                       G_DBUS_ERROR_UNKNOWN_METHOD,
-		                                       "Unknown method %s",
-		                                       method_name);
-		return;
-	}
-
-	g_variant_get (parameters, "(&s)", &ifcfg);
-	impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg);
-}
-
-static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO (
-	IFCFGRH1_BUS_NAME,
-	.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
-		NM_DEFINE_GDBUS_METHOD_INFO (
-			IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS,
-			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
-				NM_DEFINE_GDBUS_ARG_INFO ("ifcfg", "s"),
-			),
-			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
-				NM_DEFINE_GDBUS_ARG_INFO ("uuid", "s"),
-				NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
-			),
-		),
-	),
-);
-
-static void
 _dbus_request_name_done (GObject *source_object,
                          GAsyncResult *res,
                          gpointer user_data)
@@ -874,27 +830,36 @@ _dbus_request_name_done (GObject *source_object,
 	}
 
 	{
-		static const GDBusInterfaceVTable interface_vtable = {
-			.method_call = _method_call,
+		GType skeleton_type = NMDBUS_TYPE_IFCFGRH1_SKELETON;
+		gs_free char *method_name_get_ifcfg_details = NULL;
+		NMExportedObjectDBusMethodImpl methods[] = {
+			{
+				.method_name = (method_name_get_ifcfg_details = nm_exported_object_skeletonify_method_name ("GetIfcfgDetails")),
+				.impl = G_CALLBACK (impl_ifcfgrh_get_ifcfg_details),
+			},
 		};
 
-		priv->dbus.regist_id = g_dbus_connection_register_object (connection,
-		                                                          IFCFGRH1_OBJECT_PATH,
-		                                                          interface_info,
-		                                                          NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable),
-		                                                          self,
-		                                                          NULL,
-		                                                          &error);
-		if (!priv->dbus.regist_id) {
-			_LOGW ("dbus: couldn't register D-Bus service: %s", error->message);
+		priv->dbus.interface = nm_exported_object_skeleton_create (skeleton_type,
+		                                                           g_type_class_peek (SETTINGS_TYPE_PLUGIN_IFCFG),
+		                                                           methods,
+		                                                           G_N_ELEMENTS (methods),
+		                                                           (GObject *) self);
+
+		if (!g_dbus_interface_skeleton_export (priv->dbus.interface,
+		                                       priv->dbus.connection,
+		                                       IFCFGRH1_DBUS_OBJECT_PATH,
+		                                       &error)) {
+			nm_exported_object_skeleton_release (priv->dbus.interface);
+			priv->dbus.interface = NULL;
+			_LOGW ("dbus: failed exporting interface: %s", error->message);
 			_dbus_clear (self);
 			return;
 		}
 	}
 
 	_LOGD ("dbus: aquired D-Bus service %s and exported %s object",
-	       IFCFGRH1_BUS_NAME,
-	       IFCFGRH1_OBJECT_PATH);
+	       IFCFGRH1_DBUS_SERVICE_NAME,
+	       IFCFGRH1_DBUS_OBJECT_PATH);
 }
 
 static void
@@ -935,7 +900,7 @@ _dbus_create_done (GObject *source_object,
 	                        DBUS_INTERFACE_DBUS,
 	                        "RequestName",
 	                        g_variant_new ("(su)",
-	                                       IFCFGRH1_BUS_NAME,
+	                                       IFCFGRH1_DBUS_SERVICE_NAME,
 	                                       DBUS_NAME_FLAG_DO_NOT_QUEUE),
 	                        G_VARIANT_TYPE ("(u)"),
 	                        G_DBUS_CALL_FLAGS_NONE,
@@ -952,7 +917,7 @@ _dbus_setup (SettingsPluginIfcfg *self)
 	gs_free char *address = NULL;
 	gs_free_error GError *error = NULL;
 
-	_dbus_clear (self);
+	g_return_if_fail (!priv->dbus.connection);
 
 	address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
 	if (address == NULL) {
@@ -978,22 +943,17 @@ config_changed_cb (NMConfig *config,
                    NMConfigData *old_data,
                    SettingsPluginIfcfg *self)
 {
-	SettingsPluginIfcfgPrivate *priv;
-
 	/* If the dbus connection for some reason is borked the D-Bus service
 	 * won't be offered.
 	 *
 	 * On SIGHUP and SIGUSR1 try to re-connect to D-Bus. So in the unlikely
 	 * event that the D-Bus conneciton is broken, that allows for recovery
 	 * without need for restarting NetworkManager. */
-	if (!NM_FLAGS_ANY (changes,   NM_CONFIG_CHANGE_CAUSE_SIGHUP
-	                            | NM_CONFIG_CHANGE_CAUSE_SIGUSR1))
-		return;
-
-	priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self);
-	if (   !priv->dbus.connection
-	    && !priv->dbus.cancellable)
-		_dbus_setup (self);
+	if (NM_FLAGS_ANY (changes,   NM_CONFIG_CHANGE_CAUSE_SIGHUP
+	                           | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) {
+		if (!SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self)->dbus.connection)
+			_dbus_setup (self);
+	}
 }
 
 /*****************************************************************************/
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 6ef3f660..c91cd253 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -181,7 +181,7 @@ make_connection_setting (const char *file,
 	const char *v;
 	gs_free char *stable_id = NULL;
 	const char *const *iter;
-	int vint64, i_val;
+	int vint64;
 
 	ifcfg_name = utils_get_ifcfg_name (file, TRUE);
 	if (!ifcfg_name)
@@ -338,13 +338,6 @@ make_connection_setting (const char *file,
 	vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1);
 	g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (gint) vint64, NULL);
 
-	i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT;
-	if (!svGetValueEnum (ifcfg, "MDNS",
-	                     nm_setting_connection_mdns_get_type (),
-	                     &i_val, NULL))
-		PARSE_WARNING ("invalid MDNS setting");
-	g_object_set (s_con, NM_SETTING_CONNECTION_MDNS, i_val, NULL);
-
 	return NM_SETTING (s_con);
 }
 
@@ -934,7 +927,7 @@ next:
 			                                                    : ""));
 			break;
 		case PARSE_LINE_TYPE_FLAG:
-			/* NOTE: the flag (for "onlink") only allows to explictly set "TRUE".
+			/* XXX: the flag (for "onlink") only allows to explictly set "TRUE".
 			 * There is no way to express an explicit "FALSE" setting
 			 * of this attribute, hence, the file format cannot encode
 			 * that configuration. */
@@ -1345,7 +1338,29 @@ make_ip4_setting (shvarFile *ifcfg,
 	} else if (!g_ascii_strcasecmp (v, "autoip")) {
 		method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL;
 	} else if (!g_ascii_strcasecmp (v, "shared")) {
-		method = NM_SETTING_IP4_CONFIG_METHOD_SHARED;
+		int idx;
+
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED,
+		              NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
+		              NULL);
+		/* 1 IP address is allowed for shared connections. Read it. */
+		if (is_any_ip4_address_defined (ifcfg, &idx)) {
+			guint32 gw;
+			NMIPAddress *addr = NULL;
+
+			if (!read_full_ip4_address (ifcfg, idx, NULL, &addr, NULL, error))
+				return NULL;
+			if (!read_ip4_address (ifcfg, "GATEWAY", NULL, &gw, error))
+				return NULL;
+			(void) nm_setting_ip_config_add_address (s_ip4, addr);
+			nm_ip_address_unref (addr);
+			if (never_default)
+				PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled");
+			gateway = g_strdup (nm_utils_inet4_ntop (gw, inet_buf));
+			g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
+		}
+		return g_steal_pointer (&s_ip4);
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Unknown BOOTPROTO '%s'", v);
@@ -1372,7 +1387,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	              NM_SETTING_IP_CONFIG_ROUTE_TABLE, (guint) route_table,
 	              NULL);
 
-	if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
 		return g_steal_pointer (&s_ip4);
 
 	/* Handle DHCP settings */
@@ -1449,47 +1464,39 @@ make_ip4_setting (shvarFile *ifcfg,
 	if (gateway && never_default)
 		PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled");
 
-	/* We used to skip saving a lot of unused properties for the ipv4 shared method.
-	 * We want now to persist them but... unfortunately loading DNS or DOMAIN options
-	 * would cause a fail in the ipv4 verify() function. As we don't want any regression
-	 * in the unlikely event that someone has a working ifcfg file for an IPv4 shared ip
-	 * connection with a crafted "DNS" entry... don't load it. So we will avoid failing
-	 * the connection) */
-	if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
-		/* DNS servers
-		 * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
-		 */
-		for (i = 1; i <= 10; i++) {
-			char tag[256];
-
-			numbered_tag (tag, "DNS", i);
-			nm_clear_g_free (&value);
-			v = svGetValueStr (ifcfg, tag, &value);
-			if (v) {
-				if (nm_utils_ipaddr_valid (AF_INET, v)) {
-					if (!nm_setting_ip_config_add_dns (s_ip4, v))
-						PARSE_WARNING ("duplicate DNS server %s", tag);
-				} else if (nm_utils_ipaddr_valid (AF_INET6, v)) {
-					/* Ignore IPv6 addresses */
-				} else {
-					PARSE_WARNING ("invalid DNS server address %s", v);
-					return NULL;
-				}
-			}
-		}
+	/* DNS servers
+	 * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
+	 */
+	for (i = 1; i <= 10; i++) {
+		char tag[256];
 
-		/* DNS searches */
+		numbered_tag (tag, "DNS", i);
 		nm_clear_g_free (&value);
-		v = svGetValueStr (ifcfg, "DOMAIN", &value);
+		v = svGetValueStr (ifcfg, tag, &value);
 		if (v) {
-			gs_free const char **searches = NULL;
+			if (nm_utils_ipaddr_valid (AF_INET, v)) {
+				if (!nm_setting_ip_config_add_dns (s_ip4, v))
+					PARSE_WARNING ("duplicate DNS server %s", tag);
+			} else if (nm_utils_ipaddr_valid (AF_INET6, v)) {
+				/* Ignore IPv6 addresses */
+			} else {
+				PARSE_WARNING ("invalid DNS server address %s", v);
+				return NULL;
+			}
+		}
+	}
 
-			searches = nm_utils_strsplit_set (v, " ");
-			if (searches) {
-				for (item = searches; *item; item++) {
-					if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
-						PARSE_WARNING ("duplicate DNS domain '%s'", *item);
-				}
+	/* DNS searches */
+	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "DOMAIN", &value);
+	if (v) {
+		gs_free const char **searches = NULL;
+
+		searches = nm_utils_strsplit_set (v, " ");
+		if (searches) {
+			for (item = searches; *item; item++) {
+				if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
+					PARSE_WARNING ("duplicate DNS domain '%s'", *item);
 			}
 		}
 	}
@@ -1538,8 +1545,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	}
 
 	/* Legacy value NM used for a while but is incorrect (rh #459370) */
-	if (   !nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)
-	    && !nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
+	if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
 		nm_clear_g_free (&value);
 		v = svGetValueStr (ifcfg, "SEARCH", &value);
 		if (v) {
@@ -1555,14 +1561,10 @@ make_ip4_setting (shvarFile *ifcfg,
 		}
 	}
 
-	timeout = svGetValueInt64 (ifcfg, "ACD_TIMEOUT", 10, -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -2);
-	if (timeout == -2) {
-		timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1,
-		                           NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1);
-		if (timeout > 0)
-			timeout *= 1000;
-	}
-	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (gint) timeout, NULL);
+	timeout = svGetValueInt64 (ifcfg, "ARPING_WAIT", 10, -1,
+	                           NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX / 1000, -1);
+	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT,
+	              (gint) (timeout <= 0 ? timeout : timeout * 1000), NULL);
 
 	return g_steal_pointer (&s_ip4);
 }
@@ -1640,7 +1642,7 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file
 			                            read_defroute ? &gateway : NULL,
 			                            &err);
 			if (ok) {
-				nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (device));
+				nm_ip_address_set_attribute (addr, "label", g_variant_new_string (device));
 				if (!nm_setting_ip_config_add_address (s_ip4, addr))
 					PARSE_WARNING ("duplicate IP4 address in alias file %s", item);
 				if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
@@ -1999,15 +2001,11 @@ make_tc_setting (shvarFile *ifcfg)
 			break;
 
 		qdisc = nm_utils_tc_qdisc_from_str (value, &local);
-		if (!qdisc) {
-			PARSE_WARNING ("ignoring bad tc qdisc: '%s': %s", value, local->message);
-			continue;
-		}
+		if (!qdisc)
+			PARSE_WARNING ("ignoring bad qdisc: '%s': %s", value, local->message);
 
 		if (!nm_setting_tc_config_add_qdisc (s_tc, qdisc))
-			PARSE_WARNING ("duplicate tc qdisc");
-
-		nm_tc_qdisc_unref (qdisc);
+			PARSE_WARNING ("duplicate qdisc");
 	}
 
 	for (i = 1;; i++) {
@@ -2021,15 +2019,11 @@ make_tc_setting (shvarFile *ifcfg)
 			break;
 
 		tfilter = nm_utils_tc_tfilter_from_str (value, &local);
-		if (!tfilter) {
-			PARSE_WARNING ("ignoring bad tc filter: '%s': %s", value, local->message);
-			continue;
-		}
+		if (!tfilter)
+			PARSE_WARNING ("ignoring bad tfilter: '%s': %s", value, local->message);
 
 		if (!nm_setting_tc_config_add_tfilter (s_tc, tfilter))
-			PARSE_WARNING ("duplicate tc filter");
-
-		nm_tc_tfilter_unref (tfilter);
+			PARSE_WARNING ("duplicate filter");
 	}
 
 	if (   nm_setting_tc_config_get_num_qdiscs (s_tc) > 0
@@ -3558,13 +3552,6 @@ make_wpa_setting (shvarFile *ifcfg,
 		return NULL;
 	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PMF, i_val, NULL);
 
-	i_val = NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT;
-	if (!svGetValueEnum (ifcfg, "FILS",
-	                     nm_setting_wireless_security_fils_get_type (),
-	                     &i_val, error))
-		return NULL;
-	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_FILS, i_val, NULL);
-
 	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "SECURITYMODE", &value);
 	if (NM_IN_STRSET (v, NULL, "open"))
@@ -5334,8 +5321,6 @@ connection_from_file_full (const char *filename,
 	g_return_val_if_fail (filename != NULL, NULL);
 	g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL);
 
-	NM_SET_OUT (out_ignore_error, FALSE);
-
 	/* Non-NULL only for unit tests; normally use /etc/sysconfig/network */
 	if (!network_file)
 		network_file = SYSCONFDIR "/sysconfig/network";
@@ -5356,7 +5341,6 @@ connection_from_file_full (const char *filename,
 	if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
 		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
 		if (!connection) {
-			NM_SET_OUT (out_ignore_error, TRUE);
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
 		}
@@ -5366,7 +5350,8 @@ connection_from_file_full (const char *filename,
 	/* iBFT is handled by the iBFT settings plugin */
 	bootproto = svGetValueStr_cp (parsed, "BOOTPROTO");
 	if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
-		NM_SET_OUT (out_ignore_error, TRUE);
+		if (out_ignore_error)
+			*out_ignore_error = TRUE;
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Ignoring iBFT configuration");
 		g_free (bootproto);
@@ -5412,7 +5397,8 @@ connection_from_file_full (const char *filename,
 		char *device;
 
 		if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) {
-			NM_SET_OUT (out_ignore_error, TRUE);
+			if (out_ignore_error)
+				*out_ignore_error = TRUE;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring unsupported connection due to IPV6TUNNELIPV4");
 			return NULL;
@@ -5426,7 +5412,8 @@ connection_from_file_full (const char *filename,
 		}
 
 		if (!strcmp (device, "lo")) {
-			NM_SET_OUT (out_ignore_error, TRUE);
+			if (out_ignore_error)
+				*out_ignore_error = TRUE;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring loopback device config.");
 			g_free (device);
@@ -5473,7 +5460,8 @@ connection_from_file_full (const char *filename,
 					memcpy (p_path, IFUP_PATH_PREFIX, NM_STRLEN (IFUP_PATH_PREFIX));
 					if (access (p_path, X_OK) == 0) {
 						/* for all other types, this is not something we want to handle. */
-						NM_SET_OUT (out_ignore_error, TRUE);
+						if (out_ignore_error)
+							*out_ignore_error = TRUE;
 						g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 						             "Ignore script for unknown device type which has a matching %s script",
 						             p_path);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 8584772e..e9dd08b7 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -148,7 +148,16 @@ write_secrets (shvarFile *ifcfg,
 	/* we purge all existing secrets. */
 	svUnsetAll (keyfile, SV_KEY_TYPE_ANY);
 
-	secrets_keys = nm_utils_strdict_get_keys (secrets, TRUE, &secrets_keys_n);
+	/* sort the keys. */
+	secrets_keys = (const char **) g_hash_table_get_keys_as_array (secrets, &secrets_keys_n);
+	if (secrets_keys_n > 1) {
+		g_qsort_with_data (secrets_keys,
+		                   secrets_keys_n,
+		                   sizeof (const char *),
+		                   nm_strcmp_p_with_data,
+		                   NULL);
+	}
+
 	for (i = 0; i < secrets_keys_n; i++) {
 		const char *k = secrets_keys[i];
 		const char *v = g_hash_table_lookup (secrets, k);
@@ -785,13 +794,6 @@ write_wireless_security_setting (NMConnection *connection,
 		                nm_setting_wireless_security_get_pmf (s_wsec));
 	}
 
-	if (nm_setting_wireless_security_get_fils (s_wsec) == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT)
-		svUnsetValue (ifcfg, "FILS");
-	else {
-		svSetValueEnum (ifcfg, "FILS", nm_setting_wireless_security_fils_get_type (),
-		                nm_setting_wireless_security_get_fils (s_wsec));
-	}
-
 	return TRUE;
 }
 
@@ -897,16 +899,14 @@ write_wireless_setting (NMConnection *connection,
 	}
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
-	if (!mode)
-		svUnsetValue(ifcfg, "MODE");
-	else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_INFRA))
+	if (!mode || !strcmp (mode, "infrastructure")) {
 		svSetValueStr (ifcfg, "MODE", "Managed");
-	else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+	} else if (!strcmp (mode, "adhoc")) {
 		svSetValueStr (ifcfg, "MODE", "Ad-Hoc");
 		adhoc = TRUE;
-	} else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP))
+	} else if (!strcmp (mode, "ap")) {
 		svSetValueStr (ifcfg, "MODE", "Ap");
-	else {
+	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Invalid mode '%s' in '%s' setting",
 		             mode, NM_SETTING_WIRELESS_SETTING_NAME);
@@ -1728,7 +1728,6 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	GString *str;
 	const char *master, *master_iface = NULL, *type;
 	gint vint;
-	NMSettingConnectionMdns mdns;
 	guint32 vuint32;
 	const char *tmp;
 
@@ -1750,7 +1749,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 
 	/* Only save the value for master connections */
 	type = nm_setting_connection_get_connection_type (s_con);
-	if (_nm_connection_type_is_master (type)) {
+	if (   !g_strcmp0 (type, NM_SETTING_BOND_SETTING_NAME)
+	    || !g_strcmp0 (type, NM_SETTING_TEAM_SETTING_NAME)
+	    || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
 		autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
 		svSetValueStr (ifcfg, "AUTOCONNECT_SLAVES",
@@ -1887,13 +1888,6 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 
 	vint = nm_setting_connection_get_auth_retries (s_con);
 	svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint);
-
-	mdns = nm_setting_connection_get_mdns (s_con);
-	if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) {
-		svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (),
-		                mdns);
-	} else
-		svUnsetValue (ifcfg, "MDNS");
 }
 
 static char *
@@ -2287,7 +2281,7 @@ write_ip4_setting (NMConnection *connection,
 		if (i > 0) {
 			GVariant *label;
 
-			label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+			label = nm_ip_address_get_attribute (addr, "label");
 			if (label)
 				continue;
 		}
@@ -2417,15 +2411,12 @@ write_ip4_setting (NMConnection *connection,
 	NM_SET_OUT (out_route_content, write_route_file (s_ip4));
 
 	timeout = nm_setting_ip_config_get_dad_timeout (s_ip4);
-	if (timeout < 0) {
-		svUnsetValue (ifcfg, "ACD_TIMEOUT");
+	if (timeout < 0)
 		svUnsetValue (ifcfg, "ARPING_WAIT");
-	} else if (timeout == 0) {
-		svSetValueStr (ifcfg, "ACD_TIMEOUT", "0");
+	else if (timeout == 0)
 		svSetValueStr (ifcfg, "ARPING_WAIT", "0");
-	} else {
-		svSetValueInt64 (ifcfg, "ACD_TIMEOUT", timeout);
-		/* Round the value up to next integer for initscripts */
+	else {
+		/* Round the value up to next integer */
 		svSetValueInt64 (ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1);
 	}
 
@@ -2493,7 +2484,7 @@ write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path)
 
 		addr = nm_setting_ip_config_get_address (s_ip4, i);
 
-		label_var = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		label_var = nm_ip_address_get_attribute (addr, "label");
 		if (!label_var)
 			continue;
 		label = g_variant_get_string (label_var, NULL);
@@ -2996,7 +2987,7 @@ do_write_to_disk (NMConnection *connection,
 {
 	/* From here on, we persist data to disk. Before, it was all in-memory
 	 * only. But we loaded the ifcfg files from disk, and managled our
-	 * new settings (in-memory). */
+	 * new settings (in-momory). */
 
 	if (!svWriteFile (ifcfg, 0644, error))
 		return FALSE;
@@ -3147,10 +3138,10 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection,
 	 * does not yet allow to inject the configuration. */
 	if (out_reread || out_reread_same) {
 		if (!do_write_reread (connection,
-		                      svFileGetName (ifcfg),
-		                      out_reread,
-		                      out_reread_same,
-		                      &local)) {
+		                         svFileGetName (ifcfg),
+		                         out_reread,
+		                         out_reread_same,
+		                         &local)) {
 			_LOGW ("write: failure to re-read connection \"%s\": %s",
 			       svFileGetName (ifcfg), local->message);
 			g_clear_error (&local);
@@ -3197,3 +3188,4 @@ nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **err
 	             NM_PRINT_FMT_QUOTE_STRING (type));
 	return FALSE;
 }
+
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 9120b870..2b64f3fc 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -39,7 +39,7 @@
 #include "nm-core-internal.h"
 #include "nm-core-utils.h"
 #include "nm-utils/nm-enum-utils.h"
-#include "c-list/src/c-list.h"
+#include "nm-utils/c-list.h"
 
 /*****************************************************************************/
 
@@ -1278,7 +1278,7 @@ svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value)
 {
 	gs_free char *v = NULL;
 
-	v = _nm_utils_enum_to_str_full (gtype, value, " ", NULL);
+	v = _nm_utils_enum_to_str_full (gtype, value, " ");
 	return svSetValueStr (s, key, v);
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build
deleted file mode 100644
index 3596b642..00000000
--- a/src/settings/plugins/ifcfg-rh/tests/meson.build
+++ /dev/null
@@ -1,22 +0,0 @@
-test_unit = 'test-ifcfg-rh'
-
-test_ifcfg_dir = meson.current_source_dir()
-
-cflags = [
-  '-DTEST_IFCFG_DIR="@0@"'.format(test_ifcfg_dir),
-  '-DTEST_SCRATCH_DIR="@0@"'.format(test_ifcfg_dir)
-]
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: cflags,
-  link_with: libnms_ifcfg_rh_core
-)
-
-test(
-  'ifcfg-rh/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
index cf325f35..026993b8 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
@@ -1,4 +1,5 @@
 ESSID="Test SSID"
+MODE=Managed
 SSID_HIDDEN=yes
 MAC_ADDRESS_RANDOMIZATION=default
 TYPE=Wireless
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
index cd8fc96f..c0e47c48 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
@@ -14,8 +14,6 @@ DNS2=4.2.2.2
 DOMAIN="foobar.com lab.foobar.com"
 DEFROUTE=yes
 IPV4_FAILURE_FATAL=no
-ACD_TIMEOUT=400
-ARPING_WAIT=1
 IPV6INIT=no
 NAME="Test Write Wired Static Routes"
 UUID=${UUID}
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc
deleted file mode 100644
index d0a3c254..00000000
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc
+++ /dev/null
@@ -1,16 +0,0 @@
-TYPE=Ethernet
-DEVICE=eth0
-HWADDR=00:11:22:33:44:55
-BOOTPROTO=none
-ONBOOT=yes
-DNS1=4.2.2.1
-DNS2=4.2.2.2
-IPADDR=192.168.1.5
-PREFIX=24
-NETMASK=255.255.255.0
-GATEWAY=192.168.1.1
-IPV6INIT=no
-QDISC1="root fq_codel"
-FILTER1="parent 1234: matchall action simple sdata Hello"
-NAME=ethernet-tc
-UUID=a42c8d4e-11a2-4144-92d2-5cbce8c6b2c4
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected
deleted file mode 100644
index a67ca598..00000000
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc-write.cexpected
+++ /dev/null
@@ -1,16 +0,0 @@
-TYPE=Ethernet
-PROXY_METHOD=none
-BROWSER_ONLY=no
-QDISC1="parent 2468:2 pfifo_fast"
-FILTER1="parent 1234: matchall action simple sdata Hello"
-BOOTPROTO=none
-IPADDR=1.1.1.3
-PREFIX=24
-GATEWAY=1.1.1.1
-DEFROUTE=yes
-IPV4_FAILURE_FATAL=no
-IPV6INIT=no
-NAME="Test Write TC config"
-UUID=${UUID}
-DEVICE=eth0
-ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1
index db09afdb..1bc3d524 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-1
@@ -10,5 +10,3 @@ PREFIX1=16
 IPADDR2=3.3.3.3
 PREFIX2=8
 GATEWAY=1.1.1.1
-ACD_TIMEOUT=2000
-ARPING_WAIT=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2
index 6972e279..d7273e36 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-2
@@ -9,5 +9,3 @@ IPADDR2=9.8.7.6
 PREFIX2=16
 IPADDR3=3.3.3.3
 PREFIX3=8
-ACD_TIMEOUT=2000
-ARPING_WAIT=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3
index d9065994..f2457bd2 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-3
@@ -9,5 +9,3 @@ IPADDR3=9.8.7.6
 PREFIX3=16
 IPADDR4=3.3.3.3
 PREFIX4=8
-ACD_TIMEOUT=2000
-ARPING_WAIT=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4
index 935267f7..e6b77141 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv4-manual-4
@@ -9,5 +9,3 @@ IPADDR1=9.8.7.6
 PREFIX1=16
 IPADDR2=3.3.3.3
 PREFIX2=8
-ACD_TIMEOUT=2000
-ARPING_WAIT=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 57bd96e9..6bf27556 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -24,7 +24,6 @@
 #include <stdarg.h>
 #include <unistd.h>
 #include <string.h>
-#include <linux/pkt_sched.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
@@ -606,7 +605,8 @@ test_read_miscellaneous_variables (void)
 	int mac_blacklist_num, i;
 	guint64 expected_timestamp = 0;
 
-	NMTST_EXPECT_NM_WARN ("*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-misc-variables",
 	                                    NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
@@ -859,7 +859,8 @@ test_read_wired_static_no_prefix (gconstpointer user_data)
 	file = g_strdup_printf (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-static-no-prefix-%u", expected_prefix);
 	expected_id = g_strdup_printf ("System test-wired-static-no-prefix-%u", expected_prefix);
 
-	NMTST_EXPECT_NM_WARN ("*missing PREFIX, assuming*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing PREFIX, assuming*");
 	connection = _connection_from_file (file, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
 
@@ -1080,7 +1081,8 @@ test_read_wired_global_gateway_ignore (void)
 	NMSettingIPConfig *s_ip4;
 	char *unmanaged = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection has no static addresses");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection has no static addresses");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-global-gateway-ignore",
 	                                    TEST_IFCFG_DIR"/network-scripts/network-test-wired-global-gateway-ignore",
 	                                    TYPE_ETHERNET, &unmanaged);
@@ -1474,7 +1476,6 @@ test_read_wired_ipv4_manual (gconstpointer data)
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
-	g_assert_cmpint (nm_setting_ip_config_get_dad_timeout (s_ip4), ==, 2000);
 
 	/* IP addresses */
 	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 3);
@@ -1512,7 +1513,8 @@ test_read_wired_ipv6_manual (void)
 	NMIPAddress *ip6_addr;
 	NMIPRoute *ip6_route;
 
-	NMTST_EXPECT_NM_WARN ("*ignoring manual default route*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ignoring manual default route*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-manual",
 	                                    NULL, TYPE_ETHERNET, &unmanaged);
 	g_test_assert_expected_messages ();
@@ -1895,7 +1897,8 @@ test_read_write_802_1X_subj_matches (void)
 	gs_unref_object NMConnection *reread = NULL;
 	NMSetting8021x *s_8021x;
 
-	NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing IEEE_8021X_CA_CERT*peap*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1X-subj-matches",
 	                                    NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
@@ -1916,14 +1919,16 @@ test_read_write_802_1X_subj_matches (void)
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld");
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld");
 
-	NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
 	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
 	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected",
 	                        &testfile);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
 
@@ -2074,7 +2079,7 @@ test_read_wired_aliases_good (gconstpointer test_data)
 		g_assert (j < expected_num_addresses);
 
 		g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24);
-		label = nm_ip_address_get_attribute (ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		label = nm_ip_address_get_attribute (ip4_addr, "label");
 		if (expected_label[j])
 			g_assert_cmpstr (g_variant_get_string (label, NULL), ==, expected_label[j]);
 		else
@@ -2126,7 +2131,7 @@ test_read_wired_aliases_bad (const char *base, const char *expected_id)
 	g_assert (ip4_addr != NULL);
 	g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.1.5");
 	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24);
-	g_assert (nm_ip_address_get_attribute (ip4_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL);
+	g_assert (nm_ip_address_get_attribute (ip4_addr, "label") == NULL);
 
 	/* Gateway */
 	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.1.1");
@@ -2137,14 +2142,16 @@ test_read_wired_aliases_bad (const char *base, const char *expected_id)
 static void
 test_read_wired_aliases_bad_1 (void)
 {
-	NMTST_EXPECT_NM_WARN ("*aliasem1:1*has no DEVICE*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*aliasem1:1*has no DEVICE*");
 	test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem1", "System aliasem1");
 }
 
 static void
 test_read_wired_aliases_bad_2 (void)
 {
-	NMTST_EXPECT_NM_WARN ("*aliasem2:1*has invalid DEVICE*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*aliasem2:1*has invalid DEVICE*");
 	test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem2", "System aliasem2");
 }
 
@@ -3515,6 +3522,7 @@ test_write_wifi_hidden (void)
 
 	g_object_set (s_wifi,
 	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_MODE, "infrastructure",
 	              NM_SETTING_WIRELESS_HIDDEN, TRUE,
 	              NULL);
 
@@ -4766,7 +4774,6 @@ test_write_wired_static_routes (void)
 	g_object_set (s_ip4,
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
 	              NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1",
-	              NM_SETTING_IP_CONFIG_DAD_TIMEOUT, 400,
 	              NULL);
 
 	addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error);
@@ -5134,7 +5141,7 @@ test_write_wired_aliases (void)
 		addr = nm_ip_address_new (AF_INET, ip[i], 24, &error);
 		g_assert_no_error (error);
 		if (label[i])
-			nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (label[i]));
+			nm_ip_address_set_attribute (addr, "label", g_variant_new_string (label[i]));
 		nm_setting_ip_config_add_address (s_ip4, addr);
 		nm_ip_address_unref (addr);
 	}
@@ -5193,9 +5200,9 @@ test_write_wired_aliases (void)
 		else {
 			g_assert_cmpint (nm_ip_address_get_prefix (addr), ==, 24);
 			if (label[j])
-				g_assert_cmpstr (g_variant_get_string (nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL), NULL), ==, label[j]);
+				g_assert_cmpstr (g_variant_get_string (nm_ip_address_get_attribute (addr, "label"), NULL), ==, label[j]);
 			else
-				g_assert (nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL) == NULL);
+				g_assert (nm_ip_address_get_attribute (addr, "label") == NULL);
 			ip[j] = NULL;
 		}
 	}
@@ -6278,9 +6285,7 @@ test_write_wifi_wpa_eap_tls (void)
 	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
 
-	g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
-	              NM_SETTING_WIRELESS_SECURITY_FILS, (int) NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED,
-	              NULL);
+	g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL);
 	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
 	nm_setting_wireless_security_add_pairwise (s_wsec, "tkip");
 	nm_setting_wireless_security_add_group (s_wsec, "tkip");
@@ -7028,7 +7033,7 @@ test_write_wired_ctc_dhcp (void)
 	                        TEST_SCRATCH_DIR "/network-scripts/",
 	                        &testfile);
 
-	/* Ensure the CTCPROT item gets written out as its own option */
+	/* Ensure the CTCPROT item gets written out as it's own option */
 	ifcfg = _svOpenFile (testfile);
 
 	_svGetValue_check (ifcfg, "CTCPROT", "0");
@@ -7708,7 +7713,8 @@ test_read_vlan_reorder_hdr_1 (void)
 	NMConnection *connection;
 	NMSettingVlan *s_vlan;
 
-	NMTST_EXPECT_NM_WARN ("*REORDER_HDR key is deprecated, use VLAN_FLAGS*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*REORDER_HDR key is deprecated, use VLAN_FLAGS*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-reorder-hdr-1",
 	                                        NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
@@ -8502,7 +8508,8 @@ test_read_dcb_bad_booleans (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_STRICT value*not all 0s and 1s*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid DCB_PG_STRICT value*not all 0s and 1s*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-booleans",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8516,7 +8523,8 @@ test_read_dcb_short_booleans (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*DCB_PG_STRICT value*8 characters*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*DCB_PG_STRICT value*8 characters*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-booleans",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8530,7 +8538,8 @@ test_read_dcb_bad_uints (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_UP2TC value*not 0 - 7*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid DCB_PG_UP2TC value*not 0 - 7*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-uints",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8544,7 +8553,8 @@ test_read_dcb_short_uints (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*DCB_PG_UP2TC value*8 characters*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*DCB_PG_UP2TC value*8 characters*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-uints",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8558,7 +8568,8 @@ test_read_dcb_bad_percent (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage value*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid DCB_PG_PCT percentage value*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-percent",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8572,7 +8583,8 @@ test_read_dcb_short_percent (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*invalid DCB_PG_PCT percentage list value*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*invalid DCB_PG_PCT percentage list value*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-percent",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8586,7 +8598,8 @@ test_read_dcb_pgpct_not_100 (void)
 {
 	gs_free_error GError *error = NULL;
 
-	NMTST_EXPECT_NM_WARN ("*DCB_PG_PCT percentages do not equal 100*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*DCB_PG_PCT percentages do not equal 100*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-pgpct-not-100",
 	                            NULL, TYPE_ETHERNET, &error);
 	g_test_assert_expected_messages ();
@@ -8707,7 +8720,7 @@ test_read_team_master_invalid (gconstpointer user_data)
 	NMSettingConnection *s_con;
 	NMSettingTeam *s_team;
 
-	NMTST_EXPECT_NM_WARN ("*ignoring invalid team configuration*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*ignoring invalid team configuration*");
 	connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
 
@@ -9632,125 +9645,6 @@ test_utils_ignore (void)
 	do_test_utils_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE);
 }
 
-static void
-test_tc_read (void)
-{
-	NMConnection *connection;
-	NMSettingTCConfig *s_tc;
-	NMTCQdisc *qdisc;
-	NMTCTfilter *filter;
-	char *str;
-
-	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-tc",
-	                                    NULL, TYPE_ETHERNET,NULL);
-
-	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0");
-
-	s_tc = nm_connection_get_setting_tc_config (connection);
-	g_assert (s_tc);
-
-	g_assert_cmpint (nm_setting_tc_config_get_num_qdiscs (s_tc), ==, 1);
-	qdisc = nm_setting_tc_config_get_qdisc (s_tc, 0);
-	g_assert (qdisc);
-	g_assert_cmpint (nm_tc_qdisc_get_parent (qdisc), ==, TC_H_ROOT);
-	g_assert_cmpint (nm_tc_qdisc_get_handle (qdisc), ==, TC_H_UNSPEC);
-	g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc), ==, "fq_codel");
-
-	g_assert_cmpint (nm_setting_tc_config_get_num_tfilters (s_tc), ==, 1);
-	filter = nm_setting_tc_config_get_tfilter (s_tc, 0);
-	g_assert (filter);
-	str = nm_utils_tc_tfilter_to_str (filter, NULL);
-	g_assert_cmpstr (str, ==, "parent 1234: matchall action simple sdata Hello");
-	g_free (str);
-
-	g_object_unref (connection);
-}
-
-static void
-test_tc_write (void)
-{
-	nmtst_auto_unlinkfile char *testfile = NULL;
-	gs_unref_object NMConnection *connection = NULL;
-	gs_unref_object NMConnection *reread = NULL;
-	NMSettingConnection *s_con;
-	NMSettingIPConfig *s_ip4;
-	NMSettingIPConfig *s_ip6;
-	NMSettingWired *s_wired;
-	NMSettingTCConfig *s_tc;
-	NMTCQdisc *qdisc;
-	NMTCTfilter *tfilter;
-	NMIPAddress *addr;
-	GError *error = NULL;
-
-	connection = nm_simple_connection_new ();
-
-	/* Connection setting */
-	s_con = (NMSettingConnection *) nm_setting_connection_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_con));
-
-	g_object_set (s_con,
-	              NM_SETTING_CONNECTION_ID, "Test Write TC config",
-	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
-	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
-	              NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
-	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
-	              NULL);
-
-	/* Wired setting */
-	s_wired = (NMSettingWired *) nm_setting_wired_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_wired));
-
-	/* IP4 setting */
-	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
-
-	g_object_set (s_ip4,
-	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
-	              NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1",
-	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
-	              NULL);
-
-	addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error);
-	g_assert_no_error (error);
-	nm_setting_ip_config_add_address (s_ip4, addr);
-	nm_ip_address_unref (addr);
-
-	/* IP6 setting */
-	s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
-
-	g_object_set (s_ip6,
-	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
-	              NULL);
-
-	/* TC setting */
-	s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new ();
-	nm_connection_add_setting (connection, NM_SETTING (s_tc));
-
-	qdisc = nm_tc_qdisc_new ("pfifo_fast", TC_H_MAKE (0x2468 << 16, 0x2), &error);
-	g_assert_no_error (error);
-	nm_setting_tc_config_add_qdisc (s_tc, qdisc);
-	nm_tc_qdisc_unref (qdisc);
-
-	tfilter = nm_utils_tc_tfilter_from_str ("parent 1234: matchall action simple sdata Hello", &error);
-	g_assert_no_error (error);
-	nm_setting_tc_config_add_tfilter (s_tc, tfilter);
-	nm_tc_tfilter_unref (tfilter);
-
-	nm_connection_add_setting (connection, nm_setting_proxy_new ());
-
-	nmtst_assert_connection_verifies_without_normalization (connection);
-
-	_writer_new_connec_exp (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-test-tc-write.cexpected",
-	                        &testfile);
-
-	reread = _connection_from_file (testfile, NULL, TYPE_BOND, NULL);
-
-	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
-}
-
 /*****************************************************************************/
 
 #define TPATH "/settings/plugins/ifcfg-rh/"
@@ -10033,8 +9927,5 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "utils/path", test_utils_path);
 	g_test_add_func (TPATH "utils/ignore", test_utils_ignore);
 
-	g_test_add_func (TPATH "tc/read", test_tc_read);
-	g_test_add_func (TPATH "tc/write", test_tc_write);
-
 	return g_test_run ();
 }
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
new file mode 100644
index 00000000..ed0a757f
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
@@ -0,0 +1,2912 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-connection-parser.h"
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "settings/nm-settings-plugin.h"
+#include "nm-core-internal.h"
+#include "NetworkManagerUtils.h"
+#include "nm-meta-setting.h"
+
+#include "nms-ifnet-net-utils.h"
+#include "nms-ifnet-wpa-parser.h"
+#include "nms-ifnet-connection.h"
+
+static char *
+connection_id_from_ifnet_name (const char *conn_name)
+{
+	int name_len = strlen (conn_name);
+
+	/* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */
+	if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
+		GBytes *bytes = nm_utils_hexstr2bin (conn_name);
+		char *buf;
+
+		if (bytes) {
+			buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+			g_bytes_unref (bytes);
+			return buf;
+		}
+	}
+
+	return g_strdup (conn_name);
+}
+
+static gboolean eap_simple_reader (const char *eap_method,
+                                   const char *ssid,
+                                   NMSetting8021x *s_8021x,
+                                   gboolean phase2,
+                                   const char *basepath,
+                                   GError **error);
+
+static gboolean eap_tls_reader (const char *eap_method,
+                                const char *ssid,
+                                NMSetting8021x *s_8021x,
+                                gboolean phase2,
+                                const char *basepath,
+                                GError **error);
+
+static gboolean eap_peap_reader (const char *eap_method,
+                                 const char *ssid,
+                                 NMSetting8021x *s_8021x,
+                                 gboolean phase2,
+                                 const char *basepath,
+                                 GError **error);
+
+static gboolean eap_ttls_reader (const char *eap_method,
+                                 const char *ssid,
+                                 NMSetting8021x *s_8021x,
+                                 gboolean phase2,
+                                 const char *basepath,
+                                 GError **error);
+
+typedef struct {
+	const char *method;
+	 gboolean (*reader) (const char *eap_method,
+	                     const char *ssid,
+	                     NMSetting8021x *s_8021x,
+	                     gboolean phase2,
+	                     const char *basepath,
+	                     GError **error);
+	gboolean wifi_phase2_only;
+} EAPReader;
+
+static EAPReader eap_readers[] = {
+	{"md5", eap_simple_reader, TRUE},
+	{"pwd", eap_simple_reader, TRUE},
+	{"pap", eap_simple_reader, TRUE},
+	{"chap", eap_simple_reader, TRUE},
+	{"mschap", eap_simple_reader, TRUE},
+	{"mschapv2", eap_simple_reader, TRUE},
+	{"leap", eap_simple_reader, TRUE},
+	{"tls", eap_tls_reader, FALSE},
+	{"peap", eap_peap_reader, FALSE},
+	{"ttls", eap_ttls_reader, FALSE},
+	{NULL, NULL}
+};
+
+/* reading identity and password */
+static gboolean
+eap_simple_reader (const char *eap_method,
+                   const char *ssid,
+                   NMSetting8021x *s_8021x,
+                   gboolean phase2,
+                   const char *basepath,
+                   GError **error)
+{
+	const char *value;
+
+	/* identity */
+	value = wpa_get_value (ssid, "identity");
+	if (!value) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
+			     eap_method);
+		return FALSE;
+	}
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
+
+	/* password */
+	value = wpa_get_value (ssid, "password");
+	if (!value) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_PASSWORD for EAP method '%s'.",
+			     eap_method);
+		return FALSE;
+	}
+
+	g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL);
+
+	return TRUE;
+}
+
+static char *
+get_cert (const char *ssid, const char *key, const char *basepath)
+{
+	const char *orig;
+
+	/* If it's a relative path, convert to absolute using 'basepath' */
+	orig = wpa_get_value (ssid, key);
+	if (g_path_is_absolute (orig))
+		return g_strdup (orig);
+	return g_strdup_printf ("%s/%s", basepath, orig);
+}
+
+static gboolean
+eap_tls_reader (const char *eap_method,
+                const char *ssid,
+                NMSetting8021x *s_8021x,
+                gboolean phase2,
+                const char *basepath,
+                GError **error)
+{
+	const char *value;
+	char *ca_cert = NULL;
+	char *client_cert = NULL;
+	char *privkey = NULL;
+	const char *privkey_password = NULL;
+	gboolean success = FALSE;
+	NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+	/* identity */
+	value = wpa_get_value (ssid, "identity");
+	if (!value) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
+			     eap_method);
+		return FALSE;
+	}
+	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
+
+	/* ca cert */
+	ca_cert = get_cert (ssid, phase2 ? "ca_cert2" : "ca_cert", basepath);
+	if (ca_cert) {
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
+								   ca_cert,
+								   NM_SETTING_802_1X_CK_SCHEME_PATH,
+								   NULL, error))
+				goto done;
+		} else {
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+							    ca_cert,
+							    NM_SETTING_802_1X_CK_SCHEME_PATH,
+							    NULL, error))
+				goto done;
+		}
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "    missing %s for EAP method '%s'; this is insecure!",
+		             phase2 ? "IEEE_8021X_INNER_CA_CERT" :
+		             "IEEE_8021X_CA_CERT", eap_method);
+	}
+
+	/* Private key password */
+	privkey_password = wpa_get_value (ssid,
+					  phase2 ? "private_key2_passwd" :
+					  "private_key_passwd");
+
+	if (!privkey_password) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing %s for EAP method '%s'.",
+			     phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" :
+			     "IEEE_8021X_PRIVATE_KEY_PASSWORD", eap_method);
+		goto done;
+	}
+
+	/* The private key itself */
+	privkey = get_cert (ssid, phase2 ? "private_key2" : "private_key", basepath);
+	if (!privkey) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing %s for EAP method '%s'.",
+			     phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" :
+			     "IEEE_8021X_PRIVATE_KEY", eap_method);
+		goto done;
+	}
+
+	if (phase2) {
+		if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
+							       privkey,
+							       privkey_password,
+							       NM_SETTING_802_1X_CK_SCHEME_PATH,
+							       &privkey_format,
+							       error))
+			goto done;
+	} else {
+		if (!nm_setting_802_1x_set_private_key (s_8021x,
+							privkey,
+							privkey_password,
+							NM_SETTING_802_1X_CK_SCHEME_PATH,
+							&privkey_format, error))
+			goto done;
+	}
+
+	/* Only set the client certificate if the private key is not PKCS#12 format,
+	 * as NM (due to supplicant restrictions) requires.  If the key was PKCS#12,
+	 * then nm_setting_802_1x_set_private_key() already set the client certificate
+	 * to the same value as the private key.
+	 */
+	if (privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
+	    || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) {
+		client_cert = get_cert (ssid, phase2 ? "client_cert2" : "client_cert", basepath);
+		if (!client_cert) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Missing %s for EAP method '%s'.",
+				     phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" :
+				     "IEEE_8021X_CLIENT_CERT", eap_method);
+			goto done;
+		}
+
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x,
+								       client_cert,
+								       NM_SETTING_802_1X_CK_SCHEME_PATH,
+								       NULL,
+								       error))
+				goto done;
+		} else {
+			if (!nm_setting_802_1x_set_client_cert (s_8021x,
+								client_cert,
+								NM_SETTING_802_1X_CK_SCHEME_PATH,
+								NULL, error))
+				goto done;
+		}
+	}
+
+	success = TRUE;
+
+done:
+	g_free (ca_cert);
+	g_free (client_cert);
+	g_free (privkey);
+	return success;
+}
+
+static gboolean
+eap_peap_reader (const char *eap_method,
+                 const char *ssid,
+                 NMSetting8021x *s_8021x,
+                 gboolean phase2,
+                 const char *basepath,
+                 GError **error)
+{
+	char *ca_cert = NULL;
+	const char *inner_auth = NULL;
+	const char *peapver = NULL;
+	char **list = NULL, **iter, *lower;
+	gboolean success = FALSE;
+
+	ca_cert = get_cert (ssid, "ca_cert", basepath);
+	if (ca_cert) {
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+						    ca_cert,
+						    NM_SETTING_802_1X_CK_SCHEME_PATH,
+						    NULL, error))
+			goto done;
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "    missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
+		             eap_method);
+	}
+
+	peapver = wpa_get_value (ssid, "phase1");
+	/* peap version, default is automatic */
+	if (peapver && strstr (peapver, "peapver")) {
+		if (strstr (peapver, "peapver=0"))
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER,
+				      "0", NULL);
+		else if (strstr (peapver, "peapver=1"))
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER,
+				      "1", NULL);
+		else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Unknown IEEE_8021X_PEAP_VERSION value '%s'",
+				     peapver);
+			goto done;
+		}
+	}
+
+	/* peaplabel */
+	if (peapver && strstr (peapver, "peaplabel=1"))
+		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1",
+			      NULL);
+
+	inner_auth = wpa_get_value (ssid, "phase2");
+	if (!inner_auth) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_INNER_AUTH_METHODS.");
+		goto done;
+	}
+	/* Handle options for the inner auth method */
+	list = g_strsplit (inner_auth, " ", 0);
+	for (iter = list; iter && *iter; iter++) {
+		gchar *pos = NULL;
+
+		if (!strlen (*iter))
+			continue;
+
+		if (!(pos = strstr (*iter, "MSCHAPV2"))
+		    || !(pos = strstr (*iter, "MD5"))
+		    || !(pos = strstr (*iter, "GTC"))) {
+			if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error))
+				goto done;
+		} else if (!(pos = strstr (*iter, "TLS"))) {
+			if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error))
+				goto done;
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
+				     *iter);
+			goto done;
+		}
+
+		pos = strchr (*iter, '=');
+		if (pos && *pos) {
+			pos++;
+			lower = g_ascii_strdown (pos, -1);
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower,
+				      NULL);
+			g_free (lower);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "No IEEE_8021X_INNER_AUTH_METHOD.");
+			goto done;
+		}
+		break;
+	}
+
+	if (!nm_setting_802_1x_get_phase2_auth (s_8021x)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "No valid IEEE_8021X_INNER_AUTH_METHODS found.");
+		goto done;
+	}
+
+	success = TRUE;
+
+done:
+	g_free (ca_cert);
+	if (list)
+		g_strfreev (list);
+	return success;
+}
+
+static gboolean
+eap_ttls_reader (const char *eap_method,
+                 const char *ssid,
+                 NMSetting8021x *s_8021x,
+                 gboolean phase2,
+                 const char *basepath,
+                 GError **error)
+{
+	gboolean success = FALSE;
+	const char *anon_ident = NULL;
+	char *ca_cert = NULL;
+	const char *tmp;
+	char **list = NULL, **iter, *inner_auth = NULL;
+
+	/* ca cert */
+	ca_cert = get_cert (ssid, "ca_cert", basepath);
+	if (ca_cert) {
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
+						    ca_cert,
+						    NM_SETTING_802_1X_CK_SCHEME_PATH,
+						    NULL, error))
+			goto done;
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "    missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
+		             eap_method);
+	}
+
+	/* anonymous indentity for tls */
+	anon_ident = wpa_get_value (ssid, "anonymous_identity");
+	if (anon_ident && strlen (anon_ident))
+		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
+			      anon_ident, NULL);
+
+	tmp = wpa_get_value (ssid, "phase2");
+	if (!tmp) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_INNER_AUTH_METHODS.");
+		goto done;
+	}
+
+	/* Handle options for the inner auth method */
+	inner_auth = g_ascii_strdown (tmp, -1);
+	list = g_strsplit (inner_auth, " ", 0);
+	for (iter = list; iter && *iter; iter++) {
+		gchar *pos = NULL;
+
+		if (!strlen (*iter))
+			continue;
+		if ((pos = strstr (*iter, "mschapv2")) != NULL
+		    || (pos = strstr (*iter, "mschap")) != NULL
+		    || (pos = strstr (*iter, "pap")) != NULL
+		    || (pos = strstr (*iter, "chap")) != NULL) {
+			if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error))
+				goto done;
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH,
+				      pos, NULL);
+		} else if ((pos = strstr (*iter, "tls")) != NULL) {
+			if (!eap_tls_reader (pos, ssid, s_8021x, TRUE, basepath, error))
+				goto done;
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP,
+				      "tls", NULL);
+		} else if ((pos = strstr (*iter, "mschapv2")) != NULL
+			   || (pos = strstr (*iter, "md5")) != NULL) {
+			if (!eap_simple_reader (pos, ssid, s_8021x, TRUE, basepath, error)) {
+				nm_log_warn (LOGD_SETTINGS, "SIMPLE ERROR");
+				goto done;
+			}
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP,
+				      pos, NULL);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Unknown IEEE_8021X_INNER_AUTH_METHOD '%s'.",
+				     *iter);
+			goto done;
+		}
+		break;
+	}
+
+	success = TRUE;
+done:
+	g_free (ca_cert);
+	if (list)
+		g_strfreev (list);
+	g_free (inner_auth);
+	return success;
+}
+
+/* type is already decided by net_parser, this function is just used to
+ * doing tansformation*/
+static const gchar *
+guess_connection_type (const char *conn_name)
+{
+	const gchar *type = ifnet_get_data (conn_name, "type");
+	const gchar *ret_type = NULL;
+
+	if (!g_strcmp0 (type, "ppp"))
+		ret_type = NM_SETTING_PPPOE_SETTING_NAME;
+
+	if (!g_strcmp0 (type, "wireless"))
+		ret_type = NM_SETTING_WIRELESS_SETTING_NAME;
+
+	if (!ret_type)
+		ret_type = NM_SETTING_WIRED_SETTING_NAME;
+
+	nm_log_info (LOGD_SETTINGS, "guessed connection type (%s) = %s", conn_name, ret_type);
+	return ret_type;
+}
+
+/* Reading mac address for setting connection option.
+ * Unmanaged device mac address is required by NetworkManager*/
+static gboolean
+read_mac_address (const char *conn_name, const char **mac, GError **error)
+{
+	const char *value = ifnet_get_data (conn_name, "mac");
+
+	if (!value || !strlen (value))
+		return TRUE;
+
+	if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "The MAC address '%s' was invalid.", value);
+		return FALSE;
+	}
+
+	*mac = value;
+	return TRUE;
+}
+
+static gboolean
+make_wired_connection_setting (NMConnection *connection,
+                               const char *conn_name,
+                               GError **error)
+{
+	const char *mac = NULL;
+	NMSettingWired *s_wired = NULL;
+	const char *value = NULL;
+
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+
+	/* mtu_xxx */
+	value = ifnet_get_data (conn_name, "mtu");
+	if (value) {
+		long int mtu;
+
+		errno = 0;
+		mtu = strtol (value, NULL, 10);
+		if (errno || mtu < 0 || mtu > 65535) {
+			nm_log_warn (LOGD_SETTINGS, "    invalid MTU '%s' for %s", value, conn_name);
+		} else
+			g_object_set (s_wired, NM_SETTING_WIRED_MTU,
+				      (guint32) mtu, NULL);
+	}
+
+	if (!read_mac_address (conn_name, &mac, error)) {
+		g_object_unref (s_wired);
+		return FALSE;
+	}
+
+	if (mac)
+		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	return TRUE;
+}
+
+/* add NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
+ * NM_SETTING_IP_CONFIG_DHCP_CLIENT_ID in future*/
+static gboolean
+make_ip4_setting (NMConnection *connection,
+                  const char *conn_name,
+                  GError **error)
+{
+
+	NMSettingIPConfig *ip4_setting =
+	    NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
+	const char *value, *method = NULL;
+	gboolean is_static_block = is_static_ip4 (conn_name);
+	ip_block *iblock = NULL;
+
+	/* set dhcp options (dhcp_xxx) */
+	value = ifnet_get_data (conn_name, "dhcp");
+	g_object_set (ip4_setting, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, value
+		      && strstr (value, "nodns") ? TRUE : FALSE,
+		      NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, value
+		      && strstr (value, "nogateway") ? TRUE : FALSE, NULL);
+
+	if (!is_static_block) {
+		method = ifnet_get_data (conn_name, "config");
+		if (!method){
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+						 "Unknown config for %s", conn_name);
+			g_object_unref (ip4_setting);
+			return FALSE;
+		}
+		if (strstr (method, "dhcp"))
+			g_object_set (ip4_setting,
+						  NM_SETTING_IP_CONFIG_METHOD,
+						  NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+						  NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL);
+		else if (strstr (method, "autoip")) {
+			g_object_set (ip4_setting,
+						  NM_SETTING_IP_CONFIG_METHOD,
+						  NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
+						  NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL);
+			nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
+			return TRUE;
+		} else if (strstr (method, "shared")) {
+			g_object_set (ip4_setting,
+						  NM_SETTING_IP_CONFIG_METHOD,
+						  NM_SETTING_IP4_CONFIG_METHOD_SHARED,
+						  NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL);
+			nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
+			return TRUE;
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+						 "Unknown config for %s", conn_name);
+			g_object_unref (ip4_setting);
+			return FALSE;
+		}
+		nm_log_info (LOGD_SETTINGS, "Using %s method for %s", method, conn_name);
+	}else {
+		iblock = convert_ip4_config_block (conn_name);
+		if (!iblock) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Ifnet plugin: can't aquire ip configuration for %s",
+				     conn_name);
+			g_object_unref (ip4_setting);
+			return FALSE;
+		}
+		/************** add all ip settings to the connection**********/
+		while (iblock) {
+			ip_block *current_iblock;
+			NMIPAddress *ip4_addr;
+			GError *local = NULL;
+
+			ip4_addr = nm_ip_address_new (AF_INET, iblock->ip, iblock->prefix, &local);
+			if (iblock->next_hop)
+				g_object_set (ip4_setting,
+					      NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES,
+					      TRUE, NULL);
+
+			if (ip4_addr) {
+				if (!nm_setting_ip_config_add_address (ip4_setting, ip4_addr))
+					nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP4 address");
+				nm_ip_address_unref (ip4_addr);
+			} else {
+				nm_log_warn (LOGD_SETTINGS, "    ignoring invalid address entry: %s", local->message);
+				g_clear_error (&local);
+			}
+
+			current_iblock = iblock;
+			iblock = iblock->next;
+			destroy_ip_block (current_iblock);
+
+		}
+		g_object_set (ip4_setting,
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+		              NM_SETTING_IP_CONFIG_NEVER_DEFAULT, !has_default_ip4_route (conn_name),
+		              NULL);
+	}
+
+	/* add dhcp hostname and client id */
+	if (!is_static_block && strstr (method, "dhcp")) {
+		gchar *dhcp_hostname, *client_id;
+
+		get_dhcp_hostname_and_client_id (&dhcp_hostname, &client_id);
+		if (dhcp_hostname) {
+			g_object_set (ip4_setting,
+				      NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
+				      dhcp_hostname, NULL);
+			nm_log_info (LOGD_SETTINGS, "DHCP hostname: %s", dhcp_hostname);
+			g_free (dhcp_hostname);
+		}
+		if (client_id) {
+			g_object_set (ip4_setting,
+				      NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
+				      client_id, NULL);
+			nm_log_info (LOGD_SETTINGS, "DHCP client id: %s", client_id);
+			g_free (client_id);
+		}
+	}
+
+	/* add all IPv4 dns servers, IPv6 servers will be ignored */
+	set_ip4_dns_servers (ip4_setting, conn_name);
+
+	/* DNS searches */
+	value = ifnet_get_data (conn_name, "dns_search");
+	if (value) {
+		gs_free char *stripped = g_strdup (value);
+		char **searches = NULL;
+
+		strip_string (stripped, '"');
+
+		searches = g_strsplit (stripped, " ", 0);
+		if (searches) {
+			char **item;
+
+			for (item = searches; *item; item++) {
+				if (strlen (*item)) {
+					if (!nm_setting_ip_config_add_dns_search (ip4_setting, *item))
+						nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *item);
+				}
+			}
+			g_strfreev (searches);
+		}
+	}
+
+	/* static routes */
+	iblock = convert_ip4_routes_block (conn_name);
+	while (iblock) {
+		ip_block *current_iblock = iblock;
+		const char *metric_str;
+		char *stripped;
+		gint64 metric;
+		NMIPRoute *route;
+		GError *local = NULL;
+
+		if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) {
+			metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+		} else {
+			metric_str = ifnet_get_global_data ("metric");
+			if (metric_str) {
+				stripped = g_strdup (metric_str);
+				strip_string (stripped, '"');
+				metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+				g_free (stripped);
+			} else
+				metric = -1;
+		}
+
+		route = nm_ip_route_new (AF_INET, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local);
+		if (route) {
+			if (nm_setting_ip_config_add_route (ip4_setting, route))
+				nm_log_info (LOGD_SETTINGS, "new IP4 route:%s\n", iblock->ip);
+			else
+				nm_log_warn (LOGD_SETTINGS, "duplicate IP4 route");
+			nm_ip_route_unref (route);
+		} else {
+			nm_log_warn (LOGD_SETTINGS, "    ignoring invalid route entry: %s", local->message);
+			g_clear_error (&local);
+		}
+
+		current_iblock = iblock;
+		iblock = iblock->next;
+		destroy_ip_block (current_iblock);
+	}
+
+	/* Finally add setting to connection */
+	nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
+
+	return TRUE;
+}
+
+static gboolean
+make_ip6_setting (NMConnection *connection,
+                  const char *conn_name,
+                  GError **error)
+{
+	NMSettingIPConfig *s_ip6 = NULL;
+	gboolean is_static_block = is_static_ip6 (conn_name);
+
+	// used to disable IPv6
+	gboolean ipv6_enabled = FALSE;
+	gchar *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+	const char *value;
+	ip_block *iblock;
+	gboolean never_default = !has_default_ip6_route (conn_name);
+
+	s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+
+	value = ifnet_get_data (conn_name, "enable_ipv6");
+	if (value && is_true (value))
+		ipv6_enabled = TRUE;
+
+	//FIXME Handle other methods that NM supports in future
+	// Currently only Manual and DHCP are supported
+	if (!ipv6_enabled) {
+		g_object_set (s_ip6,
+			      NM_SETTING_IP_CONFIG_METHOD,
+			      NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
+		goto done;
+	} else if (!is_static_block) {
+		// config_eth* contains "dhcp6"
+		method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+		never_default = FALSE;
+	}
+	// else if (!has_ip6_address(conn_name))
+	// doesn't have "dhcp6" && doesn't have any ipv6 address
+	// method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+	else
+		// doesn't have "dhcp6" && has at least one ipv6 address
+		method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+	nm_log_info (LOGD_SETTINGS, "IPv6 for %s enabled, using %s", conn_name, method);
+
+	g_object_set (s_ip6,
+		      NM_SETTING_IP_CONFIG_METHOD, method,
+		      NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, FALSE,
+		      NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, FALSE,
+		      NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, NULL);
+
+	/* Make manual settings */
+	if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		ip_block *current_iblock;
+
+		iblock = convert_ip6_config_block (conn_name);
+		if (!iblock) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Ifnet plugin: can't aquire ip6 configuration for %s",
+				     conn_name);
+			goto error;
+		}
+		/* add all IPv6 addresses */
+		while (iblock) {
+			NMIPAddress *ip6_addr;
+			GError *local = NULL;
+
+			ip6_addr = nm_ip_address_new (AF_INET6, iblock->ip, iblock->prefix, &local);
+			if (ip6_addr) {
+				if (nm_setting_ip_config_add_address (s_ip6, ip6_addr)) {
+					nm_log_info (LOGD_SETTINGS, "ipv6 addresses count: %d",
+					             nm_setting_ip_config_get_num_addresses (s_ip6));
+				} else {
+					nm_log_warn (LOGD_SETTINGS, "ignoring duplicate IP6 address");
+				}
+				nm_ip_address_unref (ip6_addr);
+			} else {
+				nm_log_warn (LOGD_SETTINGS, "    ignoring invalid address entry: %s", local->message);
+				g_clear_error (&local);
+			}
+
+			current_iblock = iblock;
+			iblock = iblock->next;
+			destroy_ip_block (current_iblock);
+		}
+
+	} else if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		/* - autoconf or DHCPv6 stuff goes here */
+	}
+	// DNS Servers, set NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS TRUE here
+	set_ip6_dns_servers (s_ip6, conn_name);
+
+	/* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */
+
+	// Add routes
+	iblock = convert_ip6_routes_block (conn_name);
+	if (iblock)
+		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES,
+			      TRUE, NULL);
+	/* Add all IPv6 routes */
+	while (iblock) {
+		ip_block *current_iblock = iblock;
+		const char *metric_str;
+		char *stripped;
+		gint64 metric;
+		NMIPRoute *route;
+		GError *local = NULL;
+
+		/* metric is not per routes configuration right now
+		 * global metric is also supported (metric="x") */
+		if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL)
+			metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+		else {
+			metric_str = ifnet_get_global_data ("metric");
+			if (metric_str) {
+				stripped = g_strdup (metric_str);
+				strip_string (stripped, '"');
+				metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+				g_free (stripped);
+			} else
+				metric = 1;
+		}
+
+		route = nm_ip_route_new (AF_INET6, iblock->ip, iblock->prefix, iblock->next_hop, metric, &local);
+		if (route) {
+			if (nm_setting_ip_config_add_route (s_ip6, route))
+				nm_log_info (LOGD_SETTINGS, "    new IP6 route");
+			else
+				nm_log_warn (LOGD_SETTINGS, "    duplicate IP6 route");
+			nm_ip_route_unref (route);
+		} else {
+			nm_log_warn (LOGD_SETTINGS, "    ignoring invalid route entry: %s", local->message);
+			g_clear_error (&local);
+		}
+
+		current_iblock = iblock;
+		iblock = iblock->next;
+		destroy_ip_block (current_iblock);
+	}
+
+done:
+	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+	return TRUE;
+
+error:
+	g_object_unref (s_ip6);
+	nm_log_warn (LOGD_SETTINGS, "    Ignore IPv6 for %s", conn_name);
+	return FALSE;
+}
+
+static NMSetting *
+make_wireless_connection_setting (const char *conn_name,
+                                  NMSetting8021x **s_8021x,
+                                  GError **error)
+{
+	const char *mac = NULL;
+	NMSettingWireless *wireless_setting = NULL;
+	gboolean adhoc = FALSE;
+	const char *value;
+	const char *type;
+
+	/* PPP over WIFI is not supported yet */
+	g_return_val_if_fail (conn_name != NULL
+			      && strcmp (ifnet_get_data (conn_name, "type"),
+					 "ppp") != 0, NULL);
+	type = ifnet_get_data (conn_name, "type");
+	if (strcmp (type, "ppp") == 0) {
+		nm_log_warn (LOGD_SETTINGS, "PPP over WIFI is not supported yet");
+		return NULL;
+	}
+
+	wireless_setting = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+	if (read_mac_address (conn_name, &mac, error)) {
+		if (mac) {
+			g_object_set (wireless_setting,
+				      NM_SETTING_WIRELESS_MAC_ADDRESS, mac,
+				      NULL);
+		}
+	} else {
+		g_object_unref (wireless_setting);
+		return NULL;
+	}
+
+	/* handle ssid (hex and ascii) */
+	if (conn_name) {
+		GBytes *bytes;
+		gsize ssid_len = 0, value_len = strlen (conn_name);
+
+		ssid_len = value_len;
+		if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
+			/* Hex representation */
+			if (value_len % 2) {
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+					     "Invalid SSID '%s' size (looks like hex but length not multiple of 2)",
+					     conn_name);
+				goto error;
+			}
+
+			bytes = nm_utils_hexstr2bin (conn_name);
+			if (!bytes) {
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid SSID '%s' (looks like hex SSID but isn't)",
+				             conn_name);
+				goto error;
+			}
+		} else
+			bytes = g_bytes_new (conn_name, value_len);
+
+		ssid_len = g_bytes_get_size (bytes);
+		if (ssid_len > 32 || ssid_len == 0) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
+				     conn_name, ssid_len);
+			goto error;
+		}
+
+		g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL);
+		g_bytes_unref (bytes);
+	} else {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing SSID");
+		goto error;
+	}
+
+	/* mode=0: infrastructure
+	 * mode=1: adhoc */
+	value = wpa_get_value (conn_name, "mode");
+	if (value)
+		adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE;
+
+	if (exist_ssid (conn_name)) {
+		const char *mode = adhoc ? "adhoc" : "infrastructure";
+
+		g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, mode,
+			      NULL);
+		nm_log_info (LOGD_SETTINGS, "Using mode: %s", mode);
+	}
+
+	/* BSSID setting */
+	value = wpa_get_value (conn_name, "bssid");
+	if (value) {
+		if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+						 "Invalid BSSID '%s'", value);
+			goto error;
+		}
+
+		g_object_set (wireless_setting, NM_SETTING_WIRELESS_BSSID,
+			      value, NULL);
+
+	}
+
+	/* mtu_ssid="xx" */
+	value = ifnet_get_data (conn_name, "mtu");
+	if (value) {
+		long int mtu;
+
+		errno = 0;
+		mtu = strtol (value, NULL, 10);
+		if (errno || mtu < 0 || mtu > 50000) {
+			nm_log_warn (LOGD_SETTINGS, "    invalid MTU '%s' for %s", value, conn_name);
+		} else
+			g_object_set (wireless_setting, NM_SETTING_WIRELESS_MTU,
+				      (guint32) mtu, NULL);
+
+	}
+
+	nm_log_info (LOGD_SETTINGS, "wireless_setting added for %s", conn_name);
+	return NM_SETTING (wireless_setting);
+error:
+	if (wireless_setting)
+		g_object_unref (wireless_setting);
+	return NULL;
+
+}
+
+static NMSettingWirelessSecurity *
+make_leap_setting (const char *ssid, GError **error)
+{
+	NMSettingWirelessSecurity *wsec;
+	const char *value;
+
+	wsec =
+	    NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+
+	value = wpa_get_value (ssid, "password");
+	if (value && strlen (value))
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+			      value, NULL);
+
+	value = wpa_get_value (ssid, "identity");
+	if (!value || !strlen (value)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing LEAP identity");
+		goto error;
+	}
+	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, value,
+		      NULL);
+
+	g_object_set (wsec,
+		      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+		      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", NULL);
+
+	return wsec;
+error:
+	if (wsec)
+		g_object_unref (wsec);
+	return NULL;
+}
+
+static gboolean
+add_one_wep_key (const char *ssid,
+                 const char *key,
+                 int key_idx,
+                 NMSettingWirelessSecurity *s_wsec,
+                 GError **error)
+{
+	const char *value;
+	char *converted = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (ssid != NULL, FALSE);
+	g_return_val_if_fail (key != NULL, FALSE);
+	g_return_val_if_fail (key_idx >= 0 && key_idx <= 3, FALSE);
+	g_return_val_if_fail (s_wsec != NULL, FALSE);
+
+	value = wpa_get_value (ssid, key);
+	if (!value)
+		return TRUE;
+
+	/* Validate keys */
+	if (strlen (value) == 10 || strlen (value) == 26) {
+		/* Hexadecimal WEP key */
+		if (!is_hex (value)) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid hexadecimal WEP key.");
+			goto out;
+		}
+		converted = g_strdup (value);
+	} else if (value[0] == '"'
+		   && (strlen (value) == 7 || strlen (value) == 15)) {
+		/* ASCII passphrase */
+		char *tmp = g_strdup (value);
+		char *p = strip_string (tmp, '"');
+
+		if (!is_ascii (p)) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid ASCII WEP passphrase.");
+			g_free (tmp);
+			goto out;
+
+		}
+
+		converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1);
+		g_free (tmp);
+	} else {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Invalid WEP key length. Key: %s", value);
+		goto out;
+	}
+
+	if (converted) {
+		nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, converted);
+		g_free (converted);
+		success = TRUE;
+	}
+
+out:
+	return success;
+}
+
+static gboolean
+add_wep_keys (const char *ssid,
+              NMSettingWirelessSecurity *s_wsec,
+              GError **error)
+{
+	if (!add_one_wep_key (ssid, "wep_key0", 0, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ssid, "wep_key1", 1, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ssid, "wep_key2", 2, s_wsec, error))
+		return FALSE;
+	if (!add_one_wep_key (ssid, "wep_key3", 3, s_wsec, error))
+		return FALSE;
+	return TRUE;
+
+}
+
+static NMSettingWirelessSecurity *
+make_wep_setting (const char *ssid, GError **error)
+{
+	const char *auth_alg, *value;
+	int default_key_idx = 0;
+	NMSettingWirelessSecurity *s_wireless_sec;
+
+	s_wireless_sec =
+	    NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+	g_object_set (s_wireless_sec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+		      "none", NULL);
+
+	/* default key index */
+	value = wpa_get_value (ssid, "wep_tx_keyidx");
+	if (value) {
+		default_key_idx = atoi (value);
+		if (default_key_idx >= 0 && default_key_idx <= 3) {
+			g_object_set (s_wireless_sec,
+				      NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX,
+				      default_key_idx, NULL);
+			nm_log_info (LOGD_SETTINGS, "Default key index: %d", default_key_idx);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Invalid default WEP key '%s'", value);
+			goto error;
+		}
+	}
+
+	if (!add_wep_keys (ssid, s_wireless_sec, error))
+		goto error;
+
+	/* If there's a default key, ensure that key exists */
+	if ((default_key_idx == 1)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Default WEP key index was 2, but no valid KEY2 exists.");
+		goto error;
+	} else if ((default_key_idx == 2)
+		   && !nm_setting_wireless_security_get_wep_key (s_wireless_sec,
+								 2)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Default WEP key index was 3, but no valid KEY3 exists.");
+		goto error;
+	} else if ((default_key_idx == 3)
+		   && !nm_setting_wireless_security_get_wep_key (s_wireless_sec,
+								 3)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Default WEP key index was 4, but no valid KEY4 exists.");
+		goto error;
+	}
+
+	/* authentication algorithms */
+	auth_alg = wpa_get_value (ssid, "auth_alg");
+	if (auth_alg) {
+		if (strcmp (auth_alg, "OPEN") == 0) {
+			g_object_set (s_wireless_sec,
+				      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
+				      "open", NULL);
+			nm_log_info (LOGD_SETTINGS, "WEP: Use open system authentication");
+		} else if (strcmp (auth_alg, "SHARED") == 0) {
+			g_object_set (s_wireless_sec,
+				      NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
+				      "shared", NULL);
+			nm_log_info (LOGD_SETTINGS, "WEP: Use shared system authentication");
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Invalid WEP authentication algorithm '%s'",
+				     auth_alg);
+			goto error;
+		}
+
+	}
+
+	if (!nm_setting_wireless_security_get_wep_key (s_wireless_sec, 0)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 1)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 2)
+	    && !nm_setting_wireless_security_get_wep_key (s_wireless_sec, 3)
+	    && !nm_setting_wireless_security_get_wep_tx_keyidx (s_wireless_sec)) {
+		if (auth_alg && !strcmp (auth_alg, "shared")) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "WEP Shared Key authentication is invalid for "
+				     "unencrypted connections.");
+			goto error;
+		}
+		/* Unencrypted */
+		g_object_unref (s_wireless_sec);
+		s_wireless_sec = NULL;
+	}
+	return s_wireless_sec;
+
+error:
+	if (s_wireless_sec)
+		g_object_unref (s_wireless_sec);
+	return NULL;
+}
+
+static char *
+parse_wpa_psk (const char *psk, GError **error)
+{
+	char *hashed = NULL;
+	gboolean quoted = FALSE;
+
+	if (!psk) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing WPA_PSK for WPA-PSK key management");
+		return NULL;
+	}
+
+	/* Passphrase must be between 10 and 66 characters in length because WPA
+	 * hex keys are exactly 64 characters (no quoting), and WPA passphrases
+	 * are between 8 and 63 characters (inclusive), plus optional quoting if
+	 * the passphrase contains spaces.
+	 */
+
+	if (psk[0] == '"' && psk[strlen (psk) - 1] == '"')
+		quoted = TRUE;
+	if (!quoted && (strlen (psk) == 64)) {
+		/* Verify the hex PSK; 64 digits */
+		if (!is_hex (psk)) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid WPA_PSK (contains non-hexadecimal characters)");
+			goto out;
+		}
+		hashed = g_strdup (psk);
+	} else {
+		char *stripped = g_strdup (psk);
+
+		strip_string (stripped, '"');
+
+		/* Length check */
+		if (strlen (stripped) < 8 || strlen (stripped) > 63) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Invalid WPA_PSK (passphrases must be between "
+				     "8 and 63 characters long (inclusive))");
+			g_free (stripped);
+			goto out;
+		}
+
+		hashed = g_strdup (stripped);
+		g_free (stripped);
+	}
+
+	if (!hashed) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Invalid WPA_PSK (doesn't look like a passphrase or hex key)");
+		goto out;
+	}
+
+out:
+	return hashed;
+}
+
+static gboolean
+fill_wpa_ciphers (const char *ssid,
+                  NMSettingWirelessSecurity *wsec,
+                  gboolean group,
+                  gboolean adhoc)
+{
+	const char *value;
+	char **list = NULL, **iter;
+	int i = 0;
+
+	value = wpa_get_value (ssid, group ? "group" : "pairwise");
+	if (!value)
+		return TRUE;
+
+	list = g_strsplit_set (value, " ", 0);
+	for (iter = list; iter && *iter; iter++, i++) {
+		/* Ad-Hoc configurations cannot have pairwise ciphers, and can only
+		 * have one group cipher.  Ignore any additional group ciphers and
+		 * any pairwise ciphers specified.
+		 */
+		if (adhoc) {
+			if (group && (i > 0)) {
+				nm_log_warn (LOGD_SETTINGS, "    ignoring group cipher '%s' (only one group cipher allowed in Ad-Hoc mode)",
+				             *iter);
+				continue;
+			} else if (!group) {
+				nm_log_warn (LOGD_SETTINGS, "    ignoring pairwise cipher '%s' (pairwise not used in Ad-Hoc mode)",
+				             *iter);
+				continue;
+			}
+		}
+
+		if (!strcmp (*iter, "CCMP")) {
+			if (group)
+				nm_setting_wireless_security_add_group (wsec,
+									"ccmp");
+			else
+				nm_setting_wireless_security_add_pairwise (wsec,
+									   "ccmp");
+		} else if (!strcmp (*iter, "TKIP")) {
+			if (group)
+				nm_setting_wireless_security_add_group (wsec,
+									"tkip");
+			else
+				nm_setting_wireless_security_add_pairwise (wsec,
+									   "tkip");
+		} else if (group && !strcmp (*iter, "WEP104"))
+			nm_setting_wireless_security_add_group (wsec, "wep104");
+		else if (group && !strcmp (*iter, "WEP40"))
+			nm_setting_wireless_security_add_group (wsec, "wep40");
+		else {
+			nm_log_warn (LOGD_SETTINGS, "    ignoring invalid %s cipher '%s'",
+			             group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE",
+			             *iter);
+		}
+	}
+
+	if (list)
+		g_strfreev (list);
+	return TRUE;
+}
+
+static NMSetting8021x *
+fill_8021x (const char *ssid,
+            const char *key_mgmt,
+            gboolean wifi,
+            const char *basepath,
+            GError **error)
+{
+	NMSetting8021x *s_8021x;
+	const char *value;
+	char **list, **iter;
+
+	value = wpa_get_value (ssid, "eap");
+	if (!value) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing IEEE_8021X_EAP_METHODS for key management '%s'",
+			     key_mgmt);
+		return NULL;
+	}
+
+	list = g_strsplit (value, " ", 0);
+
+	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+	/* Validate and handle each EAP method */
+	for (iter = list; iter && *iter; iter++) {
+		EAPReader *eap = &eap_readers[0];
+		gboolean found = FALSE;
+		char *lower = NULL;
+
+		lower = g_ascii_strdown (*iter, -1);
+		while (eap->method) {
+			if (strcmp (eap->method, lower))
+				goto next;
+
+			/* Some EAP methods don't provide keying material, thus they
+			 * cannot be used with WiFi unless they are an inner method
+			 * used with TTLS or PEAP or whatever.
+			 */
+			if (wifi && eap->wifi_phase2_only) {
+				nm_log_warn (LOGD_SETTINGS, "    ignored invalid IEEE_8021X_EAP_METHOD '%s'; not allowed for wifi.",
+				             lower);
+				goto next;
+			}
+
+			/* Parse EAP method specific options */
+			if (!(*eap->reader) (lower, ssid, s_8021x, FALSE, basepath, error)) {
+				g_free (lower);
+				goto error;
+			}
+			nm_setting_802_1x_add_eap_method (s_8021x, lower);
+			found = TRUE;
+			break;
+
+		next:
+			eap++;
+		}
+
+		if (!found) {
+			nm_log_warn (LOGD_SETTINGS, "    ignored unknown IEEE_8021X_EAP_METHOD '%s'.", lower);
+		}
+		g_free (lower);
+	}
+	g_strfreev (list);
+
+	if (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 0) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "No valid EAP methods found in IEEE_8021X_EAP_METHODS.");
+		goto error;
+	}
+
+	return s_8021x;
+
+error:
+	g_object_unref (s_8021x);
+	return NULL;
+}
+
+static NMSettingWirelessSecurity *
+make_wpa_setting (const char *ssid,
+                  const char *basepath,
+                  NMSetting8021x **s_8021x,
+                  GError **error)
+{
+	NMSettingWirelessSecurity *wsec;
+	const char *value;
+	char *lower;
+	gboolean adhoc = FALSE;
+
+	if (!exist_ssid (ssid)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "No security info found for ssid: %s", ssid);
+		return NULL;
+	}
+
+	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+
+	/* mode=1: adhoc
+	 * mode=0: infrastructure */
+	value = wpa_get_value (ssid, "mode");
+	if (value)
+		adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE;
+
+	/* Pairwise and Group ciphers */
+	fill_wpa_ciphers (ssid, wsec, FALSE, adhoc);
+	fill_wpa_ciphers (ssid, wsec, TRUE, adhoc);
+
+	/* WPA and/or RSN */
+	if (adhoc) {
+		/* Ad-Hoc mode only supports WPA proto for now */
+		nm_setting_wireless_security_add_proto (wsec, "wpa");
+	} else {
+		nm_setting_wireless_security_add_proto (wsec, "wpa");
+		nm_setting_wireless_security_add_proto (wsec, "rsn");
+
+	}
+
+	value = wpa_get_value (ssid, "key_mgmt");
+	if (!strcmp (value, "WPA-PSK")) {
+		char *psk = parse_wpa_psk (wpa_get_value (ssid, "psk"), error);
+
+		if (!psk)
+			goto error;
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK, psk,
+			      NULL);
+		g_free (psk);
+
+		if (adhoc)
+			g_object_set (wsec,
+				      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+				      "wpa-none", NULL);
+		else
+			g_object_set (wsec,
+				      NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+				      "wpa-psk", NULL);
+	} else if (!strcmp (value, "WPA-EAP") || !strcmp (value, "IEEE8021X")) {
+		if (adhoc) {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "Ad-Hoc mode cannot be used with KEY_MGMT type '%s'",
+				     value);
+			goto error;
+		}
+		*s_8021x = fill_8021x (ssid, value, TRUE, basepath, error);
+		if (!*s_8021x)
+			goto error;
+
+		lower = g_ascii_strdown (value, -1);
+		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+			      lower, NULL);
+		g_free (lower);
+	} else {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Unknown wireless KEY_MGMT type '%s'", value);
+		goto error;
+	}
+	return wsec;
+error:
+	if (wsec)
+		g_object_unref (wsec);
+	return NULL;
+}
+
+static NMSettingWirelessSecurity *
+make_wireless_security_setting (const char *conn_name,
+                                const char *basepath,
+                                NMSetting8021x **s_8021x,
+                                GError ** error)
+{
+	NMSettingWirelessSecurity *wsec = NULL;
+	const char *ssid;
+	gboolean adhoc = FALSE;
+	const char *value;
+
+	g_return_val_if_fail (conn_name != NULL
+			      && strcmp (ifnet_get_data (conn_name, "type"),
+					 "ppp") != 0, NULL);
+	nm_log_info (LOGD_SETTINGS, "updating wireless security settings (%s).", conn_name);
+
+	ssid = conn_name;
+	value = wpa_get_value (ssid, "mode");
+	if (value)
+		adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE;
+
+	value = wpa_get_value (ssid, "key_mgmt");
+	if (!adhoc && g_strcmp0 (value, "IEEE8021X") == 0) {
+		value = wpa_get_value (ssid, "eap");
+		if (value && strcasecmp (value, "LEAP") == 0) {
+			wsec = make_leap_setting (ssid, error);
+			if (wsec == NULL)
+				goto error;
+		}
+	} else if (g_strcmp0 (value, "WPA-PSK") == 0 || g_strcmp0 (value, "WPA-EAP") == 0) {
+		wsec = make_wpa_setting (ssid, basepath, s_8021x, error);
+		if (wsec == NULL)
+			goto error;
+	}
+	if (!wsec) {
+		wsec = make_wep_setting (ssid, error);
+		if (wsec == NULL)
+			goto error;
+	}
+	return wsec;
+
+error:
+	g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		     "Can't handle security information for ssid: %s",
+		     conn_name);
+	return NULL;
+}
+
+/* Currently only support username and password */
+static gboolean
+make_pppoe_connection_setting (NMConnection *connection,
+                               const char *conn_name,
+                               GError **error)
+{
+	NMSettingPppoe *s_pppoe;
+	NMSettingPpp *s_ppp;
+	const char *value;
+
+	s_pppoe = NM_SETTING_PPPOE (nm_setting_pppoe_new ());
+
+	/* username */
+	value = ifnet_get_data (conn_name, "username");
+	if (!value) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "ppp requires at lease a username");
+		return FALSE;
+	}
+	g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, value, NULL);
+
+	/* password */
+	value = ifnet_get_data (conn_name, "password");
+	if (!value) {
+		value = "";
+	}
+
+	g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, value, NULL);
+	nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
+
+	/* PPP setting */
+	s_ppp = (NMSettingPpp *) nm_setting_ppp_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_ppp));
+
+	return TRUE;
+}
+
+NMConnection *
+ifnet_update_connection_from_config_block (const char *conn_name,
+                                           const char *basepath,
+                                           GError **error)
+{
+	const gchar *type = NULL;
+	NMConnection *connection = NULL;
+	NMSettingConnection *setting = NULL;
+	NMSetting8021x *s_8021x = NULL;
+	NMSettingWirelessSecurity *wsec = NULL;
+	gboolean auto_conn = TRUE;
+	const char *value = NULL;
+	gchar *id, *uuid;
+	gboolean success = FALSE;
+
+	connection = nm_simple_connection_new ();
+	setting = nm_connection_get_setting_connection (connection);
+	if (!setting) {
+		setting = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+		g_assert (setting);
+		nm_connection_add_setting (connection, NM_SETTING (setting));
+	}
+
+	type = guess_connection_type (conn_name);
+	value = ifnet_get_data (conn_name, "auto");
+	if (value && !strcmp (value, "false"))
+		auto_conn = FALSE;
+
+	/* Try to read UUID from the ifnet block, otherwise generate UUID from
+	 * the connection ID.
+	 */
+	id = connection_id_from_ifnet_name (conn_name);
+	uuid = g_strdup (ifnet_get_data (conn_name, "uuid"));
+	if (!uuid)
+		uuid = nm_utils_uuid_generate_from_string (id, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
+
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_TYPE, type,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, conn_name,
+	              NM_SETTING_CONNECTION_READ_ONLY, FALSE,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, auto_conn,
+	              NULL);
+	nm_log_info (LOGD_SETTINGS, "name:%s, id:%s, uuid: %s", conn_name, id, uuid);
+	g_free (id);
+	g_free (uuid);
+
+	if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type)
+	    || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) {
+		/* wired setting */
+		if (!make_wired_connection_setting (connection, conn_name, error))
+			goto error;
+
+		/* pppoe setting */
+		if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) {
+			if (!make_pppoe_connection_setting (connection, conn_name, error))
+				goto error;
+		}
+	} else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) {
+		/* wireless setting */
+		NMSetting *wireless_setting;
+
+		wireless_setting = make_wireless_connection_setting (conn_name, &s_8021x, error);
+		if (!wireless_setting)
+			goto error;
+		nm_connection_add_setting (connection, wireless_setting);
+
+		/* wireless security setting */
+		if (wpa_get_value (conn_name, "ssid")) {
+			wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error);
+			if (!wsec)
+				goto error;
+			nm_connection_add_setting (connection, NM_SETTING (wsec));
+			if (s_8021x)
+				nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+		}
+	} else
+		goto error;
+
+	/* IPv4 setting */
+	if (!make_ip4_setting (connection, conn_name, error))
+		goto error;
+
+	/* IPv6 setting */
+	if (!make_ip6_setting (connection, conn_name, error))
+		goto error;
+
+	if (nm_connection_verify (connection, error)) {
+		nm_log_info (LOGD_SETTINGS, "Connection verified %s:%d", conn_name, success);
+	} else {
+		goto error;
+	}
+
+	return connection;
+error:
+	g_object_unref (connection);
+	return NULL;
+}
+
+typedef struct Setting8021xSchemeVtable {
+	const NMSetting8021xSchemeVtable *vtable;
+	const char *ifnet_key;
+} Setting8021xSchemeVtable;
+
+static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = {
+	[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+		.ifnet_key              = "ca_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT],
+		.ifnet_key              = "ca_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		.ifnet_key              = "client_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT],
+		.ifnet_key              = "client_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY],
+		.ifnet_key              = "private_key",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY],
+		.ifnet_key              = "private_key2",
+	},
+};
+
+static gboolean
+write_object (NMSetting8021x *s_8021x,
+              const char *conn_name,
+              GBytes *override_data,
+              const Setting8021xSchemeVtable *objtype,
+              GError **error)
+{
+	NMSetting8021xCKScheme scheme;
+	const char *path = NULL;
+	GBytes *blob = NULL;
+
+	g_return_val_if_fail (conn_name != NULL, FALSE);
+	g_return_val_if_fail (objtype != NULL, FALSE);
+	if (override_data)
+		/* if given explicit data to save, always use that instead of asking
+		 * the setting what to do.
+		 */
+		blob = override_data;
+	else {
+		scheme = (*(objtype->vtable->scheme_func)) (s_8021x);
+		switch (scheme) {
+		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+			blob = (*(objtype->vtable->blob_func)) (s_8021x);
+			break;
+		case NM_SETTING_802_1X_CK_SCHEME_PATH:
+			path = (*(objtype->vtable->path_func)) (s_8021x);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* If the object path was specified, prefer that over any raw cert data that
+	 * may have been sent.
+	 */
+	if (path) {
+		wpa_set_data (conn_name, (gchar *) objtype->ifnet_key,
+		              (gchar *) path);
+		return TRUE;
+	}
+
+	/* does not support writing encryption data now */
+	if (blob)
+		nm_log_warn (LOGD_SETTINGS, "    Currently we do not support cert writing.");
+
+	return TRUE;
+}
+
+static gboolean
+write_8021x_certs (NMSetting8021x *s_8021x,
+                   gboolean phase2,
+                   const char *conn_name,
+                   GError **error)
+{
+	char *password = NULL;
+	const Setting8021xSchemeVtable *otype = NULL;
+	gboolean is_pkcs12 = FALSE, success = FALSE;
+	GBytes *blob = NULL;
+	GBytes *enc_key = NULL;
+	gchar *generated_pw = NULL;
+
+	/* CA certificate */
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT];
+
+	if (!write_object (s_8021x, conn_name, NULL, otype, error))
+		return FALSE;
+
+	/* Private key */
+	if (phase2) {
+		if (nm_setting_802_1x_get_phase2_private_key_scheme (s_8021x) !=
+		    NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+			if (nm_setting_802_1x_get_phase2_private_key_format
+			    (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+				is_pkcs12 = TRUE;
+		}
+		password = (char *)
+		    nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
+	} else {
+		if (nm_setting_802_1x_get_private_key_scheme (s_8021x) !=
+		    NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) {
+			if (nm_setting_802_1x_get_private_key_format (s_8021x)
+			    == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+				is_pkcs12 = TRUE;
+		}
+		password = (char *)
+		    nm_setting_802_1x_get_private_key_password (s_8021x);
+	}
+
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY];
+
+	if ((*(otype->vtable->scheme_func)) (s_8021x) ==
+	    NM_SETTING_802_1X_CK_SCHEME_BLOB)
+		blob = (*(otype->vtable->blob_func)) (s_8021x);
+
+	/* Only do the private key re-encrypt dance if we got the raw key data, which
+	 * by definition will be unencrypted.  If we're given a direct path to the
+	 * private key file, it'll be encrypted, so we don't need to re-encrypt.
+	 */
+	if (blob && !is_pkcs12) {
+		GByteArray *tmp_enc_key;
+
+		/* Encrypt the unencrypted private key with the fake password */
+		tmp_enc_key =
+		    nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob),
+		                              password, &generated_pw, error);
+		if (!tmp_enc_key)
+			goto out;
+
+		enc_key = g_byte_array_free_to_bytes (tmp_enc_key);
+
+		if (generated_pw)
+			password = generated_pw;
+	}
+
+	/* Save the private key */
+	if (!write_object
+	    (s_8021x, conn_name, enc_key ? enc_key : blob, otype, error))
+		goto out;
+
+	if (phase2)
+		wpa_set_data (conn_name, "private_key2_passwd", password);
+	else
+		wpa_set_data (conn_name, "private_key_passwd", password);
+
+	/* Client certificate */
+	if (is_pkcs12) {
+		wpa_set_data (conn_name,
+		              phase2 ? "client_cert2" : "client_cert", NULL);
+	} else {
+		otype = phase2
+		        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT]
+		        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT];
+
+		/* Save the client certificate */
+		if (!write_object (s_8021x, conn_name, NULL, otype, error))
+			goto out;
+	}
+
+	success = TRUE;
+out:
+	if (generated_pw) {
+		memset (generated_pw, 0, strlen (generated_pw));
+		g_free (generated_pw);
+	}
+	if (enc_key) {
+		memset ((gpointer) g_bytes_get_data (enc_key, NULL), 0, g_bytes_get_size (enc_key));
+		g_bytes_unref (enc_key);
+	}
+	return success;
+}
+
+static gboolean
+write_8021x_setting (NMConnection *connection,
+                     const char *conn_name,
+                     gboolean wired,
+                     GError **error)
+{
+	NMSetting8021x *s_8021x;
+	const char *value;
+	char *tmp = NULL;
+	gboolean success = FALSE;
+	GString *phase2_auth;
+	GString *phase1;
+
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+	if (!s_8021x) {
+		return TRUE;
+	}
+
+	nm_log_info (LOGD_SETTINGS, "Adding 8021x setting for %s", conn_name);
+
+	/* If wired, write KEY_MGMT */
+	if (wired)
+		wpa_set_data (conn_name, "key_mgmt", "IEEE8021X");
+
+	/* EAP method */
+	if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) {
+		value = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+		if (value)
+			tmp = g_ascii_strup (value, -1);
+	}
+	wpa_set_data (conn_name, "eap", tmp ? tmp : NULL);
+	g_free (tmp);
+
+	wpa_set_data (conn_name, "identity",
+		      (gchar *) nm_setting_802_1x_get_identity (s_8021x));
+
+	wpa_set_data (conn_name, "anonymous_identity", (gchar *)
+		      nm_setting_802_1x_get_anonymous_identity (s_8021x));
+
+	wpa_set_data (conn_name, "password",
+		      (gchar *) nm_setting_802_1x_get_password (s_8021x));
+
+	phase1 = g_string_new (NULL);
+
+	/* PEAP version */
+	wpa_set_data (conn_name, "phase1", NULL);
+	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
+	if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
+		g_string_append_printf (phase1, "peapver=%s ", value);
+
+	/* PEAP label */
+	value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
+	if (value && !strcmp (value, "1"))
+		g_string_append_printf (phase1, "peaplabel=%s ", value);
+	if (phase1->len) {
+		tmp = g_strstrip (g_strdup (phase1->str));
+		wpa_set_data (conn_name, "phase1", tmp);
+		g_free (tmp);
+	}
+
+	/* Phase2 auth methods */
+	wpa_set_data (conn_name, "phase2", NULL);
+	phase2_auth = g_string_new (NULL);
+
+	value = nm_setting_802_1x_get_phase2_auth (s_8021x);
+	if (value) {
+		tmp = g_ascii_strup (value, -1);
+		g_string_append_printf (phase2_auth, "auth=%s ", tmp);
+		g_free (tmp);
+	}
+
+	/* Phase2 auth heap */
+	value = nm_setting_802_1x_get_phase2_autheap (s_8021x);
+	if (value) {
+		tmp = g_ascii_strup (value, -1);
+		g_string_append_printf (phase2_auth, "autheap=%s ", tmp);
+		g_free (tmp);
+	}
+	tmp = g_strstrip (g_strdup (phase2_auth->str));
+	wpa_set_data (conn_name, "phase2", phase2_auth->len ? tmp : NULL);
+	g_free (tmp);
+
+	g_string_free (phase2_auth, TRUE);
+	g_string_free (phase1, TRUE);
+
+	success = write_8021x_certs (s_8021x, FALSE, conn_name, error);
+	if (success) {
+		/* phase2/inner certs */
+		success = write_8021x_certs (s_8021x, TRUE, conn_name, error);
+	}
+
+	return success;
+}
+
+static gboolean
+write_wireless_security_setting (NMConnection * connection,
+				 gchar * conn_name,
+				 gboolean adhoc,
+				 gboolean * no_8021x, GError ** error)
+{
+	NMSettingWirelessSecurity *s_wsec;
+	const char *key_mgmt, *auth_alg, *key, *cipher, *psk;
+	gboolean wep = FALSE, wpa = FALSE;
+	char *tmp;
+	guint32 i, num;
+	GString *str;
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	if (!s_wsec) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing '%s' setting",
+			     NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+		return FALSE;
+	}
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	g_assert (key_mgmt);
+
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+
+	if (!strcmp (key_mgmt, "none")) {
+		wpa_set_data (conn_name, "key_mgmt", "NONE");
+		wep = TRUE;
+		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "wpa-none")
+		   || !strcmp (key_mgmt, "wpa-psk")) {
+		wpa_set_data (conn_name, "key_mgmt", "WPA-PSK");
+		wpa = TRUE;
+		*no_8021x = TRUE;
+	} else if (!strcmp (key_mgmt, "ieee8021x")) {
+		wpa_set_data (conn_name, "key_mgmt", "IEEE8021X");
+	} else if (!strcmp (key_mgmt, "wpa-eap")) {
+		wpa_set_data (conn_name, "key_mgmt", "WPA-EAP");
+		wpa = TRUE;
+	} else
+		nm_log_warn (LOGD_SETTINGS, "Unknown key_mgmt: %s", key_mgmt);
+
+	if (auth_alg) {
+		if (!strcmp (auth_alg, "shared"))
+			wpa_set_data (conn_name, "auth_alg", "SHARED");
+		else if (!strcmp (auth_alg, "open"))
+			wpa_set_data (conn_name, "auth_alg", "OPEN");
+		else if (!strcmp (auth_alg, "leap")) {
+			wpa_set_data (conn_name, "auth_alg", "LEAP");
+			wpa_set_data (conn_name, "eap", "LEAP");
+			wpa_set_data (conn_name, "identity", (gchar *)
+				      nm_setting_wireless_security_get_leap_username
+				      (s_wsec));
+			wpa_set_data (conn_name, "password", (gchar *)
+				      nm_setting_wireless_security_get_leap_password
+				      (s_wsec));
+			*no_8021x = TRUE;
+		}
+	} else
+		wpa_set_data (conn_name, "auth_alg", NULL);
+
+	/* Default WEP TX key index */
+	if (wep) {
+		tmp =
+		    g_strdup_printf ("%d",
+				     nm_setting_wireless_security_get_wep_tx_keyidx
+				     (s_wsec));
+		wpa_set_data (conn_name, "wep_tx_keyidx", tmp);
+		g_free (tmp);
+	} else
+		wpa_set_data (conn_name, "wep_tx_keyidx", NULL);
+
+	/* WEP keys */
+	for (i = 0; i < 4; i++) {
+		int length;
+
+		key = nm_setting_wireless_security_get_wep_key (s_wsec, i);
+		if (!key)
+			continue;
+		tmp = g_strdup_printf ("wep_key%d", i);
+		length = strlen (key);
+		if (length == 10 || length == 26 || length == 58)
+			wpa_set_data (conn_name, tmp, (gchar *) key);
+		else {
+			gchar *tmp_key = g_strdup_printf ("\"%s\"", key);
+
+			wpa_set_data (conn_name, tmp, tmp_key);
+			g_free (tmp_key);
+		}
+		g_free (tmp);
+	}
+
+	/* WPA Pairwise ciphers */
+	wpa_set_data (conn_name, "pairwise", NULL);
+	str = g_string_new (NULL);
+	num = nm_setting_wireless_security_get_num_pairwise (s_wsec);
+	for (i = 0; i < num; i++) {
+		if (i > 0)
+			g_string_append_c (str, ' ');
+		cipher = nm_setting_wireless_security_get_pairwise (s_wsec, i);
+		tmp = g_ascii_strup (cipher, -1);
+		g_string_append (str, tmp);
+		g_free (tmp);
+	}
+	if (strlen (str->str))
+		wpa_set_data (conn_name, "pairwise", str->str);
+	g_string_free (str, TRUE);
+
+	/* WPA Group ciphers */
+	wpa_set_data (conn_name, "group", NULL);
+	str = g_string_new (NULL);
+	num = nm_setting_wireless_security_get_num_groups (s_wsec);
+	for (i = 0; i < num; i++) {
+		if (i > 0)
+			g_string_append_c (str, ' ');
+		cipher = nm_setting_wireless_security_get_group (s_wsec, i);
+		tmp = g_ascii_strup (cipher, -1);
+		g_string_append (str, tmp);
+		g_free (tmp);
+	}
+	if (strlen (str->str))
+		wpa_set_data (conn_name, "group", str->str);
+	g_string_free (str, TRUE);
+
+	/* WPA Passphrase */
+	if (wpa) {
+		GString *quoted = NULL;
+
+		psk = nm_setting_wireless_security_get_psk (s_wsec);
+		if (psk && (strlen (psk) != 64)) {
+			quoted = g_string_sized_new (strlen (psk) + 2);
+			g_string_append_c (quoted, '"');
+			g_string_append (quoted, psk);
+			g_string_append_c (quoted, '"');
+		}
+		/* psk will be lost here if we don't check it for NULL */
+		if (psk)
+			wpa_set_data (conn_name, "psk",
+					  quoted ? quoted->str : (gchar *) psk);
+		if (quoted)
+			g_string_free (quoted, TRUE);
+	} else
+		wpa_set_data (conn_name, "psk", NULL);
+
+	return TRUE;
+}
+
+/* remove old ssid and add new one*/
+static void
+update_wireless_ssid (NMConnection *connection,
+                      const char *conn_name,
+                      const char *ssid,
+                      gboolean hex)
+{
+	if(strcmp (conn_name, ssid)){
+		ifnet_delete_network (conn_name);
+		wpa_delete_security (conn_name);
+	}
+
+	ifnet_add_network (ssid, "wireless");
+	wpa_add_security (ssid);
+}
+
+static gboolean
+write_wireless_setting (NMConnection *connection,
+                        const char *conn_name,
+                        gboolean *no_8021x,
+                        const char **out_new_name,
+                        GError **error)
+{
+	NMSettingWireless *s_wireless;
+	GBytes *ssid;
+	const guint8 *ssid_data;
+	gsize ssid_len;
+	const char *mac, *bssid, *mode;
+	char buf[33];
+	guint32 mtu, i;
+	gboolean adhoc = FALSE, hex_ssid = FALSE;
+	gchar *ssid_str, *tmp;
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	if (!s_wireless) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing '%s' setting",
+			     NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	if (!ssid) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing SSID in '%s' setting",
+			     NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+	ssid_data = g_bytes_get_data (ssid, &ssid_len);
+	if (!ssid_len || ssid_len > 32) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Invalid SSID in '%s' setting",
+			     NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	/* If the SSID contains any non-alnum characters, we need to use
+	 * the hex notation of the SSID instead. (Because openrc doesn't
+	 * support these characters, see bug #356337)
+	 */
+	for (i = 0; i < ssid_len; i++) {
+		if (!g_ascii_isalnum (ssid_data[i])) {
+			hex_ssid = TRUE;
+			break;
+		}
+	}
+
+	if (hex_ssid) {
+		GString *str;
+
+		/* Hex SSIDs don't get quoted */
+		str = g_string_sized_new (ssid_len * 2 + 3);
+		g_string_append (str, "0x");
+		for (i = 0; i < ssid_len; i++)
+			g_string_append_printf (str, "%02X", ssid_data[i]);
+		update_wireless_ssid (connection, conn_name, str->str, hex_ssid);
+		ssid_str = g_string_free (str, FALSE);
+	} else {
+		/* Printable SSIDs get quoted */
+		memset (buf, 0, sizeof (buf));
+		memcpy (buf, ssid_data, ssid_len);
+		g_strstrip (buf);
+		update_wireless_ssid (connection, conn_name, buf, hex_ssid);
+		ssid_str = g_strdup (buf);
+	}
+
+	ifnet_set_data (ssid_str, "mac", NULL);
+	mac = nm_setting_wireless_get_mac_address (s_wireless);
+	if (mac)
+		ifnet_set_data (ssid_str, "mac", mac);
+
+	ifnet_set_data (ssid_str, "mtu", NULL);
+	mtu = nm_setting_wireless_get_mtu (s_wireless);
+	if (mtu) {
+		tmp = g_strdup_printf ("%u", mtu);
+		ifnet_set_data (ssid_str, "mtu", tmp);
+		g_free (tmp);
+	}
+
+	ifnet_set_data (ssid_str, "mode", NULL);
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (!mode || !strcmp (mode, "infrastructure")) {
+		wpa_set_data (ssid_str, "mode", "0");
+	} else if (!strcmp (mode, "adhoc")) {
+		wpa_set_data (ssid_str, "mode", "1");
+		adhoc = TRUE;
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "Invalid mode '%s' in '%s' setting",
+		             mode, NM_SETTING_WIRELESS_SETTING_NAME);
+		return FALSE;
+	}
+
+	wpa_set_data (ssid_str, "bssid", NULL);
+	bssid = nm_setting_wireless_get_bssid (s_wireless);
+	if (bssid)
+		wpa_set_data (ssid_str, "bssid", bssid);
+
+	if (nm_connection_get_setting_wireless_security (connection)) {
+		if (!write_wireless_security_setting
+		    (connection, ssid_str, adhoc, no_8021x, error))
+			return FALSE;
+	} else
+		wpa_delete_security (ssid_str);
+
+	if (out_new_name)
+		*out_new_name = ifnet_get_data (ssid_str, "name");
+	g_free (ssid_str);
+	return TRUE;
+}
+
+static gboolean
+write_wired_setting (NMConnection *connection,
+                     const char *conn_name,
+                     GError **error)
+{
+	NMSettingWired *s_wired;
+	const char *mac;
+	char *tmp;
+	guint32 mtu;
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (!s_wired) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing '%s' setting",
+			     NM_SETTING_WIRED_SETTING_NAME);
+		return FALSE;
+	}
+
+	ifnet_set_data (conn_name, "mac", NULL);
+	mac = nm_setting_wired_get_mac_address (s_wired);
+	if (mac)
+		ifnet_set_data (conn_name, "mac", mac);
+
+	ifnet_set_data (conn_name, "mtu", NULL);
+	mtu = nm_setting_wired_get_mtu (s_wired);
+	if (mtu) {
+		tmp = g_strdup_printf ("%u", mtu);
+		ifnet_set_data (conn_name, "mtu", tmp);
+		g_free (tmp);
+	}
+	//FIXME may add connection type in future
+	//ifnet_set_data (conn_name, "TYPE", TYPE_ETHERNET);
+
+	return TRUE;
+}
+
+static gboolean
+write_ip4_setting (NMConnection *connection, const char *conn_name, GError **error)
+{
+	NMSettingIPConfig *s_ip4;
+	const char *value;
+	guint32 i, num;
+	GString *searches;
+	GString *ips;
+	GString *routes;
+	GString *dns;
+	gboolean success = FALSE;
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (!s_ip4) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing '%s' setting",
+			     NM_SETTING_IP4_CONFIG_SETTING_NAME);
+		return FALSE;
+	}
+	routes = g_string_new (NULL);
+
+	value = nm_setting_ip_config_get_method (s_ip4);
+	g_assert (value);
+	if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+
+		num = nm_setting_ip_config_get_num_addresses (s_ip4);
+		ips = g_string_new (NULL);
+		/* IPv4 addresses */
+		for (i = 0; i < num; i++) {
+			NMIPAddress *addr;
+
+			addr = nm_setting_ip_config_get_address (s_ip4, i);
+
+			g_string_append_printf (ips, "\"%s/%u",
+			                        nm_ip_address_get_address (addr),
+			                        nm_ip_address_get_prefix (addr));
+
+			/* only the first gateway will be written */
+			if (i == 0 && nm_setting_ip_config_get_gateway (s_ip4)) {
+				g_string_append_printf (routes,
+				                        "\"default via %s\" ",
+				                        nm_setting_ip_config_get_gateway (s_ip4));
+			}
+		}
+		ifnet_set_data (conn_name, "config", ips->str);
+		g_string_free (ips, TRUE);
+	} else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+		ifnet_set_data (conn_name, "config", "shared");
+	else if (!strcmp (value, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+		ifnet_set_data (conn_name, "config", "autoip");
+	else
+		ifnet_set_data (conn_name, "config", "dhcp");
+
+	/* DNS Servers */
+	num = nm_setting_ip_config_get_num_dns (s_ip4);
+	if (num > 0) {
+		dns = g_string_new (NULL);
+		for (i = 0; i < num; i++) {
+			const char *ip;
+
+			ip = nm_setting_ip_config_get_dns (s_ip4, i);
+			g_string_append_printf (dns, " %s", ip);
+		}
+		ifnet_set_data (conn_name, "dns_servers", dns->str);
+		g_string_free (dns, TRUE);
+	} else
+		ifnet_set_data (conn_name, "dns_servers", NULL);
+
+	/* DNS Searches */
+	num = nm_setting_ip_config_get_num_dns_searches (s_ip4);
+	if (num > 0) {
+		searches = g_string_new (NULL);
+		for (i = 0; i < num; i++) {
+			if (i > 0)
+				g_string_append_c (searches, ' ');
+			g_string_append (searches,
+					 nm_setting_ip_config_get_dns_search
+					 (s_ip4, i));
+		}
+		ifnet_set_data (conn_name, "dns_search", searches->str);
+		g_string_free (searches, TRUE);
+	} else
+		ifnet_set_data (conn_name, "dns_search", NULL);
+	/* FIXME Will be implemented when configuration supports it
+	   if (!strcmp(value, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+	   value = nm_setting_ip_config_get_dhcp_hostname(s_ip4);
+	   if (value)
+	   ifnet_set_data(conn_name, "DHCP_HOSTNAME", value,
+	   FALSE);
+
+	   value = nm_setting_ip_config_get_dhcp_client_id(s_ip4);
+	   if (value)
+	   ifnet_set_data(conn_name, "DHCP_CLIENT_ID", value,
+	   FALSE);
+	   }
+	 */
+
+	/* Static routes */
+	num = nm_setting_ip_config_get_num_routes (s_ip4);
+	if (num > 0) {
+		for (i = 0; i < num; i++) {
+			NMIPRoute *route;
+			const char *next_hop;
+
+			route = nm_setting_ip_config_get_route (s_ip4, i);
+
+			next_hop = nm_ip_route_get_next_hop (route);
+			if (!next_hop)
+				next_hop = "0.0.0.0";
+
+			g_string_append_printf (routes, "\"%s/%u via %s\" ",
+			                        nm_ip_route_get_dest (route),
+			                        nm_ip_route_get_prefix (route),
+			                        next_hop);
+		}
+	}
+	if (routes->len > 0)
+		ifnet_set_data (conn_name, "routes", routes->str);
+	else
+		ifnet_set_data (conn_name, "routes", NULL);
+	g_string_free (routes, TRUE);
+
+	success = TRUE;
+
+	return success;
+}
+
+static void
+write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name)
+{
+	NMIPRoute *route;
+	const char *next_hop;
+	guint32 i, num;
+	GString *routes_string;
+	const char *old_routes;
+
+	g_return_if_fail (s_ip6 != NULL);
+	num = nm_setting_ip_config_get_num_routes (s_ip6);
+	if (num == 0)
+		return;
+
+	old_routes = ifnet_get_data (conn_name, "routes");
+	routes_string = g_string_new (old_routes);
+	if (old_routes)
+		g_string_append (routes_string, "\" ");
+	for (i = 0; i < num; i++) {
+		route = nm_setting_ip_config_get_route (s_ip6, i);
+
+		next_hop = nm_ip_route_get_next_hop (route);
+		if (!next_hop)
+			next_hop = "::";
+
+		g_string_append_printf (routes_string, "\"%s/%u via %s\" ",
+		                        nm_ip_route_get_dest (route),
+		                        nm_ip_route_get_prefix (route),
+		                        next_hop);
+	}
+	if (num > 0)
+		ifnet_set_data (conn_name, "routes", routes_string->str);
+	g_string_free (routes_string, TRUE);
+}
+
+static gboolean
+write_ip6_setting (NMConnection *connection, const char *conn_name, GError **error)
+{
+	NMSettingIPConfig *s_ip6;
+	const char *value;
+	guint32 i, num;
+	GString *searches;
+	NMIPAddress *addr;
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (!s_ip6) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing '%s' setting",
+			     NM_SETTING_IP6_CONFIG_SETTING_NAME);
+		return FALSE;
+	}
+
+	value = nm_setting_ip_config_get_method (s_ip6);
+	g_assert (value);
+	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
+		ifnet_set_data (conn_name, "enable_ipv6", "false");
+		return TRUE;
+	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		/* nothing to do now */
+	} else {
+		// if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
+		const char *config = ifnet_get_data (conn_name, "config");
+		gchar *tmp;
+
+		if (!config)
+			tmp = g_strdup_printf ("dhcp6");
+		else
+			tmp = g_strdup_printf ("%s\" \"dhcp6\"", config);
+		ifnet_set_data (conn_name, "config", tmp);
+		g_free (tmp);
+	}
+	/* else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+	   } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
+	   } else if (!strcmp(value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+	   } */
+
+	/* Remember to set IPv6 enabled */
+	ifnet_set_data (conn_name, "enable_ipv6", "true");
+
+	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		const char *config = ifnet_get_data (conn_name, "config");
+		gchar *tmp;
+		GString *ip_str;
+
+		if (!config)
+			config = "";
+		num = nm_setting_ip_config_get_num_addresses (s_ip6);
+
+		/* IPv6 addresses */
+		ip_str = g_string_new (NULL);
+		for (i = 0; i < num; i++) {
+			addr = nm_setting_ip_config_get_address (s_ip6, i);
+
+			g_string_append_printf (ip_str, "\"%s/%u\"",
+			                        nm_ip_address_get_address (addr),
+			                        nm_ip_address_get_prefix (addr));
+		}
+		tmp = g_strdup_printf ("%s\" %s", config, ip_str->str);
+		ifnet_set_data (conn_name, "config", tmp);
+		g_free (tmp);
+		g_string_free (ip_str, TRUE);
+	}
+
+	/* DNS Servers */
+	num = nm_setting_ip_config_get_num_dns (s_ip6);
+	if (num > 0) {
+		const char *dns_servers = ifnet_get_data (conn_name, "dns_servers");
+		gchar *tmp;
+		GString *dns_string = g_string_new (NULL);
+		const char *dns;
+
+		if (!dns_servers)
+			dns_servers = "";
+		for (i = 0; i < num; i++) {
+			dns = nm_setting_ip_config_get_dns (s_ip6, i);
+
+			if (!strstr (dns_servers, dns))
+				g_string_append_printf (dns_string, "%s ", dns);
+		}
+		tmp = g_strdup_printf ("%s %s", dns_servers, dns_string->str);
+		ifnet_set_data (conn_name, "dns_servers", tmp);
+		g_free (tmp);
+		g_string_free (dns_string, TRUE);
+
+	} else
+		/* DNS Searches */
+		num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
+	if (num > 0) {
+		const char *ip4_domains;
+
+		ip4_domains = ifnet_get_data (conn_name, "dns_search");
+		if (!ip4_domains)
+			ip4_domains = "";
+		searches = g_string_new (ip4_domains);
+		for (i = 0; i < num; i++) {
+			const gchar *search = NULL;
+
+			search =
+			    nm_setting_ip_config_get_dns_search (s_ip6, i);
+			if (search && !strstr (searches->str, search)) {
+				if (searches->len > 0)
+					g_string_append_c (searches, ' ');
+				g_string_append (searches, search);
+			}
+		}
+		ifnet_set_data (conn_name, "dns_search", searches->str);
+		g_string_free (searches, TRUE);
+	}
+
+	write_route6_file (s_ip6, conn_name);
+	return TRUE;
+}
+
+static gboolean
+write_pppoe_setting (const char *conn_name, NMSettingPppoe * s_pppoe)
+{
+	const gchar *value;
+
+	value = nm_setting_pppoe_get_username (s_pppoe);
+	if (!value) {
+		return FALSE;
+	}
+	ifnet_set_data (conn_name, "username", (gchar *) value);
+
+	value = nm_setting_pppoe_get_password (s_pppoe);
+	/* password could be NULL here */
+	if (value) {
+		ifnet_set_data (conn_name, "password", (gchar *) value);
+	}
+	return TRUE;
+}
+
+gboolean
+ifnet_update_parsers_by_connection (NMConnection *connection,
+                                    const char *conn_name,
+                                    const char *config_file,
+                                    const char *wpa_file,
+                                    gchar **out_new_name,
+                                    gchar **out_backup,
+                                    GError **error)
+{
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip6;
+	gboolean success = FALSE;
+	const char *type;
+	gboolean no_8021x = FALSE;
+	gboolean wired = FALSE, pppoe = TRUE;
+	const char *new_name = NULL;
+
+	if (!ifnet_can_write_connection (connection, error))
+		return FALSE;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	type = nm_setting_connection_get_connection_type (s_con);
+	if (!type) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			     "Missing connection type!");
+		goto out;
+	}
+
+	if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
+		/* Writing wired setting */
+		if (!write_wired_setting (connection, conn_name, error))
+			goto out;
+		wired = TRUE;
+		no_8021x = TRUE;
+	} else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
+		/* Writing wireless setting */
+		if (!write_wireless_setting (connection, conn_name, &no_8021x, &new_name, error))
+			goto out;
+	} else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) {
+		NMSettingPppoe *s_pppoe;
+
+		/* Writing pppoe setting */
+		s_pppoe = nm_connection_get_setting_pppoe (connection);
+		if (!write_pppoe_setting (conn_name, s_pppoe))
+			goto out;
+		pppoe = TRUE;
+		wired = TRUE;
+		no_8021x = TRUE;
+	} else {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+			     "Can't write connection type '%s'", type);
+		goto out;
+	}
+
+	/* connection name may have been updated; use it when writing out
+	 * the rest of the settings.
+	 */
+	if (new_name)
+		conn_name = new_name;
+
+	//FIXME wired connection doesn't support 8021x now
+	if (!no_8021x) {
+		if (!write_8021x_setting (connection, conn_name, wired, error))
+			goto out;
+	}
+
+	/* IPv4 Setting */
+	if (!write_ip4_setting (connection, conn_name, error))
+		goto out;
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (s_ip6) {
+		/* IPv6 Setting */
+		if (!write_ip6_setting (connection, conn_name, error))
+			goto out;
+	}
+
+	/* Connection Setting */
+	ifnet_set_data (conn_name, "auto",
+	                nm_setting_connection_get_autoconnect (s_con) ? "true" : "false");
+	ifnet_set_data (conn_name, "uuid", nm_connection_get_uuid (connection));
+
+	/* Write changes to disk */
+	success = ifnet_flush_to_file (config_file, out_backup);
+	if (success)
+		wpa_flush_to_file (wpa_file);
+
+	if (out_new_name)
+		*out_new_name = g_strdup (conn_name);
+
+out:
+	return success;
+}
+
+gboolean
+ifnet_delete_connection_in_parsers (const char *conn_name,
+                                    const char *config_file,
+                                    const char *wpa_file,
+                                    gchar **out_backup)
+{
+	gboolean result = FALSE;
+
+	ifnet_delete_network (conn_name);
+	result = ifnet_flush_to_file (config_file, out_backup);
+	if (result) {
+		/* connection may not have security information
+		 * so simply ignore the return value*/
+		wpa_delete_security (conn_name);
+		wpa_flush_to_file (wpa_file);
+	}
+
+	return result;
+}
+
+static void
+check_unsupported_secrets (NMSetting  *setting,
+                           const char *key,
+                           const GValue *value,
+                           GParamFlags flags,
+                           gpointer user_data)
+{
+	gboolean *unsupported_secret = user_data;
+
+	if (flags & NM_SETTING_PARAM_SECRET) {
+		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+		if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
+			g_return_if_reached ();
+		if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+			*unsupported_secret = TRUE;
+	}
+}
+
+gboolean
+ifnet_can_write_connection (NMConnection *connection, GError **error)
+{
+	NMSettingConnection *s_con;
+	gboolean has_unsupported_secrets = FALSE;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	/* If the connection is not available for all users, ignore
+	 * it as this plugin only deals with System Connections */
+	if (nm_setting_connection_get_num_permissions (s_con)) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+		                     "The ifnet plugin does not support non-system-wide connections.");
+		return FALSE;
+	}
+
+	/* Only support wired, wifi, and PPPoE */
+	if (   !nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)
+	    && !nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME)
+	    && !nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+		             "The ifnet plugin cannot write the connection '%s' (type '%s')",
+		             nm_connection_get_id (connection),
+		             nm_setting_connection_get_connection_type (s_con));
+		return FALSE;
+	}
+
+	/* If the connection has flagged secrets, ignore
+	 * it as this plugin does not deal with user agent service */
+	nm_connection_for_each_setting_value (connection,
+	                                      check_unsupported_secrets,
+	                                      &has_unsupported_secrets);
+	if (has_unsupported_secrets) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+		                     "The ifnet plugin only supports persistent system secrets.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/* get the available wired name(eth*). */
+static gchar *
+get_wired_name (void)
+{
+	int i = 0;
+
+	for (; i < 256; i++) {
+		gchar *conn_name = g_strdup_printf ("eth%d", i);
+
+		if (!ifnet_has_network (conn_name)) {
+			return conn_name;
+		} else
+			g_free (conn_name);
+	}
+	return NULL;
+}
+
+/* get the available pppoe name(ppp*). */
+static gchar *
+get_ppp_name (void)
+{
+	int i = 0;
+
+	for (; i < 256; i++) {
+		gchar *conn_name = g_strdup_printf ("ppp%d", i);
+
+		if (!ifnet_has_network (conn_name)) {
+			return conn_name;
+		} else
+			g_free (conn_name);
+	}
+	return NULL;
+}
+
+/* get wireless ssid */
+static gchar *
+get_wireless_name (NMConnection * connection)
+{
+	NMSettingWireless *s_wireless;
+	GBytes *ssid;
+	const guint8 *ssid_data;
+	gsize ssid_len;
+	gboolean hex_ssid = FALSE;
+	gchar *result = NULL;
+	char buf[33];
+	int i = 0;
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	if (!s_wireless)
+		return NULL;
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	ssid_data = g_bytes_get_data (ssid, &ssid_len);
+	if (!ssid_len || ssid_len > 32) {
+		return NULL;
+	}
+
+	for (i = 0; i < ssid_len; i++) {
+		if (!g_ascii_isprint (ssid_data[i])) {
+			hex_ssid = TRUE;
+			break;
+		}
+	}
+
+	if (hex_ssid) {
+		GString *str;
+
+		str = g_string_sized_new (ssid_len * 2 + 3);
+		g_string_append (str, "0x");
+		for (i = 0; i < ssid_len; i++)
+			g_string_append_printf (str, "%02X", ssid_data[i]);
+		result = g_strdup (str->str);
+		g_string_free (str, TRUE);
+	} else {
+		memset (buf, 0, sizeof (buf));
+		memcpy (buf, ssid_data, ssid_len);
+		result = g_strdup_printf ("%s", buf);
+		g_strstrip (result);
+	}
+
+	return result;
+}
+
+gboolean
+ifnet_add_new_connection (NMConnection *connection,
+                          const char *config_file,
+                          const char *wpa_file,
+                          gchar **out_new_name,
+                          gchar **out_backup,
+                          GError **error)
+{
+	NMSettingConnection *s_con;
+	gboolean success = FALSE;
+	const char *type;
+	gchar *new_type, *new_name = NULL;
+
+	if (!ifnet_can_write_connection (connection, error))
+		return FALSE;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	type = nm_setting_connection_get_connection_type (s_con);
+	g_assert (type);
+
+	nm_log_info (LOGD_SETTINGS, "Adding %s connection", type);
+
+	/* get name and type
+	 * Wireless type: wireless
+	 * Wired type: wired
+	 * PPPoE type: ppp*/
+	if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
+		new_name = get_wired_name ();
+		if (!new_name)
+			goto out;
+		new_type = "wired";
+	} else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
+		new_name = get_wireless_name (connection);
+		new_type = "wireless";
+	} else if (!strcmp (type, NM_SETTING_PPPOE_SETTING_NAME)) {
+		new_name = get_ppp_name ();
+		if (!new_name)
+			goto out;
+		new_type = "ppp";
+	} else {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+			     "Can't write connection type '%s'", type);
+		goto out;
+	}
+
+	if (ifnet_add_network (new_name, new_type)) {
+		success = ifnet_update_parsers_by_connection (connection,
+		                                              new_name,
+		                                              config_file,
+		                                              wpa_file,
+		                                              NULL,
+		                                              out_backup,
+		                                              error);
+	}
+
+	nm_log_info (LOGD_SETTINGS, "Added new connection: %s, result: %s",
+	             new_name, success ? "success" : "fail");
+
+out:
+	if (!success || !out_new_name)
+		g_free (new_name);
+	else if (out_new_name)
+		*out_new_name = new_name;
+	return success;
+}
+
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
new file mode 100644
index 00000000..51bc34b9
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef _CONNECTION_PARSER_H
+#define _CONNECTION_PARSER_H
+
+#include "nm-connection.h"
+
+#include "nms-ifnet-net-parser.h"
+
+gboolean ifnet_can_write_connection (NMConnection *connection, GError **error);
+
+NMConnection *ifnet_update_connection_from_config_block (const char *conn_name,
+                                                         const char *basepath,
+                                                         GError **error);
+
+/* nm_conn_name is used to update nm_ifnet_connection's priv data */
+gboolean ifnet_update_parsers_by_connection (NMConnection *connection,
+                                             const char *conn_name,
+                                             const char *config_file,
+                                             const char *wpa_file,
+                                             gchar **out_new_name,
+                                             gchar **out_backup,
+                                             GError **error);
+
+gboolean ifnet_delete_connection_in_parsers (const char *conn_name,
+                                             const char *config_file,
+                                             const char *wpa_file,
+                                             gchar **out_backup);
+
+gboolean ifnet_add_new_connection (NMConnection *connection,
+                                   const char *config_file,
+                                   const char *wpa_file,
+                                   gchar **out_new_name,
+                                   gchar **out_backup,
+                                   GError ** error);
+#endif
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c
new file mode 100644
index 00000000..ce0b3f2b
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-connection.h"
+
+#include <string.h>
+#include <glib/gstdio.h>
+
+#include "nm-dbus-interface.h"
+#include "nm-utils.h"
+#include "nm-setting-wireless-security.h"
+#include "settings/nm-settings-connection.h"
+#include "settings/nm-settings-plugin.h"
+
+#include "nms-ifnet-connection-parser.h"
+#include "nms-ifnet-net-parser.h"
+#include "nms-ifnet-net-utils.h"
+#include "nms-ifnet-wpa-parser.h"
+#include "nms-ifnet-plugin.h"
+
+/*****************************************************************************/
+
+enum {
+	IFNET_SETUP_MONITORS,
+	IFNET_CANCEL_MONITORS,
+	IFNET_LAST_SIGNAL
+};
+
+static guint signals[IFNET_LAST_SIGNAL] = { 0 };
+
+typedef struct {
+	gchar *conn_name;
+	NMSettingsPlugin *config;
+} NMIfnetConnectionPrivate;
+
+struct _NMIfnetConnection {
+	NMSettingsConnection parent;
+	NMIfnetConnectionPrivate _priv;
+};
+
+struct _NMIfnetConnectionClass {
+	NMSettingsConnectionClass parent;
+};
+
+G_DEFINE_TYPE (NMIfnetConnection, nm_ifnet_connection, NM_TYPE_SETTINGS_CONNECTION)
+
+#define NM_IFNET_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfnetConnection, NM_IS_IFNET_CONNECTION)
+
+/*****************************************************************************/
+
+const char *
+nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection)
+{
+	return NM_IFNET_CONNECTION_GET_PRIVATE (connection)->conn_name;
+}
+
+static gboolean
+commit_changes (NMSettingsConnection *connection,
+                NMConnection *new_connection,
+                NMSettingsConnectionCommitReason commit_reason,
+                NMConnection **out_reread_connection,
+                char **out_logmsg_change,
+                GError **error)
+{
+	NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection);
+	char *new_name = NULL;
+	gboolean success = FALSE;
+	gboolean added = FALSE;
+
+	nm_assert (out_reread_connection && !*out_reread_connection);
+	nm_assert (!out_logmsg_change || !*out_logmsg_change);
+
+	g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0);
+
+	if (priv->conn_name) {
+		success = ifnet_update_parsers_by_connection (new_connection,
+		                                              priv->conn_name,
+		                                              CONF_NET_FILE,
+		                                              WPA_SUPPLICANT_CONF,
+		                                              &new_name,
+		                                              NULL,
+		                                              error);
+	} else {
+		added = TRUE;
+		success = ifnet_add_new_connection (new_connection,
+		                                    CONF_NET_FILE,
+		                                    WPA_SUPPLICANT_CONF,
+		                                    &new_name,
+		                                    NULL,
+		                                    error);
+	}
+
+	g_assert (!!success == (new_name != NULL));
+	if (success) {
+		g_free (priv->conn_name);
+		priv->conn_name = new_name;
+	}
+
+	reload_parsers ();
+
+	g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0);
+
+	if (success) {
+		NM_SET_OUT (out_logmsg_change,
+		            g_strdup_printf ("ifcfg-rh: %s %s",
+		                             added ? "persist" : "updated",
+		                             new_name));
+	}
+	return success;
+}
+
+static gboolean
+delete (NMSettingsConnection *connection,
+        GError **error)
+{
+	NMIfnetConnectionPrivate *priv = NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) connection);
+
+	/* Only connections which exist in /etc/conf.d/net will have a conn_name */
+	if (priv->conn_name) {
+		g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0);
+
+		if (!ifnet_delete_connection_in_parsers (priv->conn_name, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL)) {
+			nm_log_warn (LOGD_SETTINGS, "Failed to delete %s", priv->conn_name);
+			reload_parsers ();
+			/* let's not return an error. */
+		}
+
+		g_signal_emit (connection, signals[IFNET_SETUP_MONITORS], 0);
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_ifnet_connection_init (NMIfnetConnection * connection)
+{
+}
+
+NMIfnetConnection *
+nm_ifnet_connection_new (NMConnection *source, const char *conn_name)
+{
+	NMConnection *tmp;
+	GObject *object;
+	GError *error = NULL;
+	gboolean update_unsaved = TRUE;
+
+	g_return_val_if_fail (source || conn_name, NULL);
+
+	if (source)
+		tmp = g_object_ref (source);
+	else {
+		tmp = ifnet_update_connection_from_config_block (conn_name, NULL, &error);
+		if (!tmp) {
+			nm_log_warn (LOGD_SETTINGS, "Could not read connection '%s': %s",
+			             conn_name, error->message);
+			g_error_free (error);
+			return NULL;
+		}
+
+		/* If we just read the connection from disk, it's clearly not Unsaved */
+		update_unsaved = FALSE;
+	}
+
+	object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL);
+
+	NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name = g_strdup (conn_name);
+	if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
+	                                    tmp,
+	                                    update_unsaved
+	                                      ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED
+	                                      : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
+	                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                                    NULL,
+	                                    NULL)) {
+		g_object_unref (object);
+		return NULL;
+	}
+	g_object_unref (tmp);
+
+	return NM_IFNET_CONNECTION (object);
+}
+
+static void
+finalize (GObject * object)
+{
+	g_free (NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name);
+	G_OBJECT_CLASS (nm_ifnet_connection_parent_class)->finalize (object);
+}
+
+static void
+nm_ifnet_connection_class_init (NMIfnetConnectionClass * ifnet_connection_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ifnet_connection_class);
+	NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifnet_connection_class);
+
+	object_class->finalize = finalize;
+
+	settings_class->delete = delete;
+	settings_class->commit_changes = commit_changes;
+
+	signals[IFNET_SETUP_MONITORS] =
+	    g_signal_new ("ifnet_setup_monitors",
+	                  G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST,
+	                  0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
+	signals[IFNET_CANCEL_MONITORS] =
+	    g_signal_new ("ifnet_cancel_monitors",
+	                  G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST,
+	                  0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
+}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.h b/src/settings/plugins/ifnet/nms-ifnet-connection.h
new file mode 100644
index 00000000..1bc06644
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_IFNET_CONNECTION_H__
+#define __NETWORKMANAGER_IFNET_CONNECTION_H__
+
+#include "settings/nm-settings-connection.h"
+
+#include "nms-ifnet-net-parser.h"
+
+#define NM_TYPE_IFNET_CONNECTION            (nm_ifnet_connection_get_type ())
+#define NM_IFNET_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnection))
+#define NM_IFNET_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass))
+#define NM_IS_IFNET_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFNET_CONNECTION))
+#define NM_IS_IFNET_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFNET_CONNECTION))
+#define NM_IFNET_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFNET_CONNECTION, NMIfnetConnectionClass))
+
+typedef struct _NMIfnetConnection NMIfnetConnection;
+typedef struct _NMIfnetConnectionClass NMIfnetConnectionClass;
+
+GType nm_ifnet_connection_get_type (void);
+
+NMIfnetConnection *nm_ifnet_connection_new (NMConnection *source,
+                                            const char *conn_name);
+
+const char *nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection);
+
+#endif /* __NETWORKMANAGER_IFNET_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.c b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c
new file mode 100644
index 00000000..d3e47219
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.c
@@ -0,0 +1,734 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-net-parser.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "settings/nm-settings-plugin.h"
+#include "platform/nm-platform.h"
+
+#include "nms-ifnet-plugin.h"
+#include "nms-ifnet-net-utils.h"
+
+/* Save all the connection information */
+static GHashTable *conn_table;
+
+/* Save global settings which are used for writing*/
+static GHashTable *global_settings_table;
+
+/* Save functions */
+static GList *functions_list;
+
+/* Used to decide whether to write changes to file*/
+static gboolean net_parser_data_changed = FALSE;
+
+static GHashTable *
+add_new_connection_config (const gchar * type, const gchar * name)
+{
+	GHashTable *new_conn;
+	gchar *new_name;
+
+	if (!name)
+		return NULL;
+
+	/* Return existing connection */
+	if ((new_conn = g_hash_table_lookup (conn_table, name)) != NULL)
+		return new_conn;
+	new_conn = g_hash_table_new (nm_str_hash, g_str_equal);
+	new_name = g_strdup (name);
+	g_hash_table_insert (new_conn, g_strdup ("name"), new_name);
+	g_hash_table_insert (new_conn, g_strdup ("type"), g_strdup (type));
+	g_hash_table_insert (conn_table, new_name, new_conn);
+	return new_conn;
+}
+
+gboolean
+ifnet_add_network (const char *name, const char *type)
+{
+	if (ifnet_has_network (name))
+		return TRUE;
+	if (add_new_connection_config (type, name)) {
+		nm_log_info (LOGD_SETTINGS, "Adding network for %s", name);
+		net_parser_data_changed = TRUE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean
+ifnet_has_network (const char *conn_name)
+{
+	return g_hash_table_lookup (conn_table, conn_name) != NULL;
+}
+
+static GHashTable *
+get_connection_config (const char *name)
+{
+	return g_hash_table_lookup (conn_table, name);
+}
+
+/* Ignored name won't be treated as wireless ssid */
+static gchar *ignore_name[] = {
+	"vlan", "bond", "atm", "ath", "ippp", "vpn", "tap", "tun", "1",
+	"br", "nas", "6to4", "timeout", "kvm", "force", NULL
+};
+
+static gboolean
+ignore_connection_name (const char *name)
+{
+	gboolean result = FALSE;
+	guint i = 0;
+
+	/* check ignore_name list */
+	while (ignore_name[i] != NULL) {
+		if (g_ascii_strncasecmp
+		    (name, ignore_name[i], strlen (ignore_name[i])) == 0) {
+			return TRUE;
+		}
+		i++;
+	}
+	/* Ignore mac address based configuration */
+	if (strlen (name) == 12 && is_hex (name))
+		result = TRUE;
+	return result;
+}
+
+static gboolean
+is_global_setting (char *key)
+{
+	static gchar *global_settings[] = { "wpa_supplicant_", NULL };
+	int i;
+
+	for (i = 0; global_settings[i] != NULL; i++) {
+		if (strstr (key, global_settings[i]))
+			return 1;
+	}
+	return 0;
+}
+
+/* Parse a complete line */
+/* Connection type is determined here */
+static void
+init_block_by_line (gchar * buf)
+{
+	gchar **key_value;
+	gchar *pos;
+	gchar *data;
+	gchar *tmp;
+	GHashTable *conn;
+
+	key_value = g_strsplit (buf, "=", 2);
+	if (g_strv_length (key_value) != 2) {
+		nm_log_warn (LOGD_SETTINGS, "Can't handle this line: %s\n", buf);
+		g_strfreev (key_value);
+		return;
+	}
+	pos = g_strrstr (key_value[0], "_");
+	if (pos == NULL || is_global_setting (key_value[0])) {
+		/* global data */
+		data = g_strdup (key_value[1]);
+		tmp = strip_string (data, '"');
+		strip_string (tmp, '\'');
+		nm_log_info (LOGD_SETTINGS, "global:%s-%s\n", key_value[0], tmp);
+		g_hash_table_insert (global_settings_table, g_strdup (key_value[0]), g_strdup (tmp));
+		g_strfreev (key_value);
+		g_free (data);
+		return;
+	}
+	*pos++ = '\0';
+	if ((conn = get_connection_config (pos)) == NULL) {
+		if (g_ascii_strncasecmp (pos, "eth", 3) == 0
+		    && strlen (pos) == 4)
+			/* wired connection */
+			conn = add_new_connection_config ("wired", pos);
+		else if (g_ascii_strncasecmp (pos, "ppp", 3) == 0
+			 && strlen (pos) == 4)
+			/* pppoe connection */
+			conn = add_new_connection_config ("ppp", pos);
+		else if (ignore_connection_name (pos)) {
+			/* ignored connection */
+			conn = add_new_connection_config ("ignore", pos);
+		} else {
+			int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, pos);
+
+			if (ifindex && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) != NM_LINK_TYPE_WIFI)
+				/* wired connection */
+				conn = add_new_connection_config ("wired", pos);
+			else
+				/* wireless connection */
+				conn = add_new_connection_config ("wireless", pos);
+		}
+	}
+	data = g_strdup (key_value[1]);
+	tmp = strip_string (data, '"');
+	strip_string (tmp, '\'');
+	if (conn)
+		g_hash_table_insert (conn, strip_string (g_strdup (key_value[0]), ' '),
+				     g_strdup (tmp));
+	g_free (data);
+	g_strfreev (key_value);
+}
+
+static void
+destroy_connection_config (GHashTable * conn)
+{
+	gpointer key, value;
+	GHashTableIter iter;
+
+	g_hash_table_iter_init (&iter, conn);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		g_free (key);
+		g_free (value);
+	}
+
+	g_hash_table_destroy (conn);
+}
+
+static void
+strip_function (GIOChannel * channel, gchar * line)
+{
+
+	int counter = 0;
+	gchar *p, *tmp;
+	gboolean begin = FALSE;
+	GString *function_str = g_string_new (line);
+
+	g_string_append (function_str, "\n");
+	while (1) {
+		p = line;
+		while (*p != '\0') {
+			if (*p == '{') {
+				counter++;
+				begin = TRUE;
+			} else if (*p == '}')
+				counter--;
+			p++;
+		}
+		if (begin && counter == 0) {
+			g_free (line);
+			goto done;
+		}
+		while (1) {
+			g_free (line);
+			if (g_io_channel_read_line
+			    (channel, &line, NULL, NULL,
+			     NULL) == G_IO_STATUS_EOF)
+				goto done;
+			g_string_append (function_str, line);
+			tmp = g_strdup (line);
+			g_strstrip (tmp);
+			if (tmp[0] != '#' && tmp[0] != '\0') {
+				g_free (tmp);
+				break;
+			} else
+				g_free (tmp);
+		}
+	}
+done:
+	functions_list =
+	    g_list_append (functions_list, g_strdup (function_str->str));
+	g_string_free (function_str, TRUE);
+}
+
+static gboolean
+is_function (gchar * line)
+{
+	static gchar *func_names[] =
+	    { "preup", "predown", "postup", "postdown", "failup", "faildown",
+		NULL,
+	};
+	int i;
+
+	for (i = 0; func_names[i]; i++) {
+		if (g_str_has_prefix (line, func_names[i])) {
+			nm_log_info (LOGD_SETTINGS, "Ignoring function: %s", func_names[i]);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static void
+append_line (GString *buf, gchar* line)
+{
+	gchar *pos = NULL;
+
+	if ((pos = strchr (line, '#')) != NULL)
+		*pos = '\0';
+	g_strstrip (line);
+
+	if (line[0] != '\0')
+		g_string_append_printf (buf, " %s", line);
+	g_free (line);
+}
+
+gboolean
+ifnet_init (gchar * config_file)
+{
+	GIOChannel *channel = NULL;
+	gchar *line;
+
+	/* Handle multiple lines with brackets */
+	gboolean complete = TRUE;
+
+	gboolean openrc_style = TRUE;
+
+	/* line buffer */
+	GString *buf;
+
+	net_parser_data_changed = FALSE;
+
+	conn_table = g_hash_table_new (nm_str_hash, g_str_equal);
+	global_settings_table = g_hash_table_new (nm_str_hash, g_str_equal);
+	functions_list = NULL;
+
+	if (g_file_test (config_file, G_FILE_TEST_IS_REGULAR))
+		channel = g_io_channel_new_file (config_file, "r", NULL);
+	if (channel == NULL) {
+		nm_log_warn (LOGD_SETTINGS, "Can't open %s", config_file);
+		return FALSE;
+	}
+
+	buf = g_string_new (NULL);
+	while (g_io_channel_read_line
+	       (channel, &line, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
+		g_strstrip (line);
+		/* convert multiple lines to a complete line and
+		 * pass it to init_block_by_line() */
+		if (is_function (line)) {
+			strip_function (channel, line);
+			continue;
+		}
+
+		// New openrc style, bash arrays are not allowed. We only care about '"'
+		if (openrc_style && line[0] != '#' && line[0] != '\0'
+				&& !strchr (line, '(') && !strchr (line, ')')) {
+			gchar *tmp = line;
+
+			while ((tmp = strchr (tmp, '"')) != NULL) {
+				complete = !complete;
+				++tmp;
+			}
+
+			append_line (buf, line);
+			// Add "(separator) for routes. It will be easier for later parsing
+			if (strstr (buf->str, "via"))
+				g_string_append_printf (buf, "\"");
+
+			if (!complete)
+				continue;
+
+			strip_string (buf->str, '"');
+
+			init_block_by_line (buf->str);
+			g_string_free (buf, TRUE);
+			buf = g_string_new (NULL);
+		}
+		// Old bash arrays for baselayout-1, to be deleted
+		else if (line[0] != '#' && line[0] != '\0') {
+			if (!complete) {
+				complete =
+				    g_strrstr (line,
+					       ")") == NULL ? FALSE : TRUE;
+
+				append_line (buf, line);
+				if (!complete) {
+					openrc_style = FALSE;
+					continue;
+				}
+				else {
+					openrc_style = TRUE;
+				}
+			} else {
+				complete =
+				    (g_strrstr (line, "(") != NULL
+				     && g_strrstr (line, ")") != NULL)
+				    || g_strrstr (line, "(") == NULL;
+
+				append_line (buf, line);
+				if (!complete)
+				{
+					openrc_style = FALSE;
+					continue;
+				} else {
+					openrc_style = TRUE;
+				}
+			}
+			init_block_by_line (buf->str);
+			g_string_free (buf, TRUE);
+			buf = g_string_new (NULL);
+		} else
+			/* Blank line or comment line */
+			g_free (line);
+	}
+
+	g_string_free (buf, TRUE);
+	g_io_channel_shutdown (channel, FALSE, NULL);
+	g_io_channel_unref (channel);
+	return TRUE;
+}
+
+const char *
+ifnet_get_data (const char *conn_name, const char *key)
+{
+	GHashTable *conn;
+
+	g_return_val_if_fail (conn_name && key, NULL);
+
+	conn = g_hash_table_lookup (conn_table, conn_name);
+
+	if (conn)
+		return g_hash_table_lookup (conn, key);
+	return NULL;
+}
+
+/* format ip values for comparison */
+static gchar*
+format_ip_for_comparison (const gchar * value)
+{
+	gchar **ipset;
+	guint length, i;
+	GString *formated_string = g_string_new (NULL);
+	gchar *formatted = NULL;
+
+	ipset = g_strsplit (value, "\"", 0);
+	length = g_strv_length (ipset);
+
+	for (i = 0; i < length; i++)
+	{
+		strip_string (ipset[i], ' ');
+		if (ipset[i][0] != '\0')
+			g_string_append_printf (formated_string,
+						"%s ", ipset[i]);
+	}
+	formatted = g_strdup (formated_string->str);
+	formatted[formated_string->len - 1] = '\0';
+
+	g_string_free (formated_string, TRUE);
+	g_strfreev (ipset);
+
+	return formatted;
+}
+
+void
+ifnet_set_data (const char *conn_name, const char *key, const char *value)
+{
+	gpointer old_key = NULL, old_value = NULL;
+	GHashTable *conn = g_hash_table_lookup (conn_table, conn_name);
+	gchar * stripped = NULL;
+
+	if (!conn) {
+		nm_log_warn (LOGD_SETTINGS, "%s does not exist!", conn_name);
+		return;
+	}
+	if (value){
+		stripped = g_strdup (value);
+		strip_string (stripped, '"');
+	}
+	/* Remove existing key value pair */
+	if (g_hash_table_lookup_extended (conn, key, &old_key, &old_value)) {
+
+		/* This ugly hack is due to baselayout compatibility. We have to
+		 * deal with different ip format. So sometimes we have the same ips
+		 * but different strings.
+		 */
+		if (stripped &&
+			(!strcmp (key, "config")
+			|| !strcmp (key, "routes")
+			|| !strcmp (key, "pppd")
+			|| !strcmp (key, "chat")))
+		{
+			gchar *old_ips = format_ip_for_comparison (old_value);
+			gchar *new_ips = format_ip_for_comparison (value);
+			if(!strcmp (old_ips, new_ips))
+			{
+				g_free (stripped);
+				g_free (old_ips);
+				g_free (new_ips);
+				return;
+			}
+			g_free (old_ips);
+			g_free (new_ips);
+		}
+
+		if (stripped && !strcmp (old_value, stripped)) {
+			g_free (stripped);
+			return;
+		}
+		g_hash_table_remove (conn, old_key);
+		g_free (old_key);
+		g_free (old_value);
+	} else if (!value)
+		return;
+	if (stripped)
+		g_hash_table_insert (conn, g_strdup (key), stripped);
+	net_parser_data_changed = TRUE;
+}
+
+// Remember to free return value
+const char *
+ifnet_get_global_data (const gchar * key)
+{
+	return g_hash_table_lookup (global_settings_table, key);
+}
+
+// Return names of legal connections
+GList *
+ifnet_get_connection_names (void)
+{
+	GList *names = g_hash_table_get_keys (conn_table);
+	GList *iter, *result = NULL;
+
+	for (iter = names; iter; iter = iter->next) {
+		if (!ignore_connection_name (iter->data))
+			result = g_list_prepend (result, iter->data);
+	}
+
+	g_list_free (names);
+	return g_list_reverse (result);
+}
+
+/* format IP and route for writing */
+static void
+format_ips (gchar * value, gchar ** out_line, gchar * key, gchar * name)
+{
+	gchar **ipset;
+	guint length, i;
+	GString *formated_string = g_string_new (NULL);
+
+	strip_string (value, '(');
+	strip_string (value, ')');
+	strip_string (value, '"');
+	ipset = g_strsplit (value, "\"", 0);
+	length = g_strv_length (ipset);
+
+	//only one line
+	if (length < 2) {
+		*out_line =
+		    g_strdup_printf ("%s_%s=\"%s\"\n", key, name, value);
+		goto done;
+	}
+	// Multiple lines
+	g_string_append_printf (formated_string, "%s_%s=\"\n", key, name);
+	for (i = 0; i < length; i++)
+	{
+		strip_string (ipset[i], ' ');
+		if (ipset[i][0] != '\0')
+			g_string_append_printf (formated_string,
+						"%s\n", ipset[i]);
+	}
+	g_string_append (formated_string, "\"\n");
+	*out_line = g_strdup (formated_string->str);
+done:
+	g_string_free (formated_string, TRUE);
+	g_strfreev (ipset);
+}
+
+gboolean
+ifnet_flush_to_file (const char *config_file, gchar **out_backup)
+{
+	GIOChannel *channel;
+	GError *error = NULL;
+	gpointer key, value, name, network;
+	GHashTableIter iter, iter_network;
+	GList *list_iter;
+	gchar *out_line = NULL;
+	gsize bytes_written;
+	gboolean result = FALSE;
+	gchar *backup;
+
+	if (!net_parser_data_changed)
+		return TRUE;
+	if (!conn_table || !global_settings_table)
+		return FALSE;
+
+	backup = backup_file (config_file);
+
+	channel = g_io_channel_new_file (config_file, "w", NULL);
+	if (!channel) {
+		nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file);
+		g_free (backup);
+		return FALSE;
+	}
+	g_hash_table_iter_init (&iter, global_settings_table);
+	nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file);
+	g_io_channel_write_chars (channel,
+				  "#Generated by NetworkManager\n"
+				  "###### Global Configuration ######\n",
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
+	/* Writing global data */
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		out_line =
+		    g_strdup_printf ("%s=\"%s\"\n", (gchar *) key, (gchar *) value);
+		g_io_channel_write_chars (channel, out_line, -1,
+					  &bytes_written, &error);
+		if (bytes_written == 0 || error)
+			goto done;
+		g_free (out_line);
+	}
+
+	/* Writing connection data */
+	g_io_channel_write_chars (channel,
+				  "\n###### Connection Configuration ######\n",
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
+	g_hash_table_iter_init (&iter, conn_table);
+	while (g_hash_table_iter_next (&iter, &name, &network)) {
+		g_hash_table_iter_init (&iter_network, (GHashTable *) network);
+		g_io_channel_write_chars (channel,
+					  "#----------------------------------\n",
+					  -1, &bytes_written, &error);
+		if (error)
+			goto done;
+
+		while (g_hash_table_iter_next (&iter_network, &key, &value)) {
+			if (!g_str_has_prefix ((gchar *) key, "name")
+			    && !g_str_has_prefix ((gchar *) key, "type")) {
+				/* These keys contain brackets */
+				if (strcmp
+				    ((gchar *) key,
+				     "config") == 0
+				    || strcmp ((gchar *) key,
+					       "routes") == 0
+				    || strcmp ((gchar *) key,
+					       "pppd") == 0
+				    || strcmp ((gchar *) key, "chat") == 0)
+					format_ips (value, &out_line, (gchar *)
+						    key, (gchar *)
+						    name);
+				else
+					out_line =
+					    g_strdup_printf
+					    ("%s_%s=\"%s\"\n",
+					     (gchar *) key,
+					     (gchar *) name, (gchar *) value);
+				g_io_channel_write_chars (channel, out_line, -1, &bytes_written, &error);
+				if (bytes_written == 0 || error)
+					goto done;
+				g_free (out_line);
+			}
+		}
+	}
+
+	/* Writing reserved functions */
+	if (functions_list) {
+		g_io_channel_write_chars (channel,
+					  "\n###### Reserved Functions ######\n",
+					  -1, &bytes_written, &error);
+		if (error)
+			goto done;
+
+		/* Writing functions */
+		for (list_iter = functions_list; list_iter;
+		     list_iter = g_list_next (list_iter)) {
+			out_line =
+			    g_strdup_printf ("%s\n", (gchar *) list_iter->data);
+			g_io_channel_write_chars (channel, out_line, -1,
+						  &bytes_written, &error);
+			if (bytes_written == 0 || error)
+				goto done;
+			g_free (out_line);
+		}
+	}
+
+	g_io_channel_flush (channel, &error);
+	if (error)
+		goto done;
+	result = TRUE;
+	net_parser_data_changed = FALSE;
+
+done:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "Error writing the configuration file: %s", error->message);
+		g_error_free (error);
+	}
+
+	if (result && out_backup)
+		*out_backup = backup;
+	else
+		g_free (backup);
+
+	g_io_channel_shutdown (channel, FALSE, NULL);
+	g_io_channel_unref (channel);
+	return result;
+}
+
+gboolean
+ifnet_delete_network (const char *conn_name)
+{
+	GHashTable *network = NULL;
+
+	g_return_val_if_fail (conn_table != NULL && conn_name != NULL, FALSE);
+	nm_log_info (LOGD_SETTINGS, "Deleting network for %s", conn_name);
+	network = g_hash_table_lookup (conn_table, conn_name);
+	if (!network)
+		return FALSE;
+	g_hash_table_remove (conn_table, conn_name);
+	destroy_connection_config (network);
+	net_parser_data_changed = TRUE;
+	return TRUE;
+}
+
+void
+ifnet_destroy (void)
+{
+	GHashTableIter iter;
+	gpointer key;
+	gpointer value;
+	GList *list_iter;
+
+	/* Destroy connection setting */
+	if (conn_table) {
+		g_hash_table_iter_init (&iter, conn_table);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			destroy_connection_config ((GHashTable *)
+						   value);
+		}
+		g_hash_table_destroy (conn_table);
+		conn_table = NULL;
+	}
+
+	/* Destroy global data */
+	if (global_settings_table) {
+		g_hash_table_iter_init (&iter, global_settings_table);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			g_free (key);
+			g_free (value);
+		}
+		g_hash_table_destroy (global_settings_table);
+		global_settings_table = NULL;
+	}
+	for (list_iter = functions_list; list_iter;
+	     list_iter = g_list_next (list_iter))
+		g_free (list_iter->data);
+	g_list_free (functions_list);
+}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-parser.h b/src/settings/plugins/ifnet/nms-ifnet-net-parser.h
new file mode 100644
index 00000000..31fc9ead
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-parser.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef _NET_PARSER_H
+#define _NET_PARSER_H
+
+#define CONF_NET_FILE SYSCONFDIR "/conf.d/net"
+
+gboolean ifnet_init (gchar * config_file);
+void ifnet_destroy (void);
+
+/* Reader functions */
+GList *ifnet_get_connection_names (void);
+const char *ifnet_get_data (const char *conn_name, const char *key);
+const char *ifnet_get_global_data (const char *key);
+gboolean ifnet_has_network (const char *conn_name);
+
+/* Writer functions */
+gboolean ifnet_flush_to_file (const char *config_file, gchar **out_backup);
+void ifnet_set_data (const char *conn_name, const char *key, const char *value);
+gboolean ifnet_add_network (const char *name, const char *type);
+gboolean ifnet_delete_network (const char *conn_name);
+
+#endif
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.c b/src/settings/plugins/ifnet/nms-ifnet-net-utils.c
new file mode 100644
index 00000000..6531a238
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.c
@@ -0,0 +1,830 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-net-utils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "nm-utils.h"
+#include "NetworkManagerUtils.h"
+#include "settings/nm-settings-plugin.h"
+#include "nm-config.h"
+#include "dhcp/nm-dhcp-manager.h"
+
+#include "nms-ifnet-wpa-parser.h"
+#include "nms-ifnet-net-parser.h"
+
+/* emit heading and tailing blank space, tab, character t */
+gchar *
+strip_string (gchar * str, gchar t)
+{
+	gchar *ret = str;
+	gint length = 0;
+	guint i = 0;
+
+	while (ret[i] != '\0'
+	       && (ret[i] == '\t' || ret[i] == ' ' || ret[i] == t)) {
+		length++;
+		i++;
+	}
+	i = 0;
+	while (ret[i + length] != '\0') {
+		ret[i] = ret[i + length];
+		i++;
+	}
+	ret[i] = '\0';
+	length = strlen (ret);
+	while ((length - 1) >= 0
+	       && (ret[length - 1] == ' ' || ret[length - 1] == '\n'
+		   || ret[length - 1] == '\t' || ret[length - 1] == t))
+		length--;
+	ret[length] = '\0';
+	return ret;
+}
+
+gboolean
+is_hex (const char *value)
+{
+	const char *p = value;
+
+	if (!p)
+		return FALSE;
+	while (*p) {
+		if (!g_ascii_isxdigit (*p++))
+			return FALSE;
+	}
+	return TRUE;
+}
+
+gboolean
+is_ascii (const char *value)
+{
+	const char *p = value;
+
+	while (*p) {
+		if (!g_ascii_isprint (*p++))
+			return FALSE;
+	}
+	return TRUE;
+
+}
+
+gboolean
+is_true (const char *str)
+{
+	if (!g_ascii_strcasecmp (str, "yes")
+	    || !g_ascii_strcasecmp (str, "true"))
+		return TRUE;
+	return FALSE;
+}
+
+static char *
+find_default_gateway_str (char *str)
+{
+	char *tmp;
+
+	if ((tmp = strstr (str, "default via ")) != NULL) {
+		return tmp + strlen ("default via ");
+	} else if ((tmp = strstr (str, "default gw ")) != NULL) {
+		return tmp + strlen ("default gw ");
+	}
+	return NULL;
+}
+
+static char *
+find_gateway_str (char *str)
+{
+	char *tmp;
+
+	if ((tmp = strstr (str, "via ")) != NULL) {
+		return tmp + strlen ("via ");
+	} else if ((tmp = strstr (str, "gw ")) != NULL) {
+		return tmp + strlen ("gw ");
+	}
+	return NULL;
+}
+
+gboolean
+reload_parsers (void)
+{
+	ifnet_destroy ();
+	wpa_parser_destroy ();
+	if (!ifnet_init (CONF_NET_FILE))
+		return FALSE;
+	wpa_parser_init (WPA_SUPPLICANT_CONF);
+	return TRUE;
+}
+
+gboolean
+is_static_ip4 (const char *conn_name)
+{
+	const char *data = ifnet_get_data (conn_name, "config");
+	const char *dhcp6;
+
+	if (!data)
+		return FALSE;
+	if (!strcmp (data, "shared"))
+		return FALSE;
+	if (!strcmp (data, "autoip"))
+		return FALSE;
+	dhcp6 = strstr (data, "dhcp6");
+	if (dhcp6) {
+		gchar *dhcp4;
+
+		if (strstr (data, "dhcp "))
+			return FALSE;
+		dhcp4 = strstr (data, "dhcp");
+		if (!dhcp4)
+			return TRUE;
+		if (dhcp4[4] == '\0')
+			return FALSE;
+		return TRUE;
+	}
+	return strstr (data, "dhcp") == NULL ? TRUE : FALSE;
+}
+
+gboolean
+is_static_ip6 (const char *conn_name)
+{
+	const char *data = ifnet_get_data (conn_name, "config");
+
+	if (!data)
+		return TRUE;
+	return strstr (data, "dhcp6") == NULL ? TRUE : FALSE;
+}
+
+gboolean
+is_ip4_address (const char *in_address)
+{
+	const char *pattern =
+	    "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.((\\{\\d{1,3}\\.\\.\\d{1,3}\\})|\\d{1,3})$";
+	gchar *address = g_strdup (in_address);
+	gboolean result = FALSE;
+	gchar *tmp;
+	GRegex *regex = g_regex_new (pattern, 0, 0, NULL);
+	GMatchInfo *match_info = NULL;
+
+	if (!address)
+		goto done;
+	g_strstrip (address);
+	if ((tmp = strstr (address, "/")) != NULL)
+		*tmp = '\0';
+	if ((tmp = strstr (address, " ")) != NULL)
+		*tmp = '\0';
+	g_regex_match (regex, address, 0, &match_info);
+	result = g_match_info_matches (match_info);
+done:
+	if (match_info)
+		g_match_info_free (match_info);
+	g_regex_unref (regex);
+	g_free (address);
+	return result;
+}
+
+gboolean
+is_ip6_address (const char *in_address)
+{
+	struct in6_addr tmp_ip6_addr;
+	gchar *tmp, *address;
+	gboolean result = FALSE;
+
+	if (!in_address)
+		return FALSE;
+	address = g_strdup (in_address);
+	g_strstrip (address);
+	if ((tmp = strchr (address, '/')) != NULL)
+		*tmp = '\0';
+	if (inet_pton (AF_INET6, address, &tmp_ip6_addr))
+		result = TRUE;
+	g_free (address);
+	return result;
+
+}
+
+// 'c' is only used for openrc style
+static gchar **
+split_addresses_by_char (const gchar *addresses, const gchar *c)
+{
+	gchar **ipset;
+
+	if (addresses == NULL)
+		return NULL;
+
+	if (strchr (addresses, '(') != NULL) { // old baselayout style
+		gchar *tmp = g_strdup (addresses);
+		strip_string (tmp, '(');
+		strip_string (tmp, ')');
+		strip_string (tmp, '"');
+		strip_string (tmp, '\'');
+		ipset = g_strsplit (tmp, "\" \"", 0);
+		g_free(tmp);
+	} else { // openrc style
+		if (strstr (addresses, "netmask"))
+			// There is only one ip address if "netmask" is specified.
+			// '\n' is not used in config so there will be only one split.
+			ipset = g_strsplit (addresses, "\n", 0);
+		else
+			ipset = g_strsplit (addresses, c, 0);
+	}
+
+	return ipset;
+}
+
+static gchar **
+split_addresses (const gchar* addresses)
+{
+	// " " is only used by openrc style
+	return split_addresses_by_char (addresses, " ");
+}
+
+static gchar **
+split_routes (const gchar* routes)
+{
+	// "\"" is only used by openrc style
+	return split_addresses_by_char (routes, "\"");
+}
+
+gboolean
+has_ip6_address (const char *conn_name)
+{
+	gchar **ipset;
+	guint length;
+	guint i;
+
+	g_return_val_if_fail (conn_name != NULL, FALSE);
+	ipset = split_addresses (ifnet_get_data (conn_name, "config"));
+	length = ipset ? g_strv_length (ipset) : 0;
+	for (i = 0; i < length; i++) {
+		if (!is_ip6_address (ipset[i]))
+			continue;
+		else {
+			g_strfreev (ipset);
+			return TRUE;
+		}
+
+	}
+	g_strfreev (ipset);
+	return FALSE;
+}
+
+gboolean
+has_default_route (const char *conn_name, gboolean (*check_fn) (const char *))
+{
+	char *routes = NULL, *end, *tmp;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (conn_name != NULL, FALSE);
+
+	routes = g_strdup (ifnet_get_data (conn_name, "routes"));
+	if (!routes)
+		return FALSE;
+	tmp = find_default_gateway_str (routes);
+	if (tmp) {
+		g_strstrip (tmp);
+		if ((end = strstr (tmp, "\"")) != NULL)
+			*end = '\0';
+		if (check_fn (tmp))
+			success = TRUE;
+	}
+
+	g_free (routes);
+	return success;
+}
+
+static ip_block *
+create_ip4_block (gchar * ip)
+{
+	ip_block *iblock = g_slice_new0 (ip_block);
+	guint32 tmp_ip4_addr;
+	int i;
+	guint length;
+	gchar **ip_mask;
+
+	/* prefix format */
+	if (strstr (ip, "/")) {
+		gchar *prefix;
+
+		ip_mask = g_strsplit (ip, "/", 0);
+		length = g_strv_length (ip_mask);
+		if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0]))
+			goto error;
+		iblock->ip = g_strdup (ip_mask[0]);
+		prefix = ip_mask[1];
+		i = 0;
+		while (i < length && g_ascii_isdigit (prefix[i]))
+			i++;
+		prefix[i] = '\0';
+		iblock->prefix = (guint32) atoi (ip_mask[1]);
+	} else if (strstr (ip, "netmask")) {
+		ip_mask = g_strsplit (ip, " ", 0);
+		length = g_strv_length (ip_mask);
+		if (!nm_utils_ipaddr_valid (AF_INET, ip_mask[0]))
+			goto error;
+		iblock->ip = g_strdup (ip_mask[0]);
+		i = 0;
+		while (i < length && !strstr (ip_mask[++i], "netmask")) ;
+		while (i < length && ip_mask[++i][0] == '\0') ;
+		if (i >= length)
+			goto error;
+		if (!inet_pton (AF_INET, ip_mask[i], &tmp_ip4_addr))
+			goto error;
+		iblock->prefix = nm_utils_ip4_netmask_to_prefix (tmp_ip4_addr);
+	} else {
+		g_slice_free (ip_block, iblock);
+		if (!is_ip6_address (ip) && !strstr (ip, "dhcp"))
+			nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, missing netmask or prefix", ip);
+		return NULL;
+	}
+	if (iblock->prefix == 0 || iblock->prefix > 32) {
+		nm_log_warn (LOGD_SETTINGS, "Can't handle ipv4 address: %s, invalid prefix", ip);
+		goto error;
+	}
+	g_strfreev (ip_mask);
+	return iblock;
+error:
+	if (!is_ip6_address (ip))
+		nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 address: %s", ip);
+	g_strfreev (ip_mask);
+	g_free (iblock->ip);
+	g_slice_free (ip_block, iblock);
+	return NULL;
+}
+
+static ip_block *
+create_ip_block (gchar * ip)
+{
+	ip_block *iblock = g_slice_new0 (ip_block);
+	gchar *dup_ip = g_strdup (ip);
+	gchar *prefix = NULL;
+
+	if ((prefix = strstr (dup_ip, "/")) != NULL) {
+		*prefix = '\0';
+		prefix++;
+	}
+	if (!nm_utils_ipaddr_valid (AF_INET6, dup_ip))
+		goto error;
+	iblock->ip = dup_ip;
+	if (prefix) {
+		errno = 0;
+		iblock->prefix = strtol (prefix, NULL, 10);
+		if (errno || iblock->prefix <= 0 || iblock->prefix > 128) {
+			goto error;
+		}
+	} else
+		iblock->prefix = 64;
+	return iblock;
+error:
+	if (!is_ip4_address (ip))
+		nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 address: %s", ip);
+	g_slice_free (ip_block, iblock);
+	g_free (dup_ip);
+	return NULL;
+}
+
+static char *
+get_ip4_gateway (gchar * gateway)
+{
+	gchar *tmp, *split;
+
+	if (!gateway)
+		return NULL;
+	tmp = find_gateway_str (gateway);
+	if (!tmp) {
+		nm_log_warn (LOGD_SETTINGS, "Couldn't obtain gateway in \"%s\"", gateway);
+		return NULL;
+	}
+	tmp = g_strdup (tmp);
+	strip_string (tmp, ' ');
+	strip_string (tmp, '"');
+
+	// Only one gateway is selected
+	if ((split = strstr (tmp, "\"")) != NULL)
+		*split = '\0';
+
+	if (!nm_utils_ipaddr_valid (AF_INET, tmp))
+		goto error;
+	return tmp;
+error:
+	if (!is_ip6_address (tmp))
+		nm_log_warn (LOGD_SETTINGS, "Can't handle IPv4 gateway: %s", tmp);
+	g_free (tmp);
+	return NULL;
+}
+
+static char *
+get_ip6_next_hop (gchar * next_hop)
+{
+	gchar *tmp;
+
+	if (!next_hop)
+		return NULL;
+	tmp = find_gateway_str (next_hop);
+	if (!tmp) {
+		nm_log_warn (LOGD_SETTINGS, "Couldn't obtain next_hop in \"%s\"", next_hop);
+		return NULL;
+	}
+	tmp = g_strdup (tmp);
+	strip_string (tmp, ' ');
+	strip_string (tmp, '"');
+	g_strstrip (tmp);
+	if (!nm_utils_ipaddr_valid (AF_INET6, tmp))
+		goto error;
+	return tmp;
+error:
+	if (!is_ip4_address (tmp))
+		nm_log_warn (LOGD_SETTINGS, "Can't handle IPv6 next_hop: %s", tmp);
+	g_free (tmp);
+
+	return NULL;
+}
+
+ip_block *
+convert_ip4_config_block (const char *conn_name)
+{
+	gchar **ipset;
+	guint length;
+	guint i;
+	gchar *ip;
+	char *def_gateway = NULL;
+	const char *routes;
+	ip_block *start = NULL, *current = NULL, *iblock = NULL;
+
+	g_return_val_if_fail (conn_name != NULL, NULL);
+
+	ipset = split_addresses (ifnet_get_data (conn_name, "config"));
+	length = ipset ? g_strv_length (ipset) : 0;
+
+	routes = ifnet_get_data (conn_name, "routes");
+	if (routes)
+		def_gateway = get_ip4_gateway (strstr (routes, "default"));
+
+	for (i = 0; i < length; i++) {
+		ip = ipset[i];
+		ip = strip_string (ip, '"');
+		iblock = create_ip4_block (ip);
+		if (iblock == NULL)
+			continue;
+		if (!iblock->next_hop && def_gateway != NULL)
+			iblock->next_hop = g_strdup (def_gateway);
+		if (start == NULL)
+			start = current = iblock;
+		else {
+			current->next = iblock;
+			current = iblock;
+		}
+	}
+	g_strfreev (ipset);
+	g_free (def_gateway);
+	return start;
+}
+
+ip_block *
+convert_ip6_config_block (const char *conn_name)
+{
+	gchar **ipset;
+	guint length;
+	guint i;
+	gchar *ip;
+	ip_block *start = NULL, *current = NULL, *iblock = NULL;
+
+	g_return_val_if_fail (conn_name != NULL, NULL);
+	ipset = split_addresses (ifnet_get_data (conn_name, "config"));
+	length = ipset ? g_strv_length (ipset) : 0;
+	for (i = 0; i < length; i++) {
+		ip = ipset[i];
+		ip = strip_string (ip, '"');
+		iblock = create_ip_block (ip);
+		if (iblock == NULL)
+			continue;
+		if (start == NULL)
+			start = current = iblock;
+		else {
+			current->next = iblock;
+			current = iblock;
+		}
+	}
+	g_strfreev (ipset);
+	return start;
+}
+
+ip_block *
+convert_ip4_routes_block (const char *conn_name)
+{
+	gchar **ipset;
+	guint length;
+	guint i;
+	gchar *ip;
+	ip_block *start = NULL, *current = NULL, *iblock = NULL;
+
+	g_return_val_if_fail (conn_name != NULL, NULL);
+
+	ipset = split_routes (ifnet_get_data (conn_name, "routes"));
+	length = ipset ? g_strv_length (ipset) : 0;
+	for (i = 0; i < length; i++) {
+		ip = ipset[i];
+		if (find_default_gateway_str (ip) || strstr (ip, "::")
+		    || !find_gateway_str (ip))
+			continue;
+		ip = strip_string (ip, '"');
+		iblock = create_ip4_block (ip);
+		if (iblock == NULL)
+			continue;
+		iblock->next_hop = get_ip4_gateway (ip);
+		if (start == NULL)
+			start = current = iblock;
+		else {
+			current->next = iblock;
+			current = iblock;
+		}
+	}
+	g_strfreev (ipset);
+	return start;
+}
+
+ip_block *
+convert_ip6_routes_block (const char *conn_name)
+{
+	gchar **ipset;
+	guint length;
+	guint i;
+	gchar *ip, *tmp_addr;
+	ip_block *start = NULL, *current = NULL, *iblock = NULL;
+
+	g_return_val_if_fail (conn_name != NULL, NULL);
+	ipset = split_routes (ifnet_get_data (conn_name, "routes"));
+	length = ipset ? g_strv_length (ipset) : 0;
+	for (i = 0; i < length; i++) {
+		ip = ipset[i];
+		ip = strip_string (ip, '"');
+		if (ip[0] == '\0')
+			continue;
+		if ((tmp_addr = find_default_gateway_str (ip)) != NULL) {
+			if (!is_ip6_address (tmp_addr))
+				continue;
+			else {
+				iblock = g_slice_new0 (ip_block);
+				iblock->ip = g_strdup ("::");
+				iblock->prefix = 128;
+			}
+		} else
+			iblock = create_ip_block (ip);
+		if (iblock == NULL)
+			continue;
+		iblock->next_hop = get_ip6_next_hop (ip);
+		if (iblock->next_hop == NULL) {
+			destroy_ip_block (iblock);
+			continue;
+		}
+		if (start == NULL)
+			start = current = iblock;
+		else {
+			current->next = iblock;
+			current = iblock;
+		}
+	}
+	g_strfreev (ipset);
+	return start;
+}
+
+void
+destroy_ip_block (ip_block * iblock)
+{
+	g_free (iblock->ip);
+	g_free (iblock->next_hop);
+	g_slice_free (ip_block, iblock);
+}
+
+void
+set_ip4_dns_servers (NMSettingIPConfig *s_ip4, const char *conn_name)
+{
+	const char *dns_servers;
+	gchar **server_list, *stripped;
+	guint length, i;
+	guint32 tmp_ip4_addr;
+
+	dns_servers = ifnet_get_data (conn_name, "dns_servers");
+	if (!dns_servers)
+		return;
+	stripped = g_strdup (dns_servers);
+	strip_string (stripped, '"');
+	server_list = g_strsplit (stripped, " ", 0);
+	g_free (stripped);
+
+	length = g_strv_length (server_list);
+	if (length)
+		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
+			      TRUE, NULL);
+	for (i = 0; i < length; i++) {
+		g_strstrip (server_list[i]);
+		if (server_list[i][0] == '\0')
+			continue;
+		if (!inet_pton (AF_INET, server_list[i], &tmp_ip4_addr)) {
+			if (!is_ip6_address (server_list[i]))
+				nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]);
+			continue;
+		}
+		if (!nm_setting_ip_config_add_dns (s_ip4, server_list[i]))
+			nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]);
+	}
+	g_strfreev (server_list);
+}
+
+void
+set_ip6_dns_servers (NMSettingIPConfig *s_ip6, const char *conn_name)
+{
+	const char *dns_servers;
+	gchar **server_list, *stripped;
+	guint length, i;
+	struct in6_addr tmp_ip6_addr;
+
+	dns_servers = ifnet_get_data (conn_name, "dns_servers");
+	if (!dns_servers)
+		return;
+
+	stripped = g_strdup (dns_servers);
+	strip_string (stripped, '"');
+	server_list = g_strsplit (stripped, " ", 0);
+	g_free (stripped);
+
+	length = g_strv_length (server_list);
+	if (length)
+		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS,
+			      TRUE, NULL);
+	for (i = 0; i < length; i++) {
+		g_strstrip (server_list[i]);
+		if (server_list[i][0] == '\0')
+			continue;
+		if (!inet_pton (AF_INET6, server_list[i], &tmp_ip6_addr)) {
+			if (is_ip6_address (server_list[i]))
+				nm_log_warn (LOGD_SETTINGS, "ignored dns: %s\n", server_list[i]);
+			continue;
+		}
+		if (!nm_setting_ip_config_add_dns (s_ip6, server_list[i]))
+			nm_log_warn (LOGD_SETTINGS, "warning: duplicate DNS server %s", server_list[i]);
+	}
+	g_strfreev (server_list);
+}
+
+gboolean
+is_managed (const char *conn_name)
+{
+	gchar *config;
+
+	g_return_val_if_fail (conn_name != NULL, FALSE);
+	config = (gchar *) ifnet_get_data (conn_name, "managed");
+	if (!config)
+		return TRUE;
+	if (strcmp (config, "false") == 0)
+		return FALSE;
+	return TRUE;
+}
+
+static char *
+_has_prefix_impl (char *str, const char *prefix, gsize prefix_len)
+{
+	if (!g_str_has_prefix (str, prefix))
+		return NULL;
+	str += prefix_len;
+	if (!g_ascii_isspace (str[0]))
+		return NULL;
+	do {
+		str++;
+	} while (g_ascii_isspace (str[0]));
+	return str;
+}
+#define _has_prefix(STR, PREFIX) _has_prefix_impl (STR, PREFIX, NM_STRLEN (PREFIX))
+
+void
+get_dhcp_hostname_and_client_id (char **hostname, char **client_id)
+{
+	const char *dhcp_client;
+	const gchar *dhcpcd_conf = SYSCONFDIR "/dhcpcd.conf";
+	const gchar *dhclient_conf = SYSCONFDIR "/dhcp/dhclient.conf";
+	gchar *line = NULL, *tmp = NULL, *contents = NULL, *tmp1;
+	gchar **all_lines;
+	guint line_num, i;
+	gboolean use_dhclient = FALSE;
+
+	*hostname = NULL;
+	*client_id = NULL;
+	dhcp_client = nm_dhcp_manager_get_config (nm_dhcp_manager_get ());
+	if (dhcp_client) {
+		if (!strcmp (dhcp_client, "dhclient")) {
+			g_file_get_contents (dhclient_conf, &contents, NULL,
+			                     NULL);
+			use_dhclient = TRUE;
+		} else if (!strcmp (dhcp_client, "dhcpcd")) {
+			g_file_get_contents (dhcpcd_conf, &contents, NULL,
+			                     NULL);
+		}
+	} else {
+		if (g_file_test (dhclient_conf, G_FILE_TEST_IS_REGULAR)) {
+			g_file_get_contents (dhclient_conf, &contents, NULL,
+			                     NULL);
+			use_dhclient = TRUE;
+		} else if (g_file_test (dhcpcd_conf, G_FILE_TEST_IS_REGULAR)) {
+			g_file_get_contents (dhcpcd_conf, &contents, NULL,
+			                     NULL);
+		}
+	}
+	if (!contents)
+		return;
+	all_lines = g_strsplit (contents, "\n", 0);
+	line_num = g_strv_length (all_lines);
+	for (i = 0; i < line_num; i++) {
+		line = all_lines[i];
+		g_strstrip (line);
+		if (line[0] == '#' || line[0] == '\0')
+			continue;
+		if (!use_dhclient) {
+			// dhcpcd.conf
+			if ((tmp = _has_prefix (line, "hostname"))) {
+				if (tmp[0] != '\0') {
+					g_free (*hostname);
+					*hostname = g_strdup (tmp);
+				} else
+					nm_log_info (LOGD_SETTINGS, "dhcpcd hostname not defined, ignoring");
+			} else if ((tmp = _has_prefix (line, "clientid"))) {
+				if (tmp[0] != '\0') {
+					g_free (*client_id);
+					*client_id = g_strdup (tmp);
+				} else
+					nm_log_info (LOGD_SETTINGS, "dhcpcd clientid not defined, ignoring");
+			}
+		} else {
+			// dhclient.conf
+			if ((tmp1 = _has_prefix (line, "send"))) {
+				if ((tmp = _has_prefix (tmp1, "host-name"))) {
+					strip_string (tmp, ';');
+					strip_string (tmp, '"');
+					if (tmp[0] != '\0') {
+						g_free (*hostname);
+						*hostname = g_strdup (tmp);
+					} else
+						nm_log_info (LOGD_SETTINGS, "dhclient hostname not defined, ignoring");
+				} else if ((tmp = _has_prefix (tmp1, "dhcp-client-identifier"))) {
+					strip_string (tmp, ';');
+					if (tmp[0] != '\0') {
+						g_free (*client_id);
+						*client_id = g_strdup (tmp);
+					} else
+						nm_log_info (LOGD_SETTINGS, "dhclient clientid not defined, ignoring");
+				}
+			}
+		}
+	}
+	g_strfreev (all_lines);
+	g_free (contents);
+}
+
+gchar *backup_file (const gchar* target)
+{
+	GFile *source, *backup;
+	gchar* backup_path;
+	GError *error = NULL;
+
+	source = g_file_new_for_path (target);
+
+	if (!g_file_query_exists (source, NULL)) {
+		g_object_unref (source);
+		return NULL;
+	}
+
+	backup_path = g_strdup_printf ("%s.bak", target);
+	backup = g_file_new_for_path (backup_path);
+
+	if (!g_file_copy (source, backup, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) {
+		nm_log_warn (LOGD_SETTINGS, "Backup failed: %s", error->message);
+		g_free (backup_path);
+		backup_path = NULL;
+		g_error_free (error);
+	}
+
+	g_object_unref (source);
+	g_object_unref (backup);
+
+	return backup_path;
+}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
new file mode 100644
index 00000000..cc273aa7
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef _IFNET_UTILS_H
+#define _IFNET_UTILS_H
+
+#define IFNET_PLUGIN_NAME "SettingsPlugin-Ifnet"
+
+#include <arpa/inet.h>
+
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ip4-config.h"
+
+#include "nms-ifnet-net-parser.h"
+
+#define has_default_ip4_route(conn_name) has_default_route((conn_name), &is_ip4_address)
+#define has_default_ip6_route(conn_name) has_default_route((conn_name), &is_ip6_address)
+
+typedef struct _ip_block {
+	char *ip;
+	guint32 prefix;
+	char *next_hop;
+	struct _ip_block *next;
+} ip_block;
+
+gboolean is_static_ip4 (const char *conn_name);
+gboolean is_static_ip6 (const char *conn_name);
+gboolean is_ip4_address (const char *in_address);
+gboolean is_ip6_address (const char *in_address);
+gboolean has_ip6_address (const char *conn_name);
+gboolean has_default_route (const char *conn_name, gboolean (*check_fn) (const char *));
+gboolean reload_parsers (void);
+
+ip_block *convert_ip4_config_block (const char *conn_name);
+ip_block *convert_ip6_config_block (const char *conn_name);
+ip_block *convert_ip4_routes_block (const char *conn_name);
+ip_block *convert_ip6_routes_block (const char *conn_name);
+void destroy_ip_block (ip_block * iblock);
+
+void set_ip4_dns_servers (NMSettingIPConfig * s_ip4, const char *conn_name);
+void set_ip6_dns_servers (NMSettingIPConfig * s_ip6, const char *conn_name);
+
+gchar *strip_string (gchar *str, gchar t);
+gboolean is_managed (const char *conn_name);
+
+gboolean is_hex (const char *value);
+gboolean is_ascii (const char *value);
+gboolean is_true (const char *str);
+
+void get_dhcp_hostname_and_client_id (char **hostname, char **client_id);
+
+gchar *backup_file (const gchar* target);
+#endif
diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c
new file mode 100644
index 00000000..8332358f
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.c
@@ -0,0 +1,524 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service (ifnet)
+ *
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-plugin.h"
+
+#include <string.h>
+#include <gmodule.h>
+
+#include "nm-utils.h"
+#include "nm-setting-connection.h"
+#include "nm-dbus-interface.h"
+#include "settings/nm-settings-plugin.h"
+#include "nm-config.h"
+#include "NetworkManagerUtils.h"
+
+#include "nms-ifnet-connection.h"
+#include "nms-ifnet-net-utils.h"
+#include "nms-ifnet-net-parser.h"
+#include "nms-ifnet-wpa-parser.h"
+#include "nms-ifnet-connection-parser.h"
+
+#define IFNET_PLUGIN_NAME_PRINT "ifnet"
+#define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [qiaomuf] prefix."
+#define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE
+
+/*****************************************************************************/
+
+typedef void (*FileChangedFn) (gpointer user_data);
+
+typedef struct {
+	FileChangedFn callback;
+	gpointer user_data;
+} FileMonitorInfo;
+
+/*****************************************************************************/
+
+typedef struct {
+	GHashTable *connections;  /* uuid::connection */
+	gboolean unmanaged_well_known;
+
+	GFileMonitor *net_monitor;
+	GFileMonitor *wpa_monitor;
+} SettingsPluginIfnetPrivate;
+
+struct _SettingsPluginIfnet {
+	GObject parent;
+	SettingsPluginIfnetPrivate _priv;
+};
+
+struct _SettingsPluginIfnetClass {
+	GObjectClass parent;
+};
+
+static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface);
+
+G_DEFINE_TYPE_EXTENDED (SettingsPluginIfnet, settings_plugin_ifnet, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN,
+                                               settings_plugin_interface_init))
+
+#define SETTINGS_PLUGIN_IFNET_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfnet, SETTINGS_IS_PLUGIN_IFNET)
+
+/*****************************************************************************/
+
+static SettingsPluginIfnet *settings_plugin_ifnet_get (void);
+
+NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfnet, settings_plugin_ifnet_get, SETTINGS_TYPE_PLUGIN_IFNET);
+
+/*****************************************************************************/
+
+static void reload_connections (NMSettingsPlugin *config);
+
+/*****************************************************************************/
+
+static gboolean
+is_managed_plugin (void)
+{
+	return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                         NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED,
+	                                         IFNET_MANAGE_WELL_KNOWN_DEFAULT);
+}
+
+static void
+file_changed (GFileMonitor * monitor,
+              GFile * file,
+              GFile * other_file,
+              GFileMonitorEvent event_type, gpointer user_data)
+{
+	FileMonitorInfo *info;
+
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+		info = (FileMonitorInfo *) user_data;
+		info->callback (info->user_data);
+		break;
+	default:
+		break;
+	}
+}
+
+static GFileMonitor *
+monitor_file_changes (const char *filename,
+                      FileChangedFn callback, gpointer user_data)
+{
+	GFile *file;
+	GFileMonitor *monitor;
+	FileMonitorInfo *info;
+	GError **error = NULL;
+
+	if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+		return NULL;
+	file = g_file_new_for_path (filename);
+	monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, error);
+	g_object_unref (file);
+
+	if (monitor) {
+		info = g_new0 (FileMonitorInfo, 1);
+		info->callback = callback;
+		info->user_data = user_data;
+		g_object_weak_ref (G_OBJECT (monitor), (GWeakNotify) g_free,
+		                   info);
+		g_signal_connect (monitor, "changed", G_CALLBACK (file_changed),
+		                  info);
+	} else {
+		nm_log_warn (LOGD_SETTINGS, "Monitoring %s failed, error: %s", filename,
+		             error == NULL ? "nothing" : (*error)->message);
+	}
+
+	return monitor;
+}
+
+static void
+setup_monitors (NMIfnetConnection *connection, gpointer user_data)
+{
+	SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data);
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self);
+
+	if (!nm_config_get_monitor_connection_files (nm_config_get ()))
+		return;
+
+	if (priv->net_monitor || priv->wpa_monitor)
+		return;
+
+	priv->net_monitor = monitor_file_changes (CONF_NET_FILE,
+	                                          (FileChangedFn) reload_connections,
+	                                          user_data);
+	priv->wpa_monitor = monitor_file_changes (WPA_SUPPLICANT_CONF,
+	                                          (FileChangedFn) reload_connections,
+	                                          user_data);
+}
+
+static void
+cancel_monitors (NMIfnetConnection *connection, gpointer user_data)
+{
+	SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (user_data);
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self);
+
+	if (priv->net_monitor) {
+		g_file_monitor_cancel (priv->net_monitor);
+		g_clear_object (&priv->net_monitor);
+	}
+	if (priv->wpa_monitor) {
+		g_file_monitor_cancel (priv->wpa_monitor);
+		g_clear_object (&priv->wpa_monitor);
+	}
+}
+
+static void
+connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
+{
+	g_hash_table_remove (SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) user_data)->connections,
+	                     nm_connection_get_uuid (NM_CONNECTION (obj)));
+}
+
+static void
+track_new_connection (SettingsPluginIfnet *self, NMIfnetConnection *connection)
+{
+	g_hash_table_insert (SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self)->connections,
+	                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
+	                     g_object_ref (connection));
+	g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
+	                  G_CALLBACK (connection_removed_cb),
+	                  self);
+}
+
+static void
+reload_connections (NMSettingsPlugin *config)
+{
+	SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config);
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self);
+	GList *conn_names = NULL, *n_iter = NULL;
+	gboolean auto_refresh;
+	GError *error = NULL;
+
+	/* save names for removing unused connections */
+	GHashTable *new_connections = NULL;
+	GHashTableIter iter;
+	const char *uuid;
+	NMSettingsConnection *candidate;
+
+	if (priv->unmanaged_well_known)
+		return;
+
+	if (!reload_parsers ())
+		return;
+
+	nm_log_info (LOGD_SETTINGS, "Loading connections");
+
+	auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                                 NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH,
+	                                                 FALSE);
+
+	new_connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
+
+	/* Reread on-disk data and refresh in-memory connections from it */
+	conn_names = ifnet_get_connection_names ();
+	for (n_iter = conn_names; n_iter; n_iter = g_list_next (n_iter)) {
+		NMIfnetConnection *new;
+		NMIfnetConnection *old;
+		const char *conn_name = n_iter->data;
+
+		/* read the new connection */
+		new = nm_ifnet_connection_new (NULL, conn_name);
+		if (!new)
+			continue;
+
+		g_signal_connect (G_OBJECT (new), "ifnet_setup_monitors",
+		                  G_CALLBACK (setup_monitors), config);
+		g_signal_connect (G_OBJECT (new), "ifnet_cancel_monitors",
+		                  G_CALLBACK (cancel_monitors), config);
+
+		old = g_hash_table_lookup (priv->connections,
+		                           nm_connection_get_uuid (NM_CONNECTION (new)));
+		if (old && new) {
+			if (auto_refresh) {
+				/* If connection has changed, remove the old one and add the
+				 * new one to force a disconnect/reconnect with new settings
+				 */
+				if (!nm_connection_compare (NM_CONNECTION (old),
+				                            NM_CONNECTION (new),
+				                            NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+				                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
+					nm_log_info (LOGD_SETTINGS, "Auto refreshing %s", conn_name);
+
+					nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (old));
+					track_new_connection (self, new);
+					if (is_managed_plugin () && is_managed (conn_name))
+						g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new);
+				}
+			} else {
+				/* Update existing connection with new settings */
+				if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (old),
+				                                    NM_CONNECTION (new),
+				                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
+				                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+				                                    "ifnet-update",
+				                                    &error)) {
+					/* Shouldn't ever get here as 'new' was verified by the reader already
+					 * and the UUID did not change. */
+					g_assert_not_reached ();
+				}
+				g_assert_no_error (error);
+				nm_log_info (LOGD_SETTINGS, "Connection %s updated",
+				             nm_connection_get_id (NM_CONNECTION (new)));
+			}
+			g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
+		} else if (new) {
+			track_new_connection (self, new);
+			if (is_managed_plugin () && is_managed (conn_name))
+				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, new);
+		}
+
+		/* Track all valid connections so we can remove deleted ones later */
+		g_hash_table_insert (new_connections,
+		                     (gpointer) nm_connection_get_uuid (NM_CONNECTION (new)),
+		                     new);
+	}
+
+	/* remove deleted/unused connections */
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, (gpointer) &uuid, (gpointer) &candidate)) {
+		/* only saved connections (which have a conn_name) get removed; unsaved
+		 * ones obviously don't exist in /etc/conf.d/net yet and shouldn't get
+		 * blown away by net file changes.
+		 */
+		if (   nm_ifnet_connection_get_conn_name (NM_IFNET_CONNECTION (candidate))
+		    && !g_hash_table_lookup (new_connections, uuid)) {
+			nm_settings_connection_signal_remove (candidate);
+			g_hash_table_iter_remove (&iter);
+		}
+	}
+	g_hash_table_destroy (new_connections);
+	g_list_free (conn_names);
+}
+
+static NMSettingsConnection *
+add_connection (NMSettingsPlugin *config,
+                NMConnection *source,
+                gboolean save_to_disk,
+                GError **error)
+{
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config);
+	NMIfnetConnection *new = NULL;
+
+	/* Ensure we reject attempts to add the connection long before we're
+	 * asked to write it to disk.
+	 */
+	if (!ifnet_can_write_connection (source, error))
+		goto out;
+
+	if (save_to_disk) {
+		if (!ifnet_add_new_connection (source, CONF_NET_FILE, WPA_SUPPLICANT_CONF, NULL, NULL, error))
+			goto out;
+		reload_connections (config);
+		new = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (source));
+	} else {
+		new = nm_ifnet_connection_new (source, NULL);
+		if (new) {
+			track_new_connection (SETTINGS_PLUGIN_IFNET (config), new);
+			/* track_new_connection refs 'new' */
+			g_object_unref (new);
+		}
+	}
+
+out:
+	if (!new && error && !*error) {
+		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+		                     "The ifnet plugin cannot add the connection (unknown error).");
+	}
+	return (NMSettingsConnection *) new;
+}
+
+static void
+check_unmanaged (gpointer key, gpointer data, gpointer user_data)
+{
+	NMIfnetConnection *connection = NM_IFNET_CONNECTION (data);
+	GSList **list = (GSList **) user_data;
+	const char *mac, *conn_name;
+	char *unmanaged_spec;
+	GSList *iter;
+
+	conn_name = nm_ifnet_connection_get_conn_name (connection);
+
+	if (!conn_name || is_managed (conn_name))
+		return;
+
+	nm_log_info (LOGD_SETTINGS, "Checking unmanaged: %s", conn_name);
+	mac = ifnet_get_data (conn_name, "mac");
+	if (mac)
+		unmanaged_spec = g_strdup_printf ("mac:%s", mac);
+	else
+		unmanaged_spec = g_strdup_printf ("interface-name:%s", conn_name);
+
+	/* Just return if the unmanaged spec is already in the list */
+	for (iter = *list; iter; iter = g_slist_next (iter)) {
+		if (g_str_equal (iter->data, unmanaged_spec)) {
+			g_free (unmanaged_spec);
+			return;
+		}
+	}
+
+	nm_log_info (LOGD_SETTINGS, "Add unmanaged: %s", unmanaged_spec);
+	*list = g_slist_prepend (*list, unmanaged_spec);
+}
+
+static GSList *
+get_unmanaged_specs (NMSettingsPlugin * config)
+{
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config);
+	GSList *list = NULL;
+
+	nm_log_info (LOGD_SETTINGS, "getting unmanaged specs...");
+	g_hash_table_foreach (priv->connections, check_unmanaged, &list);
+	return list;
+}
+
+static GSList *
+get_connections (NMSettingsPlugin *config)
+{
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) config);
+	GSList *connections = NULL;
+	GHashTableIter iter;
+	NMIfnetConnection *connection;
+
+	nm_log_info (LOGD_SETTINGS, "(%p) ... get_connections.", config);
+
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
+		const char *conn_name = nm_ifnet_connection_get_conn_name (connection);
+
+		if (!conn_name || (!priv->unmanaged_well_known && is_managed (conn_name)))
+			connections = g_slist_prepend (connections, connection);
+	}
+	nm_log_info (LOGD_SETTINGS, "(%p) connections count: %d",
+	             config, g_slist_length (connections));
+	return connections;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject * object, guint prop_id, GValue * value,
+              GParamSpec * pspec)
+{
+	switch (prop_id) {
+	case NM_SETTINGS_PLUGIN_PROP_NAME:
+		g_value_set_string (value, IFNET_PLUGIN_NAME_PRINT);
+		break;
+	case NM_SETTINGS_PLUGIN_PROP_INFO:
+		g_value_set_string (value, IFNET_PLUGIN_INFO);
+		break;
+	case NM_SETTINGS_PLUGIN_PROP_CAPABILITIES:
+		g_value_set_uint (value,
+		                  NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+init (NMSettingsPlugin *config)
+{
+	SettingsPluginIfnet *self = SETTINGS_PLUGIN_IFNET (config);
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE (self);
+
+	nm_log_info (LOGD_SETTINGS, "Initializing!");
+
+	priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+	priv->unmanaged_well_known = !is_managed_plugin ();
+	nm_log_info (LOGD_SETTINGS, "management mode: %s",
+	             priv->unmanaged_well_known ? "unmanaged" : "managed");
+
+	setup_monitors (NULL, config);
+	reload_connections (config);
+
+	nm_log_info (LOGD_SETTINGS, "Initialzation complete!");
+}
+
+/*****************************************************************************/
+
+static void
+settings_plugin_ifnet_init (SettingsPluginIfnet * plugin)
+{
+}
+
+static void
+dispose (GObject * object)
+{
+	SettingsPluginIfnet *plugin = SETTINGS_PLUGIN_IFNET (object);
+	SettingsPluginIfnetPrivate *priv = SETTINGS_PLUGIN_IFNET_GET_PRIVATE ((SettingsPluginIfnet *) plugin);
+
+	cancel_monitors (NULL, object);
+	if (priv->connections) {
+		g_hash_table_destroy (priv->connections);
+		priv->connections = NULL;
+	}
+
+	ifnet_destroy ();
+	wpa_parser_destroy ();
+	G_OBJECT_CLASS (settings_plugin_ifnet_parent_class)->dispose (object);
+}
+
+static void
+settings_plugin_ifnet_class_init (SettingsPluginIfnetClass * req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+
+	g_object_class_override_property (object_class,
+	                                  NM_SETTINGS_PLUGIN_PROP_NAME,
+	                                  NM_SETTINGS_PLUGIN_NAME);
+
+	g_object_class_override_property (object_class,
+	                                  NM_SETTINGS_PLUGIN_PROP_INFO,
+	                                  NM_SETTINGS_PLUGIN_INFO);
+
+	g_object_class_override_property (object_class,
+	                                  NM_SETTINGS_PLUGIN_PROP_CAPABILITIES,
+	                                  NM_SETTINGS_PLUGIN_CAPABILITIES);
+}
+
+static void
+settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
+{
+	plugin_iface->init = init;
+	plugin_iface->get_connections = get_connections;
+	plugin_iface->get_unmanaged_specs = get_unmanaged_specs;
+	plugin_iface->add_connection = add_connection;
+	plugin_iface->reload_connections = reload_connections;
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT GObject *
+nm_settings_plugin_factory (void)
+{
+	return G_OBJECT (g_object_ref (settings_plugin_ifnet_get ()));
+}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.h b/src/settings/plugins/ifnet/nms-ifnet-plugin.h
new file mode 100644
index 00000000..f006e7e4
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service (ifnet)
+ *
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+#define SETTINGS_TYPE_PLUGIN_IFNET            (settings_plugin_ifnet_get_type ())
+#define SETTINGS_PLUGIN_IFNET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnet))
+#define SETTINGS_PLUGIN_IFNET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass))
+#define SETTINGS_IS_PLUGIN_IFNET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFNET))
+#define SETTINGS_IS_PLUGIN_IFNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFNET))
+#define SETTINGS_PLUGIN_IFNET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFNET, SettingsPluginIfnetClass))
+
+typedef struct _SettingsPluginIfnet SettingsPluginIfnet;
+typedef struct _SettingsPluginIfnetClass SettingsPluginIfnetClass;
+
+GType settings_plugin_ifnet_get_type (void);
+
+#endif
diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c
new file mode 100644
index 00000000..2b62e886
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.c
@@ -0,0 +1,584 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nms-ifnet-wpa-parser.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "nm-utils/nm-hash-utils.h"
+#include "settings/nm-settings-plugin.h"
+
+#include "nms-ifnet-net-parser.h"
+#include "nms-ifnet-net-utils.h"
+
+/* Security information */
+static GHashTable *wsec_table = NULL;
+
+/* Global information used for writing */
+static GHashTable *wsec_global_table = NULL;
+
+static gboolean wpa_parser_data_changed = FALSE;
+
+static long
+wpa_get_long (GHashTable *table, const char *key)
+{
+	return atol (g_hash_table_lookup (table, key));
+}
+
+static void
+destroy_security (GHashTable * network)
+{
+	gpointer key, value;
+	GHashTableIter iter;
+
+	g_return_if_fail (network);
+	g_hash_table_iter_init (&iter, network);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		g_free (key);
+		g_free (value);
+	}
+
+	g_hash_table_destroy (network);
+}
+
+static GHashTable *
+add_security (GHashTable *security)
+{
+	GHashTable *oldsecurity;
+	const char *ssid, *value;
+	char *ssid_key;
+	gboolean is_hex_ssid;
+
+	/* Every security information should have a ssid */
+	ssid = g_hash_table_lookup (security, "ssid");
+	if (!ssid) {
+		destroy_security (security);
+		return NULL;
+	}
+
+	/* Hex format begins with " */
+	is_hex_ssid = (ssid[0] != '"');
+	if ((value = g_hash_table_lookup (security, "disabled")) != NULL) {
+		if (strcmp (value, "1") == 0) {
+			destroy_security (security);
+			return NULL;
+		}
+	}
+
+	/* Default priority is 1 */
+	if (g_hash_table_lookup (security, "priority") == NULL)
+		g_hash_table_insert (security, g_strdup ("priority"),
+				     g_strdup ("1"));
+
+	oldsecurity = g_hash_table_lookup (wsec_table, ssid);
+	/* Security with lower priority will be ignored */
+	if (oldsecurity != NULL) {
+		if (wpa_get_long (oldsecurity, "priority") >=
+		    wpa_get_long (security, "priority")) {
+			destroy_security (security);
+			return NULL;
+		} else {
+			g_hash_table_remove (wsec_table, ssid);
+			destroy_security (oldsecurity);
+		}
+	}
+
+	/* format ssid */
+	ssid_key =
+	    is_hex_ssid ? g_strdup_printf ("0x%s",
+					   ssid) :
+	    strip_string (g_strdup (ssid), '"');
+	g_hash_table_insert (wsec_table, ssid_key, security);
+	return security;
+}
+
+static void
+add_key_value (GHashTable * network, gchar * line)
+{
+	gpointer orig_key, orig_value;
+	gchar **key_value;
+
+	if (g_str_has_prefix (line, "network={"))
+		line += 9;
+	strip_string (line, '{');
+	strip_string (line, '}');
+	if (line[0] == '\0')
+		return;
+	key_value = g_strsplit (line, "=", 2);
+	if (g_strv_length (key_value) != 2) {
+		g_strfreev (key_value);
+		return;
+	}
+	g_strstrip (key_value[0]);
+	g_strstrip (key_value[1]);
+
+	/* Reserve quotes for psk, wep_key, ssid
+	 * Quotes will determine whether they are hex format */
+	if (strcmp (key_value[0], "psk") != 0
+	    && !g_str_has_prefix (key_value[0], "wep_key")
+	    && strcmp (key_value[0], "ssid") != 0)
+		strip_string (key_value[1], '"');
+
+	/* This sucks */
+	if (g_hash_table_lookup_extended (network, key_value[0], &orig_key, &orig_value)) {
+		g_hash_table_remove (network, orig_key);
+		g_free (orig_key);
+		g_free (orig_value);
+	}
+
+	g_hash_table_insert (network, g_strdup (key_value[0]),
+			     g_strdup (key_value[1]));
+	g_strfreev (key_value);
+}
+
+static void
+add_one_wep_key (GHashTable * table, int key_num, gchar * one_wep_key)
+{
+	if (one_wep_key[0] == 's') {
+		//asc key
+		g_hash_table_insert (table,
+				     g_strdup_printf ("wep_key%d", key_num - 1),
+				     g_strdup_printf ("\"%s\"",
+						      one_wep_key + 2));
+	} else {
+		gchar buf[30];
+		int i = 0, j = 0;
+
+		//hex key
+		while (one_wep_key[i] != '\0') {
+			if (one_wep_key[i] != '-')
+				buf[j++] = one_wep_key[i];
+			i++;
+		}
+		buf[j] = '\0';
+		g_hash_table_insert (table,
+				     g_strdup_printf ("wep_key%d", key_num - 1),
+				     g_strdup (buf));
+
+	}
+}
+
+/* Reading wep security information from /etc/conf.d/net.
+ * This should not be used in future, use wpa_supplicant instead. */
+static void
+add_keys_from_net (void)
+{
+	GList *names = ifnet_get_connection_names ();
+	GList *iter = names;
+	gchar *wep_keys = "(\\[([1-4])\\]\\s+(s:\\w{5}|s:\\w{13}|"
+	    "([\\da-fA-F]{4}\\-){2}[\\da-fA-F]{2}|"
+	    "([\\da-fA-F]{4}\\-){6}[\\da-fA-F]{2})\\s+)";
+	gchar *key_method =
+	    "\\s+key\\s+\\[([1-4])\\]\\s+enc\\s+(open|restricted)";
+	GRegex *regex_keys = g_regex_new (wep_keys, 0, 0, NULL);
+	GRegex *regex_method = g_regex_new (key_method, 0, 0, NULL);
+	GMatchInfo *keys_info;
+	GMatchInfo *method_info;
+
+	while (iter) {
+		gchar *conn_name = iter->data;
+		GHashTable *table;
+		const char *key_str;
+
+		if ((key_str = ifnet_get_data (conn_name, "key")) == NULL) {
+			iter = g_list_next (iter);
+			continue;
+		}
+
+		wpa_add_security (conn_name);
+		table = _get_hash_table (conn_name);
+		/* Give lowest priority */
+		wpa_set_data (conn_name, "priority", "0");
+		g_regex_match (regex_keys, key_str, 0, &keys_info);
+		/* add wep keys */
+		while (g_match_info_matches (keys_info)) {
+			gchar *key_num = g_match_info_fetch (keys_info, 2);
+			gchar *one_wep_key = g_match_info_fetch (keys_info, 3);
+
+			add_one_wep_key (table, atoi (key_num), one_wep_key);
+			g_free (key_num);
+			g_free (one_wep_key);
+			g_match_info_next (keys_info, NULL);
+		}
+		g_match_info_free (keys_info);
+
+		g_regex_match (regex_method, key_str, 0, &method_info);
+		/* set default key index and auth alg */
+		if (g_match_info_matches (method_info)) {
+			gchar *default_idx =
+			    g_match_info_fetch (method_info, 1);
+			gchar *method = g_match_info_fetch (method_info, 2);
+
+			default_idx[0]--;
+			g_hash_table_insert (table, g_strdup ("wep_tx_keyidx"),
+					     default_idx);
+			g_hash_table_insert (table, g_strdup ("auth_alg"),
+					     g_ascii_strup (method, -1));
+		}
+		g_match_info_free (method_info);
+		add_security (table);
+		iter = g_list_next (iter);
+	}
+	g_list_free (names);
+	g_regex_unref (regex_keys);
+	g_regex_unref (regex_method);
+}
+
+static void
+add_global_data (gchar * line)
+{
+	gchar **key_value;
+
+	g_strstrip (line);
+	key_value = g_strsplit (line, "=", 2);
+	if (g_strv_length (key_value) != 2) {
+		nm_log_warn (LOGD_SETTINGS, "Can't handle this line: %s\n", line);
+		g_strfreev (key_value);
+		return;
+	}
+	g_hash_table_insert (wsec_global_table,
+			     g_strdup (g_strstrip (key_value[0])),
+			     g_strdup (g_strstrip (key_value[1])));
+	g_strfreev (key_value);
+}
+
+void
+wpa_parser_init (const char *wpa_supplicant_conf)
+{
+	GIOChannel *channel = NULL;
+	gchar *line;
+	gboolean complete = FALSE;
+
+	wpa_parser_data_changed = FALSE;
+	wsec_table = g_hash_table_new (nm_str_hash, g_str_equal);
+	wsec_global_table = g_hash_table_new (nm_str_hash, g_str_equal);
+
+	if (g_file_test (wpa_supplicant_conf, G_FILE_TEST_IS_REGULAR))
+		channel =
+		    g_io_channel_new_file (wpa_supplicant_conf, "r", NULL);
+	if (channel == NULL) {
+		nm_log_warn (LOGD_SETTINGS, "Can't open %s for wireless security",
+		             wpa_supplicant_conf);
+		return;
+	}
+
+	while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL)
+	       != G_IO_STATUS_EOF) {
+		g_strstrip (line);
+		if (line[0] != '#' && line[0] != '\0') {
+			if (strstr (line, "network={") == NULL) {
+				add_global_data (line);
+				g_free (line);
+				continue;
+			} else {
+				GHashTable *network =
+				    g_hash_table_new (nm_str_hash, g_str_equal);
+
+				do {
+					gchar *quote_start, *quote_end = NULL, *comment;
+
+					if (line[0] == '#' || line[0] == '\0') {
+						g_free (line);
+						continue;
+					}
+					/* ignore inline comments unless inside
+					   a double-quoted string */
+					if ((quote_start = strchr (line, '"')) != NULL)
+						quote_end = strrchr (quote_start + 1, '"');
+					if ((comment = strchr ((quote_end != NULL) ?
+					                       quote_end : line, '#')) != NULL)
+						*comment = '\0';
+					if (strstr (line, "}") != NULL)
+						complete = TRUE;
+					add_key_value (network, line);
+					g_free (line);
+				} while (complete == FALSE
+					 &&
+					 g_io_channel_read_line
+					 (channel, &line, NULL,
+					  NULL, NULL) != G_IO_STATUS_EOF);
+				add_security (network);
+				//EOF in inner loop
+				if (complete == FALSE) {
+					g_free (line);
+					break;
+				}
+				complete = FALSE;
+			}
+		} else
+			g_free (line);
+	}
+
+	g_io_channel_shutdown (channel, FALSE, NULL);
+	g_io_channel_unref (channel);
+
+	add_keys_from_net ();
+}
+
+const char *
+wpa_get_value (const char *ssid, const char *key)
+{
+	GHashTable *target = g_hash_table_lookup (wsec_table, ssid);
+
+	if (target)
+		return g_hash_table_lookup (target, key);
+	return NULL;
+}
+
+gboolean
+exist_ssid (const char *ssid)
+{
+	return g_hash_table_lookup (wsec_table, ssid) != NULL;
+}
+
+GHashTable *
+_get_hash_table (const char *ssid)
+{
+	return g_hash_table_lookup (wsec_table, ssid);
+}
+
+static gchar *quoted_keys[] =
+    { "identity", "cert", "private", "phase", "password", NULL };
+
+/* tell whether the key needs quotes when writing is performed */
+static gboolean
+need_quote (gchar * key)
+{
+	int i = 0;
+
+	while (quoted_keys[i] != NULL) {
+		if (strstr (key, quoted_keys[i]))
+			return TRUE;
+		i++;
+	}
+	return FALSE;
+}
+
+gboolean
+wpa_flush_to_file (const char *config_file)
+{
+	GIOChannel *channel;
+	GError *error = NULL;
+	gpointer key, value, ssid, security;
+	GHashTableIter iter, iter_security;
+	gchar *out_line;
+	gsize bytes_written;
+	gboolean result = FALSE;
+
+	if (!wpa_parser_data_changed)
+		return TRUE;
+	if (!wsec_table || !wsec_global_table)
+		return FALSE;
+
+	backup_file (config_file);
+
+	channel = g_io_channel_new_file (config_file, "w", NULL);
+	if (!channel) {
+		nm_log_warn (LOGD_SETTINGS, "Can't open file %s for writing", config_file);
+		return FALSE;
+	}
+	g_hash_table_iter_init (&iter, wsec_global_table);
+	nm_log_info (LOGD_SETTINGS, "Writing to %s", config_file);
+	g_io_channel_write_chars (channel,
+				  "#Generated by NetworkManager\n"
+				  "###### Global Configuration ######\n",
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
+	/* Writing global information */
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		out_line =
+		    g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value);
+		g_io_channel_write_chars (channel, out_line, -1, &bytes_written,
+					  &error);
+		if (bytes_written == 0 || error)
+			break;
+		g_free (out_line);
+	}
+	if (error)
+		goto done;
+	g_io_channel_write_chars (channel,
+				  "\n###### Security Configuration ######\n",
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
+	g_hash_table_iter_init (&iter, wsec_table);
+	/* Writing security */
+	while (g_hash_table_iter_next (&iter, &ssid, &security)) {
+		g_hash_table_iter_init (&iter_security,
+					(GHashTable *) security);
+		g_io_channel_write_chars (channel, "network={\n", -1,
+					  &bytes_written, &error);
+		if (error)
+			goto done;
+		while (g_hash_table_iter_next (&iter_security, &key, &value)) {
+			out_line =
+			    g_strdup_printf (need_quote ((gchar *) key) ?
+					     "\t%s=\"%s\"\n" : "\t%s=%s\n",
+					     (gchar *) key, (gchar *) value);
+			g_io_channel_write_chars (channel, out_line, -1,
+						  &bytes_written, &error);
+			if (bytes_written == 0 || error)
+				goto done;
+			g_free (out_line);
+		}
+		g_io_channel_write_chars (channel, "}\n\n", -1, &bytes_written, &error);
+
+	}
+	g_io_channel_flush (channel, &error);
+	if (error)
+		goto done;
+
+	wpa_parser_data_changed = FALSE;
+	result = TRUE;
+done:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "Error writing WPA configuration: %s", error->message);
+		g_error_free (error);
+	}
+	g_io_channel_shutdown (channel, FALSE, NULL);
+	g_io_channel_unref (channel);
+	return result;
+}
+
+/* If value is NULL, this method will delete old key value pair */
+void
+wpa_set_data (const char *ssid, const char *key, const char *value)
+{
+	gpointer old_key = NULL, old_value = NULL;
+	GHashTable *security = g_hash_table_lookup (wsec_table, ssid);
+	gchar * stripped = NULL;
+
+	g_return_if_fail (security != NULL);
+
+	if (value){
+		stripped = g_strdup(value);
+		if (strcmp (key, "ssid") != 0 && strcmp (key, "psk") != 0
+			&& !g_str_has_prefix (key, "wep_key"))
+			strip_string (stripped, '"');
+	}
+
+	/* Remove old key value pairs */
+	if (g_hash_table_lookup_extended
+	    (security, key, &old_key, &old_value)) {
+		if (stripped && !strcmp(old_value, stripped)){
+			g_free (stripped);
+			return;
+		}
+		g_hash_table_remove (security, old_key);
+		g_free (old_key);
+		g_free (old_value);
+	} else if (!value)
+		return;
+
+	/* Add new key value */
+	if (stripped)
+		g_hash_table_insert (security, g_strdup (key), stripped);
+	wpa_parser_data_changed = TRUE;
+}
+
+gboolean
+wpa_has_security (const char *ssid)
+{
+	return g_hash_table_lookup (wsec_table, ssid) != NULL;
+}
+
+gboolean
+wpa_add_security (const char *ssid)
+{
+	if (wpa_has_security (ssid))
+		return TRUE;
+	else {
+		GHashTable *security =
+		    g_hash_table_new (nm_str_hash, g_str_equal);
+		gchar *ssid_i;
+
+		nm_log_info (LOGD_SETTINGS, "Adding security for %s", ssid);
+		if (g_str_has_prefix (ssid, "0x")) {
+			/* hex ssid */
+			ssid_i = g_strdup (ssid + 2);
+		} else {
+			/* ascii ssid requires quotes */
+			ssid_i = g_strdup_printf ("\"%s\"", ssid);
+		}
+		g_hash_table_insert (security, strdup ("ssid"), ssid_i);
+		g_hash_table_insert (security, strdup ("priority"),
+				     strdup ("1"));
+		g_hash_table_insert (wsec_table, g_strdup (ssid), security);
+		wpa_parser_data_changed = TRUE;
+		return TRUE;
+	}
+}
+
+gboolean
+wpa_delete_security (const char *ssid)
+{
+	gpointer old_key, old_value;
+
+	g_return_val_if_fail (wsec_table != NULL && ssid != NULL, FALSE);
+	nm_log_info (LOGD_SETTINGS, "Deleting security for %s", ssid);
+	if (!g_hash_table_lookup_extended
+	    (wsec_table, ssid, &old_key, &old_value))
+		return FALSE;
+	g_hash_table_remove (wsec_table, old_key);
+	g_free (old_key);
+	destroy_security ((GHashTable *) old_value);
+	wpa_parser_data_changed = TRUE;
+	return TRUE;
+
+}
+
+void
+wpa_parser_destroy (void)
+{
+	GHashTableIter iter;
+	gpointer key;
+	gpointer value;
+
+	/* Destroy security */
+	if (wsec_table) {
+		g_hash_table_iter_init (&iter, wsec_table);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			destroy_security ((GHashTable *) value);
+			g_free (key);
+		}
+
+		g_hash_table_destroy (wsec_table);
+		wsec_table = NULL;
+	}
+
+	/* Destroy global data */
+	if (wsec_global_table) {
+		g_hash_table_iter_init (&iter, wsec_global_table);
+		while (g_hash_table_iter_next (&iter, &key, &value)) {
+			g_free (key);
+			g_free (value);
+		}
+
+		g_hash_table_destroy (wsec_global_table);
+		wsec_global_table = NULL;
+	}
+}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h
new file mode 100644
index 00000000..d096f468
--- /dev/null
+++ b/src/settings/plugins/ifnet/nms-ifnet-wpa-parser.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#ifndef _WPA_PARSER_H
+#define _WPA_PARSER_H
+
+#define WPA_SUPPLICANT_CONF SYSCONFDIR "/wpa_supplicant/wpa_supplicant.conf"
+
+void wpa_parser_init (const char *wpa_supplicant_conf);
+void wpa_parser_destroy (void);
+
+/* reader functions */
+const char *wpa_get_value (const char *ssid, const char *key);
+gboolean exist_ssid (const char *ssid);
+GHashTable *_get_hash_table (const char *ssid);
+gboolean wpa_has_security (const char *ssid);
+
+/* writer functions */
+gboolean wpa_flush_to_file (const char *config_file);
+void wpa_set_data (const char *ssid, const char *key, const char *value);
+gboolean wpa_add_security (const char *ssid);
+gboolean wpa_delete_security (const char *ssid);
+#endif
diff --git a/src/settings/plugins/ifnet/tests/net b/src/settings/plugins/ifnet/tests/net
new file mode 100644
index 00000000..a5ac9ca2
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/net
@@ -0,0 +1,158 @@
+# This blank configuration will automatically use DHCP for any net.*
+# scripts in /etc/init.d.  To create a more complete configuration,
+# please review /etc/conf.d/net.example and save your configuration
+# in /etc/conf.d/net (this file :]!).
+
+modules="!wpa_supplicant"
+
+config_eth0=(
+"202.117.16.121    netmask    255.255.255.0 brd 202.117.16.255"
+"192.168.4.121/24"
+"dhcp6"
+)
+routes_eth0=( "default via     202.117.16.1" 
+		"192.168.4.0/24 via 192.168.4.1")
+dns_servers_eth0="202.117.0.20    202.117.0.21"
+dns_search_eth0="p12.edu.cn p13.edu.cn"
+
+config_eth1=(
+	"dhcp"
+)
+enable_ipv6_eth1="true"
+routes_eth1=( "default via 202.117.16.1" )
+dns_servers_eth1="202.117.0.20 202.117.0.21"
+config_eth2=(
+"202.117.16.1211    netmask    255.255.255.0 brd 202.117.16.255"
+"192.168.4.121/24"
+"4321:0:1:2:3:4:567:89ab/64"
+)
+routes_eth2=("default via  4321:0:1:2:3:4:567:89ab")
+enable_ipv6_eth2="true"
+config_eth3=("nufjlsjlll")
+managed_eth4="false"
+routes_eth4=("default via 4321:0:1:2:3:4:567:89ab")
+config_eth5=("dhcp")
+
+config_eth7=( "dhcp" )
+auto_eth7="true"
+
+# missing config_eth8
+auto_eth8="true"
+
+#new openrc style
+config_eth9="202.117.16.10/24 202.117.17.10/24"
+routes_eth9="default via 202.117.16.1
+10.0.0.0/8 via 192.168.0.1
+"
+config_eth10="202.117.16.2 netmask 255.255.255.0"
+routes_eth10="10.0.0.0/8 via 192.168.0.1"
+
+config_myxjtu2=("202.117.16.121/24 brd 202.117.16.255")
+routes_myxjtu2=("default via 202.117.16.1")
+dns_servers_myxjtu2="202.117.0.20 202.117.0.21"
+#key_myxjtu2="[1] s:xjtud key [1] enc restricted"
+#key_eth6="[1] aaaa-4444-3d [2] s:xjtudlc key [1] enc open"
+
+
+username_ppp0='user'
+password_ppp0='password'
+
+config_qiaomuf=("dhcp")
+
+config_1xtest=("dhcp")
+
+config_0xab3ace=("dhcp")
+
+modules=( "iproute2" )
+	config_kvm0=( "null" )
+	config_kvm1=( "null" )
+
+	tuntap_kvm0="tap"
+	tuntap_kvm1="tap"
+	tunctl_kvm0="-u user"
+	tunctl_kvm1="-u user"
+
+bridge_br0="eth0 kvm0 kvm1"
+config_br0=( "192.168.1.10/24" )
+	brctl_br0=( "setfd 0")
+	dhcp_eth1="nosendhost nontp -I"
+
+predown() {
+	# The default in the script is to test for NFS root and disallow
+	# downing interfaces in that case.  Note that if you specify a
+	# predown() function you will override that logic.  Here it is, in
+	# case you still want it...
+	if is_net_fs /; then
+		eerror "root filesystem is network mounted -- can't stop ${IFACE}"
+		return 1
+	fi
+
+	# Remember to return 0 on success
+	return 0
+}
+
+postup() {
+	# This function could be used, for example, to register with a
+	# dynamic DNS service.  Another possibility would be to
+	# send/receive mail once the interface is brought up.
+
+	# Here is an example that allows the use of iproute rules
+	# which have been configured using the rules_eth0 variable.
+	#rules_eth0=" \
+	#	'from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100' \
+	#	'from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100' \
+	#"
+	eval set -- \$rules_${IFVAR}
+	if [ $# != 0 ]; then
+		einfo "Adding IP policy routing rules"
+		eindent
+		# Ensure that the kernel supports policy routing
+		if ! ip rule list | grep -q "^"; then
+			eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)"
+			eerror "in your kernel to use ip rules"
+		else
+			for x; do
+				ebegin "${x}"
+				ip rule add ${x}
+				eend $?
+			done
+		fi
+		eoutdent
+		# Flush the cache
+		ip route flush cache dev "${IFACE}"
+	fi
+
+}
+
+postdown() {
+	# Enable Wake-On-LAN for every interface except for lo
+	# Probably a good idea to set ifdown="no" in /etc/conf.d/net
+	# as well ;)
+	[ "${IFACE}" != "lo" ] && ethtool -s "${IFACE}" wol g
+
+	Automatically erase any ip rules created in the example postup above
+	if interface_exists "${IFACE}"; then
+		# Remove any rules for this interface
+		local rule
+		ip rule list | grep " iif ${IFACE}[ ]*" | {
+			while read rule; do
+				rule="${rule#*:}"	
+				ip rule del ${rule}
+			done
+		}
+		# Flush the route cache
+		ip route flush cache dev "${IFACE}"
+	fi
+
+	# Return 0 always
+	return 0
+}
+
+failup() {
+       # This function is mostly here for completeness... I haven't
+       # thought of anything nifty to do with it yet ;-)
+}
+
+faildown() 
+{}
+
diff --git a/src/settings/plugins/ifnet/tests/net.all b/src/settings/plugins/ifnet/tests/net.all
new file mode 100644
index 00000000..285a4cdf
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/net.all
@@ -0,0 +1,864 @@
+##############################################################################
+# QUICK-START
+#
+# The quickest start is if you want to use DHCP.
+# In that case, everything should work out of the box, no configuration
+# necessary, though the startup script will warn you that you haven't
+# specified anything.
+
+# WARNING :- some examples have a mixture of IPv4 (ie 192.168.0.1) and IPv6
+# (ie 4321:0:1:2:3:4:567:89ab) internet addresses. They only work if you have
+# the relevant kernel option enabled. So if you don't have an IPv6 enabled
+# kernel then remove the IPv6 address from your config.
+
+# If you want to use a static address or use DHCP explicitly, jump
+# down to the section labelled INTERFACE HANDLERS.
+#
+# If you want to do anything more fancy, you should take the time to
+# read through the rest of this file.
+
+##############################################################################
+# MODULES
+#
+# We now support modular networking scripts which means we can easily
+# add support for new interface types and modules while keeping
+# compatability with existing ones.
+# 
+# Modules load by default if the package they need is installed.  If
+# you specify a module here that doesn't have it's package installed
+# then you get an error stating which package you need to install.
+# Ideally, you only use the modules setting when you have two or more
+# packages installed that supply the same service.
+#
+# In other words, you probably should DO NOTHING HERE...
+
+# Prefer ifconfig over iproute2
+modules=( "ifconfig" )
+
+# You can also specify other modules for an interface
+# In this case we prefer udhcpc over dhcpcd
+modules_eth0=( "udhcpc" )
+
+# You can also specify which modules not to use - for example you may be
+# using a supplicant or linux-wlan-ng to control wireless configuration but
+# you still want to configure network settings per ESSID associated with.
+modules=( "!iwconfig" "!wpa_supplicant" )
+# IMPORTANT: If you need the above, please disable modules in that order
+
+
+##############################################################################
+# INTERFACE HANDLERS
+# 
+# We provide two interface handlers presently: ifconfig and iproute2.
+# You need one of these to do any kind of network configuration.
+# For ifconfig support, emerge sys-apps/net-tools
+# For iproute2 support, emerge sys-apps/iproute2
+
+# If you don't specify an interface then we prefer iproute2 if it's installed
+# To prefer ifconfig over iproute2
+modules=( "ifconfig" )
+
+# For a static configuration, use something like this
+# (They all do exactly the same thing btw)
+config_eth0=( "192.168.0.2/24" )
+config_eth0=( "192.168.0.2 netmask 255.255.255.0" )
+
+# We can also specify a broadcast
+config_eth0=( "192.168.0.2/24 brd 192.168.0.255" )
+config_eth0=( "192.168.0.2 netmask 255.255.255.0 broadcast 192.168.0.255" )
+
+# If you need more than one address, you can use something like this
+# NOTE: ifconfig creates an aliased device for each extra IPv4 address
+#       (eth0:1, eth0:2, etc)
+#       iproute2 does not do this as there is no need to
+config_eth0=(
+	"192.168.0.2/24"
+	"192.168.0.3/24"
+	"192.168.0.4/24"
+)
+# Or you can use sequence expressions
+config_eth0=( "192.168.0.{2..4}/24" )
+# which does the same as above. Be careful though as if you use this and
+# fallbacks, you have to ensure that both end up with the same number of
+# values otherwise your fallback won't work correctly.
+
+# You can also use IPv6 addresses
+# (you should always specify a prefix length with IPv6 here)
+config_eth0=(
+	"192.168.0.2/24"
+	"4321:0:1:2:3:4:567:89ab/64"
+	"4321:0:1:2:3:4:567:89ac/64"
+)
+
+# If you wish to keep existing addresses + routing and the interface is up,
+# you can specify a noop (no operation). If the interface is down or there
+# are no addresses assigned, then we move onto the next step (default dhcp)
+# This is useful when configuring your interface with a kernel command line
+# or similar
+config_eth0=( "noop" "192.168.0.2/24" )
+
+# If you don't want ANY address (only useful when calling for advanced stuff)
+config_eth0=( "null" )
+
+# Here's how to do routing if you need it
+routes_eth0=(
+	"default via 192.168.0.1"		# IPv4 default route
+	"10.0.0.0/8 via 192.168.0.1"		# IPv4 subnet route
+	"::/0"					# IPv6 unicast
+)
+
+# If a specified module fails (like dhcp - see below), you can specify a
+# fallback like so
+fallback_eth0=( "192.168.0.2 netmask 255.255.255.0" )
+fallback_route_eth0=( "default via 192.168.0.1" )
+
+# NOTE: fallback entry must match the entry location in config_eth0
+# As such you can only have one fallback route.
+
+# Some users may need to alter the MTU - here's how
+mtu_eth0="1500"
+
+# Each module described below can set a default base metric, lower is
+# preferred over higher. This is so we can prefer a wired route over a
+# wireless route automaticaly. You can override this by setting
+metric_eth0="100"
+# or on a global basis
+metric="100"
+# The only downside of the global setting is that you have to ensure that
+# there are no conflicting routes yourself. For users with large routing
+# tables you may have to set a global metric as the due to a simple read of
+# the routing table taking over a minute at a time.
+
+##############################################################################
+# OPTIONAL MODULES
+
+# INTERFACE RENAMING
+# There is no consistent device renaming scheme for Linux.
+# The preferred way of naming devices is via the kernel module directly or
+# by using udev (http://www.reactivated.net/udevrules.php)
+
+# If you are unable to write udev rules, then we do provide a way of renaming
+# the interface based on it's MAC address, but it is not optimal.
+# Here is how to rename an interface whose MAC address is 00:11:22:33:44:55
+# to foo1
+rename_001122334455="foo1"
+
+# You can also do this based on current device name - although this is not
+# recommended. Here we rename eth1 to foo2.
+rename_eth1="foo2"
+
+#-----------------------------------------------------------------------------
+# WIRELESS (802.11 support)
+# Wireless can be provided by iwconfig or wpa_supplicant
+
+# iwconfig
+# emerge net-wireless/wireless-tools
+# Wireless options are held in /etc/conf.d/wireless - but could be here too
+# Consult the sample file /etc/conf.d/wireless.example for instructions
+# iwconfig is the default
+
+# wpa_supplicant
+# emerge net-wireless/wpa-supplicant
+# Wireless options are held in /etc/wpa_supplicant.conf
+# Consult the sample file /etc/wpa_supplicant.conf.example for instructions
+# To choose wpa_supplicant over iwconfig
+modules=( "wpa_supplicant" )
+# To configure wpa_supplicant
+wpa_supplicant_eth0="-Dwext" # For generic wireless
+wpa_supplicant_ath0="-Dmadwifi" # For Atheros based cards
+# Consult wpa_supplicant for more drivers
+# By default don't wait for wpa_suppliant to associate and authenticate.
+# If you would like to, so can specify how long in seconds
+associate_timeout_eth0=60
+# A value of 0 means wait forever.
+
+# GENERIC WIRELESS OPTIONS
+# PLEASE READ THE INSTRUCTIONS IN /etc/conf.d/wireless.example FOR
+# HOW TO USE THIS ESSID VARIABLE
+# You can also override any settings found here per ESSID - which is very
+# handy if you use different networks a lot
+config_ESSID=( "dhcp" )
+dhcpcd_ESSID="-t 5"
+
+# Setting name/domain server causes /etc/resolv.conf to be overwritten
+# Note that if DHCP is used, and you want this to take precedence then
+ set dhcp_ESSID="nodns"
+dns_servers_ESSID=( "192.168.0.1" "192.168.0.2" )
+dns_domain_ESSID="some.domain"
+dns_search_ESSID="search.this.domain search.that.domain"
+# Please check the man page for resolv.conf for more information
+# as domain and search are mutually exclusive.
+
+# You can also override any settings found here per MAC address of the AP
+# in case you use Access Points with the same ESSID but need different
+# networking configs. Below is an example - of course you use the same
+# method with other variables
+mac_config_001122334455=( "dhcp" )
+mac_dhcpcd_001122334455="-t 10"
+mac_dns_servers_001122334455=( "192.168.0.1" "192.168.0.2" )
+
+# When an interface has been associated with an Access Point, a global
+# variable called ESSID is set to the Access Point's ESSID for use in the
+# pre/post user functions below (although it's not available in preup as you
+# won't have associated then)
+
+# If you're using anything else to configure wireless on your interface AND
+# you have installed any of the above packages, you need to disable them
+modules=( "!iwconfig" "!wpa_supplicant" )
+
+#-----------------------------------------------------------------------------
+# DHCP
+# DHCP can be provided by dhclient, dhcpcd, pump or udhcpc.
+#
+# dhclient: emerge net-misc/dhcp
+# dhcpcd:   emerge net-misc/dhcpcd
+# pump:     emerge net-misc/pump
+# udhcpc:   emerge net-misc/udhcp
+
+# If you have more than one DHCP client installed, you need to specify which
+# one to use - otherwise we default to dhcpcd if available.
+modules=( "dhclient" ) # to select dhclient over dhcpcd
+#
+# Notes:
+# - All clients send the current hostname to the DHCP server by default
+# - dhcpcd does not daemonize when the lease time is infinite
+# - udhcp-0.9.3-r3 and earlier do not support getting NTP servers 
+# - pump does not support getting NIS servers
+# - DHCP tends to erase any existing device information - so add
+#   static addresses after dhcp if you need them
+# - dhclient and udhcpc can set other resolv.conf options such as "option"
+#   and "sortlist"- see the System module for more details
+
+# Regardless of which DHCP client you prefer, you configure them the
+# same way using one of following depending on which interface modules
+# you're using.
+config_eth0=( "dhcp" )
+
+# For passing custom options to dhcpcd use something like the following.  This
+# example reduces the timeout for retrieving an address from 60 seconds (the
+# default) to 10 seconds.
+dhcpcd_eth0="-t 10"
+
+# dhclient, udhcpc and pump don't have many runtime options
+# You can pass options to them in a similar manner to dhcpcd though
+dhclient_eth0="..."
+udhcpc_eth0="..."
+pump_eth0="..."
+
+# GENERIC DHCP OPTIONS
+# Set generic DHCP options like so
+dhcp_eth0="release nodns nontp nonis nogateway nosendhost"
+
+# This tells the dhcp client to release it's lease when it stops, not to
+# overwrite dns, ntp and nis settings, not to set a default route and not to
+# send the current hostname to the dhcp server and when it starts.
+# You can use any combination of the above options - the default is not to
+# use any of them.
+
+#-----------------------------------------------------------------------------
+# For APIPA support, emerge net-misc/iputils or net-analyzer/arping
+
+# APIPA is a module that tries to find a free address in the range
+# 169.254.0.0-169.254.255.255 by arping a random address in that range on the
+# interface. If no reply is found then we assign that address to the interface
+
+# This is only useful for LANs where there is no DHCP server and you don't
+# connect directly to the internet.
+config_eth0=( "dhcp" )
+fallback_eth0=( "apipa" )
+
+#-----------------------------------------------------------------------------
+# ARPING Gateway configuration 
+# and
+# Automatic Private IP Addressing (APIPA)
+# For arpingnet / apipa support, emerge net-misc/iputils or net-analyzer/arping
+#
+# This is a module that tries to find a gateway IP. If it exists then we use
+# that gateways configuration for our own. For the configuration variables
+# simply ensure that each octet is zero padded and the dots are removed.
+# Below is an example.
+#
+gateways_eth0="192.168.0.1 10.0.0.1"
+config_192168000001=( "192.168.0.2/24" )
+routes_192168000001=( "default via 192.168.0.1" )
+dns_servers_192168000001=( "192.168.0.1" )
+config_010000000001=( "10.0.0.254/8" )
+routes_010000000001=( "default via 10.0.0.1" )
+dns_servers_010000000001=( "10.0.0.1" )
+
+# We can also specify a specific MAC address for each gateway if different
+# networks have the same gateway.
+gateways_eth0="192.168.0.1,00:11:22:AA:BB:CC 10.0.0.1,33:44:55:DD:EE:FF"
+config_192168000001_001122AABBCC=( "192.168.0.2/24" )
+routes_192168000001_001122AABBCC=( "default via 192.168.0.1" )
+dns_servers_192168000001_001122AABBCC=( "192.168.0.1" )
+config_010000000001_334455DDEEFF=( "10.0.0.254/8" )
+routes_010000000001_334455DDEEFF=( "default via 10.0.0.1" )
+dns_servers_010000000001_334455DDEEFF=( "10.0.0.1" )
+
+# If we don't find any gateways (or there are none configured) then we try and
+# use APIPA to find a free address in the range 169.254.0.0-169.254.255.255
+# by arping a random address in that range on the interface. If no reply is
+# found then we assign that address to the interface.
+
+# This is only useful for LANs where there is no DHCP server.
+config_eth0=( "arping" )
+
+# or if no DHCP server can be found
+config_eth0=( "dhcp" )
+fallback_eth0=( "arping" )
+
+# NOTE: We default to sleeping for 1 second the first time we attempt an
+# arping to give the interface time to settle on the LAN. This appears to
+# be a good default for most instances, but if not you can alter it here.
+arping_sleep=5
+arping_sleep_lan=7
+
+# NOTE: We default to waiting 3 seconds to get an arping response. You can
+# change the default wait like so.
+arping_wait=3
+arping_wait_lan=2
+
+#-----------------------------------------------------------------------------
+# VLAN (802.1q support)
+# For VLAN support, emerge net-misc/vconfig
+
+# Specify the VLAN numbers for the interface like so
+# Please ensure your VLAN IDs are NOT zero-padded
+vlans_eth0="1 2"
+
+# You may not want to assign an IP to the physical interface, but we still
+# need it up.
+config_eth0=( "null" )
+
+# You can also configure the VLAN - see for vconfig man page for more details
+vconfig_eth0=( "set_name_type VLAN_PLUS_VID_NO_PAD" )
+vconfig_vlan1=( "set_flag 1" "set_egress_map 2 6" )
+config_vlan1=( "172.16.3.1 netmask 255.255.254.0" )
+config_vlan2=( "172.16.2.1 netmask 255.255.254.0" )
+
+# NOTE: Vlans can be configured with a . in their interface names
+# When configuring vlans with this name type, you need to replace . with a _
+config_eth0.1=( "dhcp" ) - does not work
+config_eth0_1=( "dhcp" ) - does work
+
+# NOTE: Vlans are controlled by their physical interface and not per vlan
+# This means you do not need to create init scripts in /etc/init.d for each
+# vlan, you must need to create one for the physical interface.
+# If you wish to control the configuration of each vlan through a separate
+# script, or wish to rename the vlan interface to something that vconfig
+# cannot then you need to do this.
+vlan_start_eth0="no"
+
+# If you do the above then you may want to depend on eth0 like so
+ RC_NEED_vlan1="net.eth0"
+# NOTE: depend functions only work in /etc/conf.d/net
+# and not in profile configs such as /etc/conf.d/net.foo
+
+#-----------------------------------------------------------------------------
+# Bonding
+# For link bonding/trunking emerge net-misc/ifenslave
+
+# To bond interfaces together
+slaves_bond0="eth0 eth1 eth2"
+config_bond0=( "null" ) # You may not want to assign an IP to the bond
+
+# If any of the slaves require extra configuration - for example wireless or
+# ppp devices - we need to depend function on the bonded interfaces
+RC_NEED_bond0="net.eth0 net.eth1"
+
+
+#-----------------------------------------------------------------------------
+# Classical IP over ATM 
+# For CLIP support emerge net-dialup/linux-atm
+
+# Ensure that you have /etc/atmsigd.conf setup correctly
+# Now setup each clip interface like so
+clip_atm0=( "peer_ip [if.]vpi.vci [opts]" ... )
+#  where "peer_ip" is the IP address of a PVC peer (in case of an ATM connection
+#  with your ISP, your only peer is usually the ISP gateway closest to you),
+#  "if" is the number of the ATM interface which will carry the PVC, "vpi.vci"
+#  is the ATM VC address, and "opts" may optionally specify VC parameters like
+#  qos, pcr, and the like (see "atmarp -s" for further reference). Please also
+#  note quoting: it is meant to distinguish the VCs you want to create. You may,
+#  in example, create an atm0 interface to more peers, like this:
+clip_atm0=( "1.1.1.254 0.8.35" "1.1.1.253 1.8.35" )
+
+#  By default, the PVC will use the LLC/SNAP encapsulation. If you rather need a
+#  null encapsulation (aka "VC mode"), please add the keyword "null" to opts.
+
+
+#-----------------------------------------------------------------------------
+# PPP
+# For PPP support, emerge net-dialup/ppp
+# PPP is used for most dialup connections, including ADSL.
+# The older ADSL module is documented below, but you are encouraged to try
+# this module first.
+#
+# You need to create the PPP net script yourself. Make it like so
+#ln -s net.lo /etc/init.d/net.ppp0
+#
+# We have to instruct ppp0 to actually use ppp
+config_ppp0=( "ppp" )
+#
+# Each PPP interface requires an interface to use as a "Link"
+link_ppp0="/dev/ttyS0" 		# Most PPP links will use a serial port
+link_ppp0="eth0"       		# PPPoE requires an ethernet interface
+link_ppp0="[itf.]vpi.vci"		# PPPoA requires the ATM VC's address
+link_ppp0="/dev/null"			# ISDN links should have this
+link_ppp0="pty 'your_link_command'"	# PPP links over ssh, rsh, etc
+#
+# Here you should specify what pppd plugins you want to use
+# Available plugins are: pppoe, pppoa, capi, dhcpc, minconn, radius,
+#			 radattr, radrealms and winbind 
+plugins_ppp0=(
+	"pppoe"				# Required plugin for PPPoE
+	"pppoa vc-encaps"		# Required plugin for PPPoA with an option
+	"capi"				# Required plugin for ISDN
+)
+#
+# PPP requires at least a username. You can optionally set a password here too
+# If you don't, then it will use the password specified in /etc/ppp/*-secrets
+# against the specified username
+username_ppp0='user'
+password_ppp0='password'
+# NOTE: You can set a blank password like so
+password_ppp0=
+#
+# The PPP daemon has many options you can specify - although there are many
+# and may seem daunting, it is recommended that you read the pppd man page
+# before enabling any of them
+pppd_ppp0=(
+	"maxfail 0"	# WARNING: It's not recommended you use this
+			# if you don't specify maxfail then we assume 0
+	"updetach" 	# If not set, "/etc/init.d/net.ppp0 start" will return
+			# immediately,  without waiting the link to come up
+			# for the first time.
+			# Do not use it for dial-on-demand links!
+	"debug" 	# Enables syslog debugging
+	"noauth"	# Do not require the peer to authenticate itself
+	"defaultroute"	# Make this PPP interface the default route
+	"usepeerdns"	# Use the DNS settings provided by PPP
+
+# On demand options
+	"demand"		# Enable dial on demand
+	"idle 30"		# Link goes down after 30 seconds of inactivity
+	"10.112.112.112:10.112.112.113"	# Phony IP addresses
+	"ipcp-accept-remote"	# Accept the peers idea of remote address
+	"ipcp-accept-local"	# Accept the peers idea of local address
+	"holdoff 3" 	# Wait 3 seconds after link dies before re-starting
+
+# Dead peer detection
+	"lcp-echo-interval 15"	# Send a LCP echo every 15 seconds
+	"lcp-echo-failure 3"	# Make peer dead after 3 consective
+				# echo-requests
+	
+# Compression options - use these to completely disable compression
+#	noaccomp noccp nobsdcomp nodeflate nopcomp novj novjccomp
+
+# Dial-up settings
+	"lock"				# Lock serial port
+	"115200"			# Set the serial port baud rate
+	"modem crtscts"			# Enable hardware flow control
+	"192.168.0.1:192.168.0.2"	# Local and remote IP addresses
+)
+#
+# Dial-up PPP users need to specify at least one telephone number
+phone_number_ppp0=( "12345689" ) # Maximum 2 phone numbers are supported
+# They will also need a chat script - here's a good one
+chat_ppp0=(
+#	'ABORT' 'BUSY'
+#	'ABORT' 'ERROR'
+#	'ABORT' 'NO ANSWER'
+#	'ABORT' 'NO CARRIER'
+#	'ABORT' 'NO DIALTONE'
+#	'ABORT' 'Invalid Login'
+#	'ABORT' 'Login incorrect'
+#	'TIMEOUT' '5'
+#	'' 'ATZ'
+#	'OK' 'AT' # Put your modem initialization string here
+#	'OK' 'ATDT\T'
+#	'TIMEOUT' '60'
+#	'CONNECT' ''
+#	'TIMEOUT' '5'
+#	'~--' ''
+)
+
+# If the link require extra configuration - for example wireless or
+# RFC 268 bridge - we need to depend on the bridge so they get
+# configured correctly.
+RC_NEED_ppp0="net.nas0"
+
+#WARNING: if MTU of the PPP interface is less than 1500 and you use this
+#machine as a router, you should add the following rule to your firewall
+#
+#iptables -I FORWARD 1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
+
+#-----------------------------------------------------------------------------
+# ADSL
+# For ADSL support, emerge net-dialup/rp-pppoe
+# WARNING: This ADSL module is being deprecated in favour of the PPP module
+# above.
+# You should make the following settings and also put your
+# username/password information in /etc/ppp/pap-secrets
+
+# Configure the interface to use ADSL
+config_eth0=( "adsl" )
+
+# You probably won't need to edit /etc/ppp/pppoe.conf if you set this
+adsl_user_eth0="my-adsl-username"
+
+#-----------------------------------------------------------------------------
+# ISDN
+# For ISDN support, emerge net-dialup/isdn4k-utils
+# You should make the following settings and also put your
+# username/password information in /etc/ppp/pap-secrets
+
+# Configure the interface to use ISDN
+config_ippp0=( "dhcp" )
+# It's important to specify dhcp if you need it!
+config_ippp0=( "192.168.0.1/24" )
+# Otherwise, you can use a static IP
+
+# NOTE: The interface name must be either ippp or isdn followed by a number
+
+# You may need this option to set the default route
+ipppd_eth0="defaultroute"
+
+#-----------------------------------------------------------------------------
+# MAC changer
+# To set a specific MAC address
+mac_eth0="00:11:22:33:44:55"
+
+# For changing MAC addresses using the below, emerge net-analyzer/macchanger
+# - to randomize the last 3 bytes only
+mac_eth0="random-ending"
+# - to randomize between the same physical type of connection (e.g. fibre,
+# copper, wireless) , all vendors
+mac_eth0="random-samekind"
+# - to randomize between any physical type of connection (e.g. fibre, copper,
+# wireless) , all vendors
+mac_eth0="random-anykind"
+# - full randomization - WARNING: some MAC addresses generated by this may NOT
+# act as expected
+mac_eth0="random-full"
+# custom - passes all parameters directly to net-analyzer/macchanger
+mac_eth0="some custom set of parameters"
+
+# You can also set other options based on the MAC address of your network card
+# Handy if you use different docking stations with laptops
+config_001122334455=( "dhcp" )
+
+#-----------------------------------------------------------------------------
+# TUN/TAP
+# For TUN/TAP support emerge net-misc/openvpn or sys-apps/usermode-utilities
+#
+# You must specify if we're a tun or tap device. Then you can give it any
+# name you like - such as vpn
+tuntap_vpn="tun"
+config_vpn=( "192.168.0.1/24")
+
+# Or stick wit the generic names - like tap0
+tuntap_tap0="tap"
+config_tap0=( "192.168.0.1/24")
+
+# For passing custom options to tunctl use something like the following.  This
+# example sets the owner to adm
+tunctl_tun1="-u adm"
+# When using openvpn, there are no options
+
+#-----------------------------------------------------------------------------
+# Bridging (802.1d)
+# For bridging support emerge net-misc/bridge-utils
+
+# To add ports to bridge br0
+bridge_br0="eth0 eth1"
+# or dynamically add them when the interface comes up
+bridge_add_eth0="br0"
+bridge_add_eth1="br0"
+
+# You need to configure the ports to null values so dhcp does not get started 
+config_eth0=( "null" )
+config_eth1=( "null" )
+
+# Finally give the bridge an address - dhcp or a static IP
+config_br0=( "dhcp" ) # may not work when adding ports dynamically
+config_br0=( "192.168.0.1/24" )
+
+# If any of the ports require extra configuration - for example wireless or
+# ppp devices - we need to depend on them like so.
+RC_NEED_br0="net.eth0 net.eth1"
+
+# Below is an example of configuring the bridge
+# Consult "man brctl" for more details
+brctl_br0=( "setfd 0" "sethello 0" "stp off" )
+
+#-----------------------------------------------------------------------------
+# RFC 2684 Bridge Support 
+# For RFC 2684 bridge support emerge net-misc/br2684ctl
+
+# Interface names have to be of the form nas0, nas1, nas2, etc.
+# You have to specify a VPI and VCI for the interface like so
+br2684ctl_nas0="-a 0.38" # UK VPI and VCI
+
+# You may want to configure the encapsulation method as well by adding the -e
+# option to the command above (may need to be before the -a command)
+# -e 0 # LLC (default)
+# -e 1 # VC mux
+
+# Then you can configure the interface as normal
+config_nas0=( "192.168.0.1/24" )
+
+#-----------------------------------------------------------------------------
+# Tunnelling
+# WARNING: For tunnelling it is highly recommended that you
+# emerge sys-apps/iproute2
+#
+# For GRE tunnels
+iptunnel_vpn0="mode gre remote 207.170.82.1 key 0xffffffff ttl 255"
+
+# For IPIP tunnels
+iptunnel_vpn0="mode ipip remote 207.170.82.2 ttl 255"
+
+# To configure the interface
+config_vpn0=( "192.168.0.2 pointopoint 192.168.1.2" )	# ifconfig style
+config_vpn0=( "192.168.0.2 peer 192.168.1.1" )		# iproute2 style
+
+# 6to4 Tunnels allow IPv6 to work over IPv4 addresses, provided you
+# have a non-private address configured on an interface.
+ link_6to4="eth0"		# Interface to base it's addresses on
+ config_6to4=( "ip6to4" )
+# You may want to depend on eth0 like so
+RC_NEED_6to4="net.eth0"
+# To ensure that eth0 is configured before 6to4. Of course, the tunnel could be
+# any name and this also works for any configured interface.
+# NOTE: If you're not using iproute2 then your 6to4 tunnel has to be called
+# sit0 - otherwise use a different name like 6to4 in the example above.
+
+
+#-----------------------------------------------------------------------------
+# System
+# For configuring system specifics such as domain, dns, ntp and nis servers
+# It's rare that you would need todo this, but you can anyway.
+# This is most benefit to wireless users who don't use DHCP so they can change
+# their configs based on ESSID. See wireless.example for more details
+
+# To use dns settings such as these, dns_servers_eth0 must be set!
+# If you omit the _eth0 suffix, then it applies to all interfaces unless
+# overridden by the interface suffix.
+dns_domain_eth0="your.domain"
+dns_servers_eth0="192.168.0.2 192.168.0.3"
+dns_search_eth0="this.domain that.domain"
+dns_options_eth0=( "timeout 1" "rotate" )
+dns_sortlist_eth0="130.155.160.0/255.255.240.0 130.155.0.0"
+# See the man page for resolv.conf for details about the options and sortlist
+# directives
+
+ntp_servers_eth0="192.168.0.2 192.168.0.3"
+
+nis_domain_eth0="domain"
+nis_servers_eth0="192.168.0.2 192.168.0.3"
+
+# NOTE: Setting any of these will stamp on the files in question. So if you
+# don't specify dns_servers but you do specify dns_domain then no nameservers
+# will be listed in /etc/resolv.conf even if there were any there to start
+# with.
+# If this is an issue for you then maybe you should look into a resolv.conf
+# manager like resolvconf-gentoo to manage this file for you. All packages
+# that baselayout supports use resolvconf-gentoo if installed.
+
+#-----------------------------------------------------------------------------
+# Cable in/out detection
+# Sometimes the cable is in, others it's out. Obviously you don't want to
+# restart net.eth0 every time when you plug it in either.
+#
+# netplug is a package that detects this and requires no extra configuration
+# on your part.
+# emerge sys-apps/netplug
+# or
+# emerge sys-apps/ifplugd
+# and you're done :)
+
+# By default we don't wait for netplug/ifplugd to configure the interface.
+# If you would like it to wait so that other services now that network is up
+# then you can specify a timeout here.
+plug_timeout="10"
+# A value of 0 means wait forever.
+
+# If you don't want to use netplug on a specific interface but you have it
+# installed, you can disable it for that interface via the modules statement
+modules_eth0=( "!netplug" )
+# You can do the same for ifplugd
+#
+# You can disable them both with the generic plug
+modules_eth0=( "!plug" )
+
+# To use specific ifplugd options, fex specifying wireless mode
+ifplugd_eth0="--api-mode=wlan"
+# man ifplugd for more options
+
+##############################################################################
+# ADVANCED CONFIGURATION
+# 
+# Four functions can be defined which will be called surrounding the
+# start/stop operations.  The functions are called with the interface
+# name first so that one function can control multiple adapters. An extra two
+# functions can be defined when an interface fails to start or stop.
+#
+# The return values for the preup and predown functions should be 0
+# (success) to indicate that configuration or deconfiguration of the
+# interface can continue.  If preup returns a non-zero value, then
+# interface configuration will be aborted.  If predown returns a
+# non-zero value, then the interface will not be allowed to continue
+# deconfiguration.
+#
+# The return values for the postup, postdown, failup and faildown functions are
+# ignored since there's nothing to do if they indicate failure.
+#
+# ${IFACE} is set to the interface being brought up/down
+# ${IFVAR} is ${IFACE} converted to variable name bash allows
+
+#preup() {
+#	# Test for link on the interface prior to bringing it up.  This
+#	# only works on some network adapters and requires the mii-diag
+#	# package to be installed.
+#	if mii-tool "${IFACE}" 2> /dev/null | grep -q 'no link'; then
+#		ewarn "No link on ${IFACE}, aborting configuration"
+#		return 1
+#	fi 
+#
+#	# Test for link on the interface prior to bringing it up.  This
+#	# only works on some network adapters and requires the ethtool
+#	# package to be installed.
+#	if ethtool "${IFACE}" | grep -q 'Link detected: no'; then
+#		ewarn "No link on ${IFACE}, aborting configuration"
+#		return 1
+#	fi
+#
+#
+#	# Remember to return 0 on success
+#	return 0
+#}
+
+#predown() {
+#	# The default in the script is to test for NFS root and disallow
+#	# downing interfaces in that case.  Note that if you specify a
+#	# predown() function you will override that logic.  Here it is, in
+#	# case you still want it...
+#	if is_net_fs /; then
+#		eerror "root filesystem is network mounted -- can't stop ${IFACE}"
+#		return 1
+#	fi
+#
+#	# Remember to return 0 on success
+#	return 0
+#}
+
+#postup() {
+#	# This function could be used, for example, to register with a
+#	# dynamic DNS service.  Another possibility would be to
+#	# send/receive mail once the interface is brought up.
+
+#	# Here is an example that allows the use of iproute rules
+#	# which have been configured using the rules_eth0 variable.
+#	#rules_eth0=(
+#	#	"from 24.80.102.112/32 to 192.168.1.0/24 table localnet priority 100"
+#	#	"from 216.113.223.51/32 to 192.168.1.0/24 table localnet priority 100"
+#	#)
+#	local x="rules_${IFVAR}[@]"
+#	local -a rules=( "${!x}" )
+#	if [[ -n ${rules} ]] ; then
+#		einfo "Adding IP policy routing rules"
+#		eindent
+#		# Ensure that the kernel supports policy routing
+#		if ! ip rule list | grep -q "^" ; then
+#			eerror "You need to enable IP Policy Routing (CONFIG_IP_MULTIPLE_TABLES)"
+#			eerror "in your kernel to use ip rules"
+#		else
+#			for x in "${rules[@]}" ; do
+#				ebegin "${x}"
+#				ip rule add ${x} dev "${IFACE}"
+#				eend $?
+#			done
+#		fi
+#		eoutdent
+#		# Flush the cache
+#		ip route flush cache dev "${IFACE}"
+#	fi
+
+#}
+
+#postdown() {
+#	# Enable Wake-On-LAN for every interface except for lo
+#	# Probably a good idea to set RC_DOWN_INTERFACE="no" in /etc/conf.d/rc
+#	# as well ;)
+#	[[ ${IFACE} != "lo" ]] && ethtool -s "${IFACE}" wol g
+
+#	Automatically erase any ip rules created in the example postup above
+#	if interface_exists "${IFACE}" ; then
+#		# Remove any rules for this interface
+#		local rule
+#		ip rule list | grep " iif ${IFACE}[ ]*" | {
+#			while read rule ; do
+#				rule="${rule#*:}"	
+#				ip rule del ${rule}
+#			done
+#		}
+#		# Flush the route cache
+#		ip route flush cache dev "${IFACE}"
+#	fi
+
+#	# Return 0 always
+#	return 0
+#}
+
+#failup() {
+#       # This function is mostly here for completeness... I haven't
+#       # thought of anything nifty to do with it yet ;-)
+#}
+
+#faildown() {
+#       # This function is mostly here for completeness... I haven't
+#       # thought of anything nifty to do with it yet ;-)
+#}
+
+##############################################################################
+# FORCING MODULES
+# The Big Fat Warning :- If you use module forcing do not complain to us or
+# file bugs about it not working!
+#
+# Loading modules is a slow affair - we have to check each one for the following
+# 1) Code sanity
+# 2) Has the required package been emerged?
+# 3) Has it modified anything?
+# 4) Have all the dependant modules been loaded?
+
+# Then we have to strip out the conflicting modules based on user preference
+# and default configuration and sort them into the correct order.
+# Finally we check the end result for dependencies.
+
+# This, of course, takes valuable CPU time so we provide module forcing as a
+# means to speed things up. We still do *some* checking but not much.
+
+# It is essential that you force modules in the correct order and supply all
+# the modules you need. You must always supply an interface module - we
+# supply ifconfig or iproute2.
+
+# The Big Fat Warning :- If you use module forcing do not complain to us or
+# file bugs about it not working!
+
+# Now that we've warned you twice, here's how to do it
+modules_force=( "ifconfig" )
+modules_force=( "iproute2" "dhcpcd" )
+
+# We can also apply this to a specific interface
+modules_force_eth1=( "iproute2" )
+
+# The below will not work
+modules_force=( "dhcpcd" )
+# No interface (ifconfig/iproute2)
+modules_force=( "ifconfig" "essidnet" "iwconfig" )
+# Although it will not crash, essidnet will not work as it has to come after
+# iwconfig
+modules_force=( "iproute2" "ifconfig" )
+# The interface will be setup twice which will cause problems
diff --git a/src/settings/plugins/ifnet/tests/nm-system-settings.conf b/src/settings/plugins/ifnet/tests/nm-system-settings.conf
new file mode 100644
index 00000000..39bc87b8
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/nm-system-settings.conf
@@ -0,0 +1,5 @@
+[main]
+plugins=ifnet,keyfile
+
+[ifnet]
+managed=false
diff --git a/src/settings/plugins/ifnet/tests/test-ifnet.c b/src/settings/plugins/ifnet/tests/test-ifnet.c
new file mode 100644
index 00000000..68f2b928
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/test-ifnet.c
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service (ifnet)
+ *
+ * Mu Qiao <qiaomuf@gmail.com>
+ *
+ * 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) 1999-2010 Gentoo Foundation, Inc.
+ */
+
+#include "nm-default.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "nm-utils.h"
+
+#include "platform/nm-linux-platform.h"
+#include "dhcp/nm-dhcp-manager.h"
+
+#include "settings/plugins/ifnet/nms-ifnet-net-parser.h"
+#include "settings/plugins/ifnet/nms-ifnet-net-utils.h"
+#include "settings/plugins/ifnet/nms-ifnet-wpa-parser.h"
+#include "settings/plugins/ifnet/nms-ifnet-connection-parser.h"
+
+#include "nm-test-utils-core.h"
+
+/* Fake config handling; the values it returns don't matter, so this
+ * is easier than forcing it to read our own config file, etc.
+ */
+NMDhcpManager *
+nm_dhcp_manager_get (void)
+{
+	return NULL;
+}
+
+const char *
+nm_dhcp_manager_get_config (NMDhcpManager *dhcp_manager)
+{
+	return "dhclient";
+}
+
+static void
+test_getdata (void)
+{
+	g_assert (ifnet_get_data ("eth1", "config") &&
+	          strcmp (ifnet_get_data ("eth1", "config"), "( \"dhcp\" )") == 0);
+	g_assert (ifnet_get_data ("ppp0", "username") &&
+	          strcmp (ifnet_get_data ("ppp0", "username"), "user") == 0);
+	g_assert (ifnet_get_data ("ppp0", "password") &&
+	          strcmp (ifnet_get_data ("ppp0", "password"), "password") == 0);
+	g_assert (ifnet_get_global_data ("modules") &&
+	          strcmp ("!wpa_supplicant", ifnet_get_global_data ("modules")) == 0);
+}
+
+static void
+test_is_static (void)
+{
+	g_assert (!is_static_ip4 ("eth1"));
+	g_assert (is_static_ip4 ("eth0"));
+	g_assert (!is_static_ip6 ("eth0"));
+}
+
+static void
+test_has_default_route (void)
+{
+	g_assert (has_default_ip4_route ("eth0"));
+	g_assert (has_default_ip6_route ("eth4"));
+	g_assert (!has_default_ip4_route ("eth5") &&
+	          !has_default_ip6_route ("eth5"));
+}
+
+static void
+test_has_ip6_address (void)
+{
+	g_assert (has_ip6_address ("eth2"));
+	g_assert (!has_ip6_address ("eth0"));
+}
+
+static void
+test_is_ip4_address (void)
+{
+	gchar *address1 = "192.168.4.232/24";
+	gchar *address2 = "192.168.100.{1..254}/24";
+	gchar *address3 = "192.168.4.2555/24";
+
+	g_assert (is_ip4_address (address1));
+	g_assert (is_ip4_address (address2));
+	g_assert (!is_ip4_address (address3));
+}
+
+static void
+test_is_ip6_address (void)
+{
+	gchar *address1 = "4321:0:1:2:3:4:567:89ac/24";
+
+	g_assert (is_ip6_address (address1));
+}
+
+static void
+check_ip_block (ip_block * iblock, gchar * ip, guint32 prefix, gchar * gateway)
+{
+	g_assert_cmpstr (ip, ==, iblock->ip);
+	g_assert (prefix == iblock->prefix);
+	g_assert_cmpstr (gateway, ==, iblock->next_hop);
+}
+
+static void
+test_convert_ipv4_config_block (void)
+{
+	ip_block *iblock = convert_ip4_config_block ("eth0");
+	ip_block *tmp = iblock;
+
+	g_assert (iblock != NULL);
+	check_ip_block (iblock, "202.117.16.121", 24, "202.117.16.1");
+	iblock = iblock->next;
+	destroy_ip_block (tmp);
+	g_assert (iblock != NULL);
+	check_ip_block (iblock, "192.168.4.121", 24, "202.117.16.1");
+	destroy_ip_block (iblock);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*Can't handle IPv4 address*202.117.16.1211*");
+	iblock = convert_ip4_config_block ("eth2");
+	g_test_assert_expected_messages ();
+	g_assert (iblock != NULL && iblock->next == NULL);
+	check_ip_block (iblock, "192.168.4.121", 24, NULL);
+	destroy_ip_block (iblock);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing netmask or prefix*");
+	iblock = convert_ip4_config_block ("eth3");
+	g_assert (iblock == NULL);
+}
+
+static void
+test_convert_ipv4_routes_block (void)
+{
+	ip_block *iblock = convert_ip4_routes_block ("eth0");
+	ip_block *tmp = iblock;
+
+	g_assert (iblock != NULL);
+	check_ip_block (iblock, "192.168.4.0", 24, "192.168.4.1");
+	iblock = iblock->next;
+	destroy_ip_block (tmp);
+	g_assert (iblock == NULL);
+
+	iblock = convert_ip4_routes_block ("eth9");
+	tmp = iblock;
+
+	g_assert (iblock != NULL);
+	check_ip_block (iblock, "10.0.0.0", 8, "192.168.0.1");
+	iblock = iblock->next;
+	destroy_ip_block (tmp);
+	g_assert (iblock == NULL);
+}
+
+static void
+test_wpa_parser (void)
+{
+	const char *value;
+
+	g_assert (exist_ssid ("example"));
+
+	g_assert (exist_ssid ("static-wep-test"));
+	value = wpa_get_value ("static-wep-test", "key_mgmt");
+	g_assert_cmpstr (value, ==, "NONE");
+	value = wpa_get_value ("static-wep-test", "wep_key0");
+	g_assert_cmpstr (value, ==, "\"abcde\"");
+
+	g_assert (exist_ssid ("leap-example"));
+
+	value = wpa_get_value ("test-with-hash-in-psk", "psk");
+	g_assert_cmpstr (value, ==, "\"xjtudlc3731###asdfasdfasdf\"");
+}
+
+static void
+test_strip_string (void)
+{
+	gchar *str = "( \"default via     202.117.16.1\" )";
+	gchar *result = g_strdup (str);
+	gchar *result_b = result;
+
+	result = strip_string (result, '(');
+	result = strip_string (result, ')');
+	result = strip_string (result, '"');
+	g_assert_cmpstr (result, ==, "default via     202.117.16.1");
+	g_free (result_b);
+}
+
+static void
+test_is_unmanaged (void)
+{
+	g_assert (is_managed ("eth0"));
+	g_assert (!is_managed ("eth4"));
+}
+
+static void
+test_new_connection (void)
+{
+	GError *error = NULL;
+	NMConnection *connection;
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*Can't handle IPv4 address*202.117.16.1211*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*Can't handle IPv6 address*202.117.16.1211*");
+	connection = ifnet_update_connection_from_config_block ("eth2", NULL, &error);
+	g_test_assert_expected_messages ();
+	g_assert (connection != NULL);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error);
+	g_assert (connection != NULL);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("myxjtu2", NULL, &error);
+	g_assert (connection != NULL);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("eth9", NULL, &error);
+	g_assert (connection != NULL);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("eth10", NULL, &error);
+	g_assert (connection != NULL);
+	g_object_unref (connection);
+}
+
+static void
+kill_backup (char **path)
+{
+	if (*path) {
+		unlink (*path);
+		g_free (*path);
+		*path = NULL;
+	}
+}
+
+#define NET_GEN_NAME "net.generate"
+#define SUP_GEN_NAME "wpa_supplicant.conf.generate"
+
+static void
+test_update_connection (void)
+{
+	GError *error = NULL;
+	NMConnection *connection;
+	gboolean success;
+	char *backup = NULL;
+	char *basepath = TEST_IFNET_DIR;
+
+	connection = ifnet_update_connection_from_config_block ("eth0", basepath, &error);
+	g_assert (connection != NULL);
+
+	success = ifnet_update_parsers_by_connection (connection, "eth0",
+	                                              NET_GEN_NAME,
+	                                              SUP_GEN_NAME,
+	                                              NULL,
+	                                              &backup,
+	                                              &error);
+	kill_backup (&backup);
+	g_assert (success);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("0xab3ace", basepath, &error);
+	g_assert (connection != NULL);
+
+	success = ifnet_update_parsers_by_connection (connection, "0xab3ace",
+	                                              NET_GEN_NAME,
+	                                              SUP_GEN_NAME,
+	                                              NULL,
+	                                              &backup,
+	                                              &error);
+	kill_backup (&backup);
+	g_assert (success);
+	g_object_unref (connection);
+
+	unlink (NET_GEN_NAME);
+	unlink (SUP_GEN_NAME);
+}
+
+static void
+test_add_connection (void)
+{
+	NMConnection *connection;
+	char *backup = NULL;
+	const char *basepath = TEST_IFNET_DIR;
+
+	connection = ifnet_update_connection_from_config_block ("eth0", basepath, NULL);
+	g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL));
+	kill_backup (&backup);
+	g_object_unref (connection);
+
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*Can't handle ipv4 address: brd, missing netmask or prefix*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*Can't handle ipv4 address: 202.117.16.255, missing netmask or prefix*");
+	connection = ifnet_update_connection_from_config_block ("myxjtu2", basepath, NULL);
+	g_test_assert_expected_messages ();
+	g_assert (ifnet_add_new_connection (connection, NET_GEN_NAME, SUP_GEN_NAME, NULL, &backup, NULL));
+	kill_backup (&backup);
+	g_object_unref (connection);
+
+	unlink (NET_GEN_NAME);
+	unlink (SUP_GEN_NAME);
+}
+
+static void
+test_delete_connection (void)
+{
+	GError *error = NULL;
+	NMConnection *connection;
+	char *backup = NULL;
+
+	connection = ifnet_update_connection_from_config_block ("eth7", NULL, &error);
+	g_assert (connection != NULL);
+	g_assert (ifnet_delete_connection_in_parsers ("eth7", NET_GEN_NAME, SUP_GEN_NAME, &backup));
+	kill_backup (&backup);
+	g_object_unref (connection);
+
+	connection = ifnet_update_connection_from_config_block ("qiaomuf", NULL, &error);
+	g_assert (connection != NULL);
+	g_assert (ifnet_delete_connection_in_parsers ("qiaomuf", NET_GEN_NAME, SUP_GEN_NAME, &backup));
+	kill_backup (&backup);
+	g_object_unref (connection);
+
+	unlink (NET_GEN_NAME);
+	unlink (SUP_GEN_NAME);
+}
+
+static void
+test_missing_config (void)
+{
+	gs_free_error GError *error = NULL;
+	NMConnection *connection;
+
+	connection = ifnet_update_connection_from_config_block ("eth8", NULL, &error);
+	g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION);
+	g_assert (connection == NULL && error != NULL);
+}
+
+NMTST_DEFINE ();
+
+#define TPATH "/settings/plugins/ifnet/"
+
+int
+main (int argc, char **argv)
+{
+	int ret;
+
+	nm_linux_platform_setup ();
+
+	nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT");
+
+	ifnet_init (TEST_IFNET_DIR "/net");
+	wpa_parser_init (TEST_IFNET_DIR "/wpa_supplicant.conf");
+
+	g_test_add_func (TPATH "strip-string", test_strip_string);
+	g_test_add_func (TPATH "is-static", test_is_static);
+	g_test_add_func (TPATH "has-ip6-address", test_has_ip6_address);
+	g_test_add_func (TPATH "has-default-route", test_has_default_route);
+	g_test_add_func (TPATH "get-data", test_getdata);
+	g_test_add_func (TPATH "is-ip4-address", test_is_ip4_address);
+	g_test_add_func (TPATH "is-ip6-address", test_is_ip6_address);
+	g_test_add_func (TPATH "convert-ip4-config", test_convert_ipv4_config_block);
+	g_test_add_func (TPATH "convert-ip4-routes", test_convert_ipv4_routes_block);
+	g_test_add_func (TPATH "is-unmanaged", test_is_unmanaged);
+	g_test_add_func (TPATH "wpa-parser", test_wpa_parser);
+	g_test_add_func (TPATH "new-connection", test_new_connection);
+	g_test_add_func (TPATH "update-connection", test_update_connection);
+	g_test_add_func (TPATH "add-connection", test_add_connection);
+	g_test_add_func (TPATH "delete-connection", test_delete_connection);
+	g_test_add_func (TPATH "missing-config", test_missing_config);
+
+	ret = g_test_run ();
+
+	ifnet_destroy ();
+	wpa_parser_destroy ();
+
+	return ret;
+}
diff --git a/src/settings/plugins/ifnet/tests/test_ca_cert.pem b/src/settings/plugins/ifnet/tests/test_ca_cert.pem
new file mode 100644
index 00000000..ef1be20d
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/test_ca_cert.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIJAOvnZPt59yIZMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD
+VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw
+FQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UE
+AxMEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAzMTAx
+NTEyMTRaFw0xOTAzMDgxNTEyMTRaMIGLMQswCQYDVQQGEwJVUzESMBAGA1UECBMJ
+QmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55
+IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3
+DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKot9j+/+CX1/gZLgJHIXCRgCItKLGnf7qGbgqB9T2ACBqR0jllKWwDKrcWU
+xjXNIc+GF9Wnv+lX6G0Okn4Zt3/uRNobL+2b/yOF7M3Td3/9W873zdkQQX930YZc
+Rr8uxdRPP5bxiCgtcw632y21sSEbG9mjccAUnV/0jdvfmMNj0i8gN6E0fMBiJ9S3
+FkxX/KFvt9JWE9CtoyL7ki7UIDq+6vj7Gd5N0B3dOa1y+rRHZzKlJPcSXQSEYUS4
+HmKDwiKSVahft8c4tDn7KPi0vex91hlgZVd3usL2E/Vq7o5D9FAZ5kZY0AdFXwdm
+J4lO4Mj7ac7GE4vNERNcXVIX59sCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUuDU3Mr7P
+T3n1e3Sy8hBauoDFahAwgcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDF
+ahChgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO
+BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQL
+EwdUZXN0aW5nMQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl
+c3QuY29tggkA6+dk+3n3IhkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AQEAVRG4aALIvCXCiKfe7K+iJxjBVRDFPEf7JWA9LGgbFOn6pNvbxonrR+0BETdc
+JV1ET4ct2xsE7QNFIkp9GKRC+6J32zCo8qtLCD5+v436r8TUG2/t2JRMkb9I2XVT
+p7RJoot6M0Ltf8KNQUPYh756xmKZ4USfQUwc58MOSDGY8VWEXJOYij9Pf0e0c52t
+qiCEjXH7uXiS8Pgq9TYm7AkWSOrglYhSa83x0f8mtT8Q15nBESIHZ6o8FAS2bBgn
+B0BkrKRjtBUkuJG3vTox+bYINh2Gxi1JZHWSV1tN5z3hd4VFcKqanW5OgQwToBqp
+3nniskIjbH0xjgZf/nVMyLnjxg==
+-----END CERTIFICATE-----
diff --git a/src/settings/plugins/ifnet/tests/wpa_supplicant.conf b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf
new file mode 100644
index 00000000..917d495d
--- /dev/null
+++ b/src/settings/plugins/ifnet/tests/wpa_supplicant.conf
@@ -0,0 +1,70 @@
+# Only WPA-PSK is used. Any valid cipher combination is accepted.
+network={
+	ssid="example"
+	proto=WPA
+	key_mgmt=WPA-PSK
+	pairwise=CCMP TKIP
+	group=CCMP TKIP WEP104 WEP40
+	psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+	priority=2
+}
+
+# LEAP with dynamic WEP keys
+network={
+	ssid="leap-example"
+	key_mgmt=IEEE8021X
+	eap=LEAP
+	identity="user"
+	password="foobar"
+}
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X)
+network={
+	ssid="static-wep-test"
+	key_mgmt=NONE
+	wep_key0="abcde"
+	wep_key1=0102030405
+	wep_key2="1234567890123"
+	wep_tx_keyidx=0
+	priority=5
+}
+
+# Wildcard match for SSID (plaintext APs only). This example select any
+# open AP regardless of its SSID.
+network={
+	key_mgmt=NONE
+}
+
+network={
+  ssid="myxjtu2"
+  scan_ssid=1
+  key_mgmt=WPA-PSK
+  psk="xjtudlc3731"
+  disabled=0
+  key_mgmt=NONE
+  wep_key0="12345"
+  wep_key1=1234567890
+  wep_key2="zxcvb"
+  wep_tx_keyidx=1
+  auth_alg=OPEN
+  mode=1
+}
+
+network={
+	ssid=ab3ace
+	key_mgmt=WPA-EAP
+	eap=TTLS
+	identity="user@example.com"
+	anonymous_identity="anonymous@example.com"
+	password="foobar"
+	ca_cert="test_ca_cert.pem"
+	phase2="auth=CHAP"
+	priority=20
+}
+
+network={
+  ssid="test-with-hash-in-psk"
+  key_mgmt=WPA-PSK
+  psk="xjtudlc3731###asdfasdfasdf"
+}
+
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
deleted file mode 100644
index 87b5bea2..00000000
--- a/src/settings/plugins/ifupdown/meson.build
+++ /dev/null
@@ -1,53 +0,0 @@
-sources = files(
-  'nms-ifupdown-interface-parser.c',
-  'nms-ifupdown-parser.c'
-)
-
-deps = [
-  libudev_dep,
-  nm_dep
-]
-
-cflags = '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-
-libnms_ifupdown_core = static_library(
-  'nms-ifupdown-core',
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags
-)
-
-sources = files(
-  'nms-ifupdown-connection.c',
-  'nms-ifupdown-plugin.c'
-)
-
-libnm_settings_plugin_ifupdown = shared_module(
-  'nm-settings-plugin-ifupdown',
-  sources: sources,
-  dependencies: deps,
-  c_args: cflags,
-  link_with: libnms_ifupdown_core,
-  link_args: ldflags_linker_script_settings,
-  link_depends: linker_script_settings,
-  install: true,
-  install_dir: nm_pkglibdir
-)
-
-core_plugins += libnm_settings_plugin_ifupdown
-
-# FIXME: check_so_symbols replacement
-'''
-run_target(
-  'check-local-symbols-settings-ifupdown',
-  command: [check_so_symbols, libnm_settings_plugin_ifupdown.full_path()],
-  depends: libnm_settings_plugin_ifupdown
-)
-
-check-local-symbols-settings-ifupdown: src/settings/plugins/ifupdown/libnm-settings-plugin-ifupdown.la
-  $(call check_so_symbols,$(builddir)/src/settings/plugins/ifupdown/.libs/libnm-settings-plugin-ifupdown.so)
-'''
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
index 014998a6..8421afc2 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
@@ -129,8 +129,8 @@ _recursive_ifparser (const char *eni_file, int quiet)
 
 	while (!feof(inp))
 	{
-		char *token[128]; /* 255 chars can only be split into 127 tokens */
-		char value[255];  /* large enough to join previously split tokens */
+		char *token[128];	/* 255 chars can only be split into 127 tokens */
+		char value[255];	/* large enough to join previously split tokens */
 		char *safeptr;
 		int toknum;
 		int len = 0;
@@ -169,7 +169,7 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			continue;
 		}
 
-#define SPACES " \t"
+#define SPACES	" \t"
 		/* tokenize input; */
 		for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr);
 		     token[toknum] != NULL;
diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build
deleted file mode 100644
index 7f210034..00000000
--- a/src/settings/plugins/ifupdown/tests/meson.build
+++ /dev/null
@@ -1,17 +0,0 @@
-test_unit = 'test-ifupdown'
-
-cflags = '-DTEST_ENI_DIR="@0@"'.format(meson.current_source_dir())
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: cflags,
-  link_with: libnms_ifupdown_core
-)
-
-test(
-  'ifupdown/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 56515438..d037b8a6 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -168,7 +168,7 @@ dump_blocks (void)
 	for (n = ifparser_getfirst (); n != NULL; n = n->next) {
 		if_data *m;
 
-		// each block start with its type & name
+		// each block start with its type & name 
 		// (single quotes used to show typ & name baoundaries)
 		g_print("'%s' '%s'\n", n->type, n->name);
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index df2c05b7..e6299d1e 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -436,7 +436,7 @@ read_connections (NMSettingsPlugin *config)
 		return;
 	}
 
-	alive_connections = g_hash_table_new (nm_direct_hash, NULL);
+	alive_connections = g_hash_table_new (NULL, NULL);
 
 	filenames = g_ptr_array_new_with_free_func (g_free);
 	while ((item = g_dir_read_name (dir))) {
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
index 1e62f4b3..5cb4d726 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
@@ -15,7 +15,7 @@ mtu=1400
 
 [ipv4]
 method=manual
-dns=4.2.2.1;bogus;4.2.2.2;
+dns=4.2.2.1;4.2.2.2;
 addresses1=192.168.0.5;24;192.168.0.1;
 addresses2=1.2.3.4;16;1.2.1.1;
 address=2.3.4.5/24,2.3.4.6
@@ -26,21 +26,15 @@ routes1=1.2.3.0/24,2.3.4.8,99
 route=5.6.7.8/32
 routes2=1.1.1.2/12,
 routes3=1.1.1.3/13,,
-routes7=1.1.1.7/17,0.0.0.0
 routes4=1.1.1.4/14,2.2.2.4
-address30=1.2.3.130/24
 routes5=1.1.1.5/15,2.2.2.5,
 routes6=1.1.1.6/16,2.2.2.6,0
+routes7=1.1.1.7/17,0.0.0.0
 routes8=1.1.1.8/18,0.0.0.0,
 routes9=1.1.1.9/19,0.0.0.0,0
-route10=1.1.1.10/21,,0
 routes10=1.1.1.10/20,,0
 routes11=1.1.1.11/21,,21
 routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7
-address30=1.2.3.30/24
-addresses30=1.2.3.30/25
-addresses31=1.2.3.31/25
-address31=1.2.3.31/24
 ignore-auto-routes=false
 ignore-auto-dns=false
 
diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build
deleted file mode 100644
index 54b4ee0d..00000000
--- a/src/settings/plugins/keyfile/tests/meson.build
+++ /dev/null
@@ -1,21 +0,0 @@
-test_unit = 'test-keyfile'
-
-test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles')
-
-cflags = [
-  '-DTEST_KEYFILES_DIR="@0@"'.format(test_keyfiles_dir),
-  '-DTEST_SCRATCH_DIR="@0@"'.format(test_keyfiles_dir)
-]
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: cflags
-)
-
-test(
-  'keyfile/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index b46475e8..f27efddd 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -231,21 +231,34 @@ test_read_valid_wired_connection (void)
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
 	gboolean success;
 
-	NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses1*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses2*");
-	NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*");
-	NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*");
-	NMTST_EXPECT_NM_WARN ("*ipv4.dns: ignoring invalid DNS server IPv4 address 'bogus'*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes2*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes3*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes5*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes8*");
-	NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*");
-	NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address5*");
-	NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*");
-	NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address7*");
-	NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*");
-	NMTST_EXPECT_NM_INFO ("*ipv6.route*semicolon at the end*route6*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.addresses:*semicolon at the end*addresses1*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.addresses:*semicolon at the end*addresses2*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address4*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.routes*semicolon at the end*routes2*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.routes*semicolon at the end*routes3*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.routes*semicolon at the end*routes5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.routes*semicolon at the end*routes8*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address4*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv6.address*semicolon at the end*address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv6.address*semicolon at the end*address7*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv6.routes*semicolon at the end*routes1*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv6.route*semicolon at the end*route6*");
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection", &error);
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
@@ -281,23 +294,19 @@ test_read_valid_wired_connection (void)
 	g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "4.2.2.2");
 
 	/* IPv4 addresses */
-	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 10);
+	g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 6);
 	check_ip_address (s_ip4, 0, "2.3.4.5", 24);
 	check_ip_address (s_ip4, 1, "192.168.0.5", 24);
 	check_ip_address (s_ip4, 2, "1.2.3.4", 16);
 	check_ip_address (s_ip4, 3, "3.4.5.6", 16);
 	check_ip_address (s_ip4, 4, "4.5.6.7", 24);
 	check_ip_address (s_ip4, 5, "5.6.7.8", 24);
-	check_ip_address (s_ip4, 6, "1.2.3.30", 24);
-	check_ip_address (s_ip4, 7, "1.2.3.30", 25);
-	check_ip_address (s_ip4, 8, "1.2.3.31", 24);
-	check_ip_address (s_ip4, 9, "1.2.3.31", 25);
 
 	/* IPv4 gateway */
 	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "2.3.4.6");
 
 	/* IPv4 routes */
-	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 13);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 12);
 	check_ip_route (s_ip4, 0, "5.6.7.8", 32, NULL, -1);
 	check_ip_route (s_ip4, 1, "1.2.3.0", 24, "2.3.4.8", 99);
 	check_ip_route (s_ip4, 2, "1.1.1.2", 12, NULL, -1);
@@ -308,12 +317,11 @@ test_read_valid_wired_connection (void)
 	check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1);
 	check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1);
 	check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0);
-	check_ip_route (s_ip4, 10, "1.1.1.10", 21, NULL, 0);
-	check_ip_route (s_ip4, 11, "1.1.1.10", 20, NULL, 0);
-	check_ip_route (s_ip4, 12, "1.1.1.11", 21, NULL, 21);
+	check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, 0);
+	check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21);
 
 	/* Route attributes */
-	route = nm_setting_ip_config_get_route (s_ip4, 12);
+	route = nm_setting_ip_config_get_route (s_ip4, 11);
 	g_assert (route);
 
 	nmtst_assert_route_attribute_uint32  (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10);
@@ -641,9 +649,12 @@ test_read_wired_mac_case (void)
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 };
 	gboolean success;
 
-	NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses1*");
-	NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses2*");
-	NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.addresses*semicolon at the end*addresses1*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv4.addresses*semicolon at the end*addresses2*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
+	                       "*ipv6.routes*semicolon at the end*routes1*");
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case", NULL);
 	g_test_assert_expected_messages ();
 	g_assert (connection);
@@ -1404,8 +1415,10 @@ test_read_wired_8021x_tls_blob_connection (void)
 	gboolean success;
 	GBytes *blob;
 
-	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob", &error);
 	g_assert_no_error (error);
 	g_assert (connection);
@@ -1434,7 +1447,8 @@ test_read_wired_8021x_tls_blob_connection (void)
 	g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB);
 
 	/* Make sure it's not a path, since it's a blob */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL,
+	                       NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH));
 	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
 	g_test_assert_expected_messages ();
 	g_assert (tmp == NULL);
@@ -1462,7 +1476,8 @@ test_read_wired_8021x_tls_bad_path_connection (void)
 	char *tmp2;
 	gboolean success;
 
-	NMTST_EXPECT_NM_WARN ("*does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*does not exist*");
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing", &error);
 	g_test_assert_expected_messages ();
 	g_assert_no_error (error);
@@ -1515,9 +1530,12 @@ test_read_wired_8021x_tls_old_connection (void)
 	const char *tmp;
 	gboolean success;
 
-	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
-	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old", &error);
 	g_assert_no_error (error);
 	g_assert (connection);
diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build
deleted file mode 100644
index a1aa7823..00000000
--- a/src/settings/plugins/meson.build
+++ /dev/null
@@ -1,15 +0,0 @@
-if enable_ibft
-  subdir('ibft')
-endif
-
-if enable_ifcfg_rh
-  subdir('ifcfg-rh')
-endif
-
-if enable_ifupdown
-  subdir('ifupdown')
-endif
-
-if enable_tests
-  subdir('keyfile/tests')
-endif
diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c
index 14f5cac8..16e7851a 100644
--- a/src/supplicant/nm-supplicant-config.c
+++ b/src/supplicant/nm-supplicant-config.c
@@ -47,8 +47,6 @@ typedef struct {
 	guint32    ap_scan;
 	gboolean   fast_required;
 	gboolean   dispose_has_run;
-	gboolean   support_pmf;
-	gboolean   support_fils;
 } NMSupplicantConfigPrivate;
 
 struct _NMSupplicantConfig {
@@ -67,18 +65,9 @@ G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 NMSupplicantConfig *
-nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils)
+nm_supplicant_config_new (void)
 {
-	NMSupplicantConfigPrivate *priv;
-	NMSupplicantConfig *self;
-
-	self = g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL);
-	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
-
-	priv->support_pmf = support_pmf;
-	priv->support_fils = support_fils;
-
-	return self;
+	return g_object_new (NM_TYPE_SUPPLICANT_CONFIG, NULL);
 }
 
 static void
@@ -380,6 +369,7 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self,
                                          NMSettingMacsec * setting,
                                          GError **error)
 {
+	NMSupplicantConfigPrivate *priv;
 	gs_unref_bytes GBytes *bytes = NULL;
 	const char *value;
 	char buf[32];
@@ -389,6 +379,8 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self,
 	g_return_val_if_fail (setting != NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
+	priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
+
 	if (!nm_supplicant_config_add_option (self, "macsec_policy", "1", -1, NULL, error))
 		return FALSE;
 
@@ -744,10 +736,8 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
                                                     const char *con_uuid,
                                                     guint32 mtu,
                                                     NMSettingWirelessSecurityPmf pmf,
-                                                    NMSettingWirelessSecurityFils fils,
                                                     GError **error)
 {
-	NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self);
 	const char *key_mgmt, *key_mgmt_conf, *auth_alg;
 	const char *psk;
 
@@ -756,37 +746,18 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 	g_return_val_if_fail (con_uuid != NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	/* Check if we actually support FILS */
-	if (!priv->support_fils) {
-		if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED) {
-			g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
-			                     "Supplicant does not support FILS");
-			return FALSE;
-		} else if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL)
-			fils = NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE;
-	}
-
 	key_mgmt = key_mgmt_conf = nm_setting_wireless_security_get_key_mgmt (setting);
-	if (nm_streq (key_mgmt, "wpa-psk")) {
-		if (priv->support_pmf)
+	if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) {
+		if (nm_streq (key_mgmt_conf, "wpa-psk"))
 			key_mgmt_conf = "wpa-psk wpa-psk-sha256";
-	} else if (nm_streq (key_mgmt, "wpa-eap")) {
-		switch (fils) {
-		case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL:
-			key_mgmt_conf = priv->support_pmf
-				? "wpa-eap wpa-eap-sha256 fils-sha256 fils-sha384"
-				: "wpa-eap fils-sha256 fils-sha384";
-			break;
-		case NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED:
-			key_mgmt_conf = "fils-sha256 fils-sha384";
-			break;
-		default:
-			if (priv->support_pmf)
-				key_mgmt_conf = "wpa-eap wpa-eap-sha256";
-			break;
-		}
+		else if (nm_streq (key_mgmt_conf, "wpa-eap"))
+			key_mgmt_conf = "wpa-eap wpa-eap-sha256";
+	} else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) {
+		if (nm_streq (key_mgmt_conf, "wpa-psk"))
+			key_mgmt_conf = "wpa-psk-sha256";
+		else if (nm_streq (key_mgmt_conf, "wpa-eap"))
+			key_mgmt_conf = "wpa-eap-sha256";
 	}
-
 	if (!add_string_val (self, key_mgmt_conf, "key_mgmt", TRUE, NULL, error))
 		return FALSE;
 
@@ -832,20 +803,6 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 		}
 	}
 
-	/* Don't try to enable PMF on non-WPA networks */
-	if (!NM_IN_STRSET (key_mgmt, "wpa-eap", "wpa-psk"))
-		pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE;
-
-	/* Check if we actually support PMF */
-	if (!priv->support_pmf) {
-		if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) {
-			g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
-			                     "Supplicant does not support PMF");
-			return FALSE;
-		} else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL)
-			pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE;
-	}
-
 	/* Only WPA-specific things when using WPA */
 	if (   !strcmp (key_mgmt, "wpa-none")
 	    || !strcmp (key_mgmt, "wpa-psk")
diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h
index f6c845a3..d90d82b8 100644
--- a/src/supplicant/nm-supplicant-config.h
+++ b/src/supplicant/nm-supplicant-config.h
@@ -40,7 +40,7 @@ typedef struct _NMSupplicantConfigClass NMSupplicantConfigClass;
 
 GType nm_supplicant_config_get_type (void);
 
-NMSupplicantConfig *nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils);
+NMSupplicantConfig *nm_supplicant_config_new (void);
 
 guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self);
 
@@ -65,7 +65,6 @@ gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig
                                                              const char *con_uuid,
                                                              guint32 mtu,
                                                              NMSettingWirelessSecurityPmf pmf,
-                                                             NMSettingWirelessSecurityFils fils,
                                                              GError **error);
 
 gboolean nm_supplicant_config_add_no_security (NMSupplicantConfig *self,
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
index 3511b151..44f887cb 100644
--- a/src/supplicant/nm-supplicant-interface.c
+++ b/src/supplicant/nm-supplicant-interface.c
@@ -93,7 +93,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface,
 	PROP_FAST_SUPPORT,
 	PROP_AP_SUPPORT,
 	PROP_PMF_SUPPORT,
-	PROP_FILS_SUPPORT,
 );
 
 typedef struct {
@@ -103,7 +102,6 @@ typedef struct {
 	NMSupplicantFeature fast_support;
 	NMSupplicantFeature ap_support;   /* Lightweight AP mode support */
 	NMSupplicantFeature pmf_support;
-	NMSupplicantFeature fils_support;
 	guint32        max_scan_ssids;
 	guint32        ready_count;
 
@@ -567,12 +565,6 @@ nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self)
 	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->pmf_support;
 }
 
-NMSupplicantFeature
-nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self)
-{
-	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->fils_support;
-}
-
 void
 nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
                                         NMSupplicantFeature ap_support)
@@ -604,15 +596,6 @@ nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self,
 	priv->pmf_support = pmf_support;
 }
 
-void
-nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self,
-                                          NMSupplicantFeature fils_support)
-{
-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-
-	priv->fils_support = fils_support;
-}
-
 /*****************************************************************************/
 
 static void
@@ -1074,8 +1057,9 @@ props_changed_cb (GDBusProxy *proxy,
 	}
 
 	if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) {
-		s = nm_utils_dbus_normalize_object_path (s);
-		if (!nm_streq0 (s, priv->current_bss)) {
+		if (strcmp (s, "/") == 0)
+			s = NULL;
+		if (g_strcmp0 (s, priv->current_bss) != 0) {
 			g_free (priv->current_bss);
 			priv->current_bss = g_strdup (s);
 			_notify (self, PROP_CURRENT_BSS);
@@ -1214,6 +1198,7 @@ static void
 interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
+	NMSupplicantInterfacePrivate *priv;
 	gs_unref_variant GVariant *variant = NULL;
 	gs_free_error GError *error = NULL;
 	const char *path;
@@ -1225,6 +1210,7 @@ interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 		return;
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
 	if (variant) {
 		g_variant_get (variant, "(&o)", &path);
@@ -1914,10 +1900,6 @@ set_property (GObject *object,
 		/* construct-only */
 		priv->pmf_support = g_value_get_int (value);
 		break;
-	case PROP_FILS_SUPPORT:
-		/* construct-only */
-		priv->fils_support = g_value_get_int (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1938,8 +1920,7 @@ nm_supplicant_interface_new (const char *ifname,
                              NMSupplicantDriver driver,
                              NMSupplicantFeature fast_support,
                              NMSupplicantFeature ap_support,
-                             NMSupplicantFeature pmf_support,
-                             NMSupplicantFeature fils_support)
+                             NMSupplicantFeature pmf_support)
 {
 	g_return_val_if_fail (ifname != NULL, NULL);
 
@@ -1949,7 +1930,6 @@ nm_supplicant_interface_new (const char *ifname,
 	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
 	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
 	                     NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support,
-	                     NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, (int) fils_support,
 	                     NULL);
 }
 
@@ -1984,7 +1964,7 @@ dispose (GObject *object)
 	nm_clear_g_cancellable (&priv->other_cancellable);
 
 	g_clear_object (&priv->wpas_proxy);
-	g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy);
+	g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy);
 
 	g_clear_pointer (&priv->net_path, g_free);
 	g_clear_pointer (&priv->dev, g_free);
@@ -2049,14 +2029,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                      G_PARAM_WRITABLE |
 	                      G_PARAM_CONSTRUCT_ONLY |
 	                      G_PARAM_STATIC_STRINGS);
-	obj_properties[PROP_FILS_SUPPORT] =
-	    g_param_spec_int (NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, "", "",
-	                      NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                      NM_SUPPLICANT_FEATURE_YES,
-	                      NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                      G_PARAM_WRITABLE |
-	                      G_PARAM_CONSTRUCT_ONLY |
-	                      G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
index f32ad8dd..567cf96f 100644
--- a/src/supplicant/nm-supplicant-interface.h
+++ b/src/supplicant/nm-supplicant-interface.h
@@ -61,7 +61,6 @@ typedef enum {
 #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT     "fast-support"
 #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT       "ap-support"
 #define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT      "pmf-support"
-#define NM_SUPPLICANT_INTERFACE_FILS_SUPPORT     "fils-support"
 
 /* Signals */
 #define NM_SUPPLICANT_INTERFACE_STATE            "state"
@@ -80,8 +79,7 @@ NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname,
                                                      NMSupplicantDriver driver,
                                                      NMSupplicantFeature fast_support,
                                                      NMSupplicantFeature ap_support,
-                                                     NMSupplicantFeature pmf_support,
-                                                     NMSupplicantFeature fils_support);
+                                                     NMSupplicantFeature pmf_support);
 
 void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self,
                                                        gboolean available);
@@ -125,7 +123,6 @@ gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self,
 
 NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self);
 NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self);
-NMSupplicantFeature nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self);
 
 void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
                                              NMSupplicantFeature apmode);
@@ -136,9 +133,6 @@ void nm_supplicant_interface_set_fast_support (NMSupplicantInterface *self,
 void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self,
                                               NMSupplicantFeature pmf_support);
 
-void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self,
-                                               NMSupplicantFeature fils_support);
-
 void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self,
                                          const char *const type,
                                          const char *bssid,
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index 5ab96f88..0f2eb63a 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -40,7 +40,6 @@ typedef struct {
 	NMSupplicantFeature fast_support;
 	NMSupplicantFeature ap_support;
 	NMSupplicantFeature pmf_support;
-	NMSupplicantFeature fils_support;
 	guint             die_count_reset_id;
 	guint             die_count;
 } NMSupplicantManagerPrivate;
@@ -162,8 +161,7 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self,
 	                                     driver,
 	                                     priv->fast_support,
 	                                     priv->ap_support,
-	                                     priv->pmf_support,
-	                                     priv->fils_support);
+	                                     priv->pmf_support);
 
 	priv->ifaces = g_slist_prepend (priv->ifaces, iface);
 	g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self);
@@ -198,7 +196,6 @@ update_capabilities (NMSupplicantManager *self)
 	 */
 	priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 	priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-	priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 
 	value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities");
 	if (value) {
@@ -206,25 +203,21 @@ update_capabilities (NMSupplicantManager *self)
 			array = g_variant_get_strv (value, NULL);
 			priv->ap_support = NM_SUPPLICANT_FEATURE_NO;
 			priv->pmf_support = NM_SUPPLICANT_FEATURE_NO;
-			priv->fils_support = NM_SUPPLICANT_FEATURE_NO;
 			if (array) {
 				if (g_strv_contains (array, "ap"))
 					priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
 				if (g_strv_contains (array, "pmf"))
 					priv->pmf_support = NM_SUPPLICANT_FEATURE_YES;
-				if (g_strv_contains (array, "fils"))
-					priv->fils_support = NM_SUPPLICANT_FEATURE_YES;
 				g_free (array);
 			}
 		}
 		g_variant_unref (value);
 	}
 
-	/* Tell all interfaces about results of the AP/PMF/FILS check */
+	/* Tell all interfaces about results of the AP/PMF check */
 	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) {
 		nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support);
 		nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support);
-		nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support);
 	}
 
 	_LOGD ("AP mode is %ssupported",
@@ -233,9 +226,6 @@ update_capabilities (NMSupplicantManager *self)
 	_LOGD ("PMF is %ssupported",
 	       (priv->pmf_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
 	           (priv->pmf_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
-	_LOGD ("FILS is %ssupported",
-	       (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	           (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
 
 	/* EAP-FAST */
 	priv->fast_support = NM_SUPPLICANT_FEATURE_NO;
@@ -359,7 +349,6 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
 		priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 		priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 		priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
-		priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN;
 
 		set_running (self, FALSE);
 	}
diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c
index 5198d75f..14daf693 100644
--- a/src/supplicant/nm-supplicant-settings-verify.c
+++ b/src/supplicant/nm-supplicant-settings-verify.c
@@ -73,7 +73,6 @@ const char * group_allowed[] =    { "CCMP", "TKIP", "WEP104", "WEP40", NULL };
 const char * proto_allowed[] =    { "WPA", "RSN", NULL };
 const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256",
                                     "WPA-EAP", "WPA-EAP-SHA256",
-                                    "FILS-SHA256", "FILS-SHA384",
                                     "IEEE8021X", "WPA-NONE",
                                     "NONE", NULL };
 const char * auth_alg_allowed[] = { "OPEN", "SHARED", "LEAP", NULL };
@@ -222,10 +221,10 @@ validate_type_keyword (const struct Opt * opt,
                        const char * value,
                        const guint32 len)
 {
-	char **allowed;
-	gchar **candidates = NULL;
-	char **candidate;
-	gboolean found = FALSE;
+	char **		allowed;
+	gchar **	candidates = NULL;
+	char **		candidate;
+	gboolean	found = FALSE;
 
 	g_return_val_if_fail (opt != NULL, FALSE);
 	g_return_val_if_fail (value != NULL, FALSE);
diff --git a/src/supplicant/nm-supplicant-types.h b/src/supplicant/nm-supplicant-types.h
index 747cf152..f75827ec 100644
--- a/src/supplicant/nm-supplicant-types.h
+++ b/src/supplicant/nm-supplicant-types.h
@@ -21,9 +21,9 @@
 #ifndef __NETWORKMANAGER_SUPPLICANT_TYPES_H__
 #define __NETWORKMANAGER_SUPPLICANT_TYPES_H__
 
-#define WPAS_DBUS_SERVICE       "fi.w1.wpa_supplicant1"
-#define WPAS_DBUS_PATH          "/fi/w1/wpa_supplicant1"
-#define WPAS_DBUS_INTERFACE     "fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_SERVICE	"fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_PATH		"/fi/w1/wpa_supplicant1"
+#define WPAS_DBUS_INTERFACE	"fi.w1.wpa_supplicant1"
 
 typedef struct _NMSupplicantManager NMSupplicantManager;
 typedef struct _NMSupplicantInterface NMSupplicantInterface;
diff --git a/src/supplicant/tests/meson.build b/src/supplicant/tests/meson.build
deleted file mode 100644
index e6a86b20..00000000
--- a/src/supplicant/tests/meson.build
+++ /dev/null
@@ -1,14 +0,0 @@
-test_unit = 'test-supplicant-config'
-
-exe = executable(
-  test_unit,
-  test_unit + '.c',
-  dependencies: test_nm_dep,
-  c_args: '-DTEST_CERT_DIR="@0@"'.format(join_paths(meson.current_source_dir(), 'certs'))
-)
-
-test(
-  'supplicant/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c
index 60ca5258..4b4a4935 100644
--- a/src/supplicant/tests/test-supplicant-config.c
+++ b/src/supplicant/tests/test-supplicant-config.c
@@ -95,11 +95,7 @@ validate_opt (const char *detail,
 }
 
 static GVariant *
-build_supplicant_config (NMConnection *connection,
-                         guint mtu,
-                         guint fixed_freq,
-                         gboolean support_pmf,
-                         gboolean support_fils)
+build_supplicant_config (NMConnection *connection, guint mtu, guint fixed_freq)
 {
 	gs_unref_object NMSupplicantConfig *config = NULL;
 	gs_free_error GError *error = NULL;
@@ -108,7 +104,7 @@ build_supplicant_config (NMConnection *connection,
 	NMSetting8021x *s_8021x;
 	gboolean success;
 
-	config = nm_supplicant_config_new (support_pmf, support_fils);
+	config = nm_supplicant_config_new ();
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
@@ -122,7 +118,6 @@ build_supplicant_config (NMConnection *connection,
 	s_wsec = nm_connection_get_setting_wireless_security (connection);
 	if (s_wsec) {
 		NMSettingWirelessSecurityPmf pmf = nm_setting_wireless_security_get_pmf (s_wsec);
-		NMSettingWirelessSecurityFils fils = nm_setting_wireless_security_get_fils (s_wsec);
 		s_8021x = nm_connection_get_setting_802_1x (connection);
 		success = nm_supplicant_config_add_setting_wireless_security (config,
 			                                                          s_wsec,
@@ -130,7 +125,6 @@ build_supplicant_config (NMConnection *connection,
 			                                                          nm_connection_get_uuid (connection),
 			                                                          mtu,
 			                                                          pmf,
-			                                                          fils,
 			                                                          &error);
 	} else {
 		success = nm_supplicant_config_add_no_security (config, &error);
@@ -146,6 +140,7 @@ build_supplicant_config (NMConnection *connection,
 	return nm_supplicant_config_to_variant (config);
 }
 
+#define EXPECT(msg) g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, msg)
 
 static NMConnection *
 new_basic_connection (const char *id,
@@ -204,12 +199,12 @@ test_wifi_open (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'NONE'");
-	config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE);
+	EXPECT ("*added 'ssid' value 'Test SSID'*");
+	EXPECT ("*added 'scan_ssid' value '1'*");
+	EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*");
+	EXPECT ("*added 'freq_list' value *");
+	EXPECT ("*added 'key_mgmt' value 'NONE'");
+	config_dict = build_supplicant_config (connection, 1500, 0);
 	g_test_assert_expected_messages ();
 	g_assert (config_dict);
 
@@ -254,19 +249,19 @@ test_wifi_wep_key (const char *detail,
 	g_assert_no_error (error);
 	g_assert (success);
 
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
+	EXPECT ("*added 'ssid' value 'Test SSID'*");
+	EXPECT ("*added 'scan_ssid' value '1'*");
 	if (test_bssid)
-		NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*");
+		EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*");
 
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'NONE'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'wep_key0' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'wep_tx_keyidx' value '0'");
+	EXPECT ("*added 'freq_list' value *");
+	EXPECT ("*added 'key_mgmt' value 'NONE'");
+	EXPECT ("*added 'wep_key0' value *");
+	EXPECT ("*added 'wep_tx_keyidx' value '0'");
 	if (!test_bssid)
-		NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-80:86400'*");
+		EXPECT ("*added 'bgscan' value 'simple:30:-80:86400'*");
 
-	config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE);
+	config_dict = build_supplicant_config (connection, 1500, 0);
 	g_test_assert_expected_messages ();
 	g_assert (config_dict);
 
@@ -313,8 +308,7 @@ test_wifi_wpa_psk (const char *detail,
                    OptType key_type,
                    const char *key_data,
                    const unsigned char *expected,
-                   size_t expected_size,
-                   NMSettingWirelessSecurityPmf pmf)
+                   size_t expected_size)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	gs_unref_variant GVariant *config_dict = NULL;
@@ -334,7 +328,7 @@ test_wifi_wpa_psk (const char *detail,
 	g_object_set (s_wsec,
 	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
 	              NM_SETTING_WIRELESS_SECURITY_PSK, key_data,
-	              NM_SETTING_WIRELESS_SECURITY_PMF, (int) pmf,
+	              NM_SETTING_WIRELESS_SECURITY_PMF, (int) NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL,
 	              NULL);
 	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
 	nm_setting_wireless_security_add_proto (s_wsec, "rsn");
@@ -347,26 +341,17 @@ test_wifi_wpa_psk (const char *detail,
 	g_assert_no_error (error);
 	g_assert (success);
 
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'psk' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'");
-	switch (pmf) {
-	case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL:
-		NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '1'");
-		break;
-	case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED:
-		NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '2'");
-		break;
-	default:
-		break;
-	}
-	config_dict = build_supplicant_config (connection, 1500, 0, TRUE, TRUE);
+	EXPECT ("*added 'ssid' value 'Test SSID'*");
+	EXPECT ("*added 'scan_ssid' value '1'*");
+	EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*");
+	EXPECT ("*added 'freq_list' value *");
+	EXPECT ("*added 'key_mgmt' value 'WPA-PSK WPA-PSK-SHA256'");
+	EXPECT ("*added 'psk' value *");
+	EXPECT ("*added 'proto' value 'WPA RSN'");
+	EXPECT ("*added 'pairwise' value 'TKIP CCMP'");
+	EXPECT ("*added 'group' value 'TKIP CCMP'");
+	EXPECT ("*added 'ieee80211w' value '1'");
+	config_dict = build_supplicant_config (connection, 1500, 0);
 
 	g_test_assert_expected_messages ();
 	g_assert (config_dict);
@@ -396,16 +381,12 @@ test_wifi_wpa_psk_types (void)
 	                                        0x6c, 0x2f, 0x11, 0x60, 0x5a, 0x16, 0x08, 0x93 };
 	const char *key2 = "r34lly l33t wp4 p4ssphr4s3 for t3st1ng";
 
-	test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected,
-	                   sizeof (key1_expected), NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL);
-	test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2,
-	                   (gconstpointer) key2, strlen (key2), NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED);
-	test_wifi_wpa_psk ("pmf-disabled", TYPE_STRING, key2,
-	                   (gconstpointer) key2, strlen (key2), NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE);
+	test_wifi_wpa_psk ("wifi-wpa-psk-hex", TYPE_BYTES, key1, key1_expected, sizeof (key1_expected));
+	test_wifi_wpa_psk ("wifi-wep-psk-passphrase", TYPE_STRING, key2, (gconstpointer) key2, strlen (key2));
 }
 
 static NMConnection *
-generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_str, NMSettingWirelessSecurityFils fils)
+generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_str)
 {
 	NMConnection *connection = NULL;
 	NMSettingWirelessSecurity *s_wsec;
@@ -420,7 +401,6 @@ generate_wifi_eap_connection (const char *id, GBytes *ssid, const char *bssid_st
 	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
 	g_object_set (s_wsec,
 	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
-	              NM_SETTING_WIRELESS_SECURITY_FILS, (int) fils,
 	              NULL);
 	nm_setting_wireless_security_add_proto (s_wsec, "wpa");
 	nm_setting_wireless_security_add_proto (s_wsec, "rsn");
@@ -454,22 +434,22 @@ test_wifi_eap_locked_bssid (void)
 	const char *bssid_str = "11:22:33:44:55:66";
 	guint32 mtu = 1100;
 
-	connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Locked", ssid, bssid_str, NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL);
-
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'bssid' value '11:22:33:44:55:66'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-EAP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'");
-	config_dict = build_supplicant_config (connection, mtu, 0, FALSE, FALSE);
+	connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Locked", ssid, bssid_str);
+
+	EXPECT ("*added 'ssid' value 'Test SSID'*");
+	EXPECT ("*added 'scan_ssid' value '1'*");
+	EXPECT ("*added 'bssid' value '11:22:33:44:55:66'*");
+	EXPECT ("*added 'freq_list' value *");
+	EXPECT ("*added 'key_mgmt' value 'WPA-EAP'");
+	EXPECT ("*added 'proto' value 'WPA RSN'");
+	EXPECT ("*added 'pairwise' value 'TKIP CCMP'");
+	EXPECT ("*added 'group' value 'TKIP CCMP'");
+	EXPECT ("*Config: added 'eap' value 'TLS'");
+	EXPECT ("*Config: added 'fragment_size' value '1086'");
+	EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'");
+	EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'");
+	EXPECT ("*Config: added 'proactive_key_caching' value '1'");
+	config_dict = build_supplicant_config (connection, mtu, 0);
 	g_test_assert_expected_messages ();
 	g_assert (config_dict);
 
@@ -495,69 +475,28 @@ test_wifi_eap_unlocked_bssid (void)
 	gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data));
 	guint32 mtu = 1100;
 
-	connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Unlocked", ssid, NULL, NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED);
-
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'FILS-SHA256 FILS-SHA384'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-65:300'");
-	config_dict = build_supplicant_config (connection, mtu, 0, FALSE, TRUE);
-	g_test_assert_expected_messages ();
-	g_assert (config_dict);
-
-	validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1));
-	validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid);
-	validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "FILS-SHA256 FILS-SHA384");
-	validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS");
-	validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN");
-	validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP");
-	validate_opt ("wifi-eap", config_dict, "group", TYPE_KEYWORD, "TKIP CCMP");
-	validate_opt ("wifi-eap", config_dict, "fragment_size", TYPE_INT, GINT_TO_POINTER(mtu-14));
-	validate_opt ("wifi-eap", config_dict, "bgscan", TYPE_BYTES, bgscan);
-}
-
-static void
-test_wifi_eap_fils_disabled (void)
-{
-	gs_unref_object NMConnection *connection = NULL;
-	gs_unref_variant GVariant *config_dict = NULL;
-	const unsigned char ssid_data[] = { 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x53, 0x49, 0x44 };
-	gs_unref_bytes GBytes *ssid = g_bytes_new (ssid_data, sizeof (ssid_data));
-	const char *bgscan_data = "simple:30:-65:300";
-	gs_unref_bytes GBytes *bgscan = g_bytes_new (bgscan_data, strlen (bgscan_data));
-	guint32 mtu = 1100;
-
-	connection = generate_wifi_eap_connection ("Test Wifi FILS disabled", ssid, NULL, NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE);
-
-	NMTST_EXPECT_NM_INFO ("Config: added 'ssid' value 'Test SSID'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'scan_ssid' value '1'*");
-	NMTST_EXPECT_NM_INFO ("Config: added 'freq_list' value *");
-	NMTST_EXPECT_NM_INFO ("Config: added 'key_mgmt' value 'WPA-EAP WPA-EAP-SHA256'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proto' value 'WPA RSN'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'eap' value 'TLS'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'fragment_size' value '1086'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'ca_cert' value '*/test-ca-cert.pem'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'private_key' value '*/test-cert.p12'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'proactive_key_caching' value '1'");
-	NMTST_EXPECT_NM_INFO ("Config: added 'bgscan' value 'simple:30:-65:300'");
-	config_dict = build_supplicant_config (connection, mtu, 0, TRUE, TRUE);
+	connection = generate_wifi_eap_connection ("Test Wifi EAP-TLS Unlocked", ssid, NULL);
+
+	EXPECT ("*added 'ssid' value 'Test SSID'*");
+	EXPECT ("*added 'scan_ssid' value '1'*");
+	EXPECT ("*added 'freq_list' value *");
+	EXPECT ("*added 'key_mgmt' value 'WPA-EAP'");
+	EXPECT ("*added 'proto' value 'WPA RSN'");
+	EXPECT ("*added 'pairwise' value 'TKIP CCMP'");
+	EXPECT ("*added 'group' value 'TKIP CCMP'");
+	EXPECT ("*Config: added 'eap' value 'TLS'");
+	EXPECT ("*Config: added 'fragment_size' value '1086'");
+	EXPECT ("* Config: added 'ca_cert' value '*/test-ca-cert.pem'");
+	EXPECT ("* Config: added 'private_key' value '*/test-cert.p12'");
+	EXPECT ("*Config: added 'proactive_key_caching' value '1'");
+	EXPECT ("*Config: added 'bgscan' value 'simple:30:-65:300'");
+	config_dict = build_supplicant_config (connection, mtu, 0);
 	g_test_assert_expected_messages ();
 	g_assert (config_dict);
 
 	validate_opt ("wifi-eap", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1));
 	validate_opt ("wifi-eap", config_dict, "ssid", TYPE_BYTES, ssid);
-	validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP WPA-EAP-SHA256");
+	validate_opt ("wifi-eap", config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-EAP");
 	validate_opt ("wifi-eap", config_dict, "eap", TYPE_KEYWORD, "TLS");
 	validate_opt ("wifi-eap", config_dict, "proto", TYPE_KEYWORD, "WPA RSN");
 	validate_opt ("wifi-eap", config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP");
@@ -577,7 +516,6 @@ int main (int argc, char **argv)
 	g_test_add_func ("/supplicant-config/wifi-wpa-psk-types", test_wifi_wpa_psk_types);
 	g_test_add_func ("/supplicant-config/wifi-eap/locked-bssid", test_wifi_eap_locked_bssid);
 	g_test_add_func ("/supplicant-config/wifi-eap/unlocked-bssid", test_wifi_eap_unlocked_bssid);
-	g_test_add_func ("/supplicant-config/wifi-eap/fils-disabled", test_wifi_eap_fils_disabled);
 
 	return g_test_run ();
 }
diff --git a/src/systemd/meson.build b/src/systemd/meson.build
deleted file mode 100644
index fe3d88d3..00000000
--- a/src/systemd/meson.build
+++ /dev/null
@@ -1,70 +0,0 @@
-sources = files(
-  'sd-adapt/nm-sd-adapt.c',
-  'src/basic/alloc-util.c',
-  'src/basic/escape.c',
-  'src/basic/ether-addr-util.c',
-  'src/basic/extract-word.c',
-  'src/basic/fd-util.c',
-  'src/basic/fileio.c',
-  'src/basic/fs-util.c',
-  'src/basic/hash-funcs.c',
-  'src/basic/hashmap.c',
-  'src/basic/hexdecoct.c',
-  'src/basic/hostname-util.c',
-  'src/basic/in-addr-util.c',
-  'src/basic/io-util.c',
-  'src/basic/mempool.c',
-  'src/basic/parse-util.c',
-  'src/basic/path-util.c',
-  'src/basic/prioq.c',
-  'src/basic/process-util.c',
-  'src/basic/random-util.c',
-  'src/basic/socket-util.c',
-  'src/basic/string-table.c',
-  'src/basic/string-util.c',
-  'src/basic/strv.c',
-  'src/basic/time-util.c',
-  'src/basic/utf8.c',
-  'src/basic/util.c',
-  'src/libsystemd-network/arp-util.c',
-  'src/libsystemd-network/dhcp-identifier.c',
-  'src/libsystemd-network/dhcp-network.c',
-  'src/libsystemd-network/dhcp-option.c',
-  'src/libsystemd-network/dhcp-packet.c',
-  'src/libsystemd-network/dhcp6-network.c',
-  'src/libsystemd-network/dhcp6-option.c',
-  'src/libsystemd-network/lldp-neighbor.c',
-  'src/libsystemd-network/lldp-network.c',
-  'src/libsystemd-network/network-internal.c',
-  'src/libsystemd-network/sd-dhcp-client.c',
-  'src/libsystemd-network/sd-dhcp-lease.c',
-  'src/libsystemd-network/sd-dhcp6-client.c',
-  'src/libsystemd-network/sd-dhcp6-lease.c',
-  'src/libsystemd-network/sd-ipv4acd.c',
-  'src/libsystemd-network/sd-ipv4ll.c',
-  'src/libsystemd-network/sd-lldp.c',
-  'src/libsystemd/sd-event/sd-event.c',
-  'src/libsystemd/sd-id128/id128-util.c',
-  'src/libsystemd/sd-id128/sd-id128.c',
-  'src/shared/dns-domain.c',
-  'nm-sd.c'
-)
-
-incs = [
-  src_inc,
-  include_directories(
-    'sd-adapt',
-    'src/basic',
-    'src/libsystemd-network',
-    'src/shared',
-    'src/systemd'
-  )
-]
-
-libsystemd_nm = static_library(
-  'systemd-nm',
-  sources: sources,
-  include_directories: incs,
-  dependencies: nm_core_dep,
-  c_args: '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD'
-)
diff --git a/src/systemd/sd-adapt/device-nodes.h b/src/systemd/sd-adapt/device-nodes.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/device-nodes.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/sd-adapt/errno-list.h b/src/systemd/sd-adapt/errno-list.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/errno-list.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/sd-adapt/locale-util.h b/src/systemd/sd-adapt/locale-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/locale-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/sd-adapt/memfd-util.h b/src/systemd/sd-adapt/memfd-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/memfd-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h
index 3a4125a0..0d291e26 100644
--- a/src/systemd/sd-adapt/nm-sd-adapt.h
+++ b/src/systemd/sd-adapt/nm-sd-adapt.h
@@ -26,16 +26,12 @@
 #include <sys/resource.h>
 #include <time.h>
 
+#define noreturn G_GNUC_NORETURN
+
 #ifndef CLOCK_BOOTTIME
 #define CLOCK_BOOTTIME 7
 #endif
 
-#if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1
-#define HAVE_REALLOCARRAY 1
-#else
-#define HAVE_REALLOCARRAY 0
-#endif
-
 #if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1
 #define HAVE_EXPLICIT_BZERO 1
 #else
@@ -109,7 +105,7 @@ G_STMT_START { \
  * itself.
  *****************************************************************************/
 
-#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD
+#if (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD
 
 #include <netinet/in.h>
 #include <string.h>
@@ -142,15 +138,6 @@ G_STMT_START { \
 #  endif
 #endif
 
-static inline pid_t
-raw_getpid (void) {
-#if defined(__alpha__)
-	return (pid_t) syscall (__NR_getxpid);
-#else
-	return (pid_t) syscall (__NR_getpid);
-#endif
-}
-
 /*****************************************************************************/
 
 /* work around missing uchar.h */
@@ -159,6 +146,8 @@ typedef guint32 char32_t;
 
 /*****************************************************************************/
 
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
 static inline int
 sd_notify (int unset_environment, const char *state)
 {
@@ -197,7 +186,7 @@ static inline pid_t gettid(void) {
         return (pid_t) syscall(SYS_gettid);
 }
 
-#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */
+#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */
 
 #endif /* NM_SD_ADAPT_H */
 
diff --git a/src/systemd/sd-adapt/procfs-util.h b/src/systemd/sd-adapt/procfs-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/procfs-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/sd-adapt/terminal-util.h b/src/systemd/sd-adapt/terminal-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/systemd/sd-adapt/terminal-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c
index 1a058d2f..97588312 100644
--- a/src/systemd/src/basic/alloc-util.c
+++ b/src/systemd/src/basic/alloc-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -40,7 +39,7 @@ void* memdup(const void *p, size_t l) {
         return ret;
 }
 
-void* memdup_suffix0(const void *p, size_t l) {
+void* memdup_suffix0(const void*p, size_t l) {
         void *ret;
 
         assert(l == 0 || p);
diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h
index b1e0edbb..0a89691b 100644
--- a/src/systemd/src/basic/alloc-util.h
+++ b/src/systemd/src/basic/alloc-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -55,7 +54,7 @@ static inline void *mfree(void *memory) {
         })
 
 void* memdup(const void *p, size_t l) _alloc_(2);
-void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
+void* memdup_suffix0(const void*p, size_t l) _alloc_(2);
 
 static inline void freep(void *p) {
         free(*(void**) p);
@@ -74,14 +73,12 @@ _malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t
         return malloc(size * need);
 }
 
-#if !HAVE_REALLOCARRAY
-_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) {
+_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
         return realloc(p, size * need);
 }
-#endif
 
 _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
         if (size_multiply_overflow(size, need))
@@ -130,12 +127,3 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
                 _new_ = alloca_align(_size_, (align));                  \
                 (void*)memset(_new_, 0, _size_);                        \
         })
-
-/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to
- * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
-#define TAKE_PTR(ptr)                           \
-        ({                                      \
-                typeof(ptr) _ptr_ = (ptr);      \
-                (ptr) = NULL;                   \
-                _ptr_;                          \
-        })
diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h
index 01c975bb..9bd13ff6 100644
--- a/src/systemd/src/basic/async.h
+++ b/src/systemd/src/basic/async.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -22,5 +21,5 @@
 
 int asynchronous_job(void* (*func)(void *p), void *arg);
 
-int asynchronous_sync(pid_t *ret_pid);
+int asynchronous_sync(void);
 int asynchronous_close(int fd);
diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c
index fac618dd..27a20702 100644
--- a/src/systemd/src/basic/escape.c
+++ b/src/systemd/src/basic/escape.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h
index cd5c49fa..e62347af 100644
--- a/src/systemd/src/basic/escape.h
+++ b/src/systemd/src/basic/escape.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c
index 93922a28..a793219c 100644
--- a/src/systemd/src/basic/ether-addr-util.c
+++ b/src/systemd/src/basic/ether-addr-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -20,7 +19,6 @@
 
 #include "nm-sd-adapt.h"
 
-#include <errno.h>
 #include <net/ethernet.h>
 #include <stdio.h>
 #include <sys/types.h>
@@ -74,7 +72,7 @@ int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset
                         if (s[pos] == '\0')                     \
                                 break;                          \
                         hexoff = strchr(hex, s[pos]);           \
-                        if (!hexoff)                            \
+                        if (hexoff == NULL)                     \
                                 break;                          \
                         assert(hexoff >= hex);                  \
                         x = hexoff - hex;                       \
@@ -102,7 +100,7 @@ int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset
         sep = s[strspn(s, hex)];
         if (sep == '\n')
                 return -EINVAL;
-        if (!strchr(":.-", sep))
+        if (strchr(":.-", sep) == NULL)
                 return -EINVAL;
 
         if (sep == '.') {
diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h
index 08d05a13..74e125a9 100644
--- a/src/systemd/src/basic/ether-addr-util.h
+++ b/src/systemd/src/basic/ether-addr-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c
index e52c376b..69d8c48d 100644
--- a/src/systemd/src/basic/extract-word.c
+++ b/src/systemd/src/basic/extract-word.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -196,7 +195,8 @@ finish:
 
 finish_force_next:
         s[sz] = 0;
-        *ret = TAKE_PTR(s);
+        *ret = s;
+        s = NULL;
 
         return 1;
 }
diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h
index 300c51bb..04746c6d 100644
--- a/src/systemd/src/basic/extract-word.h
+++ b/src/systemd/src/basic/extract-word.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c
index ff480f51..1c327d83 100644
--- a/src/systemd/src/basic/fd-util.c
+++ b/src/systemd/src/basic/fd-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -29,10 +28,8 @@
 
 #include "dirent-util.h"
 #include "fd-util.h"
-#include "fileio.h"
 #include "fs-util.h"
 #include "macro.h"
-#include "memfd-util.h"
 #include "missing.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -194,6 +191,12 @@ int fd_cloexec(int fd, bool cloexec) {
 }
 
 #if 0 /* NM_IGNORED */
+void stdio_unset_cloexec(void) {
+        fd_cloexec(STDIN_FILENO, false);
+        fd_cloexec(STDOUT_FILENO, false);
+        fd_cloexec(STDERR_FILENO, false);
+}
+
 _pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
         unsigned i;
 
@@ -224,21 +227,20 @@ int close_all_fds(const int except[], unsigned n_except) {
 
                 assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
                 for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
-                        int q;
 
                         if (fd_in_set(fd, except, n_except))
                                 continue;
 
-                        q = close_nointr(fd);
-                        if (q < 0 && q != -EBADF && r >= 0)
-                                r = q;
+                        if (close_nointr(fd) < 0)
+                                if (errno != EBADF && r == 0)
+                                        r = -errno;
                 }
 
                 return r;
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
-                int fd = -1, q;
+                int fd = -1;
 
                 if (safe_atoi(de->d_name, &fd) < 0)
                         /* Let's better ignore this, just in case */
@@ -253,9 +255,11 @@ int close_all_fds(const int except[], unsigned n_except) {
                 if (fd_in_set(fd, except, n_except))
                         continue;
 
-                q = close_nointr(fd);
-                if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
-                        r = q;
+                if (close_nointr(fd) < 0) {
+                        /* Valgrind has its own FD and doesn't want to have it closed */
+                        if (errno != EBADF && r == 0)
+                                r = -errno;
+                }
         }
 
         return r;
@@ -365,384 +369,15 @@ bool fdname_is_valid(const char *s) {
 }
 
 int fd_get_path(int fd, char **ret) {
-        _cleanup_close_ int dir = -1;
-        char fdname[DECIMAL_STR_MAX(int)];
+        char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int r;
 
-        dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH);
-        if (dir < 0)
-                /* /proc is not available or not set up properly, we're most likely
-                 * in some chroot environment. */
-                return errno == ENOENT ? -EOPNOTSUPP : -errno;
+        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
 
-        xsprintf(fdname, "%i", fd);
+        r = readlink_malloc(procfs_path, ret);
 
-        r = readlinkat_malloc(dir, fdname, ret);
-        if (r == -ENOENT)
-                /* If the file doesn't exist the fd is invalid */
+        if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */
                 return -EBADF;
 
         return r;
 }
-
-#if 0 /* NM_IGNORED */
-int move_fd(int from, int to, int cloexec) {
-        int r;
-
-        /* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If
-         * 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned
-         * off, if it is > 0 it is turned on. */
-
-        if (from < 0)
-                return -EBADF;
-        if (to < 0)
-                return -EBADF;
-
-        if (from == to) {
-
-                if (cloexec >= 0) {
-                        r = fd_cloexec(to, cloexec);
-                        if (r < 0)
-                                return r;
-                }
-
-                return to;
-        }
-
-        if (cloexec < 0) {
-                int fl;
-
-                fl = fcntl(from, F_GETFD, 0);
-                if (fl < 0)
-                        return -errno;
-
-                cloexec = !!(fl & FD_CLOEXEC);
-        }
-
-        r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
-        if (r < 0)
-                return -errno;
-
-        assert(r == to);
-
-        safe_close(from);
-
-        return to;
-}
-
-int acquire_data_fd(const void *data, size_t size, unsigned flags) {
-
-        _cleanup_close_pair_ int pipefds[2] = { -1, -1 };
-        char pattern[] = "/dev/shm/data-fd-XXXXXX";
-        _cleanup_close_ int fd = -1;
-        int isz = 0, r;
-        ssize_t n;
-        off_t f;
-
-        assert(data || size == 0);
-
-        /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
-         * complex than I wish it was. But here's why:
-         *
-         * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
-         *    read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
-         *
-         * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
-         *    a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
-         *    clients can only bump their size to a system-wide limit, which might be quite low.
-         *
-         * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
-         *    earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
-         *    /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
-         *
-         * d) Finally, we try creating a regular file in /dev/shm, which we then delete.
-         *
-         * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
-         * figure. */
-
-        if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
-                /* As a special case, return /dev/null if we have been called for an empty data block */
-                r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
-
-        if ((flags & ACQUIRE_NO_MEMFD) == 0) {
-                fd = memfd_new("data-fd");
-                if (fd < 0)
-                        goto try_pipe;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                f = lseek(fd, 0, SEEK_SET);
-                if (f != 0)
-                        return -errno;
-
-                r = memfd_set_sealed(fd);
-                if (r < 0)
-                        return r;
-
-                return TAKE_FD(fd);
-        }
-
-try_pipe:
-        if ((flags & ACQUIRE_NO_PIPE) == 0) {
-                if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
-                        return -errno;
-
-                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                if (isz < 0)
-                        return -errno;
-
-                if ((size_t) isz < size) {
-                        isz = (int) size;
-                        if (isz < 0 || (size_t) isz != size)
-                                return -E2BIG;
-
-                        /* Try to bump the pipe size */
-                        (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
-
-                        /* See if that worked */
-                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                        if (isz < 0)
-                                return -errno;
-
-                        if ((size_t) isz < size)
-                                goto try_dev_shm;
-                }
-
-                n = write(pipefds[1], data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                (void) fd_nonblock(pipefds[0], false);
-
-                return TAKE_FD(pipefds[0]);
-        }
-
-try_dev_shm:
-        if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
-                fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
-                if (fd < 0)
-                        goto try_dev_shm_without_o_tmpfile;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
-        }
-
-try_dev_shm_without_o_tmpfile:
-        if ((flags & ACQUIRE_NO_REGULAR) == 0) {
-                fd = mkostemp_safe(pattern);
-                if (fd < 0)
-                        return fd;
-
-                n = write(fd, data, size);
-                if (n < 0) {
-                        r = -errno;
-                        goto unlink_and_return;
-                }
-                if ((size_t) n != size) {
-                        r = -EIO;
-                        goto unlink_and_return;
-                }
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                r = open(pattern, O_RDONLY|O_CLOEXEC);
-                if (r < 0)
-                        r = -errno;
-
-        unlink_and_return:
-                (void) unlink(pattern);
-                return r;
-        }
-
-        return -EOPNOTSUPP;
-}
-#endif /* NM_IGNORED */
-
-int fd_move_above_stdio(int fd) {
-        int flags, copy;
-        PROTECT_ERRNO;
-
-        /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of
-         * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is
-         * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that
-         * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as
-         * stdin/stdout/stderr of unrelated code.
-         *
-         * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by
-         * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has
-         * been closed before.
-         *
-         * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an
-         * error we simply return the original file descriptor, and we do not touch errno. */
-
-        if (fd < 0 || fd > 2)
-                return fd;
-
-        flags = fcntl(fd, F_GETFD, 0);
-        if (flags < 0)
-                return fd;
-
-        if (flags & FD_CLOEXEC)
-                copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
-        else
-                copy = fcntl(fd, F_DUPFD, 3);
-        if (copy < 0)
-                return fd;
-
-        assert(copy > 2);
-
-        (void) close(fd);
-        return copy;
-}
-
-#if 0 /* NM_IGNORED */
-int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) {
-
-        int fd[3] = { /* Put together an array of fds we work on */
-                original_input_fd,
-                original_output_fd,
-                original_error_fd
-        };
-
-        int r, i,
-                null_fd = -1,                /* if we open /dev/null, we store the fd to it here */
-                copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */
-        bool null_readable, null_writable;
-
-        /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is
-         * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as
-         * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be
-         * on.
-         *
-         * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on
-         * failure! Thus, callers should assume that when this function returns the input fds are invalidated.
-         *
-         * Note that when this function fails stdin/stdout/stderr might remain half set up!
-         *
-         * O_CLOEXEC is turned off for all three file descriptors (which is how it should be for
-         * stdin/stdout/stderr). */
-
-        null_readable = original_input_fd < 0;
-        null_writable = original_output_fd < 0 || original_error_fd < 0;
-
-        /* First step, open /dev/null once, if we need it */
-        if (null_readable || null_writable) {
-
-                /* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */
-                null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR :
-                                             null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC);
-                if (null_fd < 0) {
-                        r = -errno;
-                        goto finish;
-                }
-
-                /* If this fd is in the 0…2 range, let's move it out of it */
-                if (null_fd < 3) {
-                        int copy;
-
-                        copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
-                        if (copy < 0) {
-                                r = -errno;
-                                goto finish;
-                        }
-
-                        safe_close(null_fd);
-                        null_fd = copy;
-                }
-        }
-
-        /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
-        for (i = 0; i < 3; i++) {
-
-                if (fd[i] < 0)
-                        fd[i] = null_fd;        /* A negative parameter means: connect this one to /dev/null */
-                else if (fd[i] != i && fd[i] < 3) {
-                        /* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */
-                        copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
-                        if (copy_fd[i] < 0) {
-                                r = -errno;
-                                goto finish;
-                        }
-
-                        fd[i] = copy_fd[i];
-                }
-        }
-
-        /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we
-         * have freedom to move them around. If the fds already were at the right places then the specific fds are
-         * -1. Let's now move them to the right places. This is the point of no return. */
-        for (i = 0; i < 3; i++) {
-
-                if (fd[i] == i) {
-
-                        /* fd is already in place, but let's make sure O_CLOEXEC is off */
-                        r = fd_cloexec(i, false);
-                        if (r < 0)
-                                goto finish;
-
-                } else {
-                        assert(fd[i] > 2);
-
-                        if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */
-                                r = -errno;
-                                goto finish;
-                        }
-                }
-        }
-
-        r = 0;
-
-finish:
-        /* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same
-         * fd passed in multiple times. */
-        safe_close_above_stdio(original_input_fd);
-        if (original_output_fd != original_input_fd)
-                safe_close_above_stdio(original_output_fd);
-        if (original_error_fd != original_input_fd && original_error_fd != original_output_fd)
-                safe_close_above_stdio(original_error_fd);
-
-        /* Close the copies we moved > 2 */
-        for (i = 0; i < 3; i++)
-                safe_close(copy_fd[i]);
-
-        /* Close our null fd, if it's > 2 */
-        safe_close_above_stdio(null_fd);
-
-        return r;
-}
-
-int fd_reopen(int fd, int flags) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-        int new_fd;
-
-        /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
-         * turn O_RDWR fds into O_RDONLY fds.
-         *
-         * This doesn't work on sockets (since they cannot be open()ed, ever).
-         *
-         * This implicitly resets the file read index to 0. */
-
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        new_fd = open(procfs_path, flags);
-        if (new_fd < 0)
-                return -errno;
-
-        return new_fd;
-}
-#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h
index 163b096b..34b98d4a 100644
--- a/src/systemd/src/basic/fd-util.h
+++ b/src/systemd/src/basic/fd-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -35,13 +34,6 @@ int close_nointr(int fd);
 int safe_close(int fd);
 void safe_close_pair(int p[]);
 
-static inline int safe_close_above_stdio(int fd) {
-        if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */
-                return -1;
-
-        return safe_close(fd);
-}
-
 void close_many(const int fds[], unsigned n_fd);
 
 int fclose_nointr(FILE *f);
@@ -71,6 +63,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
 
 int fd_nonblock(int fd, bool nonblock);
 int fd_cloexec(int fd, bool cloexec);
+void stdio_unset_cloexec(void);
 
 int close_all_fds(const int except[], unsigned n_except);
 
@@ -82,36 +75,6 @@ bool fdname_is_valid(const char *s);
 
 int fd_get_path(int fd, char **ret);
 
-int move_fd(int from, int to, int cloexec);
-
-enum {
-        ACQUIRE_NO_DEV_NULL = 1 << 0,
-        ACQUIRE_NO_MEMFD    = 1 << 1,
-        ACQUIRE_NO_PIPE     = 1 << 2,
-        ACQUIRE_NO_TMPFILE  = 1 << 3,
-        ACQUIRE_NO_REGULAR  = 1 << 4,
-};
-
-int acquire_data_fd(const void *data, size_t size, unsigned flags);
-
 /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
 #define ERRNO_IS_DISCONNECT(r) \
         IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
-
-int fd_move_above_stdio(int fd);
-
-int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
-
-static inline int make_null_stdio(void) {
-        return rearrange_stdio(-1, -1, -1);
-}
-
-/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
-#define TAKE_FD(fd)                             \
-        ({                                      \
-                int _fd_ = (fd);                \
-                (fd) = -1;                      \
-                _fd_;                           \
-        })
-
-int fd_reopen(int fd, int flags);
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index c7b4b241..51d1c052 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -25,10 +24,8 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdint.h>
-#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -65,30 +62,12 @@ int write_string_stream_ts(
                 WriteStringFileFlags flags,
                 struct timespec *ts) {
 
-        bool needs_nl;
-
         assert(f);
         assert(line);
 
-        if (ferror(f))
-                return -EIO;
-
-        needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
-
-        if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
-                /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
-                 * that the write goes out in one go, instead of two */
-
-                line = strjoina(line, "\n");
-                needs_nl = false;
-        }
-
-        if (fputs(line, f) == EOF)
-                return -errno;
-
-        if (needs_nl)
-                if (fputc('\n', f) == EOF)
-                        return -errno;
+        fputs(line, f);
+        if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"))
+                fputc('\n', f);
 
         if (ts) {
                 struct timespec twice[2] = {*ts, *ts};
@@ -120,7 +99,6 @@ static int write_string_file_atomic(
         if (r < 0)
                 return r;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
         (void) fchmod_umask(fileno(f), 0644);
 
         r = write_string_stream_ts(f, line, flags, ts);
@@ -163,7 +141,7 @@ int write_string_file_ts(
 
                 return r;
         } else
-                assert(!ts);
+                assert(ts == NULL);
 
         if (flags & WRITE_STRING_FILE_CREATE) {
                 f = fopen(fn, "we");
@@ -190,11 +168,6 @@ int write_string_file_ts(
                 }
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
-        if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
-                setvbuf(f, NULL, _IONBF, 0);
-
         r = write_string_stream_ts(f, line, flags, ts);
         if (r < 0)
                 goto fail;
@@ -228,11 +201,10 @@ int read_one_line_file(const char *fn, char **line) {
         if (!f)
                 return -errno;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         r = read_line(f, LONG_LINE_MAX, line);
         return r < 0 ? r : 0;
 }
+#endif /* NM_IGNORED */
 
 int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         _cleanup_fclose_ FILE *f = NULL;
@@ -255,8 +227,6 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         if (!f)
                 return -errno;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         /* We try to read one byte more than we need, so that we know whether we hit eof */
         errno = 0;
         k = fread(buf, 1, l + accept_extra_nl + 1, f);
@@ -272,7 +242,6 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
 
         return 1;
 }
-#endif /* NM_IGNORED */
 
 int read_full_stream(FILE *f, char **contents, size_t *size) {
         size_t n, l;
@@ -334,7 +303,8 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
         }
 
         buf[l] = 0;
-        *contents = TAKE_PTR(buf);
+        *contents = buf;
+        buf = NULL; /* do not free */
 
         if (size)
                 *size = l;
@@ -352,8 +322,6 @@ int read_full_file(const char *fn, char **contents, size_t *size) {
         if (!f)
                 return -errno;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         return read_full_stream(f, contents, size);
 }
 
@@ -366,11 +334,11 @@ static int parse_env_file_internal(
                 void *userdata,
                 int *n_pushed) {
 
+        _cleanup_free_ char *contents = NULL, *key = NULL;
         size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
-        _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
-        unsigned line = 1;
-        char *p;
+        char *p, *value = NULL;
         int r;
+        unsigned line = 1;
 
         enum {
                 PRE_KEY,
@@ -407,8 +375,10 @@ static int parse_env_file_internal(
                                 state = KEY;
                                 last_key_whitespace = (size_t) -1;
 
-                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 key[n_key++] = c;
                         }
@@ -428,8 +398,10 @@ static int parse_env_file_internal(
                                 else if (last_key_whitespace == (size_t) -1)
                                          last_key_whitespace = n_key;
 
-                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 key[n_key++] = c;
                         }
@@ -451,7 +423,7 @@ static int parse_env_file_internal(
 
                                 r = push(fname, line, key, value, userdata, n_pushed);
                                 if (r < 0)
-                                        return r;
+                                        goto fail;
 
                                 n_key = 0;
                                 value = NULL;
@@ -466,8 +438,10 @@ static int parse_env_file_internal(
                         else if (!strchr(WHITESPACE, c)) {
                                 state = VALUE;
 
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return  -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -494,7 +468,7 @@ static int parse_env_file_internal(
 
                                 r = push(fname, line, key, value, userdata, n_pushed);
                                 if (r < 0)
-                                        return r;
+                                        goto fail;
 
                                 n_key = 0;
                                 value = NULL;
@@ -509,8 +483,10 @@ static int parse_env_file_internal(
                                 else if (last_value_whitespace == (size_t) -1)
                                         last_value_whitespace = n_value;
 
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -522,8 +498,10 @@ static int parse_env_file_internal(
 
                         if (!strchr(newline, c)) {
                                 /* Escaped newlines we eat up entirely */
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -535,8 +513,10 @@ static int parse_env_file_internal(
                         else if (c == '\\')
                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
                         else {
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -547,8 +527,10 @@ static int parse_env_file_internal(
                         state = SINGLE_QUOTE_VALUE;
 
                         if (!strchr(newline, c)) {
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -560,8 +542,10 @@ static int parse_env_file_internal(
                         else if (c == '\\')
                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
                         else {
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -572,8 +556,10 @@ static int parse_env_file_internal(
                         state = DOUBLE_QUOTE_VALUE;
 
                         if (!strchr(newline, c)) {
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
+                                        r = -ENOMEM;
+                                        goto fail;
+                                }
 
                                 value[n_value++] = c;
                         }
@@ -618,12 +604,14 @@ static int parse_env_file_internal(
 
                 r = push(fname, line, key, value, userdata, n_pushed);
                 if (r < 0)
-                        return r;
-
-                value = NULL;
+                        goto fail;
         }
 
         return 0;
+
+fail:
+        free(value);
+        return r;
 }
 
 static int check_utf8ness_and_warn(
@@ -891,8 +879,7 @@ int write_env_file(const char *fname, char **l) {
         if (r < 0)
                 return r;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-        (void) fchmod_umask(fileno(f), 0644);
+        fchmod_umask(fileno(f), 0644);
 
         STRV_FOREACH(i, l)
                 write_env_var(f, *i);
@@ -910,16 +897,14 @@ int write_env_file(const char *fname, char **l) {
 }
 
 int executable_is_script(const char *path, char **interpreter) {
+        int r;
         _cleanup_free_ char *line = NULL;
-        size_t len;
+        int len;
         char *ans;
-        int r;
 
         assert(path);
 
         r = read_one_line_file(path, &line);
-        if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
-                return 0;
         if (r < 0)
                 return r;
 
@@ -1162,7 +1147,6 @@ int fflush_and_check(FILE *f) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 int fflush_sync_and_check(FILE *f) {
         int r;
 
@@ -1175,13 +1159,8 @@ int fflush_sync_and_check(FILE *f) {
         if (fsync(fileno(f)) < 0)
                 return -errno;
 
-        r = fsync_directory_of_file(fileno(f));
-        if (r < 0)
-                return r;
-
         return 0;
 }
-#endif /* NM_IGNORED */
 
 /* This is much like mkostemp() but is subject to umask(). */
 int mkostemp_safe(char *pattern) {
@@ -1218,7 +1197,8 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
         if (!filename_is_valid(fn))
                 return -EINVAL;
 
-        extra = strempty(extra);
+        if (extra == NULL)
+                extra = "";
 
         t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
         if (!t)
@@ -1252,7 +1232,8 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
         if (!filename_is_valid(fn))
                 return -EINVAL;
 
-        extra = strempty(extra);
+        if (!extra)
+                extra = "";
 
         t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
         if (!t)
@@ -1292,7 +1273,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
                         return r;
         }
 
-        extra = strempty(extra);
+        if (!extra)
+                extra = "";
 
         t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
         if (!t)
@@ -1444,7 +1426,8 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         if (fd < 0)
                 return -errno;
 
-        *ret_path = TAKE_PTR(tmp);
+        *ret_path = tmp;
+        tmp = NULL;
 
         return fd;
 }
@@ -1484,7 +1467,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
                 if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
                         return -errno;
         } else {
-                char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+                char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
 
                 xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
 
@@ -1530,7 +1513,8 @@ int read_nul_string(FILE *f, char **ret) {
                         return -ENOMEM;
         }
 
-        *ret = TAKE_PTR(x);
+        *ret = x;
+        x = NULL;
 
         return 0;
 }
@@ -1554,7 +1538,9 @@ int mkdtemp_malloc(const char *template, char **ret) {
         return 0;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
+static inline void funlockfilep(FILE **f) {
+        funlockfile(*f);
+}
 
 int read_line(FILE *f, size_t limit, char **ret) {
         _cleanup_free_ char *buffer = NULL;
@@ -1581,7 +1567,7 @@ int read_line(FILE *f, size_t limit, char **ret) {
         }
 
         {
-                _unused_ _cleanup_(funlockfilep) FILE *flocked = f;
+                _cleanup_(funlockfilep) FILE *flocked = f;
                 flockfile(f);
 
                 for (;;) {
diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h
index da5d5c66..eba05be2 100644
--- a/src/systemd/src/basic/fileio.h
+++ b/src/systemd/src/basic/fileio.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -30,17 +29,11 @@
 #include "time-util.h"
 
 typedef enum {
-        WRITE_STRING_FILE_CREATE            = 1<<0,
-        WRITE_STRING_FILE_ATOMIC            = 1<<1,
-        WRITE_STRING_FILE_AVOID_NEWLINE     = 1<<2,
+        WRITE_STRING_FILE_CREATE = 1<<0,
+        WRITE_STRING_FILE_ATOMIC = 1<<1,
+        WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2,
         WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3,
-        WRITE_STRING_FILE_SYNC              = 1<<4,
-        WRITE_STRING_FILE_DISABLE_BUFFER    = 1<<5,
-
-        /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
-           more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
-           and friends. */
-
+        WRITE_STRING_FILE_SYNC = 1<<4,
 } WriteStringFileFlags;
 
 int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts);
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
index ffd9996a..ff4ad5ab 100644
--- a/src/systemd/src/basic/fs-util.c
+++ b/src/systemd/src/basic/fs-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -41,7 +40,6 @@
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "process-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -109,6 +107,7 @@ int rmdir_parents(const char *path, const char *stop) {
         return 0;
 }
 
+
 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
         struct stat buf;
         int ret;
@@ -230,6 +229,49 @@ int readlink_and_make_absolute(const char *p, char **r) {
         return 0;
 }
 
+int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
+        char *t, *s;
+        int r;
+
+        assert(p);
+        assert(ret);
+
+        r = readlink_and_make_absolute(p, &t);
+        if (r < 0)
+                return r;
+
+        r = chase_symlinks(t, root, 0, &s);
+        if (r < 0)
+                /* If we can't follow up, then let's return the original string, slightly cleaned up. */
+                *ret = path_kill_slashes(t);
+        else {
+                *ret = s;
+                free(t);
+        }
+
+        return 0;
+}
+
+int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
+        _cleanup_free_ char *target = NULL, *t = NULL;
+        const char *full;
+        int r;
+
+        full = prefix_roota(root, path);
+        r = readlink_malloc(full, &target);
+        if (r < 0)
+                return r;
+
+        t = file_in_same_dir(path, target);
+        if (!t)
+                return -ENOMEM;
+
+        *ret = t;
+        t = NULL;
+
+        return 0;
+}
+
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         assert(path);
 
@@ -280,60 +322,43 @@ int fd_warn_permissions(const char *path, int fd) {
 }
 
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
-        char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-        _cleanup_close_ int fd = -1;
-        int r, ret = 0;
+        _cleanup_close_ int fd;
+        int r;
 
         assert(path);
 
-        /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
-         * itself which is updated, not its target
-         *
-         * Returns the first error we encounter, but tries to apply as much as possible. */
-
         if (parents)
-                (void) mkdir_parents(path, 0755);
-
-        /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
-         * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
-         * won't trigger any driver magic or so. */
-        fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
-        if (fd < 0) {
-                if (errno != ENOENT)
-                        return -errno;
+                mkdir_parents(path, 0755);
+
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
+                  IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
+        if (fd < 0)
+                return -errno;
 
-                /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
-                 * here, and nothing else */
-                fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
-                if (fd < 0)
+        if (mode != MODE_INVALID) {
+                r = fchmod(fd, mode);
+                if (r < 0)
                         return -errno;
         }
 
-        /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
-         * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
-         * something fchown(), fchmod(), futimensat() don't allow. */
-        xsprintf(fdpath, "/proc/self/fd/%i", fd);
-
-        if (mode != MODE_INVALID)
-                if (chmod(fdpath, mode) < 0)
-                        ret = -errno;
-
-        if (uid_is_valid(uid) || gid_is_valid(gid))
-                if (chown(fdpath, uid, gid) < 0 && ret >= 0)
-                        ret = -errno;
+        if (uid != UID_INVALID || gid != GID_INVALID) {
+                r = fchown(fd, uid, gid);
+                if (r < 0)
+                        return -errno;
+        }
 
         if (stamp != USEC_INFINITY) {
                 struct timespec ts[2];
 
                 timespec_store(&ts[0], stamp);
                 ts[1] = ts[0];
-                r = utimensat(AT_FDCWD, fdpath, ts, 0);
+                r = futimens(fd, ts);
         } else
-                r = utimensat(AT_FDCWD, fdpath, NULL, 0);
-        if (r < 0 && ret >= 0)
+                r = futimens(fd, NULL);
+        if (r < 0)
                 return -errno;
 
-        return ret;
+        return 0;
 }
 
 int touch(const char *path) {
@@ -465,8 +490,10 @@ int get_files_in_directory(const char *path, char ***list) {
                         n++;
         }
 
-        if (list)
-                *list = TAKE_PTR(l);
+        if (list) {
+                *list = l;
+                l = NULL; /* avoid freeing */
+        }
 
         return n;
 }
@@ -489,7 +516,7 @@ static int getenv_tmp_dir(const char **ret_path) {
                         r = -ENOTDIR;
                         goto next;
                 }
-                if (!path_is_normalized(e)) {
+                if (!path_is_safe(e)) {
                         r = -EPERM;
                         goto next;
                 }
@@ -559,19 +586,8 @@ int tmp_dir(const char **ret) {
         return tmp_dir_internal("/tmp", ret);
 }
 
-int unlink_or_warn(const char *filename) {
-        if (unlink(filename) < 0 && errno != ENOENT)
-                /* If the file doesn't exist and the fs simply was read-only (in which
-                 * case unlink() returns EROFS even if the file doesn't exist), don't
-                 * complain */
-                if (errno != EROFS || access(filename, F_OK) >= 0)
-                        return log_error_errno(errno, "Failed to remove \"%s\": %m", filename);
-
-        return 0;
-}
-
 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
-        char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+        char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         int r;
 
         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
@@ -584,39 +600,16 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
         return r;
 }
 
-static bool noop_root(const char *root) {
-        return isempty(root) || path_equal(root, "/");
-}
-
-static bool safe_transition(const struct stat *a, const struct stat *b) {
-        /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
-         * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
-         * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
-
-        if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
-                return true;
-
-        return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
-}
-
 int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
         _cleanup_close_ int fd = -1;
         unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
-        struct stat previous_stat;
         bool exists = true;
         char *todo;
         int r;
 
         assert(path);
 
-        /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
-        if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
-                return -EINVAL;
-
-        if (isempty(path))
-                return -EINVAL;
-
         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
          * symlinks relative to a root directory, instead of the root of the host.
          *
@@ -635,35 +628,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
          * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
          * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
          * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
-         * specified path. */
-
-        /* A root directory of "/" or "" is identical to none */
-        if (noop_root(original_root))
-                original_root = NULL;
-
-        if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN)) == CHASE_OPEN) {
-                /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
-                 * and doesn't care about any of the other special features we provide either. */
-                r = open(path, O_PATH|O_CLOEXEC);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
+         * specified path.
+         *
+         * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the
+         * passed root. */
 
         if (original_root) {
                 r = path_make_absolute_cwd(original_root, &root);
                 if (r < 0)
                         return r;
 
-                if (flags & CHASE_PREFIX_ROOT) {
-
-                        /* We don't support relative paths in combination with a root directory */
-                        if (!path_is_absolute(path))
-                                return -EINVAL;
-
+                if (flags & CHASE_PREFIX_ROOT)
                         path = prefix_roota(root, path);
-                }
         }
 
         r = path_make_absolute_cwd(path, &buffer);
@@ -674,11 +650,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         if (fd < 0)
                 return -errno;
 
-        if (flags & CHASE_SAFE) {
-                if (fstat(fd, &previous_stat) < 0)
-                        return -errno;
-        }
-
         todo = buffer;
         for (;;) {
                 _cleanup_free_ char *first = NULL;
@@ -697,20 +668,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 todo += m;
 
-                /* Empty? Then we reached the end. */
-                if (isempty(first))
-                        break;
-
                 /* Just a single slash? Then we reached the end. */
-                if (path_equal(first, "/")) {
-                        /* Preserve the trailing slash */
-
-                        if (flags & CHASE_TRAIL_SLASH)
-                                if (!strextend(&done, "/", NULL))
-                                        return -ENOMEM;
-
+                if (isempty(first) || path_equal(first, "/"))
                         break;
-                }
 
                 /* Just a dot? Then let's eat this up. */
                 if (path_equal(first, "/."))
@@ -719,7 +679,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 /* Two dots? Then chop off the last bit of what we already found out. */
                 if (path_equal(first, "/..")) {
                         _cleanup_free_ char *parent = NULL;
-                        _cleanup_close_ int fd_parent = -1;
+                        int fd_parent = -1;
 
                         /* If we already are at the top, then going up will not change anything. This is in-line with
                          * how the kernel handles this. */
@@ -742,18 +702,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         if (fd_parent < 0)
                                 return -errno;
 
-                        if (flags & CHASE_SAFE) {
-                                if (fstat(fd_parent, &st) < 0)
-                                        return -errno;
-
-                                if (!safe_transition(&previous_stat, &st))
-                                        return -EPERM;
-
-                                previous_stat = st;
-                        }
-
                         safe_close(fd);
-                        fd = TAKE_FD(fd_parent);
+                        fd = fd_parent;
 
                         continue;
                 }
@@ -764,16 +714,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                         if (errno == ENOENT &&
                             (flags & CHASE_NONEXISTENT) &&
-                            (isempty(todo) || path_is_normalized(todo))) {
+                            (isempty(todo) || path_is_safe(todo))) {
 
                                 /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
                                  * what we got so far. But don't allow this if the remaining path contains "../ or "./"
                                  * or something else weird. */
 
-                                /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
-                                if (streq_ptr(done, "/"))
-                                        *done = '\0';
-
                                 if (!strextend(&done, first, todo, NULL))
                                         return -ENOMEM;
 
@@ -786,14 +732,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 if (fstat(child, &st) < 0)
                         return -errno;
-                if ((flags & CHASE_SAFE) &&
-                    !safe_transition(&previous_stat, &st))
-                        return -EPERM;
-
-                previous_stat = st;
-
                 if ((flags & CHASE_NO_AUTOFS) &&
-                    fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
+                    fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0)
                         return -EREMOTE;
 
                 if (S_ISLNK(st.st_mode)) {
@@ -822,16 +762,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                 if (fd < 0)
                                         return -errno;
 
-                                if (flags & CHASE_SAFE) {
-                                        if (fstat(fd, &st) < 0)
-                                                return -errno;
-
-                                        if (!safe_transition(&previous_stat, &st))
-                                                return -EPERM;
-
-                                        previous_stat = st;
-                                }
-
                                 free(done);
 
                                 /* Note that we do not revalidate the root, we take it as is. */
@@ -843,11 +773,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                                 return -ENOMEM;
                                 }
 
-                                /* Prefix what's left to do with what we just read, and start the loop again, but
-                                 * remain in the current directory. */
-                                joined = strjoin(destination, todo);
-                        } else
-                                joined = strjoin("/", destination, todo);
+                        }
+
+                        /* Prefix what's left to do with what we just read, and start the loop again,
+                         * but remain in the current directory. */
+
+                        joined = strjoin("/", destination, todo);
                         if (!joined)
                                 return -ENOMEM;
 
@@ -858,20 +789,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 }
 
                 /* If this is not a symlink, then let's just add the name we read to what we already verified. */
-                if (!done)
-                        done = TAKE_PTR(first);
-                else {
-                        /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
-                        if (streq(done, "/"))
-                                *done = '\0';
-
+                if (!done) {
+                        done = first;
+                        first = NULL;
+                } else {
                         if (!strextend(&done, first, NULL))
                                 return -ENOMEM;
                 }
 
                 /* And iterate again, but go one directory further down. */
                 safe_close(fd);
-                fd = TAKE_FD(child);
+                fd = child;
+                child = -1;
         }
 
         if (!done) {
@@ -881,221 +810,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -ENOMEM;
         }
 
-        if (ret)
-                *ret = TAKE_PTR(done);
-
-        if (flags & CHASE_OPEN) {
-                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
-                 * opening /proc/self/fd/xyz. */
-
-                assert(fd >= 0);
-                return TAKE_FD(fd);
+        if (ret) {
+                *ret = done;
+                done = NULL;
         }
 
         return exists;
 }
-
-int chase_symlinks_and_open(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                int open_flags,
-                char **ret_path) {
-
-        _cleanup_close_ int path_fd = -1;
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        if (chase_flags & CHASE_NONEXISTENT)
-                return -EINVAL;
-
-        if (noop_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
-                /* Shortcut this call if none of the special features of this call are requested */
-                r = open(path, open_flags);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
-
-        path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
-        if (path_fd < 0)
-                return path_fd;
-
-        r = fd_reopen(path_fd, open_flags);
-        if (r < 0)
-                return r;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(p);
-
-        return r;
-}
-
-int chase_symlinks_and_opendir(
-                const char *path,
-                const char *root,
-                unsigned chase_flags,
-                char **ret_path,
-                DIR **ret_dir) {
-
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-        _cleanup_close_ int path_fd = -1;
-        _cleanup_free_ char *p = NULL;
-        DIR *d;
-
-        if (!ret_dir)
-                return -EINVAL;
-        if (chase_flags & CHASE_NONEXISTENT)
-                return -EINVAL;
-
-        if (noop_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
-                /* Shortcut this call if none of the special features of this call are requested */
-                d = opendir(path);
-                if (!d)
-                        return -errno;
-
-                *ret_dir = d;
-                return 0;
-        }
-
-        path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
-        if (path_fd < 0)
-                return path_fd;
-
-        xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
-        d = opendir(procfs_path);
-        if (!d)
-                return -errno;
-
-        if (ret_path)
-                *ret_path = TAKE_PTR(p);
-
-        *ret_dir = d;
-        return 0;
-}
-
-int access_fd(int fd, int mode) {
-        char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-        int r;
-
-        /* Like access() but operates on an already open fd */
-
-        xsprintf(p, "/proc/self/fd/%i", fd);
-        r = access(p, mode);
-        if (r < 0)
-                return -errno;
-
-        return r;
-}
-
-int unlinkat_deallocate(int fd, const char *name, int flags) {
-        _cleanup_close_ int truncate_fd = -1;
-        struct stat st;
-        off_t l, bs;
-
-        /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
-         * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
-         * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
-         * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
-         * returned to the free pool.
-         *
-         * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
-         * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
-         * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
-         * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
-         * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
-         * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
-         *
-         * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
-         * primary job – to delete the file – is accomplished. */
-
-        if ((flags & AT_REMOVEDIR) == 0) {
-                truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
-                if (truncate_fd < 0) {
-
-                        /* If this failed because the file doesn't exist propagate the error right-away. Also,
-                         * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
-                         * returned when this is a directory but we are not supposed to delete those, hence propagate
-                         * the error right-away too. */
-                        if (IN_SET(errno, ENOENT, EISDIR))
-                                return -errno;
-
-                        if (errno != ELOOP) /* don't complain if this is a symlink */
-                                log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
-                }
-        }
-
-        if (unlinkat(fd, name, flags) < 0)
-                return -errno;
-
-        if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
-                return 0;
-
-        if (fstat(truncate_fd, &st) < 0) {
-                log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name);
-                return 0;
-        }
-
-        if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
-                return 0;
-
-        /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
-         * punch-hole/truncate this to release the disk space. */
-
-        bs = MAX(st.st_blksize, 512);
-        l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
-
-        if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
-                return 0; /* Successfully punched a hole! 😊 */
-
-        /* Fall back to truncation */
-        if (ftruncate(truncate_fd, 0) < 0) {
-                log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
-                return 0;
-        }
-
-        return 0;
-}
-
-int fsync_directory_of_file(int fd) {
-        _cleanup_free_ char *path = NULL, *dn = NULL;
-        _cleanup_close_ int dfd = -1;
-        int r;
-
-        r = fd_verify_regular(fd);
-        if (r < 0)
-                return r;
-
-        r = fd_get_path(fd, &path);
-        if (r < 0) {
-                log_debug("Failed to query /proc/self/fd/%d%s: %m",
-                          fd,
-                          r == -EOPNOTSUPP ? ", ignoring" : "");
-
-                if (r == -EOPNOTSUPP)
-                        /* If /proc is not available, we're most likely running in some
-                         * chroot environment, and syncing the directory is not very
-                         * important in that case. Let's just silently do nothing. */
-                        return 0;
-
-                return r;
-        }
-
-        if (!path_is_absolute(path))
-                return -EINVAL;
-
-        dn = dirname_malloc(path);
-        if (!dn)
-                return -ENOMEM;
-
-        dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
-        if (dfd < 0)
-                return -errno;
-
-        if (fsync(dfd) < 0)
-                return -errno;
-
-        return 0;
-}
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
index 2225b7e7..d3342d5c 100644
--- a/src/systemd/src/basic/fs-util.h
+++ b/src/systemd/src/basic/fs-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -20,7 +19,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <dirent.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdbool.h>
@@ -30,7 +28,6 @@
 #include <unistd.h>
 
 #include "time-util.h"
-#include "util.h"
 
 int unlink_noerrno(const char *path);
 
@@ -42,6 +39,8 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
 int readlink_malloc(const char *p, char **r);
 int readlink_value(const char *p, char **ret);
 int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, const char *root, char **r);
+int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 
@@ -65,8 +64,6 @@ int get_files_in_directory(const char *path, char ***list);
 int tmp_dir(const char **ret);
 int var_tmp_dir(const char **ret);
 
-int unlink_or_warn(const char *filename);
-
 #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
 
 #define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
@@ -82,35 +79,22 @@ union inotify_event_buffer {
 int inotify_add_watch_fd(int fd, int what, uint32_t mask);
 
 enum {
-        CHASE_PREFIX_ROOT = 1U << 0,   /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
-        CHASE_NONEXISTENT = 1U << 1,   /* If set, it's OK if the path doesn't actually exist. */
-        CHASE_NO_AUTOFS   = 1U << 2,   /* If set, return -EREMOTE if autofs mount point found */
-        CHASE_SAFE        = 1U << 3,   /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
-        CHASE_OPEN        = 1U << 4,   /* If set, return an O_PATH object to the final component */
-        CHASE_TRAIL_SLASH = 1U << 5,   /* If set, any trailing slash will be preserved */
+        CHASE_PREFIX_ROOT = 1,   /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+        CHASE_NONEXISTENT = 2,   /* If set, it's OK if the path doesn't actually exist. */
+        CHASE_NO_AUTOFS = 4,     /* If set, return -EREMOTE if autofs mount point found */
 };
 
 int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
 
-int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
-int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
-
 /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
 static inline void rmdir_and_free(char *p) {
-        PROTECT_ERRNO;
         (void) rmdir(p);
         free(p);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
 
 static inline void unlink_and_free(char *p) {
-        (void) unlink_noerrno(p);
+        (void) unlink(p);
         free(p);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
-
-int access_fd(int fd, int mode);
-
-int unlinkat_deallocate(int fd, const char *name, int flags);
-
-int fsync_directory_of_file(int fd);
diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c
index 234565ed..4a29f43c 100644
--- a/src/systemd/src/basic/hash-funcs.c
+++ b/src/systemd/src/basic/hash-funcs.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -21,16 +20,12 @@
 
 #include "nm-sd-adapt.h"
 
-#include <string.h>
-
 #include "hash-funcs.h"
-#include "path-util.h"
 
 void string_hash_func(const void *p, struct siphash *state) {
         siphash24_compress(p, strlen(p) + 1, state);
 }
 
-#if 0 /* NM_IGNORED */
 int string_compare_func(const void *a, const void *b) {
         return strcmp(a, b);
 }
@@ -40,56 +35,6 @@ const struct hash_ops string_hash_ops = {
         .compare = string_compare_func
 };
 
-
-void path_hash_func(const void *p, struct siphash *state) {
-        const char *q = p;
-        size_t n;
-
-        assert(q);
-        assert(state);
-
-        /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also
-         * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does
-         * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those
-         * which begin in a slash or not) will hash differently though. */
-
-        n = strspn(q, "/");
-        if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */
-                siphash24_compress(q, 1, state);
-                q += n;
-        }
-
-        for (;;) {
-                /* Determine length of next component */
-                n = strcspn(q, "/");
-                if (n == 0) /* Reached the end? */
-                        break;
-
-                /* Add this component to the hash and skip over it */
-                siphash24_compress(q, n, state);
-                q += n;
-
-                /* How many slashes follow this component? */
-                n = strspn(q, "/");
-                if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */
-                        break;
-
-                /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */
-                siphash24_compress(q, 1, state);
-                q += n;
-        }
-}
-
-int path_compare_func(const void *a, const void *b) {
-        return path_compare(a, b);
-}
-
-const struct hash_ops path_hash_ops = {
-        .hash = path_hash_func,
-        .compare = path_compare_func
-};
-#endif /* NM_IGNORED */
-
 void trivial_hash_func(const void *p, struct siphash *state) {
         siphash24_compress(&p, sizeof(p), state);
 }
@@ -119,7 +64,6 @@ const struct hash_ops uint64_hash_ops = {
         .compare = uint64_compare_func
 };
 
-#if 0 /* NM_IGNORED */
 #if SIZEOF_DEV_T != 8
 void devt_hash_func(const void *p, struct siphash *state) {
         siphash24_compress(p, sizeof(dev_t), state);
@@ -137,4 +81,3 @@ const struct hash_ops devt_hash_ops = {
         .compare = devt_compare_func
 };
 #endif
-#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h
index 945b4c25..299189d1 100644
--- a/src/systemd/src/basic/hash-funcs.h
+++ b/src/systemd/src/basic/hash-funcs.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -36,28 +35,29 @@ void string_hash_func(const void *p, struct siphash *state);
 int string_compare_func(const void *a, const void *b) _pure_;
 extern const struct hash_ops string_hash_ops;
 
-void path_hash_func(const void *p, struct siphash *state);
-int path_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops path_hash_ops;
-
-/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
- * or suchlike. */
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
 void trivial_hash_func(const void *p, struct siphash *state);
 int trivial_compare_func(const void *a, const void *b) _const_;
 extern const struct hash_ops trivial_hash_ops;
 
-/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
- * values indirectly, since they don't fit in a pointer. */
+/* 32bit values we can always just embed in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
 void uint64_hash_func(const void *p, struct siphash *state);
 int uint64_compare_func(const void *a, const void *b) _pure_;
 extern const struct hash_ops uint64_hash_ops;
 
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
- * 64bit archs. Yuck! */
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
 #if SIZEOF_DEV_T != 8
 void devt_hash_func(const void *p, struct siphash *state) _pure_;
 int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops;
+extern const struct hash_ops devt_hash_ops = {
+        .hash = devt_hash_func,
+        .compare = devt_compare_func
+};
 #else
 #define devt_hash_func uint64_hash_func
 #define devt_compare_func uint64_compare_func
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
index a76ec8e8..c95d4991 100644
--- a/src/systemd/src/basic/hashmap.c
+++ b/src/systemd/src/basic/hashmap.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -28,14 +27,12 @@
 
 #include "alloc-util.h"
 #include "hashmap.h"
-#include "fileio.h"
 #include "macro.h"
 #include "mempool.h"
 #include "process-util.h"
 #include "random-util.h"
 #include "set.h"
 #include "siphash24.h"
-#include "string-util.h"
 #include "strv.h"
 #include "util.h"
 
@@ -231,8 +228,6 @@ struct HashmapBase {
         unsigned n_direct_entries:3; /* Number of entries in direct storage.
                                       * Only valid if !has_indirect. */
         bool from_pool:1;            /* whether was allocated from mempool */
-        bool dirty:1;                /* whether dirtied since last iterated_cache_get() */
-        bool cached:1;               /* whether this hashmap is being cached */
         HASHMAP_DEBUG_FIELDS         /* optional hashmap_debug_info */
 };
 
@@ -252,17 +247,6 @@ struct Set {
         struct HashmapBase b;
 };
 
-typedef struct CacheMem {
-        const void **ptr;
-        size_t n_populated, n_allocated;
-        bool active:1;
-} CacheMem;
-
-struct IteratedCache {
-        HashmapBase *hashmap;
-        CacheMem keys, values;
-};
-
 DEFINE_MEMPOOL(hashmap_pool,         Hashmap,        8);
 DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
 /* No need for a separate Set pool */
@@ -296,28 +280,6 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
         },
 };
 
-#ifdef VALGRIND
-__attribute__((destructor)) static void cleanup_pools(void) {
-        _cleanup_free_ char *t = NULL;
-        int r;
-
-        /* Be nice to valgrind */
-
-        /* The pool is only allocated by the main thread, but the memory can
-         * be passed to other threads. Let's clean up if we are the main thread
-         * and no other threads are live. */
-        if (!is_main_thread())
-                return;
-
-        r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t);
-        if (r < 0 || !streq(t, "1"))
-                return;
-
-        mempool_drop(&hashmap_pool);
-        mempool_drop(&ordered_hashmap_pool);
-}
-#endif
-
 static unsigned n_buckets(HashmapBase *h) {
         return h->has_indirect ? h->indirect.n_buckets
                                : hashmap_type_info[h->type].n_direct_buckets;
@@ -366,11 +328,6 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
 }
 #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
 
-static inline void base_set_dirty(HashmapBase *h) {
-        h->dirty = true;
-}
-#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h))
-
 static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
         static uint8_t current[HASH_KEY_SIZE];
         static bool current_initialized = false;
@@ -588,7 +545,6 @@ static void base_remove_entry(HashmapBase *h, unsigned idx) {
 
         bucket_mark_free(h, prev);
         n_entries_dec(h);
-        base_set_dirty(h);
 }
 #define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
 
@@ -758,25 +714,6 @@ bool set_iterate(Set *s, Iterator *i, void **value) {
              (idx != IDX_NIL); \
              (idx) = hashmap_iterate_entry((h), &(i)))
 
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) {
-        IteratedCache *cache;
-
-        assert(h);
-        assert(!h->cached);
-
-        if (h->cached)
-                return NULL;
-
-        cache = new0(IteratedCache, 1);
-        if (!cache)
-                return NULL;
-
-        cache->hashmap = h;
-        h->cached = true;
-
-        return cache;
-}
-
 static void reset_direct_storage(HashmapBase *h) {
         const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
         void *p;
@@ -937,8 +874,6 @@ void internal_hashmap_clear(HashmapBase *h) {
                 OrderedHashmap *lh = (OrderedHashmap*) h;
                 lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
         }
-
-        base_set_dirty(h);
 }
 
 void internal_hashmap_clear_free(HashmapBase *h) {
@@ -1083,8 +1018,6 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
         h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
 #endif
 
-        base_set_dirty(h);
-
         return 1;
 }
 #define hashmap_put_boldly(h, idx, swap, may_resize) \
@@ -1321,8 +1254,6 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) {
 #endif
                 e->b.key = key;
                 e->value = value;
-                hashmap_set_dirty(h);
-
                 return 0;
         }
 
@@ -1345,8 +1276,6 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
 
         e = plain_bucket_at(h, idx);
         e->value = value;
-        hashmap_set_dirty(h);
-
         return 0;
 }
 
@@ -1899,95 +1828,3 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
                         return r;
         }
 }
-
-/* expand the cachemem if needed, return true if newly (re)activated. */
-static int cachemem_maintain(CacheMem *mem, unsigned size) {
-        assert(mem);
-
-        if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) {
-                if (size > 0)
-                        return -ENOMEM;
-        }
-
-        if (!mem->active) {
-                mem->active = true;
-                return true;
-        }
-
-        return false;
-}
-
-int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) {
-        bool sync_keys = false, sync_values = false;
-        unsigned size;
-        int r;
-
-        assert(cache);
-        assert(cache->hashmap);
-
-        size = n_entries(cache->hashmap);
-
-        if (res_keys) {
-                r = cachemem_maintain(&cache->keys, size);
-                if (r < 0)
-                        return r;
-
-                sync_keys = r;
-        } else
-                cache->keys.active = false;
-
-        if (res_values) {
-                r = cachemem_maintain(&cache->values, size);
-                if (r < 0)
-                        return r;
-
-                sync_values = r;
-        } else
-                cache->values.active = false;
-
-        if (cache->hashmap->dirty) {
-                if (cache->keys.active)
-                        sync_keys = true;
-                if (cache->values.active)
-                        sync_values = true;
-
-                cache->hashmap->dirty = false;
-        }
-
-        if (sync_keys || sync_values) {
-                unsigned i, idx;
-                Iterator iter;
-
-                i = 0;
-                HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) {
-                        struct hashmap_base_entry *e;
-
-                        e = bucket_at(cache->hashmap, idx);
-
-                        if (sync_keys)
-                                cache->keys.ptr[i] = e->key;
-                        if (sync_values)
-                                cache->values.ptr[i] = entry_value(cache->hashmap, e);
-                        i++;
-                }
-        }
-
-        if (res_keys)
-                *res_keys = cache->keys.ptr;
-        if (res_values)
-                *res_values = cache->values.ptr;
-        if (res_n_entries)
-                *res_n_entries = size;
-
-        return 0;
-}
-
-IteratedCache *iterated_cache_free(IteratedCache *cache) {
-        if (cache) {
-                free(cache->keys.ptr);
-                free(cache->values.ptr);
-                free(cache);
-        }
-
-        return NULL;
-}
diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h
index b6749103..c1089652 100644
--- a/src/systemd/src/basic/hashmap.h
+++ b/src/systemd/src/basic/hashmap.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -53,8 +52,6 @@ typedef struct Hashmap Hashmap;               /* Maps keys to values */
 typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
 typedef struct Set Set;                       /* Stores just keys */
 
-typedef struct IteratedCache IteratedCache;   /* Caches the iterated order of one of the above */
-
 /* Ideally the Iterator would be an opaque struct, but it is instantiated
  * by hashmap users, so the definition has to be here. Do not use its fields
  * directly. */
@@ -128,9 +125,6 @@ static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h)
         return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
 }
 
-IteratedCache *iterated_cache_free(IteratedCache *cache);
-int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
-
 HashmapBase *internal_hashmap_copy(HashmapBase *h);
 static inline Hashmap *hashmap_copy(Hashmap *h) {
         return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
@@ -144,14 +138,6 @@ int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct h
 #define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
 #define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
 
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h);
-static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
-        return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
-}
-static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
-        return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
-}
-
 int hashmap_put(Hashmap *h, const void *key, void *value);
 static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
         return hashmap_put(PLAIN_HASHMAP(h), key, value);
@@ -342,29 +328,6 @@ static inline void *ordered_hashmap_first(OrderedHashmap *h) {
         return internal_hashmap_first(HASHMAP_BASE(h));
 }
 
-#define hashmap_clear_with_destructor(_s, _f)                   \
-        ({                                                      \
-                void *_item;                                    \
-                while ((_item = hashmap_steal_first(_s)))       \
-                        _f(_item);                              \
-        })
-#define hashmap_free_with_destructor(_s, _f)                    \
-        ({                                                      \
-                hashmap_clear_with_destructor(_s, _f);          \
-                hashmap_free(_s);                               \
-        })
-#define ordered_hashmap_clear_with_destructor(_s, _f)                   \
-        ({                                                              \
-                void *_item;                                            \
-                while ((_item = ordered_hashmap_steal_first(_s)))       \
-                        _f(_item);                                      \
-        })
-#define ordered_hashmap_free_with_destructor(_s, _f)                    \
-        ({                                                              \
-                ordered_hashmap_clear_with_destructor(_s, _f);          \
-                ordered_hashmap_free(_s);                               \
-        })
-
 /* no hashmap_next */
 void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
 
@@ -407,7 +370,3 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
 #define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
 #define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
 #define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
-
-#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep)
diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c
index e8e82b5f..e0ae83c1 100644
--- a/src/systemd/src/basic/hexdecoct.c
+++ b/src/systemd/src/basic/hexdecoct.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -99,10 +98,7 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
 
         assert(mem);
         assert(len);
-        assert(p || l == 0);
-
-        if (l == (size_t) -1)
-                l = strlen(p);
+        assert(p);
 
         if (l % 2 != 0)
                 return -EINVAL;
@@ -134,7 +130,6 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 /* https://tools.ietf.org/html/rfc4648#section-6
  * Notice that base32hex differs from base32 in the alphabet it uses.
  * The distinction is that the base32hex representation preserves the
@@ -167,8 +162,6 @@ char *base32hexmem(const void *p, size_t l, bool padding) {
         const uint8_t *x;
         size_t len;
 
-        assert(p || l == 0);
-
         if (padding)
                 /* five input bytes makes eight output bytes, padding is added so we must round up */
                 len = 8 * (l + 4) / 5;
@@ -278,12 +271,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
         size_t len;
         unsigned pad = 0;
 
-        assert(p || l == 0);
-        assert(mem);
-        assert(_len);
-
-        if (l == (size_t) -1)
-                l = strlen(p);
+        assert(p);
 
         /* padding ensures any base32hex input has input divisible by 8 */
         if (padding && l % 8 != 0)
@@ -533,9 +521,6 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
         char *r, *z;
         const uint8_t *x;
 
-        assert(p || l == 0);
-        assert(out);
-
         /* three input bytes makes four output bytes, padding is added so we must round up */
         z = r = malloc(4 * (l + 2) / 3 + 1);
         if (!r)
@@ -571,11 +556,10 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
         return z - r;
 }
 
-static int base64_append_width(
-                char **prefix, int plen,
-                const char *sep, int indent,
-                const void *p, size_t l,
-                int width) {
+static int base64_append_width(char **prefix, int plen,
+                               const char *sep, int indent,
+                               const void *p, size_t l,
+                               int width) {
 
         _cleanup_free_ char *x = NULL;
         char *t, *s;
@@ -586,7 +570,7 @@ static int base64_append_width(
         if (len <= 0)
                 return len;
 
-        lines = DIV_ROUND_UP(len, width);
+        lines = (len + width - 1) / width;
 
         slen = strlen_ptr(sep);
         t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
@@ -614,148 +598,118 @@ static int base64_append_width(
         return 0;
 }
 
-int base64_append(
-                char **prefix, int plen,
-                const void *p, size_t l,
-                int indent, int width) {
-
+int base64_append(char **prefix, int plen,
+                  const void *p, size_t l,
+                  int indent, int width) {
         if (plen > width / 2 || plen + indent > width)
                 /* leave indent on the left, keep last column free */
                 return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
         else
                 /* leave plen on the left, keep last column free */
                 return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
-}
-
-static int unbase64_next(const char **p, size_t *l) {
-        int ret;
-
-        assert(p);
-        assert(l);
-
-        /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
-         * greedily skip all preceeding and all following whitespace. */
-
-        for (;;) {
-                if (*l == 0)
-                        return -EPIPE;
-
-                if (!strchr(WHITESPACE, **p))
-                        break;
-
-                /* Skip leading whitespace */
-                (*p)++, (*l)--;
-        }
-
-        if (**p == '=')
-                ret = INT_MAX; /* return padding as INT_MAX */
-        else {
-                ret = unbase64char(**p);
-                if (ret < 0)
-                        return ret;
-        }
+};
 
-        for (;;) {
-                (*p)++, (*l)--;
 
-                if (*l == 0)
-                        break;
-                if (!strchr(WHITESPACE, **p))
-                        break;
-
-                /* Skip following whitespace */
-        }
-
-        return ret;
-}
-
-int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
-        _cleanup_free_ uint8_t *buf = NULL;
-        const char *x;
+int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
+        _cleanup_free_ uint8_t *r = NULL;
+        int a, b, c, d;
         uint8_t *z;
+        const char *x;
         size_t len;
 
-        assert(p || l == 0);
-        assert(ret);
-        assert(ret_size);
+        assert(p);
 
-        if (l == (size_t) -1)
-                l = strlen(p);
+        /* padding ensures any base63 input has input divisible by 4 */
+        if (l % 4 != 0)
+                return -EINVAL;
 
-        /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
-           bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
-        len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
+        /* strip the padding */
+        if (l > 0 && p[l - 1] == '=')
+                l--;
+        if (l > 0 && p[l - 1] == '=')
+                l--;
 
-        buf = malloc(len + 1);
-        if (!buf)
-                return -ENOMEM;
+        /* a group of four input bytes needs three output bytes, in case of
+           padding we need to add two or three extra bytes */
+        len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
 
-        for (x = p, z = buf;;) {
-                int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+        z = r = malloc(len + 1);
+        if (!r)
+                return -ENOMEM;
 
-                a = unbase64_next(&x, &l);
-                if (a == -EPIPE) /* End of string */
-                        break;
+        for (x = p; x < p + (l / 4) * 4; x += 4) {
+                /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+                a = unbase64char(x[0]);
                 if (a < 0)
-                        return a;
-                if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
                         return -EINVAL;
 
-                b = unbase64_next(&x, &l);
+                b = unbase64char(x[1]);
                 if (b < 0)
-                        return b;
-                if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
                         return -EINVAL;
 
-                c = unbase64_next(&x, &l);
+                c = unbase64char(x[2]);
                 if (c < 0)
-                        return c;
+                        return -EINVAL;
 
-                d = unbase64_next(&x, &l);
+                d = unbase64char(x[3]);
                 if (d < 0)
-                        return d;
+                        return -EINVAL;
 
-                if (c == INT_MAX) { /* Padding at the third character */
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+                *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
+        }
 
-                        if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
-                                return -EINVAL;
+        switch (l % 4) {
+        case 3:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
 
-                        /* b == 00YY0000 */
-                        if (b & 15)
-                                return -EINVAL;
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                c = unbase64char(x[2]);
+                if (c < 0)
+                        return -EINVAL;
 
-                        *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
-                        break;
-                }
+                /* c == 00ZZZZ00 */
+                if (c & 3)
+                        return -EINVAL;
 
-                if (d == INT_MAX) {
-                        /* c == 00ZZZZ00 */
-                        if (c & 3)
-                                return -EINVAL;
+                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                break;
+        case 2:
+                a = unbase64char(x[0]);
+                if (a < 0)
+                        return -EINVAL;
 
-                        *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
-                        *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
-                        break;
-                }
+                b = unbase64char(x[1]);
+                if (b < 0)
+                        return -EINVAL;
 
-                *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
-                *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
-                *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
+                /* b == 00YY0000 */
+                if (b & 15)
+                        return -EINVAL;
+
+                *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+
+                break;
+        case 0:
+
+                break;
+        default:
+                return -EINVAL;
         }
 
         *z = 0;
 
-        if (ret_size)
-                *ret_size = (size_t) (z - buf);
-
-        *ret = buf;
-        buf = NULL;
+        *mem = r;
+        r = NULL;
+        *_len = len;
 
         return 0;
 }
@@ -764,10 +718,7 @@ void hexdump(FILE *f, const void *p, size_t s) {
         const uint8_t *b = p;
         unsigned n = 0;
 
-        assert(b || s == 0);
-
-        if (!f)
-                f = stdout;
+        assert(s == 0 || b);
 
         while (s > 0) {
                 size_t i;
@@ -805,4 +756,3 @@ void hexdump(FILE *f, const void *p, size_t s) {
                 s -= 16;
         }
 }
-#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h
index 08d0a522..1ba2f69e 100644
--- a/src/systemd/src/basic/hexdecoct.h
+++ b/src/systemd/src/basic/hexdecoct.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c
index 66b80727..be6e9e58 100644
--- a/src/systemd/src/basic/hostname-util.c
+++ b/src/systemd/src/basic/hostname-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -27,15 +26,12 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 
-#include "alloc-util.h"
-#include "def.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "hostname-util.h"
 #include "macro.h"
 #include "string-util.h"
 
-#if 0 /* NM_IGNORED */
 bool hostname_is_set(void) {
         struct utsname u;
 
@@ -51,6 +47,7 @@ bool hostname_is_set(void) {
         return true;
 }
 
+#if 0 /* NM_IGNORED */
 char* gethostname_malloc(void) {
         struct utsname u;
 
@@ -226,88 +223,36 @@ int sethostname_idempotent(const char *s) {
         return 1;
 }
 
-int shorten_overlong(const char *s, char **ret) {
-        char *h, *p;
-
-        /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
-         * whatever comes earlier. */
-
-        assert(s);
-
-        h = strdup(s);
-        if (!h)
-                return -ENOMEM;
-
-        if (hostname_is_valid(h, false)) {
-                *ret = h;
-                return 0;
-        }
-
-        p = strchr(h, '.');
-        if (p)
-                *p = 0;
-
-        strshorten(h, HOST_NAME_MAX);
-
-        if (!hostname_is_valid(h, false)) {
-                free(h);
-                return -EDOM;
-        }
-
-        *ret = h;
-        return 1;
-}
-
-int read_etc_hostname_stream(FILE *f, char **ret) {
-        int r;
-
-        assert(f);
-        assert(ret);
-
-        for (;;) {
-                _cleanup_free_ char *line = NULL;
-                char *p;
-
-                r = read_line(f, LONG_LINE_MAX, &line);
-                if (r < 0)
-                        return r;
-                if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
-                        return -ENOENT;
-
-                p = strstrip(line);
-
-                /* File may have empty lines or comments, ignore them */
-                if (!IN_SET(*p, '\0', '#')) {
-                        char *copy;
-
-                        hostname_cleanup(p); /* normalize the hostname */
-
-                        if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */
-                                return -EBADMSG;
-
-                        copy = strdup(p);
-                        if (!copy)
-                                return -ENOMEM;
-
-                        *ret = copy;
-                        return 0;
-                }
-        }
-}
-
-int read_etc_hostname(const char *path, char **ret) {
+int read_hostname_config(const char *path, char **hostname) {
         _cleanup_fclose_ FILE *f = NULL;
+        char l[LINE_MAX];
+        char *name = NULL;
 
-        assert(ret);
-
-        if (!path)
-                path = "/etc/hostname";
+        assert(path);
+        assert(hostname);
 
         f = fopen(path, "re");
         if (!f)
                 return -errno;
 
-        return read_etc_hostname_stream(f, ret);
+        /* may have comments, ignore them */
+        FOREACH_LINE(l, f, return -errno) {
+                truncate_nl(l);
+                if (!IN_SET(l[0], '\0', '#')) {
+                        /* found line with value */
+                        name = hostname_cleanup(l);
+                        name = strdup(name);
+                        if (!name)
+                                return -ENOMEM;
+                        break;
+                }
+        }
 
+        if (!name)
+                /* no non-empty line found */
+                return -ENOENT;
+
+        *hostname = name;
+        return 0;
 }
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h
index edae52e3..7af4e6c7 100644
--- a/src/systemd/src/basic/hostname-util.h
+++ b/src/systemd/src/basic/hostname-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -21,7 +20,6 @@
 ***/
 
 #include <stdbool.h>
-#include <stdio.h>
 
 #include "macro.h"
 
@@ -40,7 +38,4 @@ bool is_gateway_hostname(const char *hostname);
 
 int sethostname_idempotent(const char *s);
 
-int shorten_overlong(const char *s, char **ret);
-
-int read_etc_hostname_stream(FILE *f, char **ret);
-int read_etc_hostname(const char *path, char **ret);
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c
index b2b68089..2a02d90b 100644
--- a/src/systemd/src/basic/in-addr-util.c
+++ b/src/systemd/src/basic/in-addr-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -333,7 +332,6 @@ int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union
         return -EINVAL;
 }
 
-#if 0 /* NM_IGNORED */
 int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
         const char *suffix;
         int r, ifi = 0;
@@ -374,7 +372,6 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
 
         return r;
 }
-#endif /* NM_IGNORED */
 
 unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
         assert(addr);
diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h
index acaae6d2..59f8eb7e 100644
--- a/src/systemd/src/basic/in-addr-util.h
+++ b/src/systemd/src/basic/in-addr-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c
index 0f10ad7f..61b667f0 100644
--- a/src/systemd/src/basic/io-util.c
+++ b/src/systemd/src/basic/io-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -35,7 +34,6 @@ int flush_fd(int fd) {
                 .fd = fd,
                 .events = POLLIN,
         };
-        int count = 0;
 
         /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
          * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
@@ -55,7 +53,7 @@ int flush_fd(int fd) {
                         return -errno;
 
                 } else if (r == 0)
-                        return count;
+                        return 0;
 
                 l = read(fd, buf, sizeof(buf));
                 if (l < 0) {
@@ -64,13 +62,11 @@ int flush_fd(int fd) {
                                 continue;
 
                         if (errno == EAGAIN)
-                                return count;
+                                return 0;
 
                         return -errno;
                 } else if (l == 0)
-                        return count;
-
-                count += (int) l;
+                        return 0;
         }
 }
 
@@ -139,7 +135,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
         assert(fd >= 0);
         assert(buf);
 
-        if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
+        if (nbytes > (size_t) SSIZE_MAX)
                 return -EINVAL;
 
         do {
@@ -205,6 +201,7 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
         r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
         if (r < 0)
                 return -errno;
+
         if (r == 0)
                 return 0;
 
diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h
index d81610ad..d9b69add 100644
--- a/src/systemd/src/basic/io-util.h
+++ b/src/systemd/src/basic/io-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h
index 7006c3e2..c3771a17 100644
--- a/src/systemd/src/basic/list.h
+++ b/src/systemd/src/basic/list.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -183,6 +182,3 @@
         for ((i) = (p)->name##_next ? (p)->name##_next : (head);        \
              (i) != (p);                                                \
              (i) = (i)->name##_next ? (i)->name##_next : (head))
-
-#define LIST_IS_EMPTY(head)                                             \
-        (!(head))
diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
index b0e963b0..67fda3f9 100644
--- a/src/systemd/src/basic/log.h
+++ b/src/systemd/src/basic/log.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -20,16 +19,18 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <errno.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
 #include <syslog.h>
 
-#include "macro.h"
+#include "sd-id128.h"
 
-/* Some structures we reference but don't want to pull in headers for */
-struct iovec;
-struct signalfd_siginfo;
+#include "macro.h"
+#include "process-util.h"
 
 typedef enum LogRealm {
         LOG_REALM_SYSTEMD,
@@ -50,6 +51,7 @@ typedef enum LogTarget{
         LOG_TARGET_SYSLOG,
         LOG_TARGET_SYSLOG_OR_KMSG,
         LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+        LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
         LOG_TARGET_NULL,
         _LOG_TARGET_MAX,
         _LOG_TARGET_INVALID = -1
@@ -96,6 +98,11 @@ int log_open(void);
 void log_close(void);
 void log_forget_fds(void);
 
+void log_close_syslog(void);
+void log_close_journal(void);
+void log_close_kmsg(void);
+void log_close_console(void);
+
 void log_parse_environment_realm(LogRealm realm);
 #define log_parse_environment() \
         log_parse_environment_realm(LOG_REALM)
@@ -149,6 +156,19 @@ int log_object_internal(
                 const char *extra,
                 const char *format, ...) _printf_(10,11);
 
+int log_object_internalv(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *object_field,
+                const char *object,
+                const char *extra_field,
+                const char *extra,
+                const char *format,
+                va_list ap) _printf_(10,0);
+
 int log_struct_internal(
                 int level,
                 int error,
@@ -165,8 +185,8 @@ int log_oom_internal(
 
 int log_format_iovec(
                 struct iovec *iovec,
-                size_t iovec_len,
-                size_t *n,
+                unsigned iovec_len,
+                unsigned *n,
                 bool newline_separator,
                 int error,
                 const char *format,
@@ -178,7 +198,7 @@ int log_struct_iovec_internal(
                 const char *file,
                 int line,
                 const char *func,
-                const struct iovec *input_iovec,
+                const struct iovec input_iovec[],
                 size_t n_input_iovec);
 
 /* This modifies the buffer passed! */
@@ -191,7 +211,7 @@ int log_dump_internal(
                 char *buffer);
 
 /* Logging for various assertions */
-_noreturn_ void log_assert_failed_realm(
+noreturn void log_assert_failed_realm(
                 LogRealm realm,
                 const char *text,
                 const char *file,
@@ -200,7 +220,7 @@ _noreturn_ void log_assert_failed_realm(
 #define log_assert_failed(text, ...) \
         log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__)
 
-_noreturn_ void log_assert_failed_unreachable_realm(
+noreturn void log_assert_failed_unreachable_realm(
                 LogRealm realm,
                 const char *text,
                 const char *file,
@@ -225,9 +245,9 @@ void log_assert_failed_return_realm(
 /* Logging with level */
 #define log_full_errno_realm(realm, level, error, ...)                  \
         ({                                                              \
-                int _level = (level), _e = (error), _realm = (realm);   \
-                (log_get_max_level_realm(_realm) >= LOG_PRI(_level))   \
-                        ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
+                int _level = (level), _e = (error);                     \
+                (log_get_max_level_realm((realm)) >= LOG_PRI(_level))   \
+                        ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \
                                              __FILE__, __LINE__, __func__, __VA_ARGS__) \
                         : -abs(_e);                                     \
         })
@@ -237,15 +257,13 @@ void log_assert_failed_return_realm(
 
 #define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
 
-int log_emergency_level(void);
-
 /* Normal logging */
 #define log_debug(...)     log_full(LOG_DEBUG,   __VA_ARGS__)
 #define log_info(...)      log_full(LOG_INFO,    __VA_ARGS__)
 #define log_notice(...)    log_full(LOG_NOTICE,  __VA_ARGS__)
 #define log_warning(...)   log_full(LOG_WARNING, __VA_ARGS__)
 #define log_error(...)     log_full(LOG_ERR,     __VA_ARGS__)
-#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
+#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
 
 /* Logging triggered by an errno-like error */
 #define log_debug_errno(error, ...)     log_full_errno(LOG_DEBUG,   error, __VA_ARGS__)
@@ -253,7 +271,7 @@ int log_emergency_level(void);
 #define log_notice_errno(error, ...)    log_full_errno(LOG_NOTICE,  error, __VA_ARGS__)
 #define log_warning_errno(error, ...)   log_full_errno(LOG_WARNING, error, __VA_ARGS__)
 #define log_error_errno(error, ...)     log_full_errno(LOG_ERR,     error, __VA_ARGS__)
-#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
 
 #ifdef LOG_TRACE
 #  define log_trace(...) log_debug(__VA_ARGS__)
@@ -289,20 +307,10 @@ LogTarget log_target_from_string(const char *s) _pure_;
 
 void log_received_signal(int level, const struct signalfd_siginfo *si);
 
-/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
 void log_set_upgrade_syslog_to_journal(bool b);
-
-/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
 void log_set_always_reopen_console(bool b);
-
-/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
- * desired as we want to reuse our logging streams. It is useful however  */
 void log_set_open_when_needed(bool b);
 
-/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
- * stderr, the console or kmsg */
-void log_set_prohibit_ipc(bool b);
-
 int log_syntax_internal(
                 const char *unit,
                 int level,
@@ -314,16 +322,6 @@ int log_syntax_internal(
                 const char *func,
                 const char *format, ...) _printf_(9, 10);
 
-int log_syntax_invalid_utf8_internal(
-                const char *unit,
-                int level,
-                const char *config_file,
-                unsigned config_line,
-                const char *file,
-                int line,
-                const char *func,
-                const char *rvalue);
-
 #define log_syntax(unit, level, config_file, config_line, error, ...)   \
         ({                                                              \
                 int _level = (level), _e = (error);                     \
@@ -335,9 +333,10 @@ int log_syntax_invalid_utf8_internal(
 #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
         ({                                                              \
                 int _level = (level);                                   \
-                (log_get_max_level() >= LOG_PRI(_level))                \
-                        ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
-                        : -EINVAL;                                      \
+                if (log_get_max_level() >= LOG_PRI(_level)) {           \
+                        _cleanup_free_ char *_p = NULL;                 \
+                        _p = utf8_escape_invalid(rvalue);               \
+                        log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
+                                            "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
+                }                                                       \
         })
-
-#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
index 025a5db7..afcde459 100644
--- a/src/systemd/src/basic/macro.h
+++ b/src/systemd/src/basic/macro.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -48,31 +47,6 @@
 #define _weakref_(x) __attribute__((weakref(#x)))
 #define _alignas_(x) __attribute__((aligned(__alignof(x))))
 #define _cleanup_(x) __attribute__((cleanup(x)))
-#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((fallthrough))
-#else
-#define _fallthrough_
-#endif
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef _noreturn_
-#if __STDC_VERSION__ >= 201112L
-#define _noreturn_ _Noreturn
-#else
-#define _noreturn_ __attribute__((noreturn))
-#endif
-#endif
-
-#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
-#  if defined(__has_feature)
-#    if __has_feature(memory_sanitizer)
-#      define HAS_FEATURE_MEMORY_SANITIZER 1
-#    endif
-#  endif
-#  if !defined(HAS_FEATURE_MEMORY_SANITIZER)
-#    define HAS_FEATURE_MEMORY_SANITIZER 0
-#  endif
-#endif
 
 #if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
 /* Temporarily disable some warnings */
@@ -168,25 +142,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
         return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
 }
 
-#ifndef __COVERITY__
-#  define VOID_0 ((void)0)
-#else
-#  define VOID_0 ((void*)0)
-#endif
-
 #define ELEMENTSOF(x)                                                    \
         __extension__ (__builtin_choose_expr(                            \
                 !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
                 sizeof(x)/sizeof((x)[0]),                                \
-                VOID_0))
-
-/*
- * STRLEN - return the length of a string literal, minus the trailing NUL byte.
- *          Contrary to strlen(), this is a constant expression.
- * @x: a string literal.
- */
-#define STRLEN(x) (sizeof(""x"") - 1)
-
+                (void)0))
 /*
  * container_of - cast a member of a structure out to the containing structure
  * @ptr: the pointer to the member.
@@ -216,7 +176,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
                 __builtin_constant_p(_B) &&                             \
                 __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
                 ((_A) > (_B)) ? (_A) : (_B),                            \
-                VOID_0))
+                (void)0))
 
 /* takes two types and returns the size of the larger one */
 #define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
@@ -443,10 +403,21 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
 #endif
 #endif
 
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef noreturn
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
+
 #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
         static inline void func##p(type *p) {                   \
                 if (*p)                                         \
                         func(*p);                               \
-        }
+        }                                                       \
+        struct __useless_struct_to_allow_trailing_semicolon__
 
 #include "log.h"
diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c
index 43ae59c9..c1635964 100644
--- a/src/systemd/src/basic/mempool.c
+++ b/src/systemd/src/basic/mempool.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h
index c9235c83..0618b8dd 100644
--- a/src/systemd/src/basic/mempool.h
+++ b/src/systemd/src/basic/mempool.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
index 44fae932..6d978e93 100644
--- a/src/systemd/src/basic/parse-util.c
+++ b/src/systemd/src/basic/parse-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -28,9 +27,7 @@
 #include <string.h>
 
 #include "alloc-util.h"
-#include "errno-list.h"
 #include "extract-word.h"
-#include "locale-util.h"
 #include "macro.h"
 #include "parse-util.h"
 #include "process-util.h"
@@ -71,6 +68,7 @@ int parse_pid(const char *s, pid_t* ret_pid) {
         *ret_pid = pid;
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int parse_mode(const char *s, mode_t *ret) {
         char *x;
@@ -87,7 +85,7 @@ int parse_mode(const char *s, mode_t *ret) {
         l = strtol(s, &x, 8);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if (l < 0 || l  > 07777)
                 return -ERANGE;
@@ -237,6 +235,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 int parse_range(const char *t, unsigned *lower, unsigned *upper) {
         _cleanup_free_ char *word = NULL;
         unsigned l, u;
@@ -273,64 +272,7 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper) {
         *upper = u;
         return 0;
 }
-
-int parse_errno(const char *t) {
-        int r, e;
-
-        assert(t);
-
-        r = errno_from_name(t);
-        if (r > 0)
-                return r;
-
-        r = safe_atoi(t, &e);
-        if (r < 0)
-                return r;
-
-        /* 0 is also allowed here */
-        if (!errno_is_valid(e) && e != 0)
-                return -ERANGE;
-
-        return e;
-}
-
-int parse_syscall_and_errno(const char *in, char **name, int *error) {
-        _cleanup_free_ char *n = NULL;
-        char *p;
-        int e = -1;
-
-        assert(in);
-        assert(name);
-        assert(error);
-
-        /*
-         * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255".
-         * If errno is omitted, then error is set to -1.
-         * Empty syscall name is not allowed.
-         * Here, we do not check that the syscall name is valid or not.
-         */
-
-        p = strchr(in, ':');
-        if (p) {
-                e = parse_errno(p + 1);
-                if (e < 0)
-                        return e;
-
-                n = strndup(in, p - in);
-        } else
-                n = strdup(in);
-
-        if (!n)
-                return -ENOMEM;
-
-        if (isempty(n))
-                return -EINVAL;
-
-        *error = e;
-        *name = TAKE_PTR(n);
-
-        return 0;
-}
+#endif /* NM_IGNORED */
 
 char *format_bytes(char *buf, size_t l, uint64_t t) {
         unsigned i;
@@ -372,15 +314,13 @@ finish:
         return buf;
 
 }
-#endif /* NM_IGNORED */
 
-int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
+int safe_atou(const char *s, unsigned *ret_u) {
         char *x = NULL;
         unsigned long l;
 
         assert(s);
         assert(ret_u);
-        assert(base <= 16);
 
         /* strtoul() is happy to parse negative values, and silently
          * converts them to unsigned values without generating an
@@ -393,10 +333,10 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
         s += strspn(s, WHITESPACE);
 
         errno = 0;
-        l = strtoul(s, &x, base);
+        l = strtoul(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if (s[0] == '-')
                 return -ERANGE;
@@ -418,7 +358,7 @@ int safe_atoi(const char *s, int *ret_i) {
         l = strtol(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if ((long) (int) l != l)
                 return -ERANGE;
@@ -440,7 +380,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
         l = strtoull(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if (*s == '-')
                 return -ERANGE;
@@ -460,7 +400,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
         l = strtoll(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
 
         *ret_lli = l;
@@ -480,7 +420,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
         l = strtoul(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if (s[0] == '-')
                 return -ERANGE;
@@ -491,21 +431,20 @@ int safe_atou8(const char *s, uint8_t *ret) {
         return 0;
 }
 
-int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
+int safe_atou16(const char *s, uint16_t *ret) {
         char *x = NULL;
         unsigned long l;
 
         assert(s);
         assert(ret);
-        assert(base <= 16);
 
         s += strspn(s, WHITESPACE);
 
         errno = 0;
-        l = strtoul(s, &x, base);
+        l = strtoul(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if (s[0] == '-')
                 return -ERANGE;
@@ -527,7 +466,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
         l = strtol(s, &x, 0);
         if (errno > 0)
                 return -errno;
-        if (!x || x == s || *x != 0)
+        if (!x || x == s || *x)
                 return -EINVAL;
         if ((long) (int16_t) l != l)
                 return -ERANGE;
@@ -536,11 +475,10 @@ int safe_atoi16(const char *s, int16_t *ret) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 int safe_atod(const char *s, double *ret_d) {
-        _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
         char *x = NULL;
         double d = 0;
+        locale_t loc;
 
         assert(s);
         assert(ret_d);
@@ -551,11 +489,16 @@ int safe_atod(const char *s, double *ret_d) {
 
         errno = 0;
         d = strtod_l(s, &x, loc);
-        if (errno > 0)
+        if (errno > 0) {
+                freelocale(loc);
                 return -errno;
-        if (!x || x == s || *x != 0)
+        }
+        if (!x || x == s || *x) {
+                freelocale(loc);
                 return -EINVAL;
+        }
 
+        freelocale(loc);
         *ret_d = (double) d;
         return 0;
 }
@@ -598,20 +541,19 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
 
 int parse_percent_unbounded(const char *p) {
         const char *pc, *n;
-        int r, v;
+        unsigned v;
+        int r;
 
         pc = endswith(p, "%");
         if (!pc)
                 return -EINVAL;
 
         n = strndupa(p, pc - p);
-        r = safe_atoi(n, &v);
+        r = safe_atou(n, &v);
         if (r < 0)
                 return r;
-        if (v < 0)
-                return -ERANGE;
 
-        return v;
+        return (int) v;
 }
 
 int parse_percent(const char *p) {
@@ -624,6 +566,7 @@ int parse_percent(const char *p) {
         return v;
 }
 
+#if 0 /* NM_IGNORED */
 int parse_nice(const char *p, int *ret) {
         int n, r;
 
diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h
index 1605cc4f..dc09782c 100644
--- a/src/systemd/src/basic/parse-util.h
+++ b/src/systemd/src/basic/parse-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -38,34 +37,18 @@ int parse_ifindex(const char *s, int *ret);
 
 int parse_size(const char *t, uint64_t base, uint64_t *size);
 int parse_range(const char *t, unsigned *lower, unsigned *upper);
-int parse_errno(const char *t);
-int parse_syscall_and_errno(const char *in, char **name, int *error);
 
 #define FORMAT_BYTES_MAX 8
 char *format_bytes(char *buf, size_t l, uint64_t t);
 
-int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
-
-static inline int safe_atou(const char *s, unsigned *ret_u) {
-        return safe_atou_full(s, 0, ret_u);
-}
-
+int safe_atou(const char *s, unsigned *ret_u);
 int safe_atoi(const char *s, int *ret_i);
 int safe_atollu(const char *s, unsigned long long *ret_u);
 int safe_atolli(const char *s, long long int *ret_i);
 
 int safe_atou8(const char *s, uint8_t *ret);
 
-int safe_atou16_full(const char *s, unsigned base, uint16_t *ret);
-
-static inline int safe_atou16(const char *s, uint16_t *ret) {
-        return safe_atou16_full(s, 0, ret);
-}
-
-static inline int safe_atoux16(const char *s, uint16_t *ret) {
-        return safe_atou16_full(s, 16, ret);
-}
-
+int safe_atou16(const char *s, uint16_t *ret);
 int safe_atoi16(const char *s, int16_t *ret);
 
 static inline int safe_atou32(const char *s, uint32_t *ret_u) {
diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c
index 23827c19..1bdcc653 100644
--- a/src/systemd/src/basic/path-util.c
+++ b/src/systemd/src/basic/path-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -84,36 +83,14 @@ char *path_make_absolute(const char *p, const char *prefix) {
         /* Makes every item in the list an absolute path by prepending
          * the prefix, if specified and necessary */
 
-        if (path_is_absolute(p) || isempty(prefix))
+        if (path_is_absolute(p) || !prefix)
                 return strdup(p);
 
-        if (endswith(prefix, "/"))
-                return strjoin(prefix, p);
-        else
-                return strjoin(prefix, "/", p);
-}
-
-int safe_getcwd(char **ret) {
-        char *cwd;
-
-        cwd = get_current_dir_name();
-        if (!cwd)
-                return negative_errno();
-
-        /* Let's make sure the directory is really absolute, to protect us from the logic behind
-         * CVE-2018-1000001 */
-        if (cwd[0] != '/') {
-                free(cwd);
-                return -ENOMEDIUM;
-        }
-
-        *ret = cwd;
-        return 0;
+        return strjoin(prefix, "/", p);
 }
 
 int path_make_absolute_cwd(const char *p, char **ret) {
         char *c;
-        int r;
 
         assert(p);
         assert(ret);
@@ -126,14 +103,11 @@ int path_make_absolute_cwd(const char *p, char **ret) {
         else {
                 _cleanup_free_ char *cwd = NULL;
 
-                r = safe_getcwd(&cwd);
-                if (r < 0)
-                        return r;
+                cwd = get_current_dir_name();
+                if (!cwd)
+                        return negative_errno();
 
-                if (endswith(cwd, "/"))
-                        c = strjoin(cwd, p);
-                else
-                        c = strjoin(cwd, "/", p);
+                c = strjoin(cwd, "/", p);
         }
         if (!c)
                 return -ENOMEM;
@@ -251,8 +225,8 @@ int path_strv_make_absolute_cwd(char **l) {
                 if (r < 0)
                         return r;
 
-                path_kill_slashes(t);
-                free_and_replace(*s, t);
+                free(*s);
+                *s = t;
         }
 
         return 0;
@@ -293,7 +267,8 @@ char **path_strv_resolve(char **l, const char *root) {
                 r = chase_symlinks(t, root, 0, &u);
                 if (r == -ENOENT) {
                         if (root) {
-                                u = TAKE_PTR(orig);
+                                u = orig;
+                                orig = NULL;
                                 free(t);
                         } else
                                 u = t;
@@ -567,7 +542,7 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
 
         assert(timestamp);
 
-        if (!paths)
+        if (paths == NULL)
                 return false;
 
         STRV_FOREACH(i, paths) {
@@ -731,37 +706,6 @@ char* dirname_malloc(const char *path) {
 
         return dir2;
 }
-
-const char *last_path_component(const char *path) {
-        /* Finds the last component of the path, preserving the
-         * optional trailing slash that signifies a directory.
-         *    a/b/c → c
-         *    a/b/c/ → c/
-         *    / → /
-         *    // → /
-         *    /foo/a → a
-         *    /foo/a/ → a/
-         * This is different than basename, which returns "" when
-         * a trailing slash is present.
-         */
-
-        unsigned l, k;
-
-        l = k = strlen(path);
-        if (l == 0) /* special case — an empty string */
-                return path;
-
-        while (k > 0 && path[k-1] == '/')
-                k--;
-
-        if (k == 0) /* the root directory */
-                return path + l - 1;
-
-        while (k > 0 && path[k-1] != '/')
-                k--;
-
-        return path + k;
-}
 #endif /* NM_IGNORED */
 
 bool filename_is_valid(const char *p) {
@@ -784,7 +728,7 @@ bool filename_is_valid(const char *p) {
 }
 
 #if 0 /* NM_IGNORED */
-bool path_is_normalized(const char *p) {
+bool path_is_safe(const char *p) {
 
         if (isempty(p))
                 return false;
@@ -798,6 +742,7 @@ bool path_is_normalized(const char *p) {
         if (strlen(p)+1 > PATH_MAX)
                 return false;
 
+        /* The following two checks are not really dangerous, but hey, they still are confusing */
         if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
                 return false;
 
@@ -913,9 +858,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
                         * for Gentoo which does a merge without making /lib a symlink.
                         */
                        "lib/systemd/libsystemd-shared-*.so\0"
-                       "lib64/systemd/libsystemd-shared-*.so\0"
-                       "usr/lib/systemd/libsystemd-shared-*.so\0"
-                       "usr/lib64/systemd/libsystemd-shared-*.so\0") {
+                       "usr/lib/systemd/libsystemd-shared-*.so\0") {
 
                 _cleanup_strv_free_ char **names = NULL;
                 _cleanup_free_ char *path = NULL;
diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h
index 8848abe9..399ed5f9 100644
--- a/src/systemd/src/basic/path-util.h
+++ b/src/systemd/src/basic/path-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -29,14 +28,8 @@
 #include "time-util.h"
 
 #if 0 /* NM_IGNORED */
-#if HAVE_SPLIT_BIN
-#  define PATH_SBIN_BIN(x) x "sbin:" x "bin"
-#else
-#  define PATH_SBIN_BIN(x) x "bin"
-#endif
-
-#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
-#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/")
+#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
+#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
 
 #if HAVE_SPLIT_USR
 #  define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
@@ -49,7 +42,6 @@ bool is_path(const char *p) _pure_;
 int path_split_and_make_absolute(const char *p, char ***ret);
 bool path_is_absolute(const char *p) _pure_;
 char* path_make_absolute(const char *p, const char *prefix);
-int safe_getcwd(char **ret);
 int path_make_absolute_cwd(const char *p, char **ret);
 int path_make_relative(const char *from_dir, const char *to_path, char **_r);
 char* path_kill_slashes(char *path);
@@ -139,10 +131,9 @@ char *prefix_root(const char *root, const char *path);
 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
 
 char* dirname_malloc(const char *path);
-const char *last_path_component(const char *path);
 
 bool filename_is_valid(const char *p) _pure_;
-bool path_is_normalized(const char *p) _pure_;
+bool path_is_safe(const char *p) _pure_;
 
 char *file_in_same_dir(const char *path, const char *filename);
 
diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c
index 1e81bc71..64ad638e 100644
--- a/src/systemd/src/basic/prioq.c
+++ b/src/systemd/src/basic/prioq.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -175,7 +174,7 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) {
                 struct prioq_item *j;
 
                 n = MAX((q->n_items+1) * 2, 16u);
-                j = reallocarray(q->items, n, sizeof(struct prioq_item));
+                j = realloc(q->items, sizeof(struct prioq_item) * n);
                 if (!j)
                         return -ENOMEM;
 
diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h
index a222955d..113c73d0 100644
--- a/src/systemd/src/basic/prioq.h
+++ b/src/systemd/src/basic/prioq.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c
index 4128ac54..272030d1 100644
--- a/src/systemd/src/basic/process-util.c
+++ b/src/systemd/src/basic/process-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -28,7 +27,6 @@
 #include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
@@ -60,7 +58,6 @@
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
-#include "terminal-util.h"
 #include "user-util.h"
 #include "util.h"
 
@@ -137,8 +134,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 return -errno;
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         if (max_length == 1) {
 
                 /* If there's only room for one byte, return the empty string */
@@ -302,17 +297,10 @@ int rename_process(const char name[]) {
         if (isempty(name))
                 return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
 
-        if (!is_main_thread())
-                return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
-                                * cache things without locking, and we make assumptions that PR_SET_NAME sets the
-                                * process name that isn't correct on any other threads */
-
         l = strlen(name);
 
-        /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
-         * can use PR_SET_NAME, which sets the thread name for the calling thread. */
-        if (prctl(PR_SET_NAME, name) < 0)
-                log_debug_errno(errno, "PR_SET_NAME failed: %m");
+        /* First step, change the comm field. */
+        (void) prctl(PR_SET_NAME, name);
         if (l > 15) /* Linux process names can be 15 chars at max */
                 truncated = true;
 
@@ -403,61 +391,35 @@ use_saved_argv:
 }
 
 int is_kernel_thread(pid_t pid) {
-        _cleanup_free_ char *line = NULL;
-        unsigned long long flags;
-        size_t l, i;
         const char *p;
-        char *q;
-        int r;
+        size_t count;
+        char c;
+        bool eof;
+        FILE *f;
 
         if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
                 return 0;
-        if (!pid_is_valid(pid))
-                return -EINVAL;
 
-        p = procfs_file_alloca(pid, "stat");
-        r = read_one_line_file(p, &line);
-        if (r == -ENOENT)
-                return -ESRCH;
-        if (r < 0)
-                return r;
+        assert(pid > 1);
 
-        /* Skip past the comm field */
-        q = strrchr(line, ')');
-        if (!q)
-                return -EINVAL;
-        q++;
-
-        /* Skip 6 fields to reach the flags field */
-        for (i = 0; i < 6; i++) {
-                l = strspn(q, WHITESPACE);
-                if (l < 1)
-                        return -EINVAL;
-                q += l;
-
-                l = strcspn(q, WHITESPACE);
-                if (l < 1)
-                        return -EINVAL;
-                q += l;
+        p = procfs_file_alloca(pid, "cmdline");
+        f = fopen(p, "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return -ESRCH;
+                return -errno;
         }
 
-        /* Skip preceeding whitespace */
-        l = strspn(q, WHITESPACE);
-        if (l < 1)
-                return -EINVAL;
-        q += l;
+        count = fread(&c, 1, 1, f);
+        eof = feof(f);
+        fclose(f);
 
-        /* Truncate the rest */
-        l = strcspn(q, WHITESPACE);
-        if (l < 1)
-                return -EINVAL;
-        q[l] = 0;
+        /* Kernel threads have an empty cmdline */
 
-        r = safe_atollu(q, &flags);
-        if (r < 0)
-                return r;
+        if (count <= 0)
+                return eof ? 1 : -errno;
 
-        return !!(flags & PF_KTHREAD);
+        return 0;
 }
 
 int get_process_capeff(pid_t pid, char **capeff) {
@@ -529,8 +491,6 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
                 return -errno;
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         FOREACH_LINE(line, f, return -errno) {
                 char *l;
 
@@ -609,8 +569,6 @@ int get_process_environ(pid_t pid, char **env) {
                 return -errno;
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         while ((c = fgetc(f)) != EOF) {
                 if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
                         return -ENOMEM;
@@ -628,7 +586,8 @@ int get_process_environ(pid_t pid, char **env) {
         } else
                 outcome[sz] = '\0';
 
-        *env = TAKE_PTR(outcome);
+        *env = outcome;
+        outcome = NULL;
 
         return 0;
 }
@@ -715,104 +674,32 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
  * A warning is emitted if the process terminates abnormally,
  * and also if it returns non-zero unless check_exit_code is true.
  */
-int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
-        _cleanup_free_ char *buffer = NULL;
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
+        int r;
         siginfo_t status;
-        int r, prio;
 
+        assert(name);
         assert(pid > 1);
 
-        if (!name) {
-                r = get_process_comm(pid, &buffer);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
-                else
-                        name = buffer;
-        }
-
-        prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
-
         r = wait_for_terminate(pid, &status);
         if (r < 0)
-                return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
+                return log_warning_errno(r, "Failed to wait for %s: %m", name);
 
         if (status.si_code == CLD_EXITED) {
-                if (status.si_status != EXIT_SUCCESS)
-                        log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
-                                 "%s failed with exit status %i.", strna(name), status.si_status);
+                if (status.si_status != 0)
+                        log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
+                                 "%s failed with error code %i.", name, status.si_status);
                 else
                         log_debug("%s succeeded.", name);
 
                 return status.si_status;
-
         } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
 
-                log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
+                log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
                 return -EPROTO;
         }
 
-        log_full(prio, "%s failed due to unknown reason.", strna(name));
-        return -EPROTO;
-}
-
-/*
- * Return values:
- * < 0 : wait_for_terminate_with_timeout() failed to get the state of the
- *       process, the process timed out, the process was terminated by a
- *       signal, or failed for an unknown reason.
- * >=0 : The process terminated normally with no failures.
- *
- * Success is indicated by a return value of zero, a timeout is indicated
- * by ETIMEDOUT, and all other child failure states are indicated by error
- * is indicated by a non-zero value.
- */
-int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
-        sigset_t mask;
-        int r;
-        usec_t until;
-
-        assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-
-        /* Drop into a sigtimewait-based timeout. Waiting for the
-         * pid to exit. */
-        until = now(CLOCK_MONOTONIC) + timeout;
-        for (;;) {
-                usec_t n;
-                siginfo_t status = {};
-                struct timespec ts;
-
-                n = now(CLOCK_MONOTONIC);
-                if (n >= until)
-                        break;
-
-                r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0;
-                /* Assuming we woke due to the child exiting. */
-                if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
-                        if (status.si_pid == pid) {
-                                /* This is the correct child.*/
-                                if (status.si_code == CLD_EXITED)
-                                        return (status.si_status == 0) ? 0 : -EPROTO;
-                                else
-                                        return -EPROTO;
-                        }
-                }
-                /* Not the child, check for errors and proceed appropriately */
-                if (r < 0) {
-                        switch (r) {
-                        case -EAGAIN:
-                                /* Timed out, child is likely hung. */
-                                return -ETIMEDOUT;
-                        case -EINTR:
-                                /* Received a different signal and should retry */
-                                continue;
-                        default:
-                                /* Return any unexpected errors */
-                                return r;
-                        }
-                }
-        }
-
+        log_warning("%s failed due to unknown reason.", name);
         return -EPROTO;
 }
 
@@ -824,8 +711,6 @@ void sigkill_wait(pid_t pid) {
 }
 
 void sigkill_waitp(pid_t *pid) {
-        PROTECT_ERRNO;
-
         if (!pid)
                 return;
         if (*pid <= 1)
@@ -834,13 +719,6 @@ void sigkill_waitp(pid_t *pid) {
         sigkill_wait(*pid);
 }
 
-void sigterm_wait(pid_t pid) {
-        assert(pid > 1);
-
-        if (kill_and_sigcont(pid, SIGTERM) > 0)
-                (void) wait_for_terminate(pid, NULL);
-}
-
 int kill_and_sigcont(pid_t pid, int sig) {
         int r;
 
@@ -854,33 +732,17 @@ int kill_and_sigcont(pid_t pid, int sig) {
         return r;
 }
 
-int getenv_for_pid(pid_t pid, const char *field, char **ret) {
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
         _cleanup_fclose_ FILE *f = NULL;
         char *value = NULL;
+        int r;
         bool done = false;
-        const char *path;
         size_t l;
+        const char *path;
 
         assert(pid >= 0);
         assert(field);
-        assert(ret);
-
-        if (pid == 0 || pid == getpid_cached()) {
-                const char *e;
-
-                e = getenv(field);
-                if (!e) {
-                        *ret = NULL;
-                        return 0;
-                }
-
-                value = strdup(e);
-                if (!value)
-                        return -ENOMEM;
-
-                *ret = value;
-                return 1;
-        }
+        assert(_value);
 
         path = procfs_file_alloca(pid, "environ");
 
@@ -888,13 +750,11 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         if (!f) {
                 if (errno == ENOENT)
                         return -ESRCH;
-
                 return -errno;
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         l = strlen(field);
+        r = 0;
 
         do {
                 char line[LINE_MAX];
@@ -919,14 +779,14 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
                         if (!value)
                                 return -ENOMEM;
 
-                        *ret = value;
-                        return 1;
+                        r = 1;
+                        break;
                 }
 
         } while (!done);
 
-        *ret = NULL;
-        return 0;
+        *_value = value;
+        return r;
 }
 
 bool pid_is_unwaited(pid_t pid) {
@@ -993,7 +853,7 @@ bool is_main_thread(void) {
 }
 
 #if 0 /* NM_IGNORED */
-_noreturn_ void freeze(void) {
+noreturn void freeze(void) {
 
         log_close();
 
@@ -1002,17 +862,6 @@ _noreturn_ void freeze(void) {
 
         sync();
 
-        /* Let's not freeze right away, but keep reaping zombies. */
-        for (;;) {
-                int r;
-                siginfo_t si = {};
-
-                r = waitid(P_ALL, 0, &si, WEXITED);
-                if (r < 0 && errno != EINTR)
-                        break;
-        }
-
-        /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
         for (;;)
                 pause();
 }
@@ -1161,7 +1010,7 @@ int ioprio_parse_priority(const char *s, int *ret) {
 
 static pid_t cached_pid = CACHED_PID_UNSET;
 
-void reset_cached_pid(void) {
+static void reset_cached_pid(void) {
         /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
         cached_pid = CACHED_PID_UNSET;
 }
@@ -1173,7 +1022,6 @@ extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void
 extern void* __dso_handle __attribute__ ((__weak__));
 
 pid_t getpid_cached(void) {
-        static bool installed = false;
         pid_t current_value;
 
         /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
@@ -1192,20 +1040,12 @@ pid_t getpid_cached(void) {
         case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
                 pid_t new_pid;
 
-                new_pid = raw_getpid();
-
-                if (!installed) {
-                        /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
-                         * only half-documented (glibc doesn't document it but LSB does — though only superficially)
-                         * we'll check for errors only in the most generic fashion possible. */
-
-                        if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
-                                /* OOM? Let's try again later */
-                                cached_pid = CACHED_PID_UNSET;
-                                return new_pid;
-                        }
+                new_pid = getpid();
 
-                        installed = true;
+                if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
+                        /* OOM? Let's try again later */
+                        cached_pid = CACHED_PID_UNSET;
+                        return new_pid;
                 }
 
                 cached_pid = new_pid;
@@ -1213,7 +1053,7 @@ pid_t getpid_cached(void) {
         }
 
         case CACHED_PID_BUSY: /* Somebody else is currently initializing */
-                return raw_getpid();
+                return getpid();
 
         default: /* Properly initialized */
                 return current_value;
@@ -1221,252 +1061,6 @@ pid_t getpid_cached(void) {
 }
 
 #if 0 /* NM_IGNORED */
-int must_be_root(void) {
-
-        if (geteuid() == 0)
-                return 0;
-
-        log_error("Need to be root.");
-        return -EPERM;
-}
-
-int safe_fork_full(
-                const char *name,
-                const int except_fds[],
-                size_t n_except_fds,
-                ForkFlags flags,
-                pid_t *ret_pid) {
-
-        pid_t original_pid, pid;
-        sigset_t saved_ss, ss;
-        bool block_signals = false;
-        int prio, r;
-
-        /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
-         * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
-
-        prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
-
-        original_pid = getpid_cached();
-
-        if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
-
-                /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
-                 * be sure that SIGTERMs are not lost we might send to the child. */
-
-                if (sigfillset(&ss) < 0)
-                        return log_full_errno(prio, errno, "Failed to reset signal set: %m");
-
-                block_signals = true;
-
-        } else if (flags & FORK_WAIT) {
-
-                /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
-
-                if (sigemptyset(&ss) < 0)
-                        return log_full_errno(prio, errno, "Failed to clear signal set: %m");
-
-                if (sigaddset(&ss, SIGCHLD) < 0)
-                        return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
-
-                block_signals = true;
-        }
-
-        if (block_signals)
-                if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
-                        return log_full_errno(prio, errno, "Failed to set signal mask: %m");
-
-        if (flags & FORK_NEW_MOUNTNS)
-                pid = raw_clone(SIGCHLD|CLONE_NEWNS);
-        else
-                pid = fork();
-        if (pid < 0) {
-                r = -errno;
-
-                if (block_signals) /* undo what we did above */
-                        (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
-
-                return log_full_errno(prio, r, "Failed to fork: %m");
-        }
-        if (pid > 0) {
-                /* We are in the parent process */
-
-                log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
-
-                if (flags & FORK_WAIT) {
-                        r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
-                        if (r < 0)
-                                return r;
-                        if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
-                                return -EPROTO;
-                }
-
-                if (block_signals) /* undo what we did above */
-                        (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
-
-                if (ret_pid)
-                        *ret_pid = pid;
-
-                return 1;
-        }
-
-        /* We are in the child process */
-
-        if (flags & FORK_REOPEN_LOG) {
-                /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
-                log_close();
-                log_set_open_when_needed(true);
-        }
-
-        if (name) {
-                r = rename_process(name);
-                if (r < 0)
-                        log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
-                                       r, "Failed to rename process, ignoring: %m");
-        }
-
-        if (flags & FORK_DEATHSIG)
-                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
-                        log_full_errno(prio, errno, "Failed to set death signal: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-        if (flags & FORK_RESET_SIGNALS) {
-                r = reset_all_signal_handlers();
-                if (r < 0) {
-                        log_full_errno(prio, r, "Failed to reset signal handlers: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                /* This implicitly undoes the signal mask stuff we did before the fork()ing above */
-                r = reset_signal_mask();
-                if (r < 0) {
-                        log_full_errno(prio, r, "Failed to reset signal mask: %m");
-                        _exit(EXIT_FAILURE);
-                }
-        } else if (block_signals) { /* undo what we did above */
-                if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
-                        log_full_errno(prio, errno, "Failed to restore signal mask: %m");
-                        _exit(EXIT_FAILURE);
-                }
-        }
-
-        if (flags & FORK_DEATHSIG) {
-                pid_t ppid;
-                /* Let's see if the parent PID is still the one we started from? If not, then the parent
-                 * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
-
-                ppid = getppid();
-                if (ppid == 0)
-                        /* Parent is in a differn't PID namespace. */;
-                else if (ppid != original_pid) {
-                        log_debug("Parent died early, raising SIGTERM.");
-                        (void) raise(SIGTERM);
-                        _exit(EXIT_FAILURE);
-                }
-        }
-
-        if (flags & FORK_CLOSE_ALL_FDS) {
-                /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
-                log_close();
-
-                r = close_all_fds(except_fds, n_except_fds);
-                if (r < 0) {
-                        log_full_errno(prio, r, "Failed to close all file descriptors: %m");
-                        _exit(EXIT_FAILURE);
-                }
-        }
-
-        /* When we were asked to reopen the logs, do so again now */
-        if (flags & FORK_REOPEN_LOG) {
-                log_open();
-                log_set_open_when_needed(false);
-        }
-
-        if (flags & FORK_NULL_STDIO) {
-                r = make_null_stdio();
-                if (r < 0) {
-                        log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
-                        _exit(EXIT_FAILURE);
-                }
-        }
-
-        if (ret_pid)
-                *ret_pid = getpid_cached();
-
-        return 0;
-}
-
-int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *ret_pid, const char *path, ...) {
-        bool stdout_is_tty, stderr_is_tty;
-        unsigned n, i;
-        va_list ap;
-        char **l;
-        int r;
-
-        assert(path);
-
-        /* Spawns a temporary TTY agent, making sure it goes away when we go away */
-
-        r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
-        if (r < 0)
-                return r;
-        if (r > 0)
-                return 0;
-
-        /* In the child: */
-
-        stdout_is_tty = isatty(STDOUT_FILENO);
-        stderr_is_tty = isatty(STDERR_FILENO);
-
-        if (!stdout_is_tty || !stderr_is_tty) {
-                int fd;
-
-                /* Detach from stdout/stderr. and reopen
-                 * /dev/tty for them. This is important to
-                 * ensure that when systemctl is started via
-                 * popen() or a similar call that expects to
-                 * read EOF we actually do generate EOF and
-                 * not delay this indefinitely by because we
-                 * keep an unused copy of stdin around. */
-                fd = open("/dev/tty", O_WRONLY);
-                if (fd < 0) {
-                        log_error_errno(errno, "Failed to open /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                safe_close_above_stdio(fd);
-        }
-
-        /* Count arguments */
-        va_start(ap, path);
-        for (n = 0; va_arg(ap, char*); n++)
-                ;
-        va_end(ap);
-
-        /* Allocate strv */
-        l = alloca(sizeof(char *) * (n + 1));
-
-        /* Fill in arguments */
-        va_start(ap, path);
-        for (i = 0; i <= n; i++)
-                l[i] = va_arg(ap, char*);
-        va_end(ap);
-
-        execv(path, l);
-        _exit(EXIT_FAILURE);
-}
-
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
@@ -1474,7 +1068,7 @@ static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_IDLE] = "idle"
 };
 
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX);
 
 static const char *const sigchld_code_table[] = {
         [CLD_EXITED] = "exited",
diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h
index 3525c2d6..e1bd2c5b 100644
--- a/src/systemd/src/basic/process-util.h
+++ b/src/systemd/src/basic/process-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -21,7 +20,6 @@
 ***/
 
 #include <alloca.h>
-#include <errno.h>
 #include <sched.h>
 #include <signal.h>
 #include <stdbool.h>
@@ -34,7 +32,6 @@
 #include "format-util.h"
 #include "ioprio.h"
 #include "macro.h"
-#include "time-util.h"
 
 #define procfs_file_alloca(pid, field)                                  \
         ({                                                              \
@@ -43,7 +40,7 @@
                 if (_pid_ == 0) {                                       \
                         _r_ = ("/proc/self/" field);                    \
                 } else {                                                \
-                        _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+                        _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
                         sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_);                       \
                 }                                                       \
                 _r_;                                                    \
@@ -62,21 +59,10 @@ int get_process_environ(pid_t pid, char **environ);
 int get_process_ppid(pid_t pid, pid_t *ppid);
 
 int wait_for_terminate(pid_t pid, siginfo_t *status);
-
-typedef enum WaitFlags {
-        WAIT_LOG_ABNORMAL             = 1U << 0,
-        WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1,
-
-        /* A shortcut for requesting the most complete logging */
-        WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
-} WaitFlags;
-
-int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
-int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
+int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
 
 void sigkill_wait(pid_t pid);
 void sigkill_waitp(pid_t *pid);
-void sigterm_wait(pid_t pid);
 
 int kill_and_sigcont(pid_t pid, int sig);
 
@@ -91,7 +77,7 @@ int pid_from_same_root_fs(pid_t pid);
 
 bool is_main_thread(void);
 
-_noreturn_ void freeze(void);
+noreturn void freeze(void);
 
 bool oom_score_adjust_is_valid(int oa);
 
@@ -117,13 +103,8 @@ int sigchld_code_from_string(const char *s) _pure_;
 int sched_policy_to_string_alloc(int i, char **s);
 int sched_policy_from_string(const char *s);
 
-static inline pid_t PTR_TO_PID(const void *p) {
-        return (pid_t) ((uintptr_t) p);
-}
-
-static inline void* PID_TO_PTR(pid_t pid) {
-        return (void*) ((uintptr_t) pid);
-}
+#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
 
 void valgrind_summary_hack(void);
 
@@ -153,56 +134,8 @@ static inline bool ioprio_priority_is_valid(int i) {
 static inline bool pid_is_valid(pid_t p) {
         return p > 0;
 }
-
-static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
-        if (!sched_policy_is_valid(n))
-                return -EINVAL;
-
-        return sched_policy_to_string_alloc(n, s);
-}
 #endif /* NM_IGNORED */
 
 int ioprio_parse_priority(const char *s, int *ret);
 
 pid_t getpid_cached(void);
-void reset_cached_pid(void);
-
-int must_be_root(void);
-
-typedef enum ForkFlags {
-        FORK_RESET_SIGNALS = 1U << 0,
-        FORK_CLOSE_ALL_FDS = 1U << 1,
-        FORK_DEATHSIG      = 1U << 2,
-        FORK_NULL_STDIO    = 1U << 3,
-        FORK_REOPEN_LOG    = 1U << 4,
-        FORK_LOG           = 1U << 5,
-        FORK_WAIT          = 1U << 6,
-        FORK_NEW_MOUNTNS   = 1U << 7,
-} ForkFlags;
-
-int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
-
-static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
-        return safe_fork_full(name, NULL, 0, flags, ret_pid);
-}
-
-int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...);
-
-#if SIZEOF_PID_T == 4
-/* The highest possibly (theoretic) pid_t value on this architecture. */
-#define PID_T_MAX ((pid_t) INT32_MAX)
-/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
- * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
- * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
- * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
- * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
- * least to define them here too. */
-#define TASKS_MAX 4194303U
-#elif SIZEOF_PID_T == 2
-#define PID_T_MAX ((pid_t) INT16_MAX)
-#define TASKS_MAX 32767U
-#else
-#error "Unknown pid_t size"
-#endif
-
-assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c
index 29ee3b45..3b6ddb7d 100644
--- a/src/systemd/src/basic/random-util.c
+++ b/src/systemd/src/basic/random-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -23,12 +22,11 @@
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/random.h>
 #include <stdbool.h>
-#include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/time.h>
+#include <linux/random.h>
+#include <stdint.h>
 
 #if HAVE_SYS_AUXV_H
 #  include <sys/auxv.h>
diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h
index dd870151..804e225f 100644
--- a/src/systemd/src/basic/random-util.h
+++ b/src/systemd/src/basic/random-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h
index ae2e446d..1d77a644 100644
--- a/src/systemd/src/basic/refcnt.h
+++ b/src/systemd/src/basic/refcnt.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h
index 156ab4b0..12d0fda1 100644
--- a/src/systemd/src/basic/set.h
+++ b/src/systemd/src/basic/set.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -108,18 +107,6 @@ static inline void *set_steal_first(Set *s) {
         return internal_hashmap_steal_first(HASHMAP_BASE(s));
 }
 
-#define set_clear_with_destructor(_s, _f)               \
-        ({                                              \
-                void *_item;                            \
-                while ((_item = set_steal_first(_s)))   \
-                        _f(_item);                      \
-        })
-#define set_free_with_destructor(_s, _f)                \
-        ({                                              \
-                set_clear_with_destructor(_s, _f);      \
-                set_free(_s);                           \
-        })
-
 /* no set_steal_first_key */
 /* no set_first_key */
 
diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h
index f6c3396e..dfd6eb56 100644
--- a/src/systemd/src/basic/signal-util.h
+++ b/src/systemd/src/basic/signal-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -45,20 +44,13 @@ static inline void block_signals_reset(sigset_t *ss) {
         assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0);
 }
 
-#define BLOCK_SIGNALS(...)                                                         \
-        _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({        \
-                sigset_t _t;                                                       \
-                assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
-                _t;                                                                \
+#define BLOCK_SIGNALS(...)                                                        \
+        _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({       \
+                sigset_t t;                                                       \
+                assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \
+                t;                                                                \
         })
 
 static inline bool SIGNAL_VALID(int signo) {
         return signo > 0 && signo < _NSIG;
 }
-
-static inline const char* signal_to_string_with_check(int n) {
-        if (!SIGNAL_VALID(n))
-                return NULL;
-
-        return signal_to_string(n);
-}
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c
index 8ae68b6f..798ab16e 100644
--- a/src/systemd/src/basic/socket-util.c
+++ b/src/systemd/src/basic/socket-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -43,7 +42,6 @@
 #include "missing.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "process-util.h"
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -54,24 +52,14 @@
 
 #if 0 /* NM_IGNORED */
 #if ENABLE_IDN
-#  define IDN_FLAGS NI_IDN
+#  define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES)
 #else
 #  define IDN_FLAGS 0
 #endif
 
-static const char* const socket_address_type_table[] = {
-        [SOCK_STREAM] = "Stream",
-        [SOCK_DGRAM] = "Datagram",
-        [SOCK_RAW] = "Raw",
-        [SOCK_RDM] = "ReliableDatagram",
-        [SOCK_SEQPACKET] = "SequentialPacket",
-        [SOCK_DCCP] = "DatagramCongestionControl",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
-
 int socket_address_parse(SocketAddress *a, const char *s) {
         char *e, *n;
+        unsigned u;
         int r;
 
         assert(a);
@@ -81,8 +69,6 @@ int socket_address_parse(SocketAddress *a, const char *s) {
         a->type = SOCK_STREAM;
 
         if (*s == '[') {
-                uint16_t port;
-
                 /* IPv6 in [x:.....:z]:p notation */
 
                 e = strchr(s+1, ']');
@@ -100,12 +86,15 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                         return -EINVAL;
 
                 e++;
-                r = parse_ip_port(e, &port);
+                r = safe_atou(e, &u);
                 if (r < 0)
                         return r;
 
+                if (u <= 0 || u > 0xFFFF)
+                        return -EINVAL;
+
                 a->sockaddr.in6.sin6_family = AF_INET6;
-                a->sockaddr.in6.sin6_port = htobe16(port);
+                a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
                 a->size = sizeof(struct sockaddr_in6);
 
         } else if (*s == '/') {
@@ -135,14 +124,13 @@ int socket_address_parse(SocketAddress *a, const char *s) {
 
         } else if (startswith(s, "vsock:")) {
                 /* AF_VSOCK socket in vsock:cid:port notation */
-                const char *cid_start = s + STRLEN("vsock:");
-                unsigned port;
+                const char *cid_start = s + strlen("vsock:");
 
                 e = strchr(cid_start, ':');
                 if (!e)
                         return -EINVAL;
 
-                r = safe_atou(e+1, &port);
+                r = safe_atou(e+1, &u);
                 if (r < 0)
                         return r;
 
@@ -155,18 +143,19 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                         a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
 
                 a->sockaddr.vm.svm_family = AF_VSOCK;
-                a->sockaddr.vm.svm_port = port;
+                a->sockaddr.vm.svm_port = u;
                 a->size = sizeof(struct sockaddr_vm);
 
         } else {
-                uint16_t port;
-
                 e = strchr(s, ':');
                 if (e) {
-                        r = parse_ip_port(e + 1, &port);
+                        r = safe_atou(e+1, &u);
                         if (r < 0)
                                 return r;
 
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
                         n = strndupa(s, e-s);
 
                         /* IPv4 in w.x.y.z:p notation? */
@@ -177,7 +166,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                         if (r > 0) {
                                 /* Gotcha, it's a traditional IPv4 address */
                                 a->sockaddr.in.sin_family = AF_INET;
-                                a->sockaddr.in.sin_port = htobe16(port);
+                                a->sockaddr.in.sin_port = htobe16((uint16_t)u);
                                 a->size = sizeof(struct sockaddr_in);
                         } else {
                                 unsigned idx;
@@ -191,7 +180,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                                         return -EINVAL;
 
                                 a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htobe16(port);
+                                a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
                                 a->sockaddr.in6.sin6_scope_id = idx;
                                 a->sockaddr.in6.sin6_addr = in6addr_any;
                                 a->size = sizeof(struct sockaddr_in6);
@@ -199,18 +188,21 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                 } else {
 
                         /* Just a port */
-                        r = parse_ip_port(s, &port);
+                        r = safe_atou(s, &u);
                         if (r < 0)
                                 return r;
 
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
                         if (socket_ipv6_is_supported()) {
                                 a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htobe16(port);
+                                a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
                                 a->sockaddr.in6.sin6_addr = in6addr_any;
                                 a->size = sizeof(struct sockaddr_in6);
                         } else {
                                 a->sockaddr.in.sin_family = AF_INET;
-                                a->sockaddr.in.sin_port = htobe16(port);
+                                a->sockaddr.in.sin_port = htobe16((uint16_t)u);
                                 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
                                 a->size = sizeof(struct sockaddr_in);
                         }
@@ -538,25 +530,22 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
         return socket_address_equal(a, &b);
 }
 
-int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
         union sockaddr_union *sa = (union sockaddr_union*) _sa;
 
-        /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */
-
         assert(sa);
 
         switch (sa->sa.sa_family) {
-
         case AF_INET:
-                *ret_port = be16toh(sa->in.sin_port);
+                *port = be16toh(sa->in.sin_port);
                 return 0;
 
         case AF_INET6:
-                *ret_port = be16toh(sa->in6.sin6_port);
+                *port = be16toh(sa->in6.sin6_port);
                 return 0;
 
         case AF_VSOCK:
-                *ret_port = sa->vm.svm_port;
+                *port = sa->vm.svm_port;
                 return 0;
 
         default:
@@ -761,6 +750,19 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
         return 0;
 }
 
+int getnameinfo_pretty(int fd, char **ret) {
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa);
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (getsockname(fd, &sa.sa, &salen) < 0)
+                return -errno;
+
+        return socknameinfo_pretty(&sa, salen, ret);
+}
+
 int socket_address_unlink(SocketAddress *a) {
         assert(a);
 
@@ -807,18 +809,6 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN
 
 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
 
-SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) {
-        int r;
-
-        r = parse_boolean(n);
-        if (r > 0)
-                return SOCKET_ADDRESS_IPV6_ONLY;
-        if (r == 0)
-                return SOCKET_ADDRESS_BOTH;
-
-        return socket_address_bind_ipv6_only_from_string(n);
-}
-
 bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
         assert(a);
         assert(b);
@@ -953,77 +943,56 @@ int getpeercred(int fd, struct ucred *ucred) {
         if (n != sizeof(struct ucred))
                 return -EIO;
 
-        /* Check if the data is actually useful and not suppressed due to namespacing issues */
-        if (!pid_is_valid(u.pid))
+        /* Check if the data is actually useful and not suppressed due
+         * to namespacing issues */
+        if (u.pid <= 0)
+                return -ENODATA;
+        if (u.uid == UID_INVALID)
+                return -ENODATA;
+        if (u.gid == GID_INVALID)
                 return -ENODATA;
-
-        /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of
-         * receiving in "invalid" user/group we get the overflow UID/GID. */
 
         *ucred = u;
         return 0;
 }
 
 int getpeersec(int fd, char **ret) {
-        _cleanup_free_ char *s = NULL;
         socklen_t n = 64;
+        char *s;
+        int r;
 
         assert(fd >= 0);
         assert(ret);
 
-        for (;;) {
-                s = new0(char, n+1);
-                if (!s)
-                        return -ENOMEM;
+        s = new0(char, n);
+        if (!s)
+                return -ENOMEM;
 
-                if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
-                        break;
+        r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+        if (r < 0) {
+                free(s);
 
                 if (errno != ERANGE)
                         return -errno;
 
-                s = mfree(s);
-        }
-
-        if (isempty(s))
-                return -EOPNOTSUPP;
-
-        *ret = TAKE_PTR(s);
-
-        return 0;
-}
-
-int getpeergroups(int fd, gid_t **ret) {
-        socklen_t n = sizeof(gid_t) * 64;
-        _cleanup_free_ gid_t *d = NULL;
-
-        assert(fd >= 0);
-        assert(ret);
-
-        for (;;) {
-                d = malloc(n);
-                if (!d)
+                s = new0(char, n);
+                if (!s)
                         return -ENOMEM;
 
-                if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
-                        break;
-
-                if (errno != ERANGE)
+                r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+                if (r < 0) {
+                        free(s);
                         return -errno;
-
-                d = mfree(d);
+                }
         }
 
-        assert_se(n % sizeof(gid_t) == 0);
-        n /= sizeof(gid_t);
-
-        if ((socklen_t) (int) n != n)
-                return -E2BIG;
-
-        *ret = d;
-        d = NULL;
+        if (isempty(s)) {
+                free(s);
+                return -EOPNOTSUPP;
+        }
 
-        return (int) n;
+        *ret = s;
+        return 0;
 }
 
 int send_one_fd_sa(
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
index ac957a76..d7e2d85f 100644
--- a/src/systemd/src/basic/socket-util.h
+++ b/src/systemd/src/basic/socket-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -36,28 +35,18 @@
 #include "util.h"
 
 union sockaddr_union {
-        /* The minimal, abstract version */
         struct sockaddr sa;
-
-        /* The libc provided version that allocates "enough room" for every protocol */
-        struct sockaddr_storage storage;
-
-        /* Protoctol-specific implementations */
         struct sockaddr_in in;
         struct sockaddr_in6 in6;
         struct sockaddr_un un;
         struct sockaddr_nl nl;
+        struct sockaddr_storage storage;
         struct sockaddr_ll ll;
 #if 0 /* NM_IGNORED */
         struct sockaddr_vm vm;
 #endif /* NM_IGNORED */
-
         /* Ensure there is enough space to store Infiniband addresses */
         uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
-
-        /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
-         * component is always followed by at least one NUL byte. */
-        uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
 };
 
 typedef struct SocketAddress {
@@ -84,9 +73,6 @@ typedef enum SocketAddressBindIPv6Only {
 
 #define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
 
-const char* socket_address_type_to_string(int t) _const_;
-int socket_address_type_from_string(const char *s) _pure_;
-
 int socket_address_parse(SocketAddress *a, const char *s);
 int socket_address_parse_and_warn(SocketAddress *a, const char *s);
 int socket_address_parse_netlink(SocketAddress *a, const char *s);
@@ -128,10 +114,10 @@ int getpeername_pretty(int fd, bool include_port, char **ret);
 int getsockname_pretty(int fd, char **ret);
 
 int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int getnameinfo_pretty(int fd, char **ret);
 
 const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
 SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
-SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s);
 
 int netlink_family_to_string_alloc(int b, char **s);
 int netlink_family_from_string(const char *s) _pure_;
@@ -149,7 +135,6 @@ bool address_label_valid(const char *p);
 
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
-int getpeergroups(int fd, gid_t **ret);
 
 int send_one_fd_sa(int transport_fd,
                    int fd,
diff --git a/src/systemd/src/basic/sparse-endian.h b/src/systemd/src/basic/sparse-endian.h
index 5e59de54..a3573b84 100644
--- a/src/systemd/src/basic/sparse-endian.h
+++ b/src/systemd/src/basic/sparse-endian.h
@@ -1,6 +1,4 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org>
+/* Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h
index d3fed365..bd1144b4 100644
--- a/src/systemd/src/basic/stdio-util.h
+++ b/src/systemd/src/basic/stdio-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -27,11 +26,9 @@
 
 #include "macro.h"
 
-#define snprintf_ok(buf, len, fmt, ...) \
-        ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
-
 #define xsprintf(buf, fmt, ...) \
-        assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
+        assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), "xsprintf: " #buf "[] must be big enough")
+
 
 #define VA_FORMAT_ADVANCE(format, ap)                                   \
 do {                                                                    \
diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c
index 8ec3b266..df26d5fb 100644
--- a/src/systemd/src/basic/string-table.c
+++ b/src/systemd/src/basic/string-table.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h
index e78a6dbd..369610ef 100644
--- a/src/systemd/src/basic/string-table.h
+++ b/src/systemd/src/basic/string-table.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #pragma once
 
@@ -93,11 +92,13 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
 
 #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope)                    \
         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
-        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)        \
+        struct __useless_struct_to_allow_trailing_semicolon__
 
 #define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope)   \
         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
-        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope)
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+        struct __useless_struct_to_allow_trailing_semicolon__
 
 #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
@@ -109,7 +110,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
 /* For string conversions where numbers are also acceptable */
 #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max)         \
         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,)  \
-        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \
+        struct __useless_struct_to_allow_trailing_semicolon__
 
 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)
diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c
index a8e595db..047eb162 100644
--- a/src/systemd/src/basic/string-util.c
+++ b/src/systemd/src/basic/string-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -24,7 +23,6 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -32,7 +30,6 @@
 #include "gunicode.h"
 #include "macro.h"
 #include "string-util.h"
-#include "terminal-util.h"
 #include "utf8.h"
 #include "util.h"
 
@@ -223,7 +220,6 @@ char *strappend(const char *s, const char *suffix) {
         return strnappend(s, suffix, strlen_ptr(suffix));
 }
 
-#if 0 /* NM_IGNORED */
 char *strjoin_real(const char *x, ...) {
         va_list ap;
         size_t l;
@@ -284,9 +280,6 @@ char *strjoin_real(const char *x, ...) {
 char *strstrip(char *s) {
         char *e;
 
-        if (!s)
-                return NULL;
-
         /* Drops trailing whitespace. Modifies the string in
          * place. Returns pointer to first non-space character */
 
@@ -304,13 +297,7 @@ char *strstrip(char *s) {
 char *delete_chars(char *s, const char *bad) {
         char *f, *t;
 
-        /* Drops all specified bad characters, regardless where in the string */
-
-        if (!s)
-                return NULL;
-
-        if (!bad)
-                bad = WHITESPACE;
+        /* Drops all whitespace, regardless where in the string */
 
         for (f = s, t = s; *f; f++) {
                 if (strchr(bad, *f))
@@ -324,27 +311,6 @@ char *delete_chars(char *s, const char *bad) {
         return s;
 }
 
-char *delete_trailing_chars(char *s, const char *bad) {
-        char *p, *c = s;
-
-        /* Drops all specified bad characters, at the end of the string */
-
-        if (!s)
-                return NULL;
-
-        if (!bad)
-                bad = WHITESPACE;
-
-        for (p = s; *p; p++)
-                if (!strchr(bad, *p))
-                        c = p + 1;
-
-        *c = 0;
-
-        return s;
-}
-#endif /* NM_IGNORED */
-
 char *truncate_nl(char *s) {
         assert(s);
 
@@ -509,10 +475,6 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
 
         assert(s);
         assert(percent <= 100);
-
-        if (new_length == (size_t) -1)
-                return strndup(s, old_length);
-
         assert(new_length >= 3);
 
         /* if no multibyte characters use ascii_ellipsize_mem for speed */
@@ -580,10 +542,6 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
 }
 
 char *ellipsize(const char *s, size_t length, unsigned percent) {
-
-        if (length == (size_t) -1)
-                return strdup(s);
-
         return ellipsize_mem(s, strlen(s), length, percent);
 }
 #endif /* NM_IGNORED */
@@ -611,26 +569,26 @@ char* strshorten(char *s, size_t l) {
 }
 
 char *strreplace(const char *text, const char *old_string, const char *new_string) {
-        size_t l, old_len, new_len, allocated = 0;
-        char *t, *ret = NULL;
         const char *f;
+        char *t, *r;
+        size_t l, old_len, new_len;
 
+        assert(text);
         assert(old_string);
         assert(new_string);
 
-        if (!text)
-                return NULL;
-
         old_len = strlen(old_string);
         new_len = strlen(new_string);
 
         l = strlen(text);
-        if (!GREEDY_REALLOC(ret, allocated, l+1))
+        r = new(char, l+1);
+        if (!r)
                 return NULL;
 
         f = text;
-        t = ret;
+        t = r;
         while (*f) {
+                char *a;
                 size_t d, nl;
 
                 if (!startswith(f, old_string)) {
@@ -638,34 +596,28 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
                         continue;
                 }
 
-                d = t - ret;
+                d = t - r;
                 nl = l - old_len + new_len;
-
-                if (!GREEDY_REALLOC(ret, allocated, nl + 1))
-                        return mfree(ret);
+                a = realloc(r, nl + 1);
+                if (!a)
+                        goto oom;
 
                 l = nl;
-                t = ret + d;
+                r = a;
+                t = r + d;
 
                 t = stpcpy(t, new_string);
                 f += old_len;
         }
 
         *t = 0;
-        return ret;
-}
-
-static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
-        if (!offsets)
-                return;
+        return r;
 
-        if ((size_t) diff < offsets[0])
-                shift[0] += size;
-        if ((size_t) diff < offsets[1])
-                shift[1] += size;
+oom:
+        return mfree(r);
 }
 
-char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
         const char *i, *begin = NULL;
         enum {
                 STATE_OTHER,
@@ -673,7 +625,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                 STATE_BRACKET
         } state = STATE_OTHER;
         char *obuf = NULL;
-        size_t osz = 0, isz, shift[2] = {};
+        size_t osz = 0, isz;
         FILE *f;
 
         assert(ibuf);
@@ -687,10 +639,10 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         if (!f)
                 return NULL;
 
-        /* Note we turn off internal locking on f for performance reasons.  It's safe to do so since we created f here
-         * and it doesn't leave our scope. */
-
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+        /* Note we use the _unlocked() stdio variants on f for performance
+         * reasons.  It's safe to do so since we created f here and it
+         * doesn't leave our scope.
+         */
 
         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
 
@@ -701,26 +653,22 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                                 break;
                         else if (*i == '\x1B')
                                 state = STATE_ESCAPE;
-                        else if (*i == '\t') {
-                                fputs("        ", f);
-                                advance_offsets(i - *ibuf, highlight, shift, 7);
-                        } else
-                                fputc(*i, f);
-
+                        else if (*i == '\t')
+                                fputs_unlocked("        ", f);
+                        else
+                                fputc_unlocked(*i, f);
                         break;
 
                 case STATE_ESCAPE:
                         if (i >= *ibuf + isz) { /* EOT */
-                                fputc('\x1B', f);
-                                advance_offsets(i - *ibuf, highlight, shift, 1);
+                                fputc_unlocked('\x1B', f);
                                 break;
                         } else if (*i == '[') {
                                 state = STATE_BRACKET;
                                 begin = i + 1;
                         } else {
-                                fputc('\x1B', f);
-                                fputc(*i, f);
-                                advance_offsets(i - *ibuf, highlight, shift, 1);
+                                fputc_unlocked('\x1B', f);
+                                fputc_unlocked(*i, f);
                                 state = STATE_OTHER;
                         }
 
@@ -730,9 +678,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
 
                         if (i >= *ibuf + isz || /* EOT */
                             (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
-                                fputc('\x1B', f);
-                                fputc('[', f);
-                                advance_offsets(i - *ibuf, highlight, shift, 2);
+                                fputc_unlocked('\x1B', f);
+                                fputc_unlocked('[', f);
                                 state = STATE_OTHER;
                                 i = begin-1;
                         } else if (*i == 'm')
@@ -754,29 +701,19 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         if (_isz)
                 *_isz = osz;
 
-        if (highlight) {
-                highlight[0] += shift[0];
-                highlight[1] += shift[1];
-        }
-
         return obuf;
 }
 
-#if 0 /* NM_IGNORED */
-char *strextend_with_separator(char **x, const char *separator, ...) {
-        bool need_separator;
-        size_t f, l, l_separator;
-        char *r, *p;
+char *strextend(char **x, ...) {
         va_list ap;
+        size_t f, l;
+        char *r, *p;
 
         assert(x);
 
         l = f = strlen_ptr(*x);
 
-        need_separator = !isempty(*x);
-        l_separator = strlen_ptr(separator);
-
-        va_start(ap, separator);
+        va_start(ap, x);
         for (;;) {
                 const char *t;
                 size_t n;
@@ -786,29 +723,22 @@ char *strextend_with_separator(char **x, const char *separator, ...) {
                         break;
 
                 n = strlen(t);
-
-                if (need_separator)
-                        n += l_separator;
-
                 if (n > ((size_t) -1) - l) {
                         va_end(ap);
                         return NULL;
                 }
 
                 l += n;
-                need_separator = true;
         }
         va_end(ap);
 
-        need_separator = !isempty(*x);
-
         r = realloc(*x, l+1);
         if (!r)
                 return NULL;
 
         p = r + f;
 
-        va_start(ap, separator);
+        va_start(ap, x);
         for (;;) {
                 const char *t;
 
@@ -816,23 +746,15 @@ char *strextend_with_separator(char **x, const char *separator, ...) {
                 if (!t)
                         break;
 
-                if (need_separator && separator)
-                        p = stpcpy(p, separator);
-
                 p = stpcpy(p, t);
-
-                need_separator = true;
         }
         va_end(ap);
 
-        assert(p == r + l);
-
         *p = 0;
         *x = r;
 
         return r + l;
 }
-#endif /* NM_IGNORED */
 
 char *strrep(const char *s, unsigned n) {
         size_t l;
diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h
index 08eda4fc..4c94b182 100644
--- a/src/systemd/src/basic/string-util.h
+++ b/src/systemd/src/basic/string-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -52,15 +51,15 @@ static inline bool streq_ptr(const char *a, const char *b) {
 }
 
 static inline const char* strempty(const char *s) {
-        return s ?: "";
+        return s ? s : "";
 }
 
 static inline const char* strnull(const char *s) {
-        return s ?: "(null)";
+        return s ? s : "(null)";
 }
 
 static inline const char *strna(const char *s) {
-        return s ?: "n/a";
+        return s ? s : "n/a";
 }
 
 static inline bool isempty(const char *p) {
@@ -134,20 +133,8 @@ char *strjoin_real(const char *x, ...) _sentinel_;
 
 char *strstrip(char *s);
 char *delete_chars(char *s, const char *bad);
-char *delete_trailing_chars(char *s, const char *bad);
 char *truncate_nl(char *s);
 
-static inline char *skip_leading_chars(const char *s, const char *bad) {
-
-        if (!s)
-                return NULL;
-
-        if (!bad)
-                bad = WHITESPACE;
-
-        return (char*) s + strspn(s, bad);
-}
-
 char ascii_tolower(char x);
 char *ascii_strlower(char *s);
 char *ascii_strlower_n(char *s, size_t n);
@@ -177,11 +164,9 @@ char* strshorten(char *s, size_t l);
 
 char *strreplace(const char *text, const char *old_string, const char *new_string);
 
-char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
-
-char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
+char *strip_tab_ansi(char **p, size_t *l);
 
-#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__)
+char *strextend(char **x, ...) _sentinel_;
 
 char *strrep(const char *s, unsigned n);
 
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
index dee5bbd7..08bcff6e 100644
--- a/src/systemd/src/basic/strv.c
+++ b/src/systemd/src/basic/strv.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -216,7 +215,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
         p = strv_length(*a);
         q = strv_length(b);
 
-        t = reallocarray(*a, p + q + 1, sizeof(char *));
+        t = realloc(*a, sizeof(char*) * (p + q + 1));
         if (!t)
                 return -ENOMEM;
 
@@ -344,7 +343,8 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
                 if (!GREEDY_REALLOC(l, allocated, n + 2))
                         return -ENOMEM;
 
-                l[n++] = TAKE_PTR(word);
+                l[n++] = word;
+                word = NULL;
 
                 l[n] = NULL;
         }
@@ -355,7 +355,8 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
                         return -ENOMEM;
         }
 
-        *t = TAKE_PTR(l);
+        *t = l;
+        l = NULL;
 
         return (int) n;
 }
@@ -395,6 +396,42 @@ char *strv_join(char **l, const char *separator) {
         return r;
 }
 
+char *strv_join_quoted(char **l) {
+        char *buf = NULL;
+        char **s;
+        size_t allocated = 0, len = 0;
+
+        STRV_FOREACH(s, l) {
+                /* assuming here that escaped string cannot be more
+                 * than twice as long, and reserving space for the
+                 * separator and quotes.
+                 */
+                _cleanup_free_ char *esc = NULL;
+                size_t needed;
+
+                if (!GREEDY_REALLOC(buf, allocated,
+                                    len + strlen(*s) * 2 + 3))
+                        goto oom;
+
+                esc = cescape(*s);
+                if (!esc)
+                        goto oom;
+
+                needed = snprintf(buf + len, allocated - len, "%s\"%s\"",
+                                  len > 0 ? " " : "", esc);
+                assert(needed < allocated - len);
+                len += needed;
+        }
+
+        if (!buf)
+                buf = malloc0(1);
+
+        return buf;
+
+ oom:
+        return mfree(buf);
+}
+
 int strv_push(char ***l, char *value) {
         char **c;
         unsigned n, m;
@@ -409,7 +446,7 @@ int strv_push(char ***l, char *value) {
         if (m < n)
                 return -ENOMEM;
 
-        c = reallocarray(*l, m, sizeof(char*));
+        c = realloc_multiply(*l, sizeof(char*), m);
         if (!c)
                 return -ENOMEM;
 
@@ -434,7 +471,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
         if (m < n)
                 return -ENOMEM;
 
-        c = reallocarray(*l, m, sizeof(char*));
+        c = realloc_multiply(*l, sizeof(char*), m);
         if (!c)
                 return -ENOMEM;
 
@@ -448,7 +485,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
         return 0;
 }
 
-int strv_insert(char ***l, unsigned position, char *value) {
+int strv_push_prepend(char ***l, char *value) {
         char **c;
         unsigned n, m, i;
 
@@ -456,7 +493,6 @@ int strv_insert(char ***l, unsigned position, char *value) {
                 return 0;
 
         n = strv_length(*l);
-        position = MIN(position, n);
 
         /* increase and check for overflow */
         m = n + 2;
@@ -467,12 +503,10 @@ int strv_insert(char ***l, unsigned position, char *value) {
         if (!c)
                 return -ENOMEM;
 
-        for (i = 0; i < position; i++)
-                c[i] = (*l)[i];
-        c[position] = value;
-        for (i = position; i < n; i++)
+        for (i = 0; i < n; i++)
                 c[i+1] = (*l)[i];
 
+        c[0] = value;
         c[n+1] = NULL;
 
         free(*l);
@@ -548,7 +582,7 @@ int strv_extend_front(char ***l, const char *value) {
         if (!v)
                 return -ENOMEM;
 
-        c = reallocarray(*l, m, sizeof(char*));
+        c = realloc_multiply(*l, sizeof(char*), m);
         if (!c) {
                 free(v);
                 return -ENOMEM;
@@ -863,7 +897,7 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
 
         k = strv_length(*l);
 
-        nl = reallocarray(*l, k + n + 1, sizeof(char *));
+        nl = realloc(*l, sizeof(char*) * (k + n + 1));
         if (!nl)
                 return -ENOMEM;
 
diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h
index f169ac5d..385ad177 100644
--- a/src/systemd/src/basic/strv.h
+++ b/src/systemd/src/basic/strv.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -54,12 +53,7 @@ int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
 int strv_extend_front(char ***l, const char *value);
 int strv_push(char ***l, char *value);
 int strv_push_pair(char ***l, char *a, char *b);
-int strv_insert(char ***l, unsigned position, char *value);
-
-static inline int strv_push_prepend(char ***l, char *value) {
-        return strv_insert(l, 0, value);
-}
-
+int strv_push_prepend(char ***l, char *value);
 int strv_consume(char ***l, char *value);
 int strv_consume_pair(char ***l, char *a, char *b);
 int strv_consume_prepend(char ***l, char *value);
@@ -91,6 +85,7 @@ char **strv_split_newlines(const char *s);
 int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
 
 char *strv_join(char **l, const char *separator);
+char *strv_join_quoted(char **l);
 
 char **strv_parse_nulstr(const char *s, size_t l);
 char **strv_split_nulstr(const char *s);
@@ -179,18 +174,9 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
 }
 
 char ***strv_free_free(char ***l);
-DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free);
 
 char **strv_skip(char **l, size_t n);
 
 int strv_extend_n(char ***l, const char *value, size_t n);
 
 int fputstrv(FILE *f, char **l, const char *separator, bool *space);
-
-#define strv_free_and_replace(a, b)             \
-        ({                                      \
-                strv_free(a);                   \
-                (a) = (b);                      \
-                (b) = NULL;                     \
-                0;                              \
-        })
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
index 95fbbd91..7f1c3f7c 100644
--- a/src/systemd/src/basic/time-util.c
+++ b/src/systemd/src/basic/time-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -40,7 +39,6 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "process-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
@@ -893,24 +891,28 @@ int parse_timestamp(const char *t, usec_t *usec) {
         char *last_space, *tz = NULL;
         ParseTimestampResult *shared, tmp;
         int r;
+        pid_t pid;
 
         last_space = strrchr(t, ' ');
         if (last_space != NULL && timezone_is_valid(last_space + 1))
                 tz = last_space + 1;
 
-        if (!tz || endswith_no_case(t, " UTC"))
+        if (tz == NULL || endswith_no_case(t, " UTC"))
                 return parse_timestamp_impl(t, usec, false);
 
         shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
         if (shared == MAP_FAILED)
                 return negative_errno();
 
-        r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
-        if (r < 0) {
+        pid = fork();
+
+        if (pid == -1) {
+                int fork_errno = errno;
                 (void) munmap(shared, sizeof *shared);
-                return r;
+                return -fork_errno;
         }
-        if (r == 0) {
+
+        if (pid == 0) {
                 bool with_tz = true;
 
                 if (setenv("TZ", tz, 1) != 0) {
@@ -933,6 +935,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
                 _exit(EXIT_SUCCESS);
         }
 
+        r = wait_for_terminate(pid, NULL);
+        if (r < 0) {
+                (void) munmap(shared, sizeof *shared);
+                return r;
+        }
+
         tmp = *shared;
         if (munmap(shared, sizeof *shared) != 0)
                 return negative_errno();
@@ -1379,7 +1387,8 @@ bool clock_supported(clockid_t clock) {
                 if (!clock_boottime_supported())
                         return false;
 
-                _fallthrough_;
+                /* fall through */
+
         default:
                 /* For everything else, check properly */
                 return clock_gettime(clock, &ts) >= 0;
@@ -1454,10 +1463,4 @@ usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
                 /* x lies in the past */
                 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
 }
-
-bool in_utc_timezone(void) {
-        tzset();
-
-        return timezone == 0 && daylight == 0;
-}
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index a8c5a837..73f7e400 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -100,12 +99,12 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
 #define TRIPLE_TIMESTAMP_HAS_CLOCK(clock)                               \
         IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
 
-static inline bool dual_timestamp_is_set(const dual_timestamp *ts) {
+static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
         return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
                 (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
 }
 
-static inline bool triple_timestamp_is_set(const triple_timestamp *ts) {
+static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
         return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
                 (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
                 (ts->boottime > 0 && ts->boottime != USEC_INFINITY));
@@ -156,8 +155,6 @@ struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
 
 unsigned long usec_to_jiffies(usec_t usec);
 
-bool in_utc_timezone(void);
-
 static inline usec_t usec_add(usec_t a, usec_t b) {
         usec_t c;
 
diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h
index 638b37d7..359d87d2 100644
--- a/src/systemd/src/basic/umask-util.h
+++ b/src/systemd/src/basic/umask-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c
index c88eef38..ff281e43 100644
--- a/src/systemd/src/basic/utf8.c
+++ b/src/systemd/src/basic/utf8.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -410,22 +409,3 @@ int utf8_encoded_valid_unichar(const char *str) {
 
         return len;
 }
-
-size_t utf8_n_codepoints(const char *str) {
-        size_t n = 0;
-
-        /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
-
-        while (*str != 0) {
-                int k;
-
-                k = utf8_encoded_valid_unichar(str);
-                if (k < 0)
-                        return (size_t) -1;
-
-                str += k;
-                n++;
-        }
-
-        return n;
-}
diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h
index c5243c24..322dac20 100644
--- a/src/systemd/src/basic/utf8.h
+++ b/src/systemd/src/basic/utf8.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -61,5 +60,3 @@ static inline bool utf16_is_trailing_surrogate(char16_t c) {
 static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) {
                 return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
 }
-
-size_t utf8_n_codepoints(const char *str);
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
index d8c9e82d..c8a22d68 100644
--- a/src/systemd/src/basic/util.c
+++ b/src/systemd/src/basic/util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -41,7 +40,6 @@
 #include "build.h"
 #include "cgroup-util.h"
 #include "def.h"
-#include "device-nodes.h"
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -54,7 +52,6 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
-#include "procfs-util.h"
 #include "set.h"
 #include "signal-util.h"
 #include "stat-util.h"
@@ -64,7 +61,6 @@
 #include "umask-util.h"
 #include "user-util.h"
 #include "util.h"
-#include "virt.h"
 
 #if 0 /* NM_IGNORED */
 int saved_argc = 0;
@@ -112,7 +108,7 @@ int socket_from_display(const char *display, char **path) {
 
         k = strspn(display+1, "0123456789");
 
-        f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
+        f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
         if (!f)
                 return -ENOMEM;
 
@@ -125,6 +121,66 @@ int socket_from_display(const char *display, char **path) {
         return 0;
 }
 
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+        char *p, *s;
+        int r;
+        unsigned n, m;
+
+        assert(ret);
+
+        /* If it has a queue this is good enough for us */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = d;
+                return 0;
+        }
+
+        /* If it is a partition find the originating device */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r < 0)
+                return -ENOENT;
+
+        /* Get parent dev_t */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = read_one_line_file(p, &s);
+        free(p);
+
+        if (r < 0)
+                return r;
+
+        r = sscanf(s, "%u:%u", &m, &n);
+        free(s);
+
+        if (r != 2)
+                return -EINVAL;
+
+        /* Only return this if it is really good enough for us. */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = makedev(m, n);
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
 bool kexec_loaded(void) {
        _cleanup_free_ char *s = NULL;
 
@@ -152,6 +208,112 @@ int prot_from_flags(int flags) {
         }
 }
 
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
+        bool stdout_is_tty, stderr_is_tty;
+        pid_t parent_pid, agent_pid;
+        sigset_t ss, saved_ss;
+        unsigned n, i;
+        va_list ap;
+        char **l;
+
+        assert(pid);
+        assert(path);
+
+        /* Spawns a temporary TTY agent, making sure it goes away when
+         * we go away */
+
+        parent_pid = getpid_cached();
+
+        /* First we temporarily block all signals, so that the new
+         * child has them blocked initially. This way, we can be sure
+         * that SIGTERMs are not lost we might send to the agent. */
+        assert_se(sigfillset(&ss) >= 0);
+        assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
+
+        agent_pid = fork();
+        if (agent_pid < 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+                return -errno;
+        }
+
+        if (agent_pid != 0) {
+                assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
+                *pid = agent_pid;
+                return 0;
+        }
+
+        /* In the child:
+         *
+         * Make sure the agent goes away when the parent dies */
+        if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+                _exit(EXIT_FAILURE);
+
+        /* Make sure we actually can kill the agent, if we need to, in
+         * case somebody invoked us from a shell script that trapped
+         * SIGTERM or so... */
+        (void) reset_all_signal_handlers();
+        (void) reset_signal_mask();
+
+        /* Check whether our parent died before we were able
+         * to set the death signal and unblock the signals */
+        if (getppid() != parent_pid)
+                _exit(EXIT_SUCCESS);
+
+        /* Don't leak fds to the agent */
+        close_all_fds(except, n_except);
+
+        stdout_is_tty = isatty(STDOUT_FILENO);
+        stderr_is_tty = isatty(STDERR_FILENO);
+
+        if (!stdout_is_tty || !stderr_is_tty) {
+                int fd;
+
+                /* Detach from stdout/stderr. and reopen
+                 * /dev/tty for them. This is important to
+                 * ensure that when systemctl is started via
+                 * popen() or a similar call that expects to
+                 * read EOF we actually do generate EOF and
+                 * not delay this indefinitely by because we
+                 * keep an unused copy of stdin around. */
+                fd = open("/dev/tty", O_WRONLY);
+                if (fd < 0) {
+                        log_error_errno(errno, "Failed to open /dev/tty: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (fd > STDERR_FILENO)
+                        close(fd);
+        }
+
+        /* Count arguments */
+        va_start(ap, path);
+        for (n = 0; va_arg(ap, char*); n++)
+                ;
+        va_end(ap);
+
+        /* Allocate strv */
+        l = alloca(sizeof(char *) * (n + 1));
+
+        /* Fill in arguments */
+        va_start(ap, path);
+        for (i = 0; i <= n; i++)
+                l[i] = va_arg(ap, char*);
+        va_end(ap);
+
+        execv(path, l);
+        _exit(EXIT_FAILURE);
+}
+
 bool in_initrd(void) {
         struct statfs s;
 
@@ -186,13 +348,11 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
         const void *p;
         int comparison;
 
-        assert(!size_multiply_overflow(nmemb, size));
-
         l = 0;
         u = nmemb;
         while (l < u) {
                 idx = (l + u) / 2;
-                p = (const uint8_t*) base + idx * size;
+                p = (const char *) base + idx * size;
                 comparison = compar(key, p, arg);
                 if (comparison < 0)
                         u = idx;
@@ -481,22 +641,31 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
 
 uint64_t system_tasks_max(void) {
 
+#if SIZEOF_PID_T == 4
+#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
+#elif SIZEOF_PID_T == 2
+#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
+#else
+#error "Unknown pid_t size"
+#endif
+
+        _cleanup_free_ char *value = NULL, *root = NULL;
         uint64_t a = TASKS_MAX, b = TASKS_MAX;
-        _cleanup_free_ char *root = NULL;
 
         /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
          * limit:
          *
-         * a) the maximum tasks value the kernel allows on this architecture
+         * a) the maximum value for the pid_t type
          * b) the cgroups pids_max attribute for the system
-         * c) the kernel's configured maximum PID value
+         * c) the kernel's configure maximum PID value
          *
          * And then pick the smallest of the three */
 
-        (void) procfs_tasks_get_limit(&a);
+        if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
+                (void) safe_atou64(value, &a);
 
         if (cg_get_root_path(&root) >= 0) {
-                _cleanup_free_ char *value = NULL;
+                value = mfree(value);
 
                 if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
                         (void) safe_atou64(value, &b);
@@ -525,83 +694,162 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
         return m / max;
 }
 
+int update_reboot_parameter_and_warn(const char *param) {
+        int r;
+
+        if (isempty(param)) {
+                if (unlink("/run/systemd/reboot-param") < 0) {
+                        if (errno == ENOENT)
+                                return 0;
+
+                        return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
+                }
+
+                return 0;
+        }
+
+        RUN_WITH_UMASK(0022) {
+                r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to write reboot parameter file: %m");
+        }
+
+        return 0;
+}
+
 int version(void) {
         puts(PACKAGE_STRING "\n"
              SYSTEMD_FEATURES);
         return 0;
 }
 
-/* This is a direct translation of str_verscmp from boot.c */
-static bool is_digit(int c) {
-        return c >= '0' && c <= '9';
-}
+int get_block_device(const char *path, dev_t *dev) {
+        struct stat st;
+        struct statfs sfs;
 
-static int c_order(int c) {
-        if (c == 0 || is_digit(c))
-                return 0;
+        assert(path);
+        assert(dev);
 
-        if ((c >= 'a') && (c <= 'z'))
-                return c;
+        /* Get's the block device directly backing a file system. If
+         * the block device is encrypted, returns the device mapper
+         * block device. */
 
-        return c + 0x10000;
-}
+        if (lstat(path, &st))
+                return -errno;
+
+        if (major(st.st_dev) != 0) {
+                *dev = st.st_dev;
+                return 1;
+        }
 
-int str_verscmp(const char *s1, const char *s2) {
-        const char *os1, *os2;
+        if (statfs(path, &sfs) < 0)
+                return -errno;
 
-        assert(s1);
-        assert(s2);
+        if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
+                return btrfs_get_block_device(path, dev);
 
-        os1 = s1;
-        os2 = s2;
+        return 0;
+}
 
-        while (*s1 || *s2) {
-                int first;
+int get_block_device_harder(const char *path, dev_t *dev) {
+        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_free_ char *p = NULL, *t = NULL;
+        struct dirent *de, *found = NULL;
+        const char *q;
+        unsigned maj, min;
+        dev_t dt;
+        int r;
 
-                while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
-                        int order;
+        assert(path);
+        assert(dev);
 
-                        order = c_order(*s1) - c_order(*s2);
-                        if (order != 0)
-                                return order;
-                        s1++;
-                        s2++;
-                }
+        /* Gets the backing block device for a file system, and
+         * handles LUKS encrypted file systems, looking for its
+         * immediate parent, if there is one. */
 
-                while (*s1 == '0')
-                        s1++;
-                while (*s2 == '0')
-                        s2++;
-
-                first = 0;
-                while (is_digit(*s1) && is_digit(*s2)) {
-                        if (first == 0)
-                                first = *s1 - *s2;
-                        s1++;
-                        s2++;
-                }
+        r = get_block_device(path, &dt);
+        if (r <= 0)
+                return r;
+
+        if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
+                return -ENOMEM;
 
-                if (is_digit(*s1))
-                        return 1;
-                if (is_digit(*s2))
-                        return -1;
+        d = opendir(p);
+        if (!d) {
+                if (errno == ENOENT)
+                        goto fallback;
 
-                if (first != 0)
-                        return first;
+                return -errno;
         }
 
-        return strcmp(os1, os2);
-}
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
 
-/* Turn off core dumps but only if we're running outside of a container. */
-void disable_coredumps(void) {
-        int r;
+                if (dot_or_dot_dot(de->d_name))
+                        continue;
 
-        if (detect_container() > 0)
-                return;
+                if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+                        continue;
 
-        r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+                if (found) {
+                        _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
+
+                        /* We found a device backed by multiple other devices. We don't really support automatic
+                         * discovery on such setups, with the exception of dm-verity partitions. In this case there are
+                         * two backing devices: the data partition and the hash partition. We are fine with such
+                         * setups, however, only if both partitions are on the same physical device. Hence, let's
+                         * verify this. */
+
+                        u = strjoin(p, "/", de->d_name, "/../dev");
+                        if (!u)
+                                return -ENOMEM;
+
+                        v = strjoin(p, "/", found->d_name, "/../dev");
+                        if (!v)
+                                return -ENOMEM;
+
+                        r = read_one_line_file(u, &a);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to read %s: %m", u);
+                                goto fallback;
+                        }
+
+                        r = read_one_line_file(v, &b);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to read %s: %m", v);
+                                goto fallback;
+                        }
+
+                        /* Check if the parent device is the same. If not, then the two backing devices are on
+                         * different physical devices, and we don't support that. */
+                        if (!streq(a, b))
+                                goto fallback;
+                }
+
+                found = de;
+        }
+
+        if (!found)
+                goto fallback;
+
+        q = strjoina(p, "/", found->d_name, "/dev");
+
+        r = read_one_line_file(q, &t);
+        if (r == -ENOENT)
+                goto fallback;
         if (r < 0)
-                log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
+                return r;
+
+        if (sscanf(t, "%u:%u", &maj, &min) != 2)
+                return -EINVAL;
+
+        if (maj == 0)
+                goto fallback;
+
+        *dev = makedev(maj, min);
+        return 1;
+
+fallback:
+        *dev = dt;
+        return 1;
 }
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index 19e9eae1..b31dfd1c 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -71,6 +70,8 @@ bool plymouth_running(void);
 bool display_is_local(const char *display) _pure_;
 int socket_from_display(const char *display, char **path);
 
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 
@@ -84,6 +85,8 @@ bool kexec_loaded(void);
 
 int prot_from_flags(int flags) _const_;
 
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
+
 bool in_initrd(void);
 void in_initrd_force(bool value);
 
@@ -92,19 +95,6 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
                  void *arg);
 
 /**
- * Normal bsearch requires base to be nonnull. Here were require
- * that only if nmemb > 0.
- */
-static inline void* bsearch_safe(const void *key, const void *base,
-                                 size_t nmemb, size_t size, comparison_fn_t compar) {
-        if (nmemb <= 0)
-                return NULL;
-
-        assert(base);
-        return bsearch(key, base, nmemb, size, compar);
-}
-
-/**
  * Normal qsort requires base to be nonnull. Here were require
  * that only if nmemb > 0.
  */
@@ -199,8 +189,9 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max);
 uint64_t system_tasks_max(void);
 uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
 
-int version(void);
+int update_reboot_parameter_and_warn(const char *param);
 
-int str_verscmp(const char *s1, const char *s2);
+int version(void);
 
-void disable_coredumps(void);
+int get_block_device(const char *path, dev_t *dev);
+int get_block_device_harder(const char *path, dev_t *dev);
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
index 9b721ee6..69bd3e75 100644
--- a/src/systemd/src/libsystemd-network/arp-util.c
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -26,7 +25,6 @@
 
 #include "arp-util.h"
 #include "fd-util.h"
-#include "unaligned.h"
 #include "util.h"
 
 int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
@@ -51,12 +49,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),                        /* protocol == reply ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
                 /* Sender Hardware Address must be different from our own */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))),                  /* A <- 4 bytes of client's MAC */
                 BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)),       /* A <- 4 bytes of SHA */
                 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6),                                  /* A == 0 ? */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
                 BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
                 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4),   /* A <- remainder of SHA */
                 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
@@ -105,7 +103,10 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
         if (r < 0)
                 return -errno;
 
-        return TAKE_FD(s);
+        r = s;
+        s = -1;
+
+        return r;
 }
 
 static int arp_send_packet(int fd, int ifindex,
diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h
index decfce3f..3ef56b00 100644
--- a/src/systemd/src/libsystemd-network/arp-util.h
+++ b/src/systemd/src/libsystemd-network/arp-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
index df4d8afb..c1fa8763 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.c
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h
index 0ccee7a7..1cc0f9fb 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.h
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index a5352695..3fdf02da 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
index 65c182f4..7847ce07 100644
--- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -34,8 +33,6 @@ struct sd_dhcp_route {
         struct in_addr dst_addr;
         struct in_addr gw_addr;
         unsigned char dst_prefixlen;
-
-        uint8_t option;
 };
 
 struct sd_dhcp_raw_option {
diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c
index d9067ffb..f01b2cfe 100644
--- a/src/systemd/src/libsystemd-network/dhcp-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp-network.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -34,7 +33,6 @@
 #include "dhcp-internal.h"
 #include "fd-util.h"
 #include "socket-util.h"
-#include "unaligned.h"
 
 static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                             uint32_t xid, const uint8_t *mac_addr,
@@ -73,13 +71,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)),    /* A <- client identifier */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0),                        /* client identifier == xid ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),        /* A <- 4 bytes of client's MAC */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))),                     /* A <- 4 bytes of client's MAC */
                 BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),                 /* A <- 4 bytes of MAC from dhcp.chaddr */
                 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),        /* A <- remainder of client's MAC */
+                BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))),   /* A <- remainder of client's MAC */
                 BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
                 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),             /* A <- remainder of MAC from dhcp.chaddr */
                 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
@@ -125,7 +123,10 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
         if (r < 0)
                 return -errno;
 
-        return TAKE_FD(s);
+        r = s;
+        s = -1;
+
+        return r;
 }
 
 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
@@ -210,7 +211,10 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
         if (r < 0)
                 return -errno;
 
-        return TAKE_FD(s);
+        r = s;
+        s = -1;
+
+        return r;
 }
 
 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c
index 08ed8a9f..e003e088 100644
--- a/src/systemd/src/libsystemd-network/dhcp-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp-option.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -193,7 +192,9 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
                                 if (!ascii_is_valid(string))
                                         return -EINVAL;
 
-                                free_and_replace(*error_message, string);
+                                free(*error_message);
+                                *error_message = string;
+                                string = NULL;
                         }
 
                         break;
@@ -255,8 +256,10 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t c
         if (message_type == 0)
                 return -ENOMSG;
 
-        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE))
-                *_error_message = TAKE_PTR(error_message);
+        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+                *_error_message = error_message;
+                error_message = NULL;
+        }
 
         return message_type;
 }
diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c
index 6d8f01f2..1bb1bfcf 100644
--- a/src/systemd/src/libsystemd-network/dhcp-packet.c
+++ b/src/systemd/src/libsystemd-network/dhcp-packet.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h
index 73a9f75e..5cf7abbf 100644
--- a/src/systemd/src/libsystemd-network/dhcp-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index 13844a86..945c3b97 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -29,65 +28,25 @@
 #include "macro.h"
 #include "sparse-endian.h"
 
-/* Common option header */
-typedef struct DHCP6Option {
-        be16_t code;
-        be16_t len;
-        uint8_t data[];
-} _packed_ DHCP6Option;
-
-/* Address option */
-struct iaaddr {
-        struct in6_addr address;
-        be32_t lifetime_preferred;
-        be32_t lifetime_valid;
-} _packed_;
-
-/* Prefix Delegation Prefix option */
-struct iapdprefix {
-        be32_t lifetime_preferred;
-        be32_t lifetime_valid;
-        uint8_t prefixlen;
-        struct in6_addr address;
-} _packed_;
-
 typedef struct DHCP6Address DHCP6Address;
 
 struct DHCP6Address {
         LIST_FIELDS(DHCP6Address, addresses);
 
-        union {
-                struct iaaddr iaaddr;
-                struct iapdprefix iapdprefix;
-        };
+        struct {
+                struct in6_addr address;
+                be32_t lifetime_preferred;
+                be32_t lifetime_valid;
+        } iaaddr _packed_;
 };
 
-/* Non-temporary Address option */
-struct ia_na {
-        be32_t id;
-        be32_t lifetime_t1;
-        be32_t lifetime_t2;
-} _packed_;
-
-/* Prefix Delegation option */
-struct ia_pd {
-        be32_t id;
-        be32_t lifetime_t1;
-        be32_t lifetime_t2;
-} _packed_;
-
-/* Temporary Address option */
-struct ia_ta {
-        be32_t id;
-} _packed_;
-
 struct DHCP6IA {
         uint16_t type;
-        union {
-                struct ia_na ia_na;
-                struct ia_pd ia_pd;
-                struct ia_ta ia_ta;
-        };
+        struct {
+                be32_t id;
+                be32_t lifetime_t1;
+                be32_t lifetime_t2;
+        } _packed_;
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
 
@@ -102,12 +61,10 @@ typedef struct DHCP6IA DHCP6IA;
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
                         size_t optlen, const void *optval);
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
-int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
-int dhcp6_option_parse_status(DHCP6Option *option);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia);
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
                                 size_t *allocated);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
index 45e0e824..14e708ef 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -36,10 +35,8 @@ struct sd_dhcp6_lease {
         bool rapid_commit;
 
         DHCP6IA ia;
-        DHCP6IA pd;
 
         DHCP6Address *addr_iter;
-        DHCP6Address *prefix_iter;
 
         struct in6_addr *dns;
         size_t dns_count;
diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c
index 85231084..e4883019 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-network.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -69,7 +68,9 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
         if (r < 0)
                 return -errno;
 
-        return TAKE_FD(s);
+        r = s;
+        s = -1;
+        return r;
 }
 
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index d449a9c7..3a77e34d 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -28,7 +27,6 @@
 
 #include "alloc-util.h"
 #include "dhcp6-internal.h"
-#include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
 #include "dns-domain.h"
 #include "sparse-endian.h"
@@ -36,27 +34,14 @@
 #include "unaligned.h"
 #include "util.h"
 
-typedef struct DHCP6StatusOption {
-        struct DHCP6Option option;
-        be16_t status;
-        char msg[];
-} _packed_ DHCP6StatusOption;
+#define DHCP6_OPTION_IA_NA_LEN                  12
+#define DHCP6_OPTION_IA_TA_LEN                  4
 
-typedef struct DHCP6AddressOption {
-        struct DHCP6Option option;
-        struct iaaddr iaaddr;
-        uint8_t options[];
-} _packed_ DHCP6AddressOption;
-
-typedef struct DHCP6PDPrefixOption {
-        struct DHCP6Option option;
-        struct iapdprefix iapdprefix;
-        uint8_t options[];
-} _packed_ DHCP6PDPrefixOption;
-
-#define DHCP6_OPTION_IA_NA_LEN                  (sizeof(struct ia_na))
-#define DHCP6_OPTION_IA_PD_LEN                  (sizeof(struct ia_pd))
-#define DHCP6_OPTION_IA_TA_LEN                  (sizeof(struct ia_ta))
+typedef struct DHCP6Option {
+        be16_t code;
+        be16_t len;
+        uint8_t data[];
+} _packed_ DHCP6Option;
 
 static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
                              size_t optlen) {
@@ -99,7 +84,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         uint16_t len;
         uint8_t *ia_hdr;
-        size_t iaid_offset, ia_buflen, ia_addrlen = 0;
+        size_t ia_buflen, ia_addrlen = 0;
         DHCP6Address *addr;
         int r;
 
@@ -108,12 +93,10 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         switch (ia->type) {
         case SD_DHCP6_OPTION_IA_NA:
                 len = DHCP6_OPTION_IA_NA_LEN;
-                iaid_offset = offsetof(DHCP6IA, ia_na);
                 break;
 
         case SD_DHCP6_OPTION_IA_TA:
                 len = DHCP6_OPTION_IA_TA_LEN;
-                iaid_offset = offsetof(DHCP6IA, ia_ta);
                 break;
 
         default:
@@ -129,7 +112,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         *buf += sizeof(DHCP6Option);
         *buflen -= sizeof(DHCP6Option);
 
-        memcpy(*buf, (char*) ia + iaid_offset, len);
+        memcpy(*buf, &ia->id, len);
 
         *buf += len;
         *buflen -= len;
@@ -155,69 +138,6 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         return 0;
 }
 
-int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
-        uint8_t buffer[1 + DNS_WIRE_FOMAT_HOSTNAME_MAX];
-        int r;
-
-        assert_return(buf && *buf && buflen && fqdn, -EINVAL);
-
-        buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
-
-        /* Store domain name after flags field */
-        r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1,  false);
-        if (r <= 0)
-                return r;
-
-        /*
-         * According to RFC 4704, chapter 4.2 only add terminating zero-length
-         * label in case a FQDN is provided. Since dns_name_to_wire_format
-         * always adds terminating zero-length label remove if only a hostname
-         * is provided.
-         */
-        if (dns_name_is_single_label(fqdn))
-                r--;
-
-        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
-
-        return r;
-}
-
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
-        DHCP6Option *option = (DHCP6Option *)buf;
-        size_t i = sizeof(*option) + sizeof(pd->ia_pd);
-        DHCP6Address *prefix;
-
-        assert_return(buf, -EINVAL);
-        assert_return(pd, -EINVAL);
-        assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
-
-        if (len < i)
-                return -ENOBUFS;
-
-        option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
-
-        memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
-
-        LIST_FOREACH(addresses, prefix, pd->addresses) {
-                DHCP6PDPrefixOption *prefix_opt;
-
-                if (len < i + sizeof(*prefix_opt))
-                        return -ENOBUFS;
-
-                prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
-                prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
-                prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
-
-                memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
-                       sizeof(struct iapdprefix));
-
-                i += sizeof(*prefix_opt);
-        }
-
-        option->len = htobe16(i - sizeof(*option));
-
-        return i;
-}
 
 static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
         DHCP6Option *option = (DHCP6Option*) *buf;
@@ -264,147 +184,35 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
         return 0;
 }
 
-int dhcp6_option_parse_status(DHCP6Option *option) {
-        DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
-
-        if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
-                return -ENOBUFS;
-
-        return be16toh(statusopt->status);
-}
-
-static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
-                                      uint32_t *lifetime_valid) {
-        DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
-        DHCP6Address *addr;
-        uint32_t lt_valid, lt_pref;
-        int r;
-
-        if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
-                return -ENOBUFS;
-
-        lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
-        lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
-
-        if (lt_valid == 0 || lt_pref > lt_valid) {
-                log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
-                                 lt_pref, lt_valid);
-
-                return 0;
-        }
-
-        if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
-                if (r != 0)
-                        return r < 0 ? r: 0;
-        }
-
-        addr = new0(DHCP6Address, 1);
-        if (!addr)
-                return -ENOMEM;
-
-        LIST_INIT(addresses, addr);
-        memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
-
-        LIST_PREPEND(addresses, ia->addresses, addr);
-
-        *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
-
-        return 0;
-}
-
-static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
-                                       uint32_t *lifetime_valid) {
-        DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
-        DHCP6Address *prefix;
-        uint32_t lt_valid, lt_pref;
+int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
+                          DHCP6IA *ia) {
         int r;
-
-        if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
-                return -ENOBUFS;
-
-        lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
-        lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
-
-        if (lt_valid == 0 || lt_pref > lt_valid) {
-                log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
-                                 lt_pref, lt_valid);
-
-                return 0;
-        }
-
-        if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
-                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
-                if (r != 0)
-                        return r < 0 ? r: 0;
-        }
-
-        prefix = new0(DHCP6Address, 1);
-        if (!prefix)
-                return -ENOMEM;
-
-        LIST_INIT(addresses, prefix);
-        memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
-
-        LIST_PREPEND(addresses, ia->addresses, prefix);
-
-        *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
-
-        return 0;
-}
-
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
-        uint16_t iatype, optlen;
-        size_t i, len;
-        int r = 0, status;
-        uint16_t opt;
+        uint16_t opt, status;
+        size_t optlen;
         size_t iaaddr_offset;
-        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
+        DHCP6Address *addr;
+        uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
 
         assert_return(ia, -EINVAL);
         assert_return(!ia->addresses, -EINVAL);
 
-        iatype = be16toh(iaoption->code);
-        len = be16toh(iaoption->len);
-
         switch (iatype) {
         case SD_DHCP6_OPTION_IA_NA:
 
-                if (len < DHCP6_OPTION_IA_NA_LEN) {
+                if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
+                    sizeof(addr->iaaddr)) {
                         r = -ENOBUFS;
                         goto error;
                 }
 
                 iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
-                memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
-
-                lt_t1 = be32toh(ia->ia_na.lifetime_t1);
-                lt_t2 = be32toh(ia->ia_na.lifetime_t2);
-
-                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
-                        log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
-                                         lt_t1, lt_t2);
-                        r = -EINVAL;
-                        goto error;
-                }
-
-                break;
+                memcpy(&ia->id, *buf, iaaddr_offset);
 
-        case SD_DHCP6_OPTION_IA_PD:
-
-                if (len < sizeof(ia->ia_pd)) {
-                        r = -ENOBUFS;
-                        goto error;
-                }
-
-                iaaddr_offset = sizeof(ia->ia_pd);
-                memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
-
-                lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
-                lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
+                lt_t1 = be32toh(ia->lifetime_t1);
+                lt_t2 = be32toh(ia->lifetime_t2);
 
                 if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
-                        log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
+                        log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
                                          lt_t1, lt_t2);
                         r = -EINVAL;
                         goto error;
@@ -413,13 +221,17 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 break;
 
         case SD_DHCP6_OPTION_IA_TA:
-                if (len < DHCP6_OPTION_IA_TA_LEN) {
+                if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
+                    sizeof(addr->iaaddr)) {
                         r = -ENOBUFS;
                         goto error;
                 }
 
                 iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
-                memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
+                memcpy(&ia->id, *buf, iaaddr_offset);
+
+                ia->lifetime_t1 = 0;
+                ia->lifetime_t2 = 0;
 
                 break;
 
@@ -429,63 +241,48 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
         }
 
         ia->type = iatype;
-        i = iaaddr_offset;
 
-        while (i < len) {
-                DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
-
-                if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
-                        r = -ENOBUFS;
-                        goto error;
-                }
+        *buflen -= iaaddr_offset;
+        *buf += iaaddr_offset;
 
-                opt = be16toh(option->code);
-                optlen = be16toh(option->len);
+        while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
 
                 switch (opt) {
                 case SD_DHCP6_OPTION_IAADDR:
 
-                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
-                                log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
-                                r = -EINVAL;
+                        addr = new0(DHCP6Address, 1);
+                        if (!addr) {
+                                r = -ENOMEM;
                                 goto error;
                         }
 
-                        r = dhcp6_option_parse_address(option, ia, &lt_valid);
-                        if (r < 0)
-                                goto error;
-
-                        if (lt_valid < lt_min)
-                                lt_min = lt_valid;
+                        LIST_INIT(addresses, addr);
 
-                        break;
+                        memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
 
-                case SD_DHCP6_OPTION_IA_PD_PREFIX:
+                        lt_valid = be32toh(addr->iaaddr.lifetime_valid);
+                        lt_pref = be32toh(addr->iaaddr.lifetime_valid);
 
-                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
-                                log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
-                                r = -EINVAL;
-                                goto error;
+                        if (!lt_valid || lt_pref > lt_valid) {
+                                log_dhcp6_client(client, "IA preferred %ds > valid %ds",
+                                                 lt_pref, lt_valid);
+                                free(addr);
+                        } else {
+                                LIST_PREPEND(addresses, ia->addresses, addr);
+                                if (lt_valid < lt_min)
+                                        lt_min = lt_valid;
                         }
 
-                        r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
-                        if (r < 0)
-                                goto error;
-
-                        if (lt_valid < lt_min)
-                                lt_min = lt_valid;
-
                         break;
 
                 case SD_DHCP6_OPTION_STATUS_CODE:
+                        if (optlen < sizeof(status))
+                                break;
 
-                        status = dhcp6_option_parse_status(option);
+                        status = (*buf)[0] << 8 | (*buf)[1];
                         if (status) {
                                 log_dhcp6_client(client, "IA status %d",
                                                  status);
-
-                                dhcp6_lease_free_ia(ia);
-
                                 r = -EINVAL;
                                 goto error;
                         }
@@ -497,41 +294,30 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                         break;
                 }
 
-                i += sizeof(*option) + optlen;
+                *buflen -= optlen;
+                *buf += optlen;
         }
 
-        switch(iatype) {
-        case SD_DHCP6_OPTION_IA_NA:
-                if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
-                        lt_t1 = lt_min / 2;
-                        lt_t2 = lt_min / 10 * 8;
-                        ia->ia_na.lifetime_t1 = htobe32(lt_t1);
-                        ia->ia_na.lifetime_t2 = htobe32(lt_t2);
-
-                        log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
-                                         lt_t1, lt_t2);
-                }
-
-                break;
-
-        case SD_DHCP6_OPTION_IA_PD:
-                if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
-                        lt_t1 = lt_min / 2;
-                        lt_t2 = lt_min / 10 * 8;
-                        ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
-                        ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
+        if (r == -ENOMSG)
+                r = 0;
 
-                        log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
-                                         lt_t1, lt_t2);
-                }
+        if (!ia->lifetime_t1 && !ia->lifetime_t2) {
+                lt_t1 = lt_min / 2;
+                lt_t2 = lt_min / 10 * 8;
+                ia->lifetime_t1 = htobe32(lt_t1);
+                ia->lifetime_t2 = htobe32(lt_t2);
 
-                break;
-
-        default:
-                break;
+                log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
+                                 lt_t1, lt_t2);
         }
 
+        if (*buflen)
+                r = -ENOMSG;
+
 error:
+        *buf += *buflen;
+        *buflen = 0;
+
         return r;
 }
 
@@ -619,7 +405,8 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
                 idx++;
         }
 
-        *str_arr = TAKE_PTR(names);
+        *str_arr = names;
+        names = NULL;
 
         return idx;
 
diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
index 5f7e809b..2487c470 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -34,7 +33,6 @@ struct DHCP6Message {
                 } _packed_;
                 be32_t transaction_id;
         };
-        uint8_t options[];
 } _packed_;
 
 typedef struct DHCP6Message DHCP6Message;
@@ -106,9 +104,3 @@ enum {
         DHCP6_STATUS_USE_MULTICAST              = 5,
         _DHCP6_STATUS_MAX                       = 6,
 };
-
-enum {
-        DHCP6_FQDN_FLAG_S = (1 << 0),
-        DHCP6_FQDN_FLAG_O = (1 << 1),
-        DHCP6_FQDN_FLAG_N = (1 << 2),
-};
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h
index 2673aa1c..becc162f 100644
--- a/src/systemd/src/libsystemd-network/lldp-internal.h
+++ b/src/systemd/src/libsystemd-network/lldp-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
index cffbfb98..c560a864 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.c
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h
index 76d4c0c7..c1a7606d 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.h
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
index b8a10d54..4466a050 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.c
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -94,5 +93,8 @@ int lldp_network_bind_raw_socket(int ifindex) {
         if (r < 0)
                 return -errno;
 
-        return TAKE_FD(fd);
+        r = fd;
+        fd = -1;
+
+        return r;
 }
diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h
index c0f8cbe1..c4cf8c79 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.h
+++ b/src/systemd/src/libsystemd-network/lldp-network.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index a8a5658b..de37b9f0 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
  This file is part of systemd.
 
@@ -24,7 +23,6 @@
 #include <linux/if.h>
 #include <netinet/ether.h>
 
-#include "sd-id128.h"
 #include "sd-ndisc.h"
 
 #include "alloc-util.h"
@@ -120,8 +118,7 @@ bool net_match_config(const struct ether_addr *match_mac,
                       char * const *match_names,
                       Condition *match_host,
                       Condition *match_virt,
-                      Condition *match_kernel_cmdline,
-                      Condition *match_kernel_version,
+                      Condition *match_kernel,
                       Condition *match_arch,
                       const struct ether_addr *dev_mac,
                       const char *dev_path,
@@ -136,10 +133,7 @@ bool net_match_config(const struct ether_addr *match_mac,
         if (match_virt && condition_test(match_virt) <= 0)
                 return false;
 
-        if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0)
-                return false;
-
-        if (match_kernel_version && condition_test(match_kernel_version) <= 0)
+        if (match_kernel && condition_test(match_kernel) <= 0)
                 return false;
 
         if (match_arch && condition_test(match_arch) <= 0)
@@ -279,9 +273,10 @@ int config_parse_ifalias(const char *unit,
         }
 
         free(*s);
-        if (*n)
-                *s = TAKE_PTR(n);
-        else
+        if (*n) {
+                *s = n;
+                n = NULL;
+        } else
                 *s = NULL;
 
         return 0;
@@ -427,7 +422,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
                 if (r == 0)
                         break;
 
-                new_addresses = reallocarray(addresses, size + 1, sizeof(struct in_addr));
+                new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr));
                 if (!new_addresses)
                         return -ENOMEM;
                 else
@@ -440,7 +435,8 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
                 size++;
         }
 
-        *ret = TAKE_PTR(addresses);
+        *ret = addresses;
+        addresses = NULL;
 
         return size;
 }
@@ -480,7 +476,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
                 if (r == 0)
                         break;
 
-                new_addresses = reallocarray(addresses, size + 1, sizeof(struct in6_addr));
+                new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr));
                 if (!new_addresses)
                         return -ENOMEM;
                 else
@@ -493,7 +489,8 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
                 size++;
         }
 
-        *ret = TAKE_PTR(addresses);
+        *ret = addresses;
+        addresses = NULL;
 
         return size;
 }
@@ -586,7 +583,8 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
 
         *ret_size = size;
         *ret_allocated = allocated;
-        *ret = TAKE_PTR(routes);
+        *ret = routes;
+        routes = NULL;
 
         return 0;
 }
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index 4e69f1a5..4666f174 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -37,8 +36,7 @@ bool net_match_config(const struct ether_addr *match_mac,
                       char * const *match_name,
                       Condition *match_host,
                       Condition *match_virt,
-                      Condition *match_kernel_cmdline,
-                      Condition *match_kernel_version,
+                      Condition *match_kernel,
                       Condition *match_arch,
                       const struct ether_addr *dev_mac,
                       const char *dev_path,
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index b93b7334..5eab0050 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -358,10 +357,9 @@ int sd_dhcp_client_set_client_id(
  * without further modification. Otherwise, if duid_type is supported, DUID
  * is set based on that type. Otherwise, an error is returned.
  */
-static int dhcp_client_set_iaid_duid(
+int sd_dhcp_client_set_iaid_duid(
                 sd_dhcp_client *client,
                 uint32_t iaid,
-                bool append_iaid,
                 uint16_t duid_type,
                 const void *duid,
                 size_t duid_len) {
@@ -382,17 +380,15 @@ static int dhcp_client_set_iaid_duid(
         zero(client->client_id);
         client->client_id.type = 255;
 
-        if (append_iaid) {
-                /* If IAID is not configured, generate it. */
-                if (iaid == 0) {
-                        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
-                                                     client->mac_addr_len,
-                                                     &client->client_id.ns.iaid);
-                        if (r < 0)
-                                return r;
-                } else
-                        client->client_id.ns.iaid = htobe32(iaid);
-        }
+        /* If IAID is not configured, generate it. */
+        if (iaid == 0) {
+                r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
+                                             client->mac_addr_len,
+                                             &client->client_id.ns.iaid);
+                if (r < 0)
+                        return r;
+        } else
+                client->client_id.ns.iaid = htobe32(iaid);
 
         if (duid != NULL) {
                 client->client_id.ns.duid.type = htobe16(duid_type);
@@ -406,7 +402,7 @@ static int dhcp_client_set_iaid_duid(
                 return -EOPNOTSUPP;
 
         client->client_id_len = sizeof(client->client_id.type) + len +
-                                (append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
+                                sizeof(client->client_id.ns.iaid);
 
         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
                 log_dhcp_client(client, "Configured IAID+DUID, restarting.");
@@ -416,23 +412,6 @@ static int dhcp_client_set_iaid_duid(
 
         return 0;
 }
-
-int sd_dhcp_client_set_iaid_duid(
-                sd_dhcp_client *client,
-                uint32_t iaid,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp_client_set_iaid_duid(client, iaid, true, duid_type, duid, duid_len);
-}
-
-int sd_dhcp_client_set_duid(
-                sd_dhcp_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len) {
-        return dhcp_client_set_iaid_duid(client, 0, false, duid_type, duid, duid_len);
-}
 #endif /* NM_IGNORED */
 
 int sd_dhcp_client_set_hostname(
@@ -441,9 +420,9 @@ int sd_dhcp_client_set_hostname(
 
         assert_return(client, -EINVAL);
 
-        /* Make sure hostnames qualify as DNS and as Linux hostnames */
+        /* Refuse hostnames that neither qualify as DNS nor as Linux hosntames */
         if (hostname &&
-            !(hostname_is_valid(hostname, false) && dns_name_is_valid(hostname) > 0))
+            !(hostname_is_valid(hostname, false) || dns_name_is_valid(hostname) > 0))
                 return -EINVAL;
 
         return free_and_strdup(&client->hostname, hostname);
@@ -854,6 +833,7 @@ static int client_send_request(sd_dhcp_client *client) {
                    client’s IP address.
                 */
 
+                /* fall through */
         case DHCP_STATE_REBINDING:
                 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
                    option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
@@ -1285,9 +1265,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
         if (!lease->have_subnet_mask) {
                 r = dhcp_lease_set_default_subnet_mask(lease);
                 if (r < 0) {
-                        log_dhcp_client(client,
-                                        "received lease lacks subnet mask, "
-                                        "and a fallback one cannot be generated, ignoring");
+                        log_dhcp_client(client, "received lease lacks subnet "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
                         return -ENOMSG;
                 }
         }
@@ -1356,9 +1336,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
         if (lease->subnet_mask == INADDR_ANY) {
                 r = dhcp_lease_set_default_subnet_mask(lease);
                 if (r < 0) {
-                        log_dhcp_client(client,
-                                        "received lease lacks subnet mask, "
-                                        "and a fallback one cannot be generated, ignoring");
+                        log_dhcp_client(client, "received lease lacks subnet "
+                                        "mask, and a fallback one can not be "
+                                        "generated, ignoring");
                         return -ENOMSG;
                 }
         }
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index c272a61e..c00190b5 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -24,7 +23,6 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <stdio.h>
-#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -396,7 +394,9 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
         if (dns_name_is_root(normalized))
                 return -EINVAL;
 
-        free_and_replace(*ret, normalized);
+        free(*ret);
+        *ret = normalized;
+        normalized = NULL;
 
         return 0;
 }
@@ -473,7 +473,6 @@ static int lease_parse_routes(
                 struct sd_dhcp_route *route = *routes + *routes_size;
                 int r;
 
-                route->option = SD_DHCP_OPTION_STATIC_ROUTE;
                 r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
                 if (r < 0) {
                         log_debug("Failed to determine destination prefix length from class based IP, ignoring");
@@ -517,7 +516,6 @@ static int lease_parse_classless_routes(
                         return -ENOMEM;
 
                 route = *routes + *routes_size;
-                route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
 
                 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
                 route->dst_prefixlen = *option;
@@ -687,7 +685,9 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                         return 0;
                 }
 
-                free_and_replace(lease->timezone, tz);
+                free(lease->timezone);
+                lease->timezone = tz;
+                tz = NULL;
 
                 break;
         }
@@ -809,7 +809,8 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
                       pos = next_chunk;
         }
 
-        *domains = TAKE_PTR(names);
+        *domains = names;
+        names = NULL;
 
         return cnt;
 }
@@ -880,8 +881,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         if (r < 0)
                 goto fail;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-        (void) fchmod(fileno(f), 0644);
+        fchmod(fileno(f), 0644);
 
         fprintf(f,
                 "# This is private data. Do not parse.\n");
@@ -928,16 +928,16 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
 
         r = sd_dhcp_lease_get_dns(lease, &addresses);
         if (r > 0) {
-                fputs("DNS=", f);
+                fputs_unlocked("DNS=", f);
                 serialize_in_addrs(f, addresses, r);
-                fputs("\n", f);
+                fputs_unlocked("\n", f);
         }
 
         r = sd_dhcp_lease_get_ntp(lease, &addresses);
         if (r > 0) {
-                fputs("NTP=", f);
+                fputs_unlocked("NTP=", f);
                 serialize_in_addrs(f, addresses, r);
-                fputs("\n", f);
+                fputs_unlocked("\n", f);
         }
 
         r = sd_dhcp_lease_get_domainname(lease, &string);
@@ -946,9 +946,9 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
 
         r = sd_dhcp_lease_get_search_domains(lease, &search_domains);
         if (r > 0) {
-                fputs("DOMAIN_SEARCH_LIST=", f);
+                fputs_unlocked("DOMAIN_SEARCH_LIST=", f);
                 fputstrv(f, search_domains, NULL, NULL);
-                fputs("\n", f);
+                fputs_unlocked("\n", f);
         }
 
         r = sd_dhcp_lease_get_hostname(lease, &string);
@@ -992,7 +992,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         }
 
         LIST_FOREACH(options, option, lease->private_options) {
-                char key[STRLEN("OPTION_000")+1];
+                char key[strlen("OPTION_000")+1];
 
                 xsprintf(key, "OPTION_%" PRIu8, option->tag);
                 r = serialize_dhcp_option(f, key, option->data, option->length);
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index f22daa3c..f512b65b 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -32,9 +31,7 @@
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
-#include "dns-domain.h"
 #include "fd-util.h"
-#include "hostname-util.h"
 #include "in-addr-util.h"
 #include "network-internal.h"
 #include "random-util.h"
@@ -56,8 +53,6 @@ struct sd_dhcp6_client {
         size_t mac_addr_len;
         uint16_t arp_type;
         DHCP6IA ia_na;
-        DHCP6IA ia_pd;
-        bool prefix_delegation;
         be32_t transaction_id;
         usec_t transaction_start;
         struct sd_dhcp6_lease *lease;
@@ -66,7 +61,6 @@ struct sd_dhcp6_client {
         be16_t *req_opts;
         size_t req_opts_allocated;
         size_t req_opts_len;
-        char *fqdn;
         sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
@@ -235,27 +229,12 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         assert_return(client, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
-        client->ia_na.ia_na.id = htobe32(iaid);
-        client->ia_pd.ia_pd.id = htobe32(iaid);
+        client->ia_na.id = htobe32(iaid);
 
         return 0;
 }
 #endif /* NM_IGNORED */
 
-int sd_dhcp6_client_set_fqdn(
-                sd_dhcp6_client *client,
-                const char *fqdn) {
-
-        assert_return(client, -EINVAL);
-
-        /* Make sure FQDN qualifies as DNS and as Linux hostname */
-        if (fqdn &&
-            !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
-                return -EINVAL;
-
-        return free_and_strdup(&client->fqdn, fqdn);
-}
-
 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
         assert_return(client, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@@ -286,7 +265,6 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         case SD_DHCP6_OPTION_DOMAIN_LIST:
         case SD_DHCP6_OPTION_SNTP_SERVERS:
         case SD_DHCP6_OPTION_NTP_SERVER:
-        case SD_DHCP6_OPTION_RAPID_COMMIT:
                 break;
 
         default:
@@ -306,14 +284,6 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         return 0;
 }
 
-int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
-        assert_return(client, -EINVAL);
-
-        client->prefix_delegation = delegation;
-
-        return 0;
-}
-
 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -352,6 +322,8 @@ static int client_reset(sd_dhcp6_client *client) {
         client->receive_message =
                 sd_event_source_unref(client->receive_message);
 
+        client->fd = safe_close(client->fd);
+
         client->transaction_id = 0;
         client->transaction_start = 0;
 
@@ -421,21 +393,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->prefix_delegation) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
                 break;
 
         case DHCP6_STATE_REQUEST:
@@ -456,21 +413,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->prefix_delegation) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
                 break;
 
         case DHCP6_STATE_REBIND:
@@ -480,21 +422,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
-                if (client->fqdn) {
-                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (client->prefix_delegation) {
-                        r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
-                        if (r < 0)
-                                return r;
-
-                        opt += r;
-                        optlen -= r;
-                }
-
                 break;
 
         case DHCP6_STATE_STOPPED:
@@ -508,7 +435,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         if (r < 0)
                 return r;
 
-        assert(client->duid_len);
+        assert (client->duid_len);
         r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
                                 client->duid_len, &client->duid);
         if (r < 0)
@@ -750,20 +677,16 @@ error:
 
 static int client_ensure_iaid(sd_dhcp6_client *client) {
         int r;
-        be32_t iaid;
 
         assert(client);
 
-        if (client->ia_na.ia_na.id)
+        if (client->ia_na.id)
                 return 0;
 
-        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
+        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
         if (r < 0)
                 return r;
 
-        client->ia_na.ia_na.id = iaid;
-        client->ia_pd.ia_pd.id = iaid;
-
         return 0;
 }
 
@@ -772,33 +695,23 @@ static int client_parse_message(
                 DHCP6Message *message,
                 size_t len,
                 sd_dhcp6_lease *lease) {
-        size_t pos = 0;
         int r;
+        uint8_t *optval, *option, *id = NULL;
+        uint16_t optcode, status;
+        size_t optlen, id_len;
         bool clientid = false;
-        uint32_t lt_t1 = ~0, lt_t2 = ~0;
+        be32_t iaid_lease;
 
         assert(client);
         assert(message);
         assert(len >= sizeof(DHCP6Message));
         assert(lease);
 
+        option = (uint8_t *)message + sizeof(DHCP6Message);
         len -= sizeof(DHCP6Message);
 
-        while (pos < len) {
-                DHCP6Option *option = (DHCP6Option *)&message->options[pos];
-                uint16_t optcode, optlen;
-                int status;
-                uint8_t *optval;
-                be32_t iaid_lease;
-
-                if (len < offsetof(DHCP6Option, data) ||
-                    len < offsetof(DHCP6Option, data) + be16toh(option->len))
-                        return -ENOBUFS;
-
-                optcode = be16toh(option->code);
-                optlen = be16toh(option->len);
-                optval = option->data;
-
+        while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
+                                       &optval)) >= 0) {
                 switch (optcode) {
                 case SD_DHCP6_OPTION_CLIENTID:
                         if (clientid) {
@@ -819,8 +732,8 @@ static int client_parse_message(
                         break;
 
                 case SD_DHCP6_OPTION_SERVERID:
-                        r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                        if (r >= 0) {
+                        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+                        if (r >= 0 && id) {
                                 log_dhcp6_client(client, "%s contains multiple serverids",
                                                  dhcp6_message_type_to_string(message->type));
                                 return -EINVAL;
@@ -836,21 +749,21 @@ static int client_parse_message(
                         if (optlen != 1)
                                 return -EINVAL;
 
-                        r = dhcp6_lease_set_preference(lease, optval[0]);
+                        r = dhcp6_lease_set_preference(lease, *optval);
                         if (r < 0)
                                 return r;
 
                         break;
 
                 case SD_DHCP6_OPTION_STATUS_CODE:
-                        status = dhcp6_option_parse_status(option);
+                        if (optlen < 2)
+                                return -EINVAL;
+
+                        status = optval[0] << 8 | optval[1];
                         if (status) {
                                 log_dhcp6_client(client, "%s Status %s",
                                                  dhcp6_message_type_to_string(message->type),
                                                  dhcp6_message_status_to_string(status));
-                                dhcp6_lease_free_ia(&lease->ia);
-                                dhcp6_lease_free_ia(&lease->pd);
-
                                 return -EINVAL;
                         }
 
@@ -863,35 +776,8 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->ia);
-                        if (r < 0 && r != -ENOMSG)
-                                return r;
-
-                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
-                        if (r < 0)
-                                return r;
-
-                        if (client->ia_na.ia_na.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA NA",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
-
-                        if (lease->ia.addresses) {
-                                lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
-                                lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
-                        }
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_PD:
-                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Information request ignoring IA PD option");
-
-                                break;
-                        }
-
-                        r = dhcp6_option_parse_ia(option, &lease->pd);
+                        r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
+                                                  &lease->ia);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
@@ -899,17 +785,12 @@ static int client_parse_message(
                         if (r < 0)
                                 return r;
 
-                        if (client->ia_pd.ia_pd.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA PD",
+                        if (client->ia_na.id != iaid_lease) {
+                                log_dhcp6_client(client, "%s has wrong IAID",
                                                  dhcp6_message_type_to_string(message->type));
                                 return -EINVAL;
                         }
 
-                        if (lease->pd.addresses) {
-                                lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
-                                lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
-                        }
-
                         break;
 
                 case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -948,36 +829,25 @@ static int client_parse_message(
                         break;
                 }
 
-                pos += sizeof(*option) + optlen;
         }
 
-        if (!clientid) {
+        if (r == -ENOMSG)
+                r = 0;
+
+        if (r < 0 || !clientid) {
                 log_dhcp6_client(client, "%s has incomplete options",
                                  dhcp6_message_type_to_string(message->type));
                 return -EINVAL;
         }
 
         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
-                r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                if (r < 0) {
+                r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+                if (r < 0)
                         log_dhcp6_client(client, "%s has no server id",
                                          dhcp6_message_type_to_string(message->type));
-                        return -EINVAL;
-                }
-
-        } else {
-                if (lease->ia.addresses) {
-                        lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
-                        lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
-                }
-
-                if (lease->pd.addresses) {
-                        lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
-                        lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
-                }
         }
 
-        return 0;
+        return r;
 }
 
 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
@@ -1133,7 +1003,7 @@ static int client_receive_message(
                         break;
                 }
 
-                _fallthrough_; /* for Soliciation Rapid Commit option check */
+                /* fall through */ /* for Soliciation Rapid Commit option check */
         case DHCP6_STATE_REQUEST:
         case DHCP6_STATE_RENEW:
         case DHCP6_STATE_REBIND:
@@ -1190,24 +1060,6 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
         if (r < 0)
                 return r;
 
-        if (!client->receive_message) {
-                r = sd_event_add_io(client->event, &client->receive_message,
-                                    client->fd, EPOLLIN, client_receive_message,
-                                    client);
-                if (r < 0)
-                        goto error;
-
-                r = sd_event_source_set_priority(client->receive_message,
-                                                 client->event_priority);
-                if (r < 0)
-                        goto error;
-
-                r = sd_event_source_set_description(client->receive_message,
-                                                    "dhcp6-receive-message");
-                if (r < 0)
-                        goto error;
-        }
-
         switch (state) {
         case DHCP6_STATE_STOPPED:
                 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
@@ -1216,7 +1068,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                         return 0;
                 }
 
-                _fallthrough_;
+                /* fall through */
         case DHCP6_STATE_SOLICITATION:
                 client->state = DHCP6_STATE_SOLICITATION;
 
@@ -1233,17 +1085,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
 
         case DHCP6_STATE_BOUND:
 
-                if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
-                    client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
+                if (client->lease->ia.lifetime_t1 == 0xffffffff ||
+                    client->lease->ia.lifetime_t2 == 0xffffffff) {
 
                         log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
-                                         be32toh(client->lease->ia.ia_na.lifetime_t1),
-                                         be32toh(client->lease->ia.ia_na.lifetime_t2));
+                                         be32toh(client->lease->ia.lifetime_t1),
+                                         be32toh(client->lease->ia.lifetime_t2));
 
                         return 0;
                 }
 
-                timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
 
                 log_dhcp6_client(client, "T1 expires in %s",
                                  format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1254,18 +1106,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                                       10 * USEC_PER_SEC, client_timeout_t1,
                                       client);
                 if (r < 0)
-                        goto error;
+                        return r;
 
                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
                                                  client->event_priority);
                 if (r < 0)
-                        goto error;
+                        return r;
 
                 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
                 if (r < 0)
-                        goto error;
+                        return r;
 
-                timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
+                timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
 
                 log_dhcp6_client(client, "T2 expires in %s",
                                  format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
@@ -1276,16 +1128,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                                       10 * USEC_PER_SEC, client_timeout_t2,
                                       client);
                 if (r < 0)
-                        goto error;
+                        return r;
 
                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
                                                  client->event_priority);
                 if (r < 0)
-                        goto error;
+                        return r;
 
                 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
                 if (r < 0)
-                        goto error;
+                        return r;
 
                 client->state = state;
 
@@ -1299,22 +1151,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
                               client);
         if (r < 0)
-                goto error;
+                return r;
 
         r = sd_event_source_set_priority(client->timeout_resend,
                                          client->event_priority);
         if (r < 0)
-                goto error;
+                return r;
 
         r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
         if (r < 0)
-                goto error;
+                return r;
 
         return 0;
-
- error:
-        client_reset(client);
-        return r;
 }
 
 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
@@ -1322,8 +1170,6 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
 
         client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
 
-        client->fd = safe_close(client->fd);
-
         return 0;
 }
 
@@ -1357,19 +1203,33 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        if (client->fd < 0) {
-                r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
-                if (r < 0) {
-                        _cleanup_free_ char *p = NULL;
-
-                        (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
-                        return log_dhcp6_client_errno(client, r,
-                                                      "Failed to bind to UDP socket at address %s: %m", strna(p));
-                }
+        r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
+        if (r < 0) {
+                _cleanup_free_ char *p = NULL;
 
-                client->fd = r;
+                (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
+                return log_dhcp6_client_errno(client, r,
+                                              "Failed to bind to UDP socket at address %s: %m", strna(p));
         }
 
+        client->fd = r;
+
+        r = sd_event_add_io(client->event, &client->receive_message,
+                            client->fd, EPOLLIN, client_receive_message,
+                            client);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_priority(client->receive_message,
+                                         client->event_priority);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_description(client->receive_message,
+                                            "dhcp6-receive-message");
+        if (r < 0)
+                goto error;
+
         if (client->information_request)
                 state = DHCP6_STATE_INFORMATION_REQUEST;
 
@@ -1378,6 +1238,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
                          "Managed");
 
         return client_start(client, state);
+
+error:
+        client_reset(client);
+        return r;
 }
 
 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
@@ -1437,12 +1301,9 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
 
         client_reset(client);
 
-        client->fd = safe_close(client->fd);
-
         sd_dhcp6_client_detach_event(client);
 
         free(client->req_opts);
-        free(client->fqdn);
         return mfree(client);
 }
 
@@ -1458,7 +1319,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
 
         client->n_ref = 1;
         client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
-        client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
         client->ifindex = -1;
         client->fd = -1;
 
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
index 00b9fdf0..63a813aa 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -51,7 +50,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
                         valid = t;
         }
 
-        t = be32toh(ia->ia_na.lifetime_t2);
+        t = be32toh(ia->lifetime_t2);
         if (t > valid)
                 return -EINVAL;
 
@@ -97,14 +96,11 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
 
 int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
         assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+        assert_return(len, -EINVAL);
 
-        if (!lease->serverid)
-                return -ENOMSG;
-
-        if (id)
-                *id = lease->serverid;
-        if (len)
-                *len = lease->serverid_len;
+        *id = lease->serverid;
+        *len = lease->serverid_len;
 
         return 0;
 }
@@ -149,7 +145,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
         assert_return(lease, -EINVAL);
         assert_return(iaid, -EINVAL);
 
-        *iaid = lease->ia.ia_na.id;
+        *iaid = lease->ia.id;
 
         return 0;
 }
@@ -181,37 +177,6 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
                 lease->addr_iter = lease->ia.addresses;
 }
 
-int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
-                          uint8_t *prefix_len,
-                          uint32_t *lifetime_preferred,
-                          uint32_t *lifetime_valid) {
-        assert_return(lease, -EINVAL);
-        assert_return(prefix, -EINVAL);
-        assert_return(prefix_len, -EINVAL);
-        assert_return(lifetime_preferred, -EINVAL);
-        assert_return(lifetime_valid, -EINVAL);
-
-        if (!lease->prefix_iter)
-                return -ENOMSG;
-
-        memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
-               sizeof(struct in6_addr));
-        *prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
-        *lifetime_preferred =
-                be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
-        *lifetime_valid =
-                be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
-
-        lease->prefix_iter = lease->prefix_iter->addresses_next;
-
-        return 0;
-}
-
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
-        if (lease)
-                lease->prefix_iter = lease->pd.addresses;
-}
-
 int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
         int r;
 
@@ -418,7 +383,6 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
 
         free(lease->serverid);
         dhcp6_lease_free_ia(&lease->ia);
-        dhcp6_lease_free_ia(&lease->pd);
 
         free(lease->dns);
 
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
index 08f46dd2..694384b5 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -290,7 +289,8 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
                         break;
                 }
 
-                _fallthrough_;
+                /* fall through */
+
         case IPV4ACD_STATE_WAITING_ANNOUNCE:
                 /* Send announcement packet */
                 r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index f1e7b404..47fc141c 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -27,7 +26,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "sd-id128.h"
 #include "sd-ipv4acd.h"
 #include "sd-ipv4ll.h"
 
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
index 20956041..31e24486 100644
--- a/src/systemd/src/libsystemd-network/sd-lldp.c
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
index 4fb8ed7f..9dfe6847 100644
--- a/src/systemd/src/libsystemd/sd-event/sd-event.c
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -124,7 +123,6 @@ struct sd_event_source {
                         uint32_t events;
                         uint32_t revents;
                         bool registered:1;
-                        bool owned:1;
                 } io;
                 struct {
                         sd_event_time_handler_t callback;
@@ -243,14 +241,8 @@ struct sd_event {
         unsigned delays[sizeof(usec_t) * 8];
 };
 
-static thread_local sd_event *default_event = NULL;
-
 static void source_disconnect(sd_event_source *s);
 
-static sd_event *event_resolve(sd_event *e) {
-        return e == SD_EVENT_DEFAULT ? default_event : e;
-}
-
 static int pending_prioq_compare(const void *a, const void *b) {
         const sd_event_source *x = a, *y = b;
 
@@ -459,8 +451,6 @@ _public_ int sd_event_new(sd_event** ret) {
                 goto fail;
         }
 
-        e->epoll_fd = fd_move_above_stdio(e->epoll_fd);
-
         if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
                 log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
                 e->profile_delays = true;
@@ -699,7 +689,7 @@ static int event_make_signal_data(
                 return 0;
         }
 
-        d->fd = fd_move_above_stdio(r);
+        d->fd = r;
 
         ev.events = EPOLLIN;
         ev.data.ptr = d;
@@ -894,10 +884,6 @@ static void source_free(sd_event_source *s) {
         assert(s);
 
         source_disconnect(s);
-
-        if (s->type == SOURCE_IO && s->io.owned)
-                safe_close(s->io.fd);
-
         free(s->description);
         free(s);
 }
@@ -982,7 +968,6 @@ _public_ int sd_event_add_io(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(fd >= 0, -EBADF);
         assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
         assert_return(callback, -EINVAL);
@@ -1049,8 +1034,6 @@ static int event_setup_timer_fd(
         if (fd < 0)
                 return -errno;
 
-        fd = fd_move_above_stdio(fd);
-
         ev.events = EPOLLIN;
         ev.data.ptr = d;
 
@@ -1085,7 +1068,6 @@ _public_ int sd_event_add_time(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(accuracy != (uint64_t) -1, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
@@ -1168,7 +1150,6 @@ _public_ int sd_event_add_signal(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(SIGNAL_VALID(sig), -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
@@ -1229,7 +1210,6 @@ _public_ int sd_event_add_child(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(pid > 1, -EINVAL);
         assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
         assert_return(options != 0, -EINVAL);
@@ -1287,7 +1267,6 @@ _public_ int sd_event_add_defer(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(callback, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
@@ -1322,7 +1301,6 @@ _public_ int sd_event_add_post(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(callback, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
@@ -1361,7 +1339,6 @@ _public_ int sd_event_add_exit(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(callback, -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
@@ -1507,21 +1484,6 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
         return 0;
 }
 
-_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-
-        return s->io.owned;
-}
-
-_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
-        assert_return(s, -EINVAL);
-        assert_return(s->type == SOURCE_IO, -EDOM);
-
-        s->io.owned = own;
-        return 0;
-}
-
 _public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
         assert_return(s, -EINVAL);
         assert_return(events, -EINVAL);
@@ -2490,7 +2452,6 @@ _public_ int sd_event_prepare(sd_event *e) {
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2548,7 +2509,6 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         int r, m, i;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
@@ -2655,7 +2615,6 @@ _public_ int sd_event_dispatch(sd_event *e) {
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
@@ -2697,7 +2656,6 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
@@ -2742,7 +2700,6 @@ _public_ int sd_event_loop(sd_event *e) {
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
@@ -2764,7 +2721,6 @@ finish:
 _public_ int sd_event_get_fd(sd_event *e) {
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
 
         return e->epoll_fd;
@@ -2772,7 +2728,6 @@ _public_ int sd_event_get_fd(sd_event *e) {
 
 _public_ int sd_event_get_state(sd_event *e) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
 
         return e->state;
@@ -2780,7 +2735,6 @@ _public_ int sd_event_get_state(sd_event *e) {
 
 _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(code, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2793,7 +2747,6 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
 
 _public_ int sd_event_exit(sd_event *e, int code) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2805,7 +2758,6 @@ _public_ int sd_event_exit(sd_event *e, int code) {
 
 _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(usec, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2830,6 +2782,8 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
 }
 
 _public_ int sd_event_default(sd_event **ret) {
+
+        static thread_local sd_event *default_event = NULL;
         sd_event *e = NULL;
         int r;
 
@@ -2856,7 +2810,6 @@ _public_ int sd_event_default(sd_event **ret) {
 #if 0 /* NM_IGNORED */
 _public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(tid, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2872,7 +2825,6 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
 
         if (e->watchdog == !!b)
@@ -2923,7 +2875,6 @@ fail:
 
 _public_ int sd_event_get_watchdog(sd_event *e) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
 
         return e->watchdog;
@@ -2931,7 +2882,6 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
 
 _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
         assert_return(e, -EINVAL);
-        assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_pid_changed(e), -ECHILD);
 
         *ret = e->iteration;
diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c
index f1033346..19277021 100644
--- a/src/systemd/src/libsystemd/sd-id128/id128-util.c
+++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -20,12 +19,10 @@
 
 #include "nm-sd-adapt.h"
 
-#include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
 
 #include "fd-util.h"
-#include "fs-util.h"
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
@@ -123,7 +120,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
                 if (buffer[32] != '\n')
                         return -EINVAL;
 
-                _fallthrough_;
+                /* fall through */
         case 32: /* plain UUID without trailing newline */
                 if (f == ID128_UUID)
                         return -EINVAL;
@@ -135,7 +132,7 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
                 if (buffer[36] != '\n')
                         return -EINVAL;
 
-                _fallthrough_;
+                /* fall through */
         case 36: /* RFC UUID without trailing newline */
                 if (f == ID128_PLAIN)
                         return -EINVAL;
@@ -186,13 +183,9 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
         if (do_sync) {
                 if (fsync(fd) < 0)
                         return -errno;
-
-                r = fsync_directory_of_file(fd);
-                if (r < 0)
-                        return r;
         }
 
-        return 0;
+        return r;
 }
 
 int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h
index 9f3340e5..6b3855ac 100644
--- a/src/systemd/src/libsystemd/sd-id128/id128-util.h
+++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
index e5bd447a..052110d5 100644
--- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c
index cfc1f3b3..c313a033 100644
--- a/src/systemd/src/shared/dns-domain.c
+++ b/src/systemd/src/shared/dns-domain.c
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -302,7 +301,8 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
         if (r < 0)
                 return r;
 
-        *ret = TAKE_PTR(s);
+        *ret = s;
+        s = NULL;
 
         return r;
 }
@@ -609,7 +609,8 @@ int dns_name_endswith(const char *name, const char *suffix) {
 
                         /* Not the same, let's jump back, and try with the next label again */
                         s = suffix;
-                        n = TAKE_PTR(saved_n);
+                        n = saved_n;
+                        saved_n = NULL;
                 }
         }
 }
@@ -700,26 +701,23 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
 }
 
 int dns_name_between(const char *a, const char *b, const char *c) {
+        int n;
+
         /* Determine if b is strictly greater than a and strictly smaller than c.
            We consider the order of names to be circular, so that if a is
            strictly greater than c, we consider b to be between them if it is
            either greater than a or smaller than c. This is how the canonical
            DNS name order used in NSEC records work. */
 
-        if (dns_name_compare_func(a, c) < 0)
-                /*
-                   a and c are properly ordered:
-                   a<---b--->c
-                */
+        n = dns_name_compare_func(a, c);
+        if (n == 0)
+                return -EINVAL;
+        else if (n < 0)
+                /*       a<---b--->c       */
                 return dns_name_compare_func(a, b) < 0 &&
                        dns_name_compare_func(b, c) < 0;
         else
-                /*
-                   a and c are equal or 'reversed':
-                   <--b--c         a----->
-                   or:
-                   <-----c         a--b-->
-                */
+                /* <--b--c         a--b--> */
                 return dns_name_compare_func(b, c) < 0 ||
                        dns_name_compare_func(a, b) < 0;
 }
@@ -971,12 +969,6 @@ bool dns_srv_type_is_valid(const char *name) {
         return c == 2; /* exactly two labels */
 }
 
-bool dnssd_srv_type_is_valid(const char *name) {
-        return dns_srv_type_is_valid(name) &&
-                ((dns_name_endswith(name, "_tcp") > 0) ||
-                 (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
-}
-
 bool dns_service_name_is_valid(const char *name) {
         size_t l;
 
diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h
index 3ebd4018..d1a99be7 100644
--- a/src/systemd/src/shared/dns-domain.h
+++ b/src/systemd/src/shared/dns-domain.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 /***
@@ -98,7 +97,6 @@ bool dns_name_is_single_label(const char *name);
 int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical);
 
 bool dns_srv_type_is_valid(const char *name);
-bool dnssd_srv_type_is_valid(const char *name);
 bool dns_service_name_is_valid(const char *name);
 
 int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h
index b4400e7b..97c39438 100644
--- a/src/systemd/src/systemd/_sd-common.h
+++ b/src/systemd/src/systemd/_sd-common.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdcommonhfoo
 #define foosdcommonhfoo
 
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index 789cc501..5e46d8d0 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosddhcpclienthfoo
 #define foosddhcpclienthfoo
 
@@ -132,11 +131,6 @@ int sd_dhcp_client_set_iaid_duid(
                 uint16_t duid_type,
                 const void *duid,
                 size_t duid_len);
-int sd_dhcp_client_set_duid(
-                sd_dhcp_client *client,
-                uint16_t duid_type,
-                const void *duid,
-                size_t duid_len);
 int sd_dhcp_client_get_client_id(
                 sd_dhcp_client *client,
                 uint8_t *type,
diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h
index 3cc7fcab..7ab99ccc 100644
--- a/src/systemd/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp-lease.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosddhcpleasehfoo
 #define foosddhcpleasehfoo
 
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index cadb32a0..7819f0d2 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosddhcp6clienthfoo
 #define foosddhcp6clienthfoo
 
@@ -23,7 +22,6 @@
 
 #include <inttypes.h>
 #include <net/ethernet.h>
-#include <stdbool.h>
 #include <sys/types.h>
 
 #include "sd-dhcp6-lease.h"
@@ -65,15 +63,11 @@ enum {
 
         SD_DHCP6_OPTION_DNS_SERVERS                = 23,  /* RFC 3646 */
         SD_DHCP6_OPTION_DOMAIN_LIST                = 24,  /* RFC 3646 */
-        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, prefix delegation */
-        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, prefix delegation */
 
         SD_DHCP6_OPTION_SNTP_SERVERS               = 31,  /* RFC 4075, deprecated */
 
         /* option code 35 is unassigned */
 
-        SD_DHCP6_OPTION_FQDN                       = 39,  /* RFC 4704 */
-
         SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
 
         /* option codes 89-142 are unassigned */
@@ -107,9 +101,6 @@ int sd_dhcp6_client_set_duid(
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
-int sd_dhcp6_client_set_fqdn(
-                sd_dhcp6_client *client,
-                const char *fqdn);
 int sd_dhcp6_client_set_information_request(
                 sd_dhcp6_client *client,
                 int enabled);
@@ -119,8 +110,6 @@ int sd_dhcp6_client_get_information_request(
 int sd_dhcp6_client_set_request_option(
                 sd_dhcp6_client *client,
                 uint16_t option);
-int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
-                                          bool delegation);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h
index 22a5f8ce..184fbb8e 100644
--- a/src/systemd/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosddhcp6leasehfoo
 #define foosddhcp6leasehfoo
 
@@ -36,11 +35,6 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
                                struct in6_addr *addr,
                                uint32_t *lifetime_preferred,
                                uint32_t *lifetime_valid);
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
-int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
-                          uint8_t *prefix_len,
-                          uint32_t *lifetime_preferred,
-                          uint32_t *lifetime_valid);
 
 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
index ec4b7bcf..f8cb8956 100644
--- a/src/systemd/src/systemd/sd-event.h
+++ b/src/systemd/src/systemd/sd-event.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdeventhfoo
 #define foosdeventhfoo
 
@@ -26,7 +25,6 @@
 #include <sys/epoll.h>
 #include <sys/signalfd.h>
 #include <sys/types.h>
-#include <time.h>
 
 #include "_sd-common.h"
 
@@ -41,8 +39,6 @@
 
 _SD_BEGIN_DECLARATIONS;
 
-#define SD_EVENT_DEFAULT ((sd_event *) 1)
-
 typedef struct sd_event sd_event;
 typedef struct sd_event_source sd_event_source;
 
@@ -127,8 +123,6 @@ int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
 int sd_event_source_set_enabled(sd_event_source *s, int enabled);
 int sd_event_source_get_io_fd(sd_event_source *s);
 int sd_event_source_set_io_fd(sd_event_source *s, int fd);
-int sd_event_source_get_io_fd_own(sd_event_source *s);
-int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
 int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
 int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
 int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h
index 67fc5956..9b38969b 100644
--- a/src/systemd/src/systemd/sd-id128.h
+++ b/src/systemd/src/systemd/sd-id128.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdid128hfoo
 #define foosdid128hfoo
 
diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h
index 677ae3b2..16d99983 100644
--- a/src/systemd/src/systemd/sd-ipv4acd.h
+++ b/src/systemd/src/systemd/sd-ipv4acd.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdipv4acdfoo
 #define foosdipv4acdfoo
 
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
index c330b0ae..5ba92083 100644
--- a/src/systemd/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdipv4llfoo
 #define foosdipv4llfoo
 
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
index 0a76fa63..3f35eebe 100644
--- a/src/systemd/src/systemd/sd-lldp.h
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdlldphfoo
 #define foosdlldphfoo
 
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
index 15211450..9f7d4ef7 100644
--- a/src/systemd/src/systemd/sd-ndisc.h
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -1,4 +1,3 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
 #ifndef foosdndiscfoo
 #define foosdndiscfoo
 
diff --git a/src/tests/config/meson.build b/src/tests/config/meson.build
deleted file mode 100644
index fb40d9a9..00000000
--- a/src/tests/config/meson.build
+++ /dev/null
@@ -1,26 +0,0 @@
-test_unit = 'test-config'
-
-sources = files(
-  'nm-test-device.c',
-  'test-config.c'
-)
-
-test_config_dir = meson.current_source_dir()
-
-cflags = [
-  '-DSRCDIR="@0@"'.format(test_config_dir),
-  '-DBUILDDIR="@0@"'.format(test_config_dir)
-]
-
-exe = executable(
-  test_unit,
-  sources,
-  dependencies: test_nm_dep,
-  c_args: cflags
-)
-
-test(
-  'config/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c
index 49631583..3ec866f5 100644
--- a/src/tests/config/nm-test-device.c
+++ b/src/tests/config/nm-test-device.c
@@ -57,7 +57,7 @@ nm_test_device_init (NMTestDevice *self)
 }
 
 /* We jump over NMDevice's construct/destruct methods, which require NMPlatform
- * and NMSettings to be initialized.
+ * and NMConnectionProvider to be initialized.
  */
 static void
 constructed (GObject *object)
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index 50215756..89b22a9f 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -25,7 +25,7 @@
 #include "nm-config.h"
 #include "nm-test-device.h"
 #include "platform/nm-fake-platform.h"
-#include "nm-dbus-manager.h"
+#include "nm-bus-manager.h"
 #include "nm-connectivity.h"
 
 #include "nm-test-utils-core.h"
@@ -254,7 +254,7 @@ test_config_global_dns (void)
 	NMConfig *config;
 	const NMGlobalDnsConfig *dns;
 	NMGlobalDnsDomain *domain;
-	const char *const*strv;
+	const char *const *strv;
 
 	config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL,
 	                       "/no/such/dir", "", NULL);
@@ -318,10 +318,10 @@ test_config_global_dns (void)
 	g_object_unref (config);
 }
 
+#if WITH_CONCHECK
 static void
 test_config_connectivity_check (void)
 {
-#if WITH_CONCHECK
 	const char *CONFIG_INTERN = BUILDDIR"/test-connectivity-check-intern.conf";
 	NMConfig *config;
 	NMConnectivity *connectivity;
@@ -334,14 +334,14 @@ test_config_connectivity_check (void)
 	g_assert (nm_connectivity_check_enabled (connectivity));
 
 	/* disable connectivity checking */
-	NMTST_EXPECT_NM_INFO ("config: signal: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *");
 	nm_config_set_connectivity_check_enabled (config, FALSE);
 	g_test_assert_expected_messages ();
 
 	g_assert (!nm_connectivity_check_enabled (connectivity));
 
 	/* re-enable connectivity checking */
-	NMTST_EXPECT_NM_INFO ("config: signal: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *");
 	nm_config_set_connectivity_check_enabled (config, TRUE);
 	g_test_assert_expected_messages ();
 
@@ -351,10 +351,8 @@ test_config_connectivity_check (void)
 	g_object_unref (config);
 
 	g_assert (remove (CONFIG_INTERN) == 0);
-#else
-	g_test_skip ("concheck disabled");
-#endif
 }
+#endif
 
 static void
 test_config_no_auto_default (void)
@@ -388,7 +386,7 @@ test_config_no_auto_default (void)
 	g_assert (!nm_config_get_no_auto_default_for_device (config, dev3));
 	g_assert (nm_config_get_no_auto_default_for_device (config, dev4));
 
-	NMTST_EXPECT_NM_INFO ("config: signal: NO_AUTO_DEFAULT,no-auto-default *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal NO_AUTO_DEFAULT,no-auto-default *");
 	nm_config_set_no_auto_default_for_device (config, dev3);
 	g_test_assert_expected_messages ();
 
@@ -599,9 +597,9 @@ _set_values_user (NMConfig *config,
 	config_data_before = g_object_ref (nm_config_get_data (config));
 
 	if (expected_changes != NM_CONFIG_CHANGE_NONE)
-		NMTST_EXPECT_NM_INFO ("config: signal: *");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *");
 	else
-		NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*");
 
 	nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGHUP);
 
@@ -643,7 +641,7 @@ _set_values_intern (NMConfig *config,
 	                  &config_changed_data);
 
 	if (expected_changes != NM_CONFIG_CHANGE_NONE)
-		NMTST_EXPECT_NM_INFO ("config: signal: *");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal *");
 
 	nm_config_set_values (config, keyfile_intern, TRUE, FALSE);
 
@@ -906,15 +904,15 @@ test_config_signal (void)
 	                  &expected);
 
 	expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR1;
-	NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR1");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR1");
 	nm_config_reload (config, expected);
 
 	expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2;
-	NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2");
 	nm_config_reload (config, expected);
 
 	expected = NM_CONFIG_CHANGE_CAUSE_SIGHUP;
-	NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*");
 	nm_config_reload (config, expected);
 
 
@@ -927,7 +925,7 @@ test_config_signal (void)
 	                  G_CALLBACK (_test_signal_config_changed_cb2),
 	                  &expected);
 	expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2;
-	NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2");
 	nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGUSR2);
 	g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb2, &expected);
 
@@ -1039,6 +1037,13 @@ main (int argc, char **argv)
 {
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
+	/* Initialize the DBus manager singleton explicitly, because it is accessed by
+	 * the class initializer of NMDevice (used by the NMTestDevice stub).
+	 * This way, we skip calling nm_bus_manager_init_bus() which would
+	 * either fail and/or cause unexpected actions in the test.
+	 * */
+	nm_bus_manager_setup (g_object_new (NM_TYPE_BUS_MANAGER, NULL));
+
 	nm_fake_platform_setup ();
 
 	g_test_add_func ("/config/simple", test_config_simple);
@@ -1050,7 +1055,9 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/config/set-values", test_config_set_values);
 	g_test_add_func ("/config/global-dns", test_config_global_dns);
+#if WITH_CONCHECK
 	g_test_add_func ("/config/connectivity-check", test_config_connectivity_check);
+#endif
 
 	g_test_add_func ("/config/signal", test_config_signal);
 
diff --git a/src/tests/meson.build b/src/tests/meson.build
deleted file mode 100644
index 386ede87..00000000
--- a/src/tests/meson.build
+++ /dev/null
@@ -1,47 +0,0 @@
-subdir('config')
-
-test_units = [
-  'test-general',
-  'test-general-with-expect',
-  'test-ip4-config',
-  'test-ip6-config',
-  'test-dcb',
-  'test-wired-defname',
-  'test-utils'
-]
-
-foreach test_unit: test_units
-  exe = executable(
-    test_unit,
-    test_unit + '.c',
-    dependencies: test_nm_dep
-  )
-
-  test(
-    'src/' + test_unit,
-    test_script,
-    args: test_args + [exe.full_path()]
-  )
-endforeach
-
-test_unit = 'test-systemd'
-
-cflags = [
-  '-DNETWORKMANAGER_COMPILATION_TEST',
-  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD',
-]
-
-exe = executable(
-  test_unit,
-  [test_unit + '.c'] + shared_siphash,
-  include_directories: src_inc,
-  dependencies: nm_core_dep,
-  c_args: cflags,
-  link_with: libsystemd_nm
-)
-
-test(
-  'src/' + test_unit,
-  test_script,
-  args: test_args + [exe.full_path()]
-)
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index f67c332b..2911d84d 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -167,7 +167,7 @@ test_nm_utils_kill_child_spawn (char **argv, gboolean do_not_reap_child)
 }
 
 static pid_t
-do_test_nm_utils_kill_child_create_and_join_pgroup (void)
+test_nm_utils_kill_child_create_and_join_pgroup (void)
 {
 	int err, tmp = 0;
 	int pipefd[2];
@@ -177,7 +177,10 @@ do_test_nm_utils_kill_child_create_and_join_pgroup (void)
 	g_assert (err == 0);
 
 	pgid = fork();
-	g_assert (pgid >= 0);
+	if (pgid < 0) {
+		g_assert_not_reached ();
+		return pgid;
+	}
 
 	if (pgid == 0) {
 		/* child process... */
@@ -203,6 +206,7 @@ do_test_nm_utils_kill_child_create_and_join_pgroup (void)
 	err = setpgid (0, pgid);
 	g_assert (err == 0);
 
+
 	do {
 		err = waitpid (pgid, &tmp, 0);
 	} while (err == -1 && errno == EINTR);
@@ -215,8 +219,9 @@ do_test_nm_utils_kill_child_create_and_join_pgroup (void)
 #define TEST_TOKEN  "nm_test_kill_child_process"
 
 static void
-do_test_nm_utils_kill_child (void)
+test_nm_utils_kill_child (void)
 {
+	int err;
 	GLogLevelFlags fatal_mask;
 	char *argv_watchdog[] = {
 			"bash",
@@ -250,6 +255,7 @@ do_test_nm_utils_kill_child (void)
 			"trap \"while true; do :; done\" TERM; while true; do :; done; #" TEST_TOKEN,
 			NULL,
 		};
+	pid_t gpid;
 	pid_t pid1a_1, pid1a_2, pid1a_3, pid2a, pid3a, pid4a;
 	pid_t pid1s_1, pid1s_2, pid1s_3, pid2s, pid3s, pid4s;
 
@@ -257,6 +263,8 @@ do_test_nm_utils_kill_child (void)
 	const int expected_signal_TERM = SIGTERM;
 	const int expected_signal_KILL = SIGKILL;
 
+	gpid = test_nm_utils_kill_child_create_and_join_pgroup ();
+
 	test_nm_utils_kill_child_spawn (argv_watchdog, FALSE);
 
 	pid1s_1 = test_nm_utils_kill_child_spawn (argv1, TRUE);
@@ -276,122 +284,90 @@ do_test_nm_utils_kill_child (void)
 	/* give processes time to start (and potentially block signals) ... */
 	g_usleep (G_USEC_PER_SEC / 10);
 
+
 	fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-1' (*): after sending SIGTERM (15), process * exited by signal 15 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-1' (*): after sending SIGTERM (15), process * exited by signal 15 (* usec elapsed)");
 	test_nm_utils_kill_child_sync_do ("test-s-1-1", pid1s_1, SIGTERM, 3000, TRUE,  &expected_signal_TERM);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-2' (*): waiting for process to terminate after sending SIGKILL (9)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-2' (*): after sending SIGKILL (9), process * exited by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-2' (*): waiting for process to terminate after sending SIGKILL (9)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-2' (*): after sending SIGKILL (9), process * exited by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_sync_do ("test-s-1-2", pid1s_2, SIGKILL, 1000 / 2, TRUE,  &expected_signal_KILL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): waiting up to 1 milliseconds for process to terminate normally after sending no signal (0)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): sending SIGKILL...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-1-3' (*): after sending no signal (0) and SIGKILL, process * exited by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): waiting up to 1 milliseconds for process to terminate normally after sending no signal (0)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): sending SIGKILL...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-1-3' (*): after sending no signal (0) and SIGKILL, process * exited by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_sync_do ("test-s-1-3", pid1s_3, 0, 1, TRUE,  &expected_signal_KILL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-2' (*): process * already terminated normally with status 47");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-2' (*): process * already terminated normally with status 47");
 	test_nm_utils_kill_child_sync_do ("test-s-2", pid2s, SIGTERM, 3000, TRUE,  &expected_exit_47);
 
 	/* send invalid signal. */
-	NMTST_EXPECT_NM_ERROR ("kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)");
 	test_nm_utils_kill_child_sync_do ("test-s-3-0", pid3s, -1, 0, FALSE, NULL);
 
 	/* really kill pid3s */
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-3-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-3-1' (*): after sending SIGTERM (15), process * exited normally with status 47 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-3-1' (*): waiting up to 3000 milliseconds for process to terminate normally after sending SIGTERM (15)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-3-1' (*): after sending SIGTERM (15), process * exited normally with status 47 (* usec elapsed)");
 	test_nm_utils_kill_child_sync_do ("test-s-3-1", pid3s, SIGTERM, 3000, TRUE,  &expected_exit_47);
 
 	/* pid3s should not be a valid process, hence the call should fail. Note, that there
 	 * is a race here. */
-	NMTST_EXPECT_NM_ERROR ("kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
 	test_nm_utils_kill_child_sync_do ("test-s-3-2", pid3s, 0, 0, FALSE, NULL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): waiting up to 1 milliseconds for process to terminate normally after sending SIGTERM (15)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): sending SIGKILL...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-s-4' (*): after sending SIGTERM (15) and SIGKILL, process * exited by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): waiting up to 1 milliseconds for process to terminate normally after sending SIGTERM (15)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): sending SIGKILL...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): after sending SIGTERM (15) and SIGKILL, process * exited by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_sync_do ("test-s-4", pid4s, SIGTERM, 1, TRUE, &expected_signal_KILL);
 
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-1' (*): terminated by signal 15 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-1' (*): terminated by signal 15 (* usec elapsed)");
 	test_nm_utils_kill_child_async_do ("test-a-1-1", pid1a_1, SIGTERM, 3000, TRUE, &expected_signal_TERM);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-2' (*): wait for process to terminate after sending SIGKILL (9)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-2' (*): terminated by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-2' (*): wait for process to terminate after sending SIGKILL (9)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-2' (*): terminated by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_async_do ("test-a-1-2", pid1a_2, SIGKILL, 1000 / 2, TRUE, &expected_signal_KILL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): wait for process to terminate after sending no signal (0) (send SIGKILL in 1 milliseconds)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): process not terminated after * usec. Sending SIGKILL signal");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-1-3' (*): terminated by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): wait for process to terminate after sending no signal (0) (send SIGKILL in 1 milliseconds)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): process not terminated after * usec. Sending SIGKILL signal");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-1-3' (*): terminated by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_async_do ("test-a-1-3", pid1a_3, 0, 1, TRUE, &expected_signal_KILL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-2' (*): process * already terminated normally with status 47");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-2' (*): process * already terminated normally with status 47");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47");
 	test_nm_utils_kill_child_async_do ("test-a-2", pid2a, SIGTERM, 3000, TRUE, &expected_exit_47);
 
-	NMTST_EXPECT_NM_ERROR ("kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-0' (*): invoke callback: killing child failed");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-0' (*): invoke callback: killing child failed");
 	/* coverity[negative_returns] */
 	test_nm_utils_kill_child_async_do ("test-a-3-0", pid3a, -1, 1000 / 2, FALSE, NULL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-1' (*): terminated normally with status 47 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-1' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-1' (*): terminated normally with status 47 (* usec elapsed)");
 	test_nm_utils_kill_child_async_do ("test-a-3-1", pid3a, SIGTERM, 3000, TRUE, &expected_exit_47);
 
 	/* pid3a should not be a valid process, hence the call should fail. Note, that there
 	 * is a race here. */
-	NMTST_EXPECT_NM_ERROR ("kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-3-2' (*): invoke callback: killing child failed");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-2' (*): invoke callback: killing child failed");
 	test_nm_utils_kill_child_async_do ("test-a-3-2", pid3a, 0, 0, FALSE, NULL);
 
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 1 milliseconds)...");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): process not terminated after * usec. Sending SIGKILL signal");
-	NMTST_EXPECT_NM_DEBUG ("kill child process 'test-a-4' (*): terminated by signal 9 (* usec elapsed)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): wait for process to terminate after sending SIGTERM (15) (send SIGKILL in 1 milliseconds)...");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): process not terminated after * usec. Sending SIGKILL signal");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-4' (*): terminated by signal 9 (* usec elapsed)");
 	test_nm_utils_kill_child_async_do ("test-a-4", pid4a, SIGTERM, 1, TRUE, &expected_signal_KILL);
 
-	g_log_set_always_fatal (fatal_mask);
-
-	g_test_assert_expected_messages ();
-}
-
-static void
-test_nm_utils_kill_child (void)
-{
-	int err;
-	int exit_status;
-	pid_t gpid;
-	pid_t child_pid;
-
-	/* the tests spawns several processes, we want to clean them up
-	 * by sending a SIGKILL to the process group.
-	 *
-	 * The current process might be a session leader, which prevents it from
-	 * creating a new process group. Hence, first fork and let the child
-	 * create a new process group, run the tests, and kill all pending
-	 * processes. */
-	child_pid = fork ();
-	g_assert (child_pid >= 0);
-
-	if (child_pid == 0) {
-		gpid = do_test_nm_utils_kill_child_create_and_join_pgroup ();
-
-		do_test_nm_utils_kill_child ();
-
-		err = setpgid (0, 0);
-		g_assert (err == 0);
+	err = setpgid (0, 0);
+	g_assert (err == 0);
 
-		kill (-gpid, SIGKILL);
+	kill (-gpid, SIGKILL);
 
-		exit (0);
-	};
+	g_log_set_always_fatal (fatal_mask);
 
-	do {
-		err = waitpid (child_pid, &exit_status, 0);
-	} while (err == -1 && errno == EINTR);
-	g_assert (err == child_pid);
-	g_assert (WIFEXITED (exit_status) && WEXITSTATUS(exit_status) == 0);
+	g_test_assert_expected_messages ();
 }
 
 /*****************************************************************************/
@@ -456,7 +432,7 @@ test_nm_utils_array_remove_at_indexes (void)
 
 	idx = g_array_new (FALSE, FALSE, sizeof (guint));
 	array = g_array_new (FALSE, FALSE, sizeof (gssize));
-	unique = g_hash_table_new (nm_direct_hash, NULL);
+	unique = g_hash_table_new (NULL, NULL);
 	for (i_len = 1; i_len < 20; i_len++) {
 		for (i_idx_len = 1; i_idx_len <= i_len; i_idx_len++) {
 			for (i_rnd = 0; i_rnd < 20; i_rnd++) {
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index 38eaf25e..bfa2ea73 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -232,13 +232,13 @@ test_nm_utils_log_connection_diff (void)
 
 	connection = nm_simple_connection_new ();
 	nm_connection_add_setting (connection, nm_setting_connection_new ());
-	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> ");
 
 	nm_connection_add_setting (connection, nm_setting_wired_new ());
-	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> ");
 
 	connection2 = nm_simple_connection_new_clone (connection);
-	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test3", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test3", ">>> ");
 
 	g_object_set (nm_connection_get_setting_connection (connection),
 	              NM_SETTING_CONNECTION_ID, "id",
@@ -248,24 +248,24 @@ test_nm_utils_log_connection_diff (void)
 	              NM_SETTING_CONNECTION_ID, "id2",
 	              NM_SETTING_CONNECTION_MASTER, "master2",
 	              NULL);
-	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test4", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test4", ">>> ");
 
 	nm_connection_add_setting (connection, nm_setting_802_1x_new ());
-	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test5", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test5", ">>> ");
 
 	g_object_set (nm_connection_get_setting_802_1x (connection),
 	              NM_SETTING_802_1X_PASSWORD, "id2",
 	              NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
 	              NULL);
-	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> ", NULL);
-	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test7", ">>> ", NULL);
-	nm_utils_log_connection_diff (connection2, connection, LOGL_DEBUG, LOGD_CORE, "test8", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> ");
+	nm_utils_log_connection_diff (connection, connection2, LOGL_DEBUG, LOGD_CORE, "test7", ">>> ");
+	nm_utils_log_connection_diff (connection2, connection, LOGL_DEBUG, LOGD_CORE, "test8", ">>> ");
 
 	g_clear_object (&connection);
 	g_clear_object (&connection2);
 
 	connection = nmtst_create_minimal_connection ("id-vpn-1", NULL, NM_SETTING_VPN_SETTING_NAME, NULL);
-	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test-vpn-1", ">>> ", NULL);
+	nm_utils_log_connection_diff (connection, NULL, LOGL_DEBUG, LOGD_CORE, "test-vpn-1", ">>> ");
 
 	g_clear_object (&connection);
 }
@@ -1460,11 +1460,6 @@ test_nm_utils_strbuf_append (void)
 static void
 test_duplicate_decl_specifier (void)
 {
-	/* We're intentionally assigning values to static arrays v_const
-	 * and v_result without using it afterwards just so that valgrind
-	 * doesn't complain about the leak. */
-	NM_PRAGMA_WARNING_DISABLE("-Wunused-but-set-variable")
-
 	/* have some static variables, so that the result is certainly not optimized out. */
 	static const int v_const[1] = { 1 };
 	static int v_result[1] = { };
@@ -1482,8 +1477,6 @@ test_duplicate_decl_specifier (void)
 	})
 
 	v_result[0] = TEST_MAX (v_const[0], nmtst_get_rand_int () % 5) + v2;
-
-	NM_PRAGMA_WARNING_REENABLE
 }
 
 static void
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index 816a816f..bcbeee3e 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -246,10 +246,8 @@ test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6Config
 	int *idx = g_new (int, addr_count);
 
 	nm_ip6_config_set_privacy (config, use_tempaddr);
-	copy = nm_ip6_config_clone (config);
-	g_assert (copy);
-	copy2 = nm_ip6_config_clone (config);
-	g_assert (copy2);
+	copy = nmtst_ip6_config_clone (config);
+	copy2 = nmtst_ip6_config_clone (config);
 
 	/* initialize the array of indeces, and keep shuffling them for every @repeat iteration. */
 	for (i = 0; i < addr_count; i++)
@@ -298,9 +296,9 @@ test_nm_ip6_config_addresses_sort (void)
 	ADDR_ADD("2607:f0d0:1002:51::4",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, 0);
 	ADDR_ADD("2607:f0d0:1002:51::5",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, 0);
 	ADDR_ADD("2607:f0d0:1002:51::6",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_NDISC,  0, 0, 0, IFA_F_MANAGETEMPADDR);
-	ADDR_ADD("2607:f0d0:1002:51::3",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_TEMPORARY);
-	ADDR_ADD("2607:f0d0:1002:51::8",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_TEMPORARY);
-	ADDR_ADD("2607:f0d0:1002:51::0",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_TEMPORARY);
+	ADDR_ADD("2607:f0d0:1002:51::3",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_SECONDARY);
+	ADDR_ADD("2607:f0d0:1002:51::8",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_SECONDARY);
+	ADDR_ADD("2607:f0d0:1002:51::0",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_SECONDARY);
 	ADDR_ADD("fec0::1",                  NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
 	ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
 	ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
@@ -311,11 +309,11 @@ test_nm_ip6_config_addresses_sort (void)
 	test_nm_ip6_config_addresses_sort_check (config, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, 8);
 
 	nm_ip6_config_reset_addresses (config);
-	ADDR_ADD("2607:f0d0:1002:51::3",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_TEMPORARY);
+	ADDR_ADD("2607:f0d0:1002:51::3",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_SECONDARY);
 	ADDR_ADD("2607:f0d0:1002:51::4",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, 0);
 	ADDR_ADD("2607:f0d0:1002:51::5",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, 0);
-	ADDR_ADD("2607:f0d0:1002:51::8",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_TEMPORARY);
-	ADDR_ADD("2607:f0d0:1002:51::0",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_TEMPORARY);
+	ADDR_ADD("2607:f0d0:1002:51::8",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_USER,   0, 0, 0, IFA_F_SECONDARY);
+	ADDR_ADD("2607:f0d0:1002:51::0",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, IFA_F_SECONDARY);
 	ADDR_ADD("2607:f0d0:1002:51::6",     NULL,  64, 0, NM_IP_CONFIG_SOURCE_NDISC,  0, 0, 0, IFA_F_MANAGETEMPADDR);
 	ADDR_ADD("fec0::1",                  NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
 	ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0);
diff --git a/src/tests/test-resolvconf-capture.c b/src/tests/test-resolvconf-capture.c
new file mode 100644
index 00000000..2c34ff74
--- /dev/null
+++ b/src/tests/test-resolvconf-capture.c
@@ -0,0 +1,298 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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) 2013 Red Hat, Inc.
+ *
+ */
+
+#include "nm-default.h"
+
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "NetworkManagerUtils.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+#include "platform/nm-platform.h"
+
+#include "nm-test-utils-core.h"
+
+static void
+test_capture_empty (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr));
+
+	g_assert (!nm_utils_resolve_conf_parse (AF_INET, "", ns4, NULL));
+	g_assert_cmpint (ns4->len, ==, 0);
+
+	g_assert (!nm_utils_resolve_conf_parse (AF_INET6, "", ns6, NULL));
+	g_assert_cmpint (ns6->len, ==, 0);
+
+	g_array_free (ns4, TRUE);
+	g_array_free (ns6, TRUE);
+}
+
+#define assert_dns4_entry(a, i, s) \
+	g_assert_cmpint ((g_array_index ((a), guint32, (i))), ==, nmtst_inet4_from_string (s));
+
+#define assert_dns6_entry(a, i, s) \
+	g_assert (IN6_ARE_ADDR_EQUAL (&g_array_index ((a), struct in6_addr, (i)), nmtst_inet6_from_string (s)))
+
+#define assert_dns_option(a, i, s) \
+	g_assert_cmpstr ((a)->pdata[(i)], ==, (s));
+
+static void
+test_capture_basic4 (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 4.2.2.1\r\n"
+"nameserver 4.2.2.2\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL));
+	g_assert_cmpint (ns4->len, ==, 2);
+	assert_dns4_entry (ns4, 0, "4.2.2.1");
+	assert_dns4_entry (ns4, 1, "4.2.2.2");
+
+	g_array_free (ns4, TRUE);
+}
+
+static void
+test_capture_dup4 (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 4.2.2.1\r\n"
+"nameserver 4.2.2.1\r\n"
+"nameserver 4.2.2.2\r\n";
+
+	/* Check that duplicates are ignored */
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL));
+	g_assert_cmpint (ns4->len, ==, 2);
+	assert_dns4_entry (ns4, 0, "4.2.2.1");
+	assert_dns4_entry (ns4, 1, "4.2.2.2");
+
+	g_array_free (ns4, TRUE);
+}
+
+static void
+test_capture_basic6 (void)
+{
+	GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 2001:4860:4860::8888\r\n"
+"nameserver 2001:4860:4860::8844\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL));
+	g_assert_cmpint (ns6->len, ==, 2);
+	assert_dns6_entry (ns6, 0, "2001:4860:4860::8888");
+	assert_dns6_entry (ns6, 1, "2001:4860:4860::8844");
+
+	g_array_free (ns6, TRUE);
+}
+
+static void
+test_capture_dup6 (void)
+{
+	GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 2001:4860:4860::8888\r\n"
+"nameserver 2001:4860:4860::8888\r\n"
+"nameserver 2001:4860:4860::8844\r\n";
+
+	/* Check that duplicates are ignored */
+	g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL));
+	g_assert_cmpint (ns6->len, ==, 2);
+	assert_dns6_entry (ns6, 0, "2001:4860:4860::8888");
+	assert_dns6_entry (ns6, 1, "2001:4860:4860::8844");
+
+	g_array_free (ns6, TRUE);
+}
+
+static void
+test_capture_addr4_with_6 (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 4.2.2.1\r\n"
+"nameserver 4.2.2.2\r\n"
+"nameserver 2001:4860:4860::8888\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL));
+	g_assert_cmpint (ns4->len, ==, 2);
+	assert_dns4_entry (ns4, 0, "4.2.2.1");
+	assert_dns4_entry (ns4, 1, "4.2.2.2");
+
+	g_array_free (ns4, TRUE);
+}
+
+static void
+test_capture_addr6_with_4 (void)
+{
+	GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr));
+	const char *rc =
+"# neato resolv.conf\r\n"
+"domain foobar.com\r\n"
+"search foobar.com\r\n"
+"nameserver 4.2.2.1\r\n"
+"nameserver 2001:4860:4860::8888\r\n"
+"nameserver 2001:4860:4860::8844\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, NULL));
+	g_assert_cmpint (ns6->len, ==, 2);
+	assert_dns6_entry (ns6, 0, "2001:4860:4860::8888");
+	assert_dns6_entry (ns6, 1, "2001:4860:4860::8844");
+
+	g_array_free (ns6, TRUE);
+}
+
+static void
+test_capture_format (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	const char *rc =
+" nameserver 4.2.2.1\r\n"     /* bad */
+"nameserver4.2.2.1\r\n"       /* bad */
+"nameserver     4.2.2.3\r"    /* good */
+"nameserver\t\t4.2.2.4\r\n"   /* good */
+"nameserver  4.2.2.5\t\t\r\n" /* good */
+"nameserver  4.2.2.6   \r\n"; /* good */
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, NULL));
+	g_assert_cmpint (ns4->len, ==, 4);
+	assert_dns4_entry (ns4, 0, "4.2.2.3");
+	assert_dns4_entry (ns4, 1, "4.2.2.4");
+	assert_dns4_entry (ns4, 2, "4.2.2.5");
+	assert_dns4_entry (ns4, 3, "4.2.2.6");
+
+	g_array_free (ns4, TRUE);
+}
+
+static void
+test_capture_dns_options (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free);
+	const char *rc =
+"nameserver 4.2.2.1\r\n"
+"options debug rotate  timeout:5 \r\n"
+"options edns0\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options));
+	g_assert_cmpint (dns_options->len, ==, 4);
+	assert_dns_option (dns_options, 0, "debug");
+	assert_dns_option (dns_options, 1, "rotate");
+	assert_dns_option (dns_options, 2, "timeout:5");
+	assert_dns_option (dns_options, 3, "edns0");
+
+	g_array_free (ns4, TRUE);
+	g_ptr_array_free (dns_options, TRUE);
+}
+
+static void
+test_capture_dns_options_dup (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free);
+	const char *rc =
+"options debug rotate timeout:3\r\n"
+"options edns0 debug\r\n"
+"options timeout:5\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options));
+	g_assert_cmpint (dns_options->len, ==, 4);
+	assert_dns_option (dns_options, 0, "debug");
+	assert_dns_option (dns_options, 1, "rotate");
+	assert_dns_option (dns_options, 2, "timeout:3");
+	assert_dns_option (dns_options, 3, "edns0");
+
+	g_array_free (ns4, TRUE);
+	g_ptr_array_free (dns_options, TRUE);
+}
+
+static void
+test_capture_dns_options_valid4 (void)
+{
+	GArray *ns4 = g_array_new (FALSE, FALSE, sizeof (guint32));
+	GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free);
+	const char *rc =
+"options debug: rotate:yes edns0 foobar : inet6\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET, rc, ns4, dns_options));
+	g_assert_cmpint (dns_options->len, ==, 1);
+	assert_dns_option (dns_options, 0, "edns0");
+
+	g_array_free (ns4, TRUE);
+	g_ptr_array_free (dns_options, TRUE);
+}
+
+static void
+test_capture_dns_options_valid6 (void)
+{
+	GArray *ns6 = g_array_new (FALSE, FALSE, sizeof (struct in6_addr));
+	GPtrArray *dns_options = g_ptr_array_new_with_free_func (g_free);
+	const char *rc =
+"options inet6 debug foobar rotate:\r\n";
+
+	g_assert (nm_utils_resolve_conf_parse (AF_INET6, rc, ns6, dns_options));
+	g_assert_cmpint (dns_options->len, ==, 2);
+	assert_dns_option (dns_options, 0, "inet6");
+	assert_dns_option (dns_options, 1, "debug");
+
+	g_array_free (ns6, TRUE);
+	g_ptr_array_free (dns_options, TRUE);
+}
+/*****************************************************************************/
+
+NMTST_DEFINE ();
+
+int
+main (int argc, char **argv)
+{
+	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
+
+	g_test_add_func ("/resolvconf-capture/empty", test_capture_empty);
+	g_test_add_func ("/resolvconf-capture/basic4", test_capture_basic4);
+	g_test_add_func ("/resolvconf-capture/dup4", test_capture_dup4);
+	g_test_add_func ("/resolvconf-capture/basic6", test_capture_basic6);
+	g_test_add_func ("/resolvconf-capture/dup6", test_capture_dup6);
+	g_test_add_func ("/resolvconf-capture/addr4-with-6", test_capture_addr4_with_6);
+	g_test_add_func ("/resolvconf-capture/addr6-with-4", test_capture_addr6_with_4);
+	g_test_add_func ("/resolvconf-capture/format", test_capture_format);
+	g_test_add_func ("/resolvconf-capture/dns-options", test_capture_dns_options);
+	g_test_add_func ("/resolvconf-capture/dns-options-dup", test_capture_dns_options_dup);
+	g_test_add_func ("/resolvconf-capture/dns-options-valid4", test_capture_dns_options_valid4);
+	g_test_add_func ("/resolvconf-capture/dns-options-valid6", test_capture_dns_options_valid6);
+
+	return g_test_run ();
+}
+
diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c
index 436fc68b..12ee0059 100644
--- a/src/vpn/nm-vpn-connection.c
+++ b/src/vpn/nm-vpn-connection.c
@@ -51,6 +51,8 @@
 #include "nm-vpn-manager.h"
 #include "dns/nm-dns-manager.h"
 
+#include "introspection/org.freedesktop.NetworkManager.VPN.Connection.h"
+
 typedef enum {
 	/* Only system secrets */
 	SECRETS_REQ_SYSTEM = 0,
@@ -80,6 +82,7 @@ typedef enum {
 } VpnState;
 
 enum {
+	VPN_STATE_CHANGED,
 	INTERNAL_STATE_CHANGED,
 	INTERNAL_RETRY_AFTER_FAILURE,
 
@@ -156,6 +159,19 @@ struct _NMVpnConnection {
 
 struct _NMVpnConnectionClass {
 	NMActiveConnectionClass parent;
+
+	/* Signals */
+	void (*vpn_state_changed) (NMVpnConnection *self,
+	                           NMVpnConnectionState new_state,
+	                           NMActiveConnectionStateReason reason);
+
+	/* not exported over D-Bus */
+	void (*internal_state_changed) (NMVpnConnection *self,
+	                                NMVpnConnectionState new_state,
+	                                NMVpnConnectionState old_state,
+	                                NMActiveConnectionStateReason reason);
+
+	void (*internal_failed_retry)  (NMVpnConnection *self);
 };
 
 G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
@@ -164,9 +180,6 @@ G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
 
 /*****************************************************************************/
 
-static const NMDBusInterfaceInfoExtended interface_info_vpn_connection;
-static const GDBusSignalInfo signal_info_vpn_state_changed;
-
 static NMSettingsConnection *_get_settings_connection (NMVpnConnection *self,
                                                        gboolean allow_missing);
 
@@ -498,12 +511,7 @@ _set_vpn_state (NMVpnConnection *self,
 	old_external_state = _state_to_nm_vpn_state (old_vpn_state);
 	new_external_state = _state_to_nm_vpn_state (priv->vpn_state);
 	if (new_external_state != old_external_state) {
-		nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self),
-		                            &interface_info_vpn_connection,
-		                            &signal_info_vpn_state_changed,
-		                            "(uu)",
-		                            (guint32) new_external_state,
-		                            (guint32) reason);
+		g_signal_emit (self, signals[VPN_STATE_CHANGED], 0, new_external_state, reason);
 		g_signal_emit (self, signals[INTERNAL_STATE_CHANGED], 0,
 		               new_external_state,
 		               old_external_state,
@@ -855,7 +863,6 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection,
 {
 	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
 	g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
-	g_return_val_if_fail (specific_object, NULL);
 
 	return (NMVpnConnection *) g_object_new (NM_TYPE_VPN_CONNECTION,
 	                                         NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, settings_connection,
@@ -1171,8 +1178,8 @@ _cleanup_failed_config (NMVpnConnection *self)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	nm_dbus_object_clear_and_unexport (&priv->ip4_config);
-	nm_dbus_object_clear_and_unexport (&priv->ip6_config);
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 
 	_LOGW ("VPN connection: did not receive valid IP config information");
 	_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
@@ -1389,12 +1396,12 @@ nm_vpn_connection_config_get (NMVpnConnection *self, GVariant *dict)
 	priv->has_ip4 = FALSE;
 	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &b))
 		priv->has_ip4 = b;
-	nm_dbus_object_clear_and_unexport (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
 
 	priv->has_ip6 = FALSE;
 	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &b))
 		priv->has_ip6 = b;
-	nm_dbus_object_clear_and_unexport (&priv->ip6_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 
 	nm_vpn_connection_config_maybe_complete (self, TRUE);
 }
@@ -1455,7 +1462,6 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	NMPlatformIP4Address address;
 	guint32 u32, route_metric;
 	NMSettingIPConfig *s_ip;
-	NMSettingConnection *s_con;
 	guint32 route_table;
 	NMIP4Config *config;
 	GVariantIter *iter;
@@ -1560,7 +1566,6 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	route_table = get_route_table (self, AF_INET, TRUE);
 	route_metric = nm_vpn_connection_get_ip4_route_metric (self);
 	s_ip = nm_connection_get_setting_ip4_config (_get_applied_connection (self));
-	s_con = nm_connection_get_setting_connection (_get_applied_connection (self));
 
 	if (nm_setting_ip_config_get_ignore_auto_routes (s_ip)) {
 		/* ignore VPN routes */
@@ -1618,7 +1623,6 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	/* Merge in user overrides from the NMConnection's IPv4 setting */
 	nm_ip4_config_merge_setting (config,
 	                             s_ip,
-	                             nm_setting_connection_get_mdns (s_con),
 	                             route_table,
 	                             route_metric);
 
@@ -1648,7 +1652,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 		g_object_unref (config);
 	} else {
 		priv->ip4_config = config;
-		nm_dbus_object_export (NM_DBUS_OBJECT (config));
+		nm_exported_object_export (NM_EXPORTED_OBJECT (config));
 		g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP4_CONFIG);
 	}
 
@@ -1837,7 +1841,7 @@ next:
 		g_object_unref (config);
 	} else {
 		priv->ip6_config = config;
-		nm_dbus_object_export (NM_DBUS_OBJECT (config));
+		nm_exported_object_export (NM_EXPORTED_OBJECT (config));
 		g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP6_CONFIG);
 	}
 
@@ -2324,7 +2328,7 @@ nm_vpn_connection_activate (NMVpnConnection *self,
 	if (nm_vpn_plugin_info_supports_multiple (plugin_info)) {
 		const char *path;
 
-		path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self));
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
 		if (path)
 			path = strrchr (path, '/');
 		g_return_if_fail (path);
@@ -2547,6 +2551,7 @@ static void
 plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMVpnConnection *self;
+	NMVpnConnectionPrivate *priv;
 	gs_unref_variant GVariant *reply = NULL;
 	gs_free_error GError *error = NULL;
 
@@ -2555,6 +2560,7 @@ plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da
 		return;
 
 	self = NM_VPN_CONNECTION (user_data);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (error) {
 		g_dbus_error_strip_remote_error (error);
@@ -2774,8 +2780,8 @@ dispose (GObject *object)
 	nm_clear_g_cancellable (&priv->cancellable);
 
 	g_clear_object (&priv->proxy_config);
-	nm_dbus_object_clear_and_unexport (&priv->ip4_config);
-	nm_dbus_object_clear_and_unexport (&priv->ip6_config);
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 	g_clear_object (&priv->proxy);
 	g_clear_object (&priv->plugin_info);
 
@@ -2825,14 +2831,14 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_string (value, priv->banner ? priv->banner : "");
 		break;
 	case PROP_IP4_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip4_config : NULL);
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip4_config : NULL);
 		break;
 	case PROP_IP6_CONFIG:
-		nm_dbus_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip6_config : NULL);
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip6_config : NULL);
 		break;
 	case PROP_MASTER:
 		parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object));
-		nm_dbus_utils_g_value_set_object_path (value, parent_dev);
+		nm_utils_g_value_set_object_path (value, parent_dev);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2840,42 +2846,16 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
-static const GDBusSignalInfo signal_info_vpn_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
-	"VpnStateChanged",
-	.args = NM_DEFINE_GDBUS_ARG_INFOS (
-		NM_DEFINE_GDBUS_ARG_INFO ("state",  "u"),
-		NM_DEFINE_GDBUS_ARG_INFO ("reason", "u"),
-	),
-);
-
-static const NMDBusInterfaceInfoExtended interface_info_vpn_connection = {
-	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
-		NM_DBUS_INTERFACE_VPN_CONNECTION,
-		.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
-			&nm_signal_info_property_changed_legacy,
-			&signal_info_vpn_state_changed,
-		),
-		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("VpnState", "u", NM_VPN_CONNECTION_VPN_STATE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Banner",   "s", NM_VPN_CONNECTION_BANNER),
-		),
-	),
-	.legacy_property_changed = TRUE,
-};
-
 static void
 nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
 	NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS (connection_class);
-	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (connection_class);
-
-	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_vpn_connection);
 
+	/* virtual methods */
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
-
 	active_class->device_state_changed = device_state_changed;
 	active_class->device_changed = device_changed;
 
@@ -2902,6 +2882,13 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 	g_object_class_override_property (object_class, PROP_IP6_CONFIG,
 	                                  NM_ACTIVE_CONNECTION_IP6_CONFIG);
 
+	signals[VPN_STATE_CHANGED] =
+	    g_signal_new ("vpn-state-changed",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
 	signals[INTERNAL_STATE_CHANGED] =
 	    g_signal_new (NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -2915,4 +2902,8 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (connection_class),
+	                                        NMDBUS_TYPE_VPN_CONNECTION_SKELETON,
+	                                        NULL);
 }
diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h
index 3046634d..b287c334 100644
--- a/src/vpn/nm-vpn-connection.h
+++ b/src/vpn/nm-vpn-connection.h
@@ -42,6 +42,7 @@
 #define NM_VPN_CONNECTION_BANNER "banner"
 
 /* Signals */
+/* not exported: includes old reason code */
 #define NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED       "internal-state-changed"
 #define NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE "internal-retry-after-failure"