summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-08-26 02:18:32 +0200
committerMichael Biebl <biebl@debian.org>2016-08-26 02:18:32 +0200
commit7514efc2f38c9ace4557d4e69d68e7d380389030 (patch)
tree7fb00fda86cfcc2ca377f191633a7cfdbfea7ca3 /src
parentd6201f5d8daada3d64a0a3e0038e14eebec683ce (diff)
Imported Upstream version 1.4.0 upstream/1.4.0
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am313
-rw-r--r--src/Makefile.in607
-rw-r--r--src/NetworkManager.ver3
-rw-r--r--src/NetworkManagerUtils.c10
-rw-r--r--src/devices/Makefile.in14
-rw-r--r--src/devices/adsl/Makefile.am1
-rw-r--r--src/devices/adsl/Makefile.in15
-rw-r--r--src/devices/adsl/nm-atm-manager.h4
-rw-r--r--src/devices/adsl/nm-device-adsl.h7
-rw-r--r--src/devices/bluetooth/Makefile.am1
-rw-r--r--src/devices/bluetooth/Makefile.in15
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c49
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h3
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c17
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h4
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c16
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h5
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c18
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.h8
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h2
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c22
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h7
-rw-r--r--src/devices/bluetooth/nm-device-bt.c21
-rw-r--r--src/devices/bluetooth/nm-device-bt.h4
-rw-r--r--src/devices/nm-arping-manager.h4
-rw-r--r--src/devices/nm-device-bond.c154
-rw-r--r--src/devices/nm-device-bond.h6
-rw-r--r--src/devices/nm-device-bridge.h7
-rw-r--r--src/devices/nm-device-ethernet-utils.c28
-rw-r--r--src/devices/nm-device-ethernet-utils.h4
-rw-r--r--src/devices/nm-device-ethernet.c304
-rw-r--r--src/devices/nm-device-ethernet.h19
-rw-r--r--src/devices/nm-device-factory.h4
-rw-r--r--src/devices/nm-device-generic.h6
-rw-r--r--src/devices/nm-device-infiniband.c13
-rw-r--r--src/devices/nm-device-infiniband.h6
-rw-r--r--src/devices/nm-device-ip-tunnel.c9
-rw-r--r--src/devices/nm-device-ip-tunnel.h6
-rw-r--r--src/devices/nm-device-logging.h1
-rw-r--r--src/devices/nm-device-macvlan.c36
-rw-r--r--src/devices/nm-device-macvlan.h6
-rw-r--r--src/devices/nm-device-private.h15
-rw-r--r--src/devices/nm-device-tun.c10
-rw-r--r--src/devices/nm-device-tun.h6
-rw-r--r--src/devices/nm-device-veth.c55
-rw-r--r--src/devices/nm-device-veth.h10
-rw-r--r--src/devices/nm-device-vlan.c62
-rw-r--r--src/devices/nm-device-vlan.h6
-rw-r--r--src/devices/nm-device-vxlan.c19
-rw-r--r--src/devices/nm-device-vxlan.h6
-rw-r--r--src/devices/nm-device.c1945
-rw-r--r--src/devices/nm-device.h31
-rw-r--r--src/devices/nm-lldp-listener.c9
-rw-r--r--src/devices/nm-lldp-listener.h4
-rw-r--r--src/devices/team/Makefile.am1
-rw-r--r--src/devices/team/Makefile.in15
-rw-r--r--src/devices/team/nm-device-team.c118
-rw-r--r--src/devices/team/nm-device-team.h9
-rw-r--r--src/devices/team/nm-team-factory.h2
-rw-r--r--src/devices/tests/Makefile.in14
-rw-r--r--src/devices/tests/test-lldp.c2
-rw-r--r--src/devices/wifi/Makefile.am1
-rw-r--r--src/devices/wifi/Makefile.in15
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.h6
-rw-r--r--src/devices/wifi/nm-device-wifi.c451
-rw-r--r--src/devices/wifi/nm-device-wifi.h47
-rw-r--r--src/devices/wifi/nm-wifi-ap.c210
-rw-r--r--src/devices/wifi/nm-wifi-ap.h10
-rw-r--r--src/devices/wifi/tests/Makefile.in14
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c2
-rw-r--r--src/devices/wwan/Makefile.am13
-rw-r--r--src/devices/wwan/Makefile.in51
-rw-r--r--src/devices/wwan/nm-device-modem.c33
-rw-r--r--src/devices/wwan/nm-modem-broadband.c158
-rw-r--r--src/devices/wwan/nm-modem-broadband.h6
-rw-r--r--src/devices/wwan/nm-modem-enum-types.c1
-rw-r--r--src/devices/wwan/nm-modem-manager.c372
-rw-r--r--src/devices/wwan/nm-modem-ofono.c1202
-rw-r--r--src/devices/wwan/nm-modem-ofono.h53
-rw-r--r--src/devices/wwan/nm-modem.c8
-rw-r--r--src/devices/wwan/nm-modem.h5
-rw-r--r--src/devices/wwan/nm-wwan-factory.c4
-rw-r--r--src/devices/wwan/nm-wwan-factory.h2
-rw-r--r--src/dhcp-manager/Makefile.in14
-rw-r--r--src/dhcp-manager/nm-dhcp-client-logging.h24
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c99
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c2
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c6
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.h1
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.c10
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c10
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c28
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c150
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.h2
-rw-r--r--src/dhcp-manager/tests/Makefile.in14
-rw-r--r--src/dhcp-manager/tests/test-dhcp-dhclient.c2
-rw-r--r--src/dhcp-manager/tests/test-dhcp-utils.c2
-rw-r--r--src/dns-manager/nm-dns-manager.h5
-rw-r--r--src/dns-manager/nm-dns-plugin.h2
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.c107
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.h5
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-utils.h1
-rw-r--r--src/dnsmasq-manager/tests/Makefile.in14
-rw-r--r--src/dnsmasq-manager/tests/test-dnsmasq-utils.c2
-rw-r--r--src/main-utils.c3
-rw-r--r--src/main-utils.h2
-rw-r--r--src/main.c95
-rw-r--r--src/nm-activation-request.c6
-rw-r--r--src/nm-activation-request.h2
-rw-r--r--src/nm-active-connection.c15
-rw-r--r--src/nm-audit-manager.c11
-rw-r--r--src/nm-audit-manager.h32
-rw-r--r--src/nm-auth-manager.h7
-rw-r--r--src/nm-auth-subject.c48
-rw-r--r--src/nm-auth-subject.h8
-rw-r--r--src/nm-auth-utils.c4
-rw-r--r--src/nm-bus-manager.c48
-rw-r--r--src/nm-bus-manager.h10
-rw-r--r--src/nm-checkpoint-manager.c298
-rw-r--r--src/nm-checkpoint-manager.h50
-rw-r--r--src/nm-checkpoint.c433
-rw-r--r--src/nm-checkpoint.h49
-rw-r--r--src/nm-config-data.c201
-rw-r--r--src/nm-config-data.h11
-rw-r--r--src/nm-config.c283
-rw-r--r--src/nm-config.h30
-rw-r--r--src/nm-connection-provider.c126
-rw-r--r--src/nm-connection-provider.h127
-rw-r--r--src/nm-connectivity.c4
-rw-r--r--src/nm-connectivity.h2
-rw-r--r--src/nm-core-utils.c526
-rw-r--r--src/nm-core-utils.h45
-rw-r--r--src/nm-dcb.h1
-rw-r--r--src/nm-default-route-manager.c14
-rw-r--r--src/nm-default-route-manager.h5
-rw-r--r--src/nm-dispatcher.c54
-rw-r--r--src/nm-dispatcher.h7
-rw-r--r--src/nm-enum-types.c116
-rw-r--r--src/nm-enum-types.h8
-rw-r--r--src/nm-exported-object.c20
-rw-r--r--src/nm-exported-object.h8
-rw-r--r--src/nm-firewall-manager.h5
-rw-r--r--src/nm-iface-helper.c73
-rw-r--r--src/nm-ip4-config.c255
-rw-r--r--src/nm-ip4-config.h13
-rw-r--r--src/nm-ip6-config.c266
-rw-r--r--src/nm-ip6-config.h15
-rw-r--r--src/nm-logging.c247
-rw-r--r--src/nm-logging.h29
-rw-r--r--src/nm-manager.c520
-rw-r--r--src/nm-manager.h21
-rw-r--r--src/nm-multi-index.h7
-rw-r--r--src/nm-policy.c67
-rw-r--r--src/nm-policy.h2
-rw-r--r--src/nm-rfkill-manager.c4
-rw-r--r--src/nm-rfkill-manager.h5
-rw-r--r--src/nm-route-manager.c4
-rw-r--r--src/nm-route-manager.h3
-rw-r--r--src/nm-session-monitor.c39
-rw-r--r--src/nm-session-monitor.h13
-rw-r--r--src/nm-sleep-monitor.h4
-rw-r--r--src/nm-test-utils-core.h296
-rw-r--r--src/nm-types.h25
-rw-r--r--src/platform/Makefile.in14
-rw-r--r--src/platform/nm-fake-platform.c27
-rw-r--r--src/platform/nm-linux-platform.c316
-rw-r--r--src/platform/nm-linux-platform.h6
-rw-r--r--src/platform/nm-platform-utils.c134
-rw-r--r--src/platform/nm-platform-utils.h7
-rw-r--r--src/platform/nm-platform.c199
-rw-r--r--src/platform/nm-platform.h49
-rw-r--r--src/platform/nmp-object.c14
-rw-r--r--src/platform/nmp-object.h6
-rw-r--r--src/platform/tests/Makefile.am6
-rw-r--r--src/platform/tests/Makefile.in25
-rw-r--r--src/platform/tests/monitor.c2
-rw-r--r--src/platform/tests/test-common.h4
-rw-r--r--src/platform/tests/test-general.c2
-rw-r--r--src/platform/tests/test-link.c73
-rw-r--r--src/platform/tests/test-nmp-object.c2
-rw-r--r--src/platform/tests/test-route.c16
-rw-r--r--src/platform/wifi/wifi-utils-private.h2
-rw-r--r--src/platform/wifi/wifi-utils.c21
-rw-r--r--src/platform/wifi/wifi-utils.h3
-rw-r--r--src/ppp-manager/Makefile.am3
-rw-r--r--src/ppp-manager/Makefile.in20
-rw-r--r--src/ppp-manager/nm-ppp-manager.c22
-rw-r--r--src/ppp-manager/nm-pppd-plugin.c2
-rw-r--r--src/rdisc/Makefile.in14
-rw-r--r--src/rdisc/nm-fake-rdisc.c44
-rw-r--r--src/rdisc/nm-lndp-rdisc.c135
-rw-r--r--src/rdisc/nm-lndp-rdisc.h23
-rw-r--r--src/rdisc/nm-rdisc-private.h30
-rw-r--r--src/rdisc/nm-rdisc.c581
-rw-r--r--src/rdisc/nm-rdisc.h68
-rw-r--r--src/rdisc/tests/Makefile.am2
-rw-r--r--src/rdisc/tests/Makefile.in18
-rw-r--r--src/rdisc/tests/test-rdisc-fake.c181
-rw-r--r--src/rdisc/tests/test-rdisc-linux.c3
-rw-r--r--src/settings/nm-inotify-helper.h2
-rw-r--r--src/settings/nm-secret-agent.h1
-rw-r--r--src/settings/nm-settings-connection.c78
-rw-r--r--src/settings/nm-settings-connection.h5
-rw-r--r--src/settings/nm-settings-plugin.h8
-rw-r--r--src/settings/nm-settings.c236
-rw-r--r--src/settings/nm-settings.h29
-rw-r--r--src/settings/plugins/Makefile.in14
-rw-r--r--src/settings/plugins/ibft/Makefile.in14
-rw-r--r--src/settings/plugins/ibft/nm-ibft-connection.h4
-rw-r--r--src/settings/plugins/ibft/plugin.h2
-rw-r--r--src/settings/plugins/ibft/reader.h2
-rw-r--r--src/settings/plugins/ibft/tests/Makefile.am3
-rw-r--r--src/settings/plugins/ibft/tests/Makefile.in47
-rw-r--r--src/settings/plugins/ibft/tests/test-ibft.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/Makefile.in14
-rw-r--r--src/settings/plugins/ifcfg-rh/common.h4
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c7
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h8
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c23
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.h4
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c207
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.h5
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c20
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/Makefile.am3
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/Makefile.in46
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in14
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c66
-rw-r--r--src/settings/plugins/ifcfg-rh/utils.c3
-rw-r--r--src/settings/plugins/ifcfg-rh/utils.h5
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c93
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.h6
-rw-r--r--src/settings/plugins/ifnet/Makefile.in14
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c2
-rw-r--r--src/settings/plugins/ifnet/net_parser.c10
-rw-r--r--src/settings/plugins/ifnet/net_parser.h3
-rw-r--r--src/settings/plugins/ifnet/net_utils.h5
-rw-r--r--src/settings/plugins/ifnet/nm-ifnet-connection.h5
-rw-r--r--src/settings/plugins/ifnet/plugin.h2
-rw-r--r--src/settings/plugins/ifnet/tests/Makefile.am5
-rw-r--r--src/settings/plugins/ifnet/tests/Makefile.in49
-rw-r--r--src/settings/plugins/ifnet/tests/test-ifnet.c4
-rw-r--r--src/settings/plugins/ifnet/wpa_parser.c13
-rw-r--r--src/settings/plugins/ifnet/wpa_parser.h2
-rw-r--r--src/settings/plugins/ifupdown/Makefile.in14
-rw-r--r--src/settings/plugins/ifupdown/interface_parser.h2
-rw-r--r--src/settings/plugins/ifupdown/nm-ifupdown-connection.h6
-rw-r--r--src/settings/plugins/ifupdown/parser.h7
-rw-r--r--src/settings/plugins/ifupdown/plugin.h2
-rw-r--r--src/settings/plugins/ifupdown/tests/Makefile.am6
-rw-r--r--src/settings/plugins/ifupdown/tests/Makefile.in49
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c5
-rw-r--r--src/settings/plugins/keyfile/Makefile.in14
-rw-r--r--src/settings/plugins/keyfile/nm-keyfile-connection.h4
-rw-r--r--src/settings/plugins/keyfile/plugin.c1
-rw-r--r--src/settings/plugins/keyfile/plugin.h2
-rw-r--r--src/settings/plugins/keyfile/reader.h2
-rw-r--r--src/settings/plugins/keyfile/tests/Makefile.in14
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.in14
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c4
-rw-r--r--src/settings/plugins/keyfile/utils.h1
-rw-r--r--src/settings/plugins/keyfile/writer.h2
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c47
-rw-r--r--src/supplicant-manager/nm-supplicant-config.h12
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c26
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.h3
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.c16
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.h3
-rw-r--r--src/supplicant-manager/tests/Makefile.am2
-rw-r--r--src/supplicant-manager/tests/Makefile.in16
-rw-r--r--src/supplicant-manager/tests/certs/Makefile.in14
-rw-r--r--src/supplicant-manager/tests/test-supplicant-config.c16
-rw-r--r--src/systemd/nm-sd.c7
-rw-r--r--src/systemd/nm-sd.h11
-rw-r--r--src/systemd/sd-adapt/build.h3
-rw-r--r--src/systemd/sd-adapt/cgroup-util.h3
-rw-r--r--src/systemd/sd-adapt/condition.h5
-rw-r--r--src/systemd/sd-adapt/conf-parser.h3
-rw-r--r--src/systemd/sd-adapt/def.h3
-rw-r--r--src/systemd/sd-adapt/dirent-util.h3
-rw-r--r--src/systemd/sd-adapt/formats-util.h3
-rw-r--r--src/systemd/sd-adapt/gunicode.h3
-rw-r--r--src/systemd/sd-adapt/libudev.h5
-rw-r--r--src/systemd/sd-adapt/missing.h3
-rw-r--r--src/systemd/sd-adapt/mkdir.h3
-rw-r--r--src/systemd/sd-adapt/nm-sd-adapt.c (renamed from src/systemd/nm-sd-adapt.c)0
-rw-r--r--src/systemd/sd-adapt/nm-sd-adapt.h (renamed from src/systemd/nm-sd-adapt.h)6
-rw-r--r--src/systemd/sd-adapt/process-util.h3
-rw-r--r--src/systemd/sd-adapt/sd-daemon.h3
-rw-r--r--src/systemd/sd-adapt/stat-util.h3
-rw-r--r--src/systemd/sd-adapt/udev-util.h3
-rw-r--r--src/systemd/sd-adapt/udev.h5
-rw-r--r--src/systemd/sd-adapt/user-util.h3
-rw-r--r--src/systemd/sd-adapt/virt.h3
-rw-r--r--src/systemd/src/basic/escape.h2
-rw-r--r--src/systemd/src/basic/ether-addr-util.c69
-rw-r--r--src/systemd/src/basic/ether-addr-util.h2
-rw-r--r--src/systemd/src/basic/extract-word.c302
-rw-r--r--src/systemd/src/basic/extract-word.h35
-rw-r--r--src/systemd/src/basic/fd-util.c26
-rw-r--r--src/systemd/src/basic/fd-util.h3
-rw-r--r--src/systemd/src/basic/fileio.c210
-rw-r--r--src/systemd/src/basic/fileio.h8
-rw-r--r--src/systemd/src/basic/fs-util.c112
-rw-r--r--src/systemd/src/basic/fs-util.h5
-rw-r--r--src/systemd/src/basic/hashmap.c37
-rw-r--r--src/systemd/src/basic/hostname-util.c10
-rw-r--r--src/systemd/src/basic/in-addr-util.c105
-rw-r--r--src/systemd/src/basic/in-addr-util.h5
-rw-r--r--src/systemd/src/basic/io-util.c5
-rw-r--r--src/systemd/src/basic/macro.h9
-rw-r--r--src/systemd/src/basic/parse-util.c46
-rw-r--r--src/systemd/src/basic/parse-util.h17
-rw-r--r--src/systemd/src/basic/path-util.c79
-rw-r--r--src/systemd/src/basic/path-util.h20
-rw-r--r--src/systemd/src/basic/random-util.c4
-rw-r--r--src/systemd/src/basic/set.h10
-rw-r--r--src/systemd/src/basic/signal-util.h56
-rw-r--r--src/systemd/src/basic/siphash24.c2
-rw-r--r--src/systemd/src/basic/socket-util.c122
-rw-r--r--src/systemd/src/basic/socket-util.h17
-rw-r--r--src/systemd/src/basic/string-table.h56
-rw-r--r--src/systemd/src/basic/string-util.c47
-rw-r--r--src/systemd/src/basic/string-util.h8
-rw-r--r--src/systemd/src/basic/strv.c90
-rw-r--r--src/systemd/src/basic/strv.h9
-rw-r--r--src/systemd/src/basic/time-util.c258
-rw-r--r--src/systemd/src/basic/time-util.h32
-rw-r--r--src/systemd/src/basic/unaligned.h18
-rw-r--r--src/systemd/src/basic/utf8.h2
-rw-r--r--src/systemd/src/basic/util.c229
-rw-r--r--src/systemd/src/basic/util.h17
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c10
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c36
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.h40
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h3
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-network.c4
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-protocol.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h3
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c51
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.h8
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c3
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c186
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h6
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c270
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c171
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c505
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c180
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c105
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c87
-rw-r--r--src/systemd/src/libsystemd/sd-id128/id128-util.c200
-rw-r--r--src/systemd/src/libsystemd/sd-id128/id128-util.h45
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c122
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h78
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h61
-rw-r--r--src/systemd/src/systemd/sd-event.h1
-rw-r--r--src/systemd/src/systemd/sd-ipv4acd.h28
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h6
-rw-r--r--src/systemd/src/systemd/sd-lldp.h90
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h114
-rw-r--r--src/tests/Makefile.am12
-rw-r--r--src/tests/Makefile.in31
-rw-r--r--src/tests/config/Makefile.am7
-rw-r--r--src/tests/config/Makefile.in24
-rw-r--r--src/tests/config/NetworkManager.state4
-rw-r--r--src/tests/config/nm-test-device.c14
-rw-r--r--src/tests/config/nm-test-device.h6
-rw-r--r--src/tests/config/test-config.c63
-rw-r--r--src/tests/test-dcb.c2
-rw-r--r--src/tests/test-general-with-expect.c2
-rw-r--r--src/tests/test-general.c2
-rw-r--r--src/tests/test-ip4-config.c42
-rw-r--r--src/tests/test-ip6-config.c42
-rw-r--r--src/tests/test-resolvconf-capture.c2
-rw-r--r--src/tests/test-route-manager.c101
-rw-r--r--src/tests/test-systemd.c56
-rw-r--r--src/tests/test-utils.c83
-rw-r--r--src/tests/test-wired-defname.c26
-rw-r--r--src/vpn-manager/nm-vpn-connection.c291
-rw-r--r--src/vpn-manager/nm-vpn-connection.h22
-rw-r--r--src/vpn-manager/nm-vpn-manager.h1
387 files changed, 14060 insertions, 6747 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 48af5a06..c460caff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,41 +32,95 @@ SUBDIRS += \
 	tests
 endif
 
-AM_CPPFLAGS =				\
-	-I$(top_srcdir)/shared		\
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/shared \
 	-I$(top_builddir)/shared \
-	-I$(top_builddir)/introspection	\
-	-I$(top_srcdir)/libnm-core	\
-	-I$(top_builddir)/libnm-core	\
-	-I$(top_srcdir)/callouts	\
+	-I$(top_builddir)/introspection \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	\
+	 $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d) \
+	\
 	-DPREFIX=\"$(prefix)\"          \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
-	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON
+	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
+	\
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
+	-DLIBEXECDIR=\"$(libexecdir)\" \
+	-DLOCALSTATEDIR=\"$(localstatedir)\" \
+	-DRUNSTATEDIR=\"$(runstatedir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DRUNDIR=\"$(rundir)\" \
+	\
+	-DNMCONFDIR=\"$(nmconfdir)\" \
+	-DNMLOCALEDIR=\"$(datadir)/locale\" \
+	-DNMPLUGINDIR=\"$(pkglibdir)\" \
+	-DNMRUNDIR=\"$(nmrundir)\" \
+	-DNMSTATEDIR=\"$(nmstatedir)\" \
+	-DNMLIBDIR=\"$(nmlibdir)\" \
+	\
+	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \
+	-DPPPD_PLUGIN_DIR=\"$(PPPD_PLUGIN_DIR)\" \
+	\
+	$(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(LIBNDP_CFLAGS) \
+	$(LIBSOUP_CFLAGS) \
+	$(SELINUX_CFLAGS) \
+	$(LIBAUDIT_CFLAGS) \
+	$(SYSTEMD_LOGIN_CFLAGS) \
+	$(SYSTEMD_JOURNAL_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS) \
+	\
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/ \
+	\
+	$(NULL)
 
-# add each subdirectory that contains a libNM source file. $(sort) is being used
-# primarily for its side effect of removing duplicates.
-AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
+sbin_PROGRAMS = \
+	NetworkManager
+
+libexec_PROGRAMS = \
+	nm-iface-helper
 
 noinst_LTLIBRARIES = \
+	libNetworkManagerBase.la \
 	libNetworkManager.la \
+	libNetworkManagerTest.la \
 	libnm-iface-helper.la \
 	libsystemd-nm.la
 
-######################
-# libsystemd-nm
-######################
-
-SYSTEMD_NM_CFLAGS_PATHS = \
-	-I$(top_srcdir)/src/systemd/src/systemd \
-	-I$(top_srcdir)/src/systemd/src/libsystemd-network \
-	-I$(top_srcdir)/src/systemd/src/basic \
-	-I$(top_srcdir)/src/systemd
+###############################################################################
 
 libsystemd_nm_la_SOURCES = \
 	systemd/nm-sd.c \
 	systemd/nm-sd.h \
-	systemd/nm-sd-adapt.c \
-	systemd/nm-sd-adapt.h \
+	systemd/sd-adapt/nm-sd-adapt.c \
+	systemd/sd-adapt/nm-sd-adapt.h \
+	systemd/sd-adapt/build.h \
+	systemd/sd-adapt/cgroup-util.h \
+	systemd/sd-adapt/condition.h \
+	systemd/sd-adapt/conf-parser.h \
+	systemd/sd-adapt/def.h \
+	systemd/sd-adapt/dirent-util.h \
+	systemd/sd-adapt/formats-util.h \
+	systemd/sd-adapt/gunicode.h \
+	systemd/sd-adapt/libudev.h \
+	systemd/sd-adapt/missing.h \
+	systemd/sd-adapt/mkdir.h \
+	systemd/sd-adapt/process-util.h \
+	systemd/sd-adapt/sd-daemon.h \
+	systemd/sd-adapt/stat-util.h \
+	systemd/sd-adapt/udev-util.h \
+	systemd/sd-adapt/udev.h \
+	systemd/sd-adapt/user-util.h \
+	systemd/sd-adapt/virt.h \
 	systemd/src/basic/alloc-util.c \
 	systemd/src/basic/alloc-util.h \
 	systemd/src/basic/async.h \
@@ -74,6 +128,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/escape.h \
 	systemd/src/basic/ether-addr-util.c \
 	systemd/src/basic/ether-addr-util.h \
+	systemd/src/basic/extract-word.c \
+	systemd/src/basic/extract-word.h \
 	systemd/src/basic/fd-util.c \
 	systemd/src/basic/fd-util.h \
 	systemd/src/basic/fileio.c \
@@ -107,6 +163,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/random-util.h \
 	systemd/src/basic/refcnt.h \
 	systemd/src/basic/set.h \
+	systemd/src/basic/signal-util.h \
 	systemd/src/basic/siphash24.c \
 	systemd/src/basic/siphash24.h \
 	systemd/src/basic/socket-util.c \
@@ -157,6 +214,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/sd-ipv4ll.c \
 	systemd/src/libsystemd-network/sd-lldp.c \
 	systemd/src/libsystemd/sd-event/sd-event.c \
+	systemd/src/libsystemd/sd-id128/id128-util.c \
+	systemd/src/libsystemd/sd-id128/id128-util.h \
 	systemd/src/libsystemd/sd-id128/sd-id128.c \
 	systemd/src/shared/dns-domain.c \
 	systemd/src/shared/dns-domain.h \
@@ -173,64 +232,40 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/systemd/sd-ndisc.h
 
 libsystemd_nm_la_CPPFLAGS = \
-	-I$(top_srcdir)/shared \
-	-I$(top_builddir)/shared \
+	-I$(top_srcdir)/shared/ \
+	-I$(top_builddir)/shared/ \
 	-I$(top_srcdir)/libnm-core \
 	-I$(top_builddir)/libnm-core \
-	$(SYSTEMD_NM_CFLAGS_PATHS) \
-	-I$(top_srcdir)/src/systemd/src/shared \
+	-I$(top_srcdir)/src/systemd/sd-adapt/ \
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/basic/ \
+	-I$(top_srcdir)/src/systemd/src/shared/ \
+	-I$(top_srcdir)/src/systemd/src/libsystemd-network/ \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD \
 	$(GLIB_CFLAGS)
 
 libsystemd_nm_la_LIBADD = \
 	$(GLIB_LIBS)
 
-######################
-# libsystemd-nm-base
-######################
+###############################################################################
 
-if ENABLE_TESTS
-noinst_LTLIBRARIES += \
-	libNetworkManager-base.la
-
-libNetworkManager_base_la_SOURCES = \
+libNetworkManagerBase_la_SOURCES = \
 	nm-core-utils.c \
 	nm-core-utils.h \
 	nm-logging.c \
 	nm-logging.h
 
-libNetworkManager_base_la_CPPFLAGS = \
-	-I$(top_srcdir)/shared \
-	-I$(top_builddir)/shared \
-	-I$(top_srcdir)/libnm-core \
-	-I$(top_builddir)/libnm-core \
-	-I$(top_srcdir)/src/platform \
-	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	-DNO_SYSTEMD_JOURNAL \
-	-DPREFIX=\"$(prefix)\" \
-	-DLOCALSTATEDIR=\"$(localstatedir)\" \
-	-DNMSTATEDIR=\"$(nmstatedir)\" \
-	$(GLIB_CFLAGS)
-
-libNetworkManager_base_la_LIBADD = \
+libNetworkManagerBase_la_LIBADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	$(GLIB_LIBS)
-endif
-
-###########################################
-# NetworkManager
-###########################################
-
-sbin_PROGRAMS = NetworkManager
+	$(GLIB_LIBS) \
+	$(SYSTEMD_JOURNAL_LIBS) \
+	$(NULL)
 
-NetworkManager_SOURCES = \
-	$(nm_device_sources) $(nm_device_headers) \
-	$(nm_dhcp_client_sources) $(nm_dhcp_client_headers) \
-	main-utils.c \
-	main-utils.h \
-	main.c
+###############################################################################
 
-NetworkManager_LDADD = libNetworkManager.la
+# These source files have a attribute((constructor)) to register their factories.
+# This gets stripped out from the resulting binary if we add them to libNetworkManager.la.
+# Instead, add them to the binary. An alternative would be to link with --as-needed.
 
 nm_device_sources = \
 	devices/nm-device-bond.c \
@@ -255,23 +290,33 @@ nm_device_headers = \
 	devices/nm-device-tun.h \
 	devices/nm-device-veth.h \
 	devices/nm-device-vlan.h \
-	devices/nm-device-vxlan.h
+	devices/nm-device-vxlan.h \
+	$(NULL)
 
 nm_dhcp_client_sources = \
 	dhcp-manager/nm-dhcp-dhclient.c \
-	dhcp-manager/nm-dhcp-dhclient-utils.c \
 	dhcp-manager/nm-dhcp-dhcpcd.c \
-	dhcp-manager/nm-dhcp-systemd.c
+	dhcp-manager/nm-dhcp-systemd.c \
+	$(NULL)
 
 nm_dhcp_client_headers = \
 	dhcp-manager/nm-dhcp-dhclient.h \
-	dhcp-manager/nm-dhcp-dhclient-utils.h \
 	dhcp-manager/nm-dhcp-dhcpcd.h \
-	dhcp-manager/nm-dhcp-systemd.h
+	dhcp-manager/nm-dhcp-systemd.h \
+	$(NULL)
+
+###############################################################################
 
 libNetworkManager_la_SOURCES = \
-	$(nm_device_headers) \
-	$(nm_dhcp_client_headers) \
+	\
+	dhcp-manager/nm-dhcp-dhclient-utils.c \
+	dhcp-manager/nm-dhcp-dhclient-utils.h \
+	\
+	nm-checkpoint-manager.c \
+	nm-checkpoint-manager.h \
+	nm-checkpoint.c \
+	nm-checkpoint.h \
+	\
 	devices/nm-device.c \
 	devices/nm-device.h \
 	devices/nm-lldp-listener.c \
@@ -321,16 +366,12 @@ libNetworkManager_la_SOURCES = \
 	platform/nm-platform.h \
 	platform/nm-linux-platform.c \
 	platform/nm-linux-platform.h \
-	platform/nm-fake-platform.c \
-	platform/nm-fake-platform.h \
 	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h \
 	platform/wifi/wifi-utils.c \
 	platform/wifi/wifi-utils.h \
 	\
-	rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h \
 	rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h \
 	rdisc/nm-rdisc.c \
@@ -392,8 +433,6 @@ libNetworkManager_la_SOURCES = \
 	nm-config.h \
 	nm-config-data.c \
 	nm-config-data.h \
-	nm-connection-provider.c \
-	nm-connection-provider.h \
 	nm-connectivity.c \
 	nm-connectivity.h \
 	nm-dcb.c \
@@ -418,8 +457,6 @@ libNetworkManager_la_SOURCES = \
 	nm-ip4-config.h \
 	nm-ip6-config.c \
 	nm-ip6-config.h \
-	nm-logging.c \
-	nm-logging.h \
 	nm-auth-manager.c \
 	nm-auth-manager.h \
 	nm-auth-subject.c \
@@ -439,11 +476,9 @@ libNetworkManager_la_SOURCES = \
 	nm-sleep-monitor.c \
 	nm-sleep-monitor.h \
 	nm-types.h \
-	nm-core-utils.c \
-	nm-core-utils.h \
 	NetworkManagerUtils.c \
-	NetworkManagerUtils.h
-
+	NetworkManagerUtils.h \
+	$(NULL)
 
 if WITH_WEXT
 libNetworkManager_la_SOURCES += \
@@ -452,64 +487,17 @@ libNetworkManager_la_SOURCES += \
 endif
 
 
-GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n'
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_enum_types_sources = $(filter-out \
-                              %/nm-device-private.h \
-                              %/nm-rdisc-private.h \
-                              %/wifi-utils-private.h \
-                              %/nm-dhcp-client-logging.h \
-                              %/nm-device-logging.h \
-                              , $(libNetworkManager_la_SOURCES))
-
-BUILT_SOURCES = $(GLIB_GENERATED)
-
-AM_CPPFLAGS += \
-	$(GLIB_CFLAGS) \
-	$(GUDEV_CFLAGS) \
-	$(LIBNL_CFLAGS) \
-	$(LIBNDP_CFLAGS) \
-	$(LIBSOUP_CFLAGS) \
-	$(SELINUX_CFLAGS) \
-	$(LIBAUDIT_CFLAGS) \
-	$(SYSTEMD_LOGIN_CFLAGS) \
-	$(SYSTEMD_JOURNAL_CFLAGS) \
-	$(SYSTEMD_NM_CFLAGS_PATHS) \
-	\
-	-DBINDIR=\"$(bindir)\" \
-	-DDATADIR=\"$(datadir)\" \
-	-DLIBEXECDIR=\"$(libexecdir)\" \
-	-DLOCALSTATEDIR=\"$(localstatedir)\" \
-	-DRUNSTATEDIR=\"$(runstatedir)\" \
-	-DSBINDIR=\"$(sbindir)\" \
-	-DSYSCONFDIR=\"$(sysconfdir)\" \
-	-DRUNDIR=\"$(rundir)\" \
-	\
-	-DNMCONFDIR=\"$(nmconfdir)\" \
-	-DNMLOCALEDIR=\"$(datadir)/locale\" \
-	-DNMPLUGINDIR=\"$(pkglibdir)\" \
-	-DNMRUNDIR=\"$(nmrundir)\" \
-	-DNMSTATEDIR=\"$(nmstatedir)\" \
-	-DNMLIBDIR=\"$(nmlibdir)\" \
-	\
-	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
-	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \
-	-DPPPD_PLUGIN_DIR=\"$(PPPD_PLUGIN_DIR)\" \
-	\
-	$(NULL)
-
 libNetworkManager_la_LIBADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
 	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la \
 	libsystemd-nm.la \
 	$(GLIB_LIBS) \
 	$(GUDEV_LIBS) \
 	$(LIBNL_LIBS) \
 	$(SYSTEMD_LOGIN_LIBS) \
-	$(SYSTEMD_JOURNAL_LIBS) \
 	$(LIBNDP_LIBS) \
-	$(LIBDL) \
+	$(DL_LIBS) \
 	$(LIBM) \
 	$(SELINUX_LIBS) \
 	$(LIBAUDIT_LIBS)
@@ -518,13 +506,42 @@ if WITH_LIBSOUP
 libNetworkManager_la_LIBADD += $(LIBSOUP_LIBS)
 endif
 
+###############################################################################
+
+libNetworkManagerTest_la_SOURCES = \
+	rdisc/nm-fake-rdisc.c \
+	rdisc/nm-fake-rdisc.h \
+	platform/nm-fake-platform.c \
+	platform/nm-fake-platform.h
+
+libNetworkManagerTest_la_LIBADD = \
+	libNetworkManager.la
+
+###############################################################################
+
+NetworkManager_SOURCES = \
+	\
+	$(nm_device_sources) \
+	$(nm_device_headers) \
+	\
+	$(nm_dhcp_client_sources) \
+	$(nm_dhcp_client_headers) \
+	\
+	main-utils.c \
+	main-utils.h \
+	main.c
+
+NetworkManager_LDADD = \
+	libNetworkManager.la
+
 NetworkManager_LDFLAGS = \
 	-rdynamic \
 	-Wl,--version-script="$(srcdir)/NetworkManager.ver"
 
-######################
+###############################################################################
 
 libnm_iface_helper_la_SOURCES = \
+	\
 	dhcp-manager/nm-dhcp-client.c \
 	dhcp-manager/nm-dhcp-client.h \
 	dhcp-manager/nm-dhcp-client-logging.h \
@@ -549,8 +566,6 @@ libnm_iface_helper_la_SOURCES = \
 	platform/wifi/wifi-utils.c \
 	platform/wifi/wifi-utils.h \
 	\
-	rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h \
 	rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h \
 	rdisc/nm-rdisc.c \
@@ -568,12 +583,8 @@ libnm_iface_helper_la_SOURCES = \
 	\
 	nm-enum-types.c \
 	nm-enum-types.h \
-	nm-logging.c \
-	nm-logging.h \
 	nm-multi-index.c \
 	nm-multi-index.h \
-	nm-core-utils.c \
-	nm-core-utils.h \
 	NetworkManagerUtils.c \
 	NetworkManagerUtils.h
 
@@ -586,16 +597,15 @@ endif
 libnm_iface_helper_la_LIBADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
 	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la \
 	libsystemd-nm.la \
 	$(GLIB_LIBS) \
 	$(GUDEV_LIBS) \
 	$(LIBNL_LIBS) \
 	$(LIBNDP_LIBS) \
-	$(LIBDL) \
+	$(DL_LIBS) \
 	$(LIBM)
 
-libexec_PROGRAMS = nm-iface-helper
-
 nm_iface_helper_SOURCES = \
 	dhcp-manager/nm-dhcp-systemd.h \
 	dhcp-manager/nm-dhcp-systemd.c \
@@ -605,18 +615,32 @@ nm_iface_helper_SOURCES = \
 
 nm_iface_helper_LDADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	libsystemd-nm.la \
 	libnm-iface-helper.la \
 	$(GLIB_LIBS) \
 	$(GUDEV_LIBS) \
-	$(SYSTEMD_JOURNAL_LIBS) \
 	$(LIBNL_LIBS) \
 	$(LIBNDP_LIBS) \
 	$(LIBM)
 
-nm_iface_helper_LDFLAGS = -rdynamic
+nm_iface_helper_LDFLAGS = \
+	-rdynamic
+
+###############################################################################
+
+GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n'
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM --fhead '\#include "nm-default.h"\n'
+nm_enum_types_sources = $(filter-out \
+                              %/nm-device-private.h \
+                              %/nm-rdisc-private.h \
+                              %/wifi-utils-private.h \
+                              %/nm-dhcp-client-logging.h \
+                              %/nm-device-logging.h \
+                              , $(libNetworkManager_la_SOURCES))
+
+BUILT_SOURCES = $(GLIB_GENERATED)
 
-######################
+###############################################################################
 
 dbusservicedir = $(DBUS_SYS_DIR)
 dbusservice_DATA = org.freedesktop.NetworkManager.conf
@@ -624,6 +648,7 @@ dbusservice_DATA = org.freedesktop.NetworkManager.conf
 EXTRA_DIST = \
 	$(dbusservice_DATA) \
 	$(NetworkManager_DATA) \
+	nm-test-utils-core.h \
 	NetworkManager.ver
 
 rundir=$(runstatedir)/NetworkManager
diff --git a/src/Makefile.in b/src/Makefile.in
index 1047880d..7f4cf252 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -102,28 +102,22 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@	supplicant-manager/tests \
 @ENABLE_TESTS_TRUE@	tests
 
-
-######################
-# libsystemd-nm-base
-######################
-@ENABLE_TESTS_TRUE@am__append_5 = \
-@ENABLE_TESTS_TRUE@	libNetworkManager-base.la
-
 sbin_PROGRAMS = NetworkManager$(EXEEXT)
-@WITH_WEXT_TRUE@am__append_6 = \
+libexec_PROGRAMS = nm-iface-helper$(EXEEXT)
+@WITH_WEXT_TRUE@am__append_5 = \
 @WITH_WEXT_TRUE@	platform/wifi/wifi-utils-wext.c \
 @WITH_WEXT_TRUE@	platform/wifi/wifi-utils-wext.h
 
-@WITH_LIBSOUP_TRUE@am__append_7 = $(LIBSOUP_LIBS)
-@WITH_WEXT_TRUE@am__append_8 = \
+@WITH_LIBSOUP_TRUE@am__append_6 = $(LIBSOUP_LIBS)
+@WITH_WEXT_TRUE@am__append_7 = \
 @WITH_WEXT_TRUE@	platform/wifi/wifi-utils-wext.c \
 @WITH_WEXT_TRUE@	platform/wifi/wifi-utils-wext.h
 
-libexec_PROGRAMS = nm-iface-helper$(EXEEXT)
 subdir = src
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -144,39 +138,20 @@ CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-@ENABLE_TESTS_TRUE@libNetworkManager_base_la_DEPENDENCIES =  \
-@ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-core/libnm-core.la \
-@ENABLE_TESTS_TRUE@	$(am__DEPENDENCIES_1)
-am__libNetworkManager_base_la_SOURCES_DIST = nm-core-utils.c \
-	nm-core-utils.h nm-logging.c nm-logging.h
-@ENABLE_TESTS_TRUE@am_libNetworkManager_base_la_OBJECTS =  \
-@ENABLE_TESTS_TRUE@	libNetworkManager_base_la-nm-core-utils.lo \
-@ENABLE_TESTS_TRUE@	libNetworkManager_base_la-nm-logging.lo
-libNetworkManager_base_la_OBJECTS =  \
-	$(am_libNetworkManager_base_la_OBJECTS)
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-am__v_lt_1 = 
-@ENABLE_TESTS_TRUE@am_libNetworkManager_base_la_rpath =
 @WITH_LIBSOUP_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
 libNetworkManager_la_DEPENDENCIES =  \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	$(top_builddir)/introspection/libnmdbus.la libsystemd-nm.la \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la libsystemd-nm.la \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_2)
-am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \
-	devices/nm-device-bridge.h devices/nm-device-ethernet.h \
-	devices/nm-device-infiniband.h devices/nm-device-ip-tunnel.h \
-	devices/nm-device-macvlan.h devices/nm-device-tun.h \
-	devices/nm-device-veth.h devices/nm-device-vlan.h \
-	devices/nm-device-vxlan.h dhcp-manager/nm-dhcp-dhclient.h \
-	dhcp-manager/nm-dhcp-dhclient-utils.h \
-	dhcp-manager/nm-dhcp-dhcpcd.h dhcp-manager/nm-dhcp-systemd.h \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+am__libNetworkManager_la_SOURCES_DIST =  \
+	dhcp-manager/nm-dhcp-dhclient-utils.c \
+	dhcp-manager/nm-dhcp-dhclient-utils.h nm-checkpoint-manager.c \
+	nm-checkpoint-manager.h nm-checkpoint.c nm-checkpoint.h \
 	devices/nm-device.c devices/nm-device.h \
 	devices/nm-lldp-listener.c devices/nm-lldp-listener.h \
 	devices/nm-arping-manager.c devices/nm-arping-manager.h \
@@ -203,12 +178,11 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \
 	platform/nmp-object.h platform/nm-platform-utils.c \
 	platform/nm-platform-utils.h platform/nm-platform.c \
 	platform/nm-platform.h platform/nm-linux-platform.c \
-	platform/nm-linux-platform.h platform/nm-fake-platform.c \
-	platform/nm-fake-platform.h platform/wifi/wifi-utils-nl80211.c \
+	platform/nm-linux-platform.h \
+	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \
-	platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \
+	platform/wifi/wifi-utils.h rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \
 	rdisc/nm-rdisc-private.h ppp-manager/nm-ppp-manager.c \
 	ppp-manager/nm-ppp-manager.h ppp-manager/nm-ppp-status.h \
@@ -244,59 +218,72 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \
 	nm-activation-request.h nm-active-connection.c \
 	nm-active-connection.h nm-audit-manager.c nm-audit-manager.h \
 	nm-bus-manager.c nm-bus-manager.h nm-config.c nm-config.h \
-	nm-config-data.c nm-config-data.h nm-connection-provider.c \
-	nm-connection-provider.h nm-connectivity.c nm-connectivity.h \
-	nm-dcb.c nm-dcb.h nm-route-manager.c nm-route-manager.h \
-	nm-default-route-manager.c nm-default-route-manager.h \
-	nm-dhcp4-config.c nm-dhcp4-config.h nm-dhcp6-config.c \
-	nm-dhcp6-config.h nm-dispatcher.c nm-dispatcher.h \
-	nm-enum-types.c nm-enum-types.h nm-exported-object.c \
-	nm-exported-object.h nm-firewall-manager.c \
-	nm-firewall-manager.h nm-ip4-config.c nm-ip4-config.h \
-	nm-ip6-config.c nm-ip6-config.h nm-logging.c nm-logging.h \
+	nm-config-data.c nm-config-data.h nm-connectivity.c \
+	nm-connectivity.h nm-dcb.c nm-dcb.h nm-route-manager.c \
+	nm-route-manager.h nm-default-route-manager.c \
+	nm-default-route-manager.h nm-dhcp4-config.c nm-dhcp4-config.h \
+	nm-dhcp6-config.c nm-dhcp6-config.h nm-dispatcher.c \
+	nm-dispatcher.h nm-enum-types.c nm-enum-types.h \
+	nm-exported-object.c nm-exported-object.h \
+	nm-firewall-manager.c nm-firewall-manager.h nm-ip4-config.c \
+	nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h \
 	nm-auth-manager.c nm-auth-manager.h nm-auth-subject.c \
 	nm-auth-subject.h nm-auth-utils.c nm-auth-utils.h nm-manager.c \
 	nm-manager.h nm-multi-index.c nm-multi-index.h nm-policy.c \
 	nm-policy.h nm-rfkill-manager.c nm-rfkill-manager.h \
 	nm-session-monitor.h nm-session-monitor.c nm-sleep-monitor.c \
-	nm-sleep-monitor.h nm-types.h nm-core-utils.c nm-core-utils.h \
-	NetworkManagerUtils.c NetworkManagerUtils.h \
-	platform/wifi/wifi-utils-wext.c \
+	nm-sleep-monitor.h nm-types.h NetworkManagerUtils.c \
+	NetworkManagerUtils.h platform/wifi/wifi-utils-wext.c \
 	platform/wifi/wifi-utils-wext.h
-am__objects_1 =
-@WITH_WEXT_TRUE@am__objects_2 = wifi-utils-wext.lo
-am_libNetworkManager_la_OBJECTS = $(am__objects_1) $(am__objects_1) \
-	nm-device.lo nm-lldp-listener.lo nm-arping-manager.lo \
+@WITH_WEXT_TRUE@am__objects_1 = wifi-utils-wext.lo
+am_libNetworkManager_la_OBJECTS = nm-dhcp-dhclient-utils.lo \
+	nm-checkpoint-manager.lo nm-checkpoint.lo nm-device.lo \
+	nm-lldp-listener.lo nm-arping-manager.lo \
 	nm-device-ethernet-utils.lo nm-device-factory.lo \
 	nm-device-generic.lo nm-dhcp-client.lo nm-dhcp-utils.lo \
 	nm-dhcp-listener.lo nm-dhcp-manager.lo nm-dns-dnsmasq.lo \
 	nm-dns-unbound.lo nm-dns-manager.lo nm-dns-plugin.lo \
 	nm-dnsmasq-manager.lo nm-dnsmasq-utils.lo nmp-netns.lo \
 	nmp-object.lo nm-platform-utils.lo nm-platform.lo \
-	nm-linux-platform.lo nm-fake-platform.lo wifi-utils-nl80211.lo \
-	wifi-utils.lo nm-fake-rdisc.lo nm-lndp-rdisc.lo nm-rdisc.lo \
-	nm-ppp-manager.lo nm-agent-manager.lo nm-inotify-helper.lo \
-	nm-secret-agent.lo nm-settings-connection.lo \
-	nm-settings-plugin.lo nm-settings.lo nm-keyfile-connection.lo \
-	plugin.lo reader.lo utils.lo writer.lo nm-supplicant-config.lo \
-	nm-supplicant-interface.lo nm-supplicant-manager.lo \
-	nm-supplicant-settings-verify.lo nm-vpn-connection.lo \
-	nm-vpn-manager.lo nm-activation-request.lo \
-	nm-active-connection.lo nm-audit-manager.lo nm-bus-manager.lo \
-	nm-config.lo nm-config-data.lo nm-connection-provider.lo \
-	nm-connectivity.lo nm-dcb.lo nm-route-manager.lo \
-	nm-default-route-manager.lo nm-dhcp4-config.lo \
-	nm-dhcp6-config.lo nm-dispatcher.lo nm-enum-types.lo \
-	nm-exported-object.lo nm-firewall-manager.lo nm-ip4-config.lo \
-	nm-ip6-config.lo nm-logging.lo nm-auth-manager.lo \
+	nm-linux-platform.lo wifi-utils-nl80211.lo wifi-utils.lo \
+	nm-lndp-rdisc.lo nm-rdisc.lo nm-ppp-manager.lo \
+	nm-agent-manager.lo nm-inotify-helper.lo nm-secret-agent.lo \
+	nm-settings-connection.lo nm-settings-plugin.lo nm-settings.lo \
+	nm-keyfile-connection.lo plugin.lo reader.lo utils.lo \
+	writer.lo nm-supplicant-config.lo nm-supplicant-interface.lo \
+	nm-supplicant-manager.lo nm-supplicant-settings-verify.lo \
+	nm-vpn-connection.lo nm-vpn-manager.lo \
+	nm-activation-request.lo nm-active-connection.lo \
+	nm-audit-manager.lo nm-bus-manager.lo nm-config.lo \
+	nm-config-data.lo nm-connectivity.lo nm-dcb.lo \
+	nm-route-manager.lo nm-default-route-manager.lo \
+	nm-dhcp4-config.lo nm-dhcp6-config.lo nm-dispatcher.lo \
+	nm-enum-types.lo nm-exported-object.lo nm-firewall-manager.lo \
+	nm-ip4-config.lo nm-ip6-config.lo nm-auth-manager.lo \
 	nm-auth-subject.lo nm-auth-utils.lo nm-manager.lo \
 	nm-multi-index.lo nm-policy.lo nm-rfkill-manager.lo \
-	nm-session-monitor.lo nm-sleep-monitor.lo nm-core-utils.lo \
-	NetworkManagerUtils.lo $(am__objects_2)
+	nm-session-monitor.lo nm-sleep-monitor.lo \
+	NetworkManagerUtils.lo $(am__objects_1)
 libNetworkManager_la_OBJECTS = $(am_libNetworkManager_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libNetworkManagerBase_la_DEPENDENCIES =  \
+	$(top_builddir)/libnm-core/libnm-core.la $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am_libNetworkManagerBase_la_OBJECTS = nm-core-utils.lo nm-logging.lo
+libNetworkManagerBase_la_OBJECTS =  \
+	$(am_libNetworkManagerBase_la_OBJECTS)
+libNetworkManagerTest_la_DEPENDENCIES = libNetworkManager.la
+am_libNetworkManagerTest_la_OBJECTS = nm-fake-rdisc.lo \
+	nm-fake-platform.lo
+libNetworkManagerTest_la_OBJECTS =  \
+	$(am_libNetworkManagerTest_la_OBJECTS)
 libnm_iface_helper_la_DEPENDENCIES =  \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	$(top_builddir)/introspection/libnmdbus.la libsystemd-nm.la \
+	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la libsystemd-nm.la \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
@@ -313,33 +300,31 @@ am__libnm_iface_helper_la_SOURCES_DIST =  \
 	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \
-	platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \
+	platform/wifi/wifi-utils.h rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \
 	nm-route-manager.c nm-route-manager.h nm-exported-object.c \
 	nm-exported-object.h nm-ip4-config.c nm-ip4-config.h \
 	nm-ip6-config.c nm-ip6-config.h nm-enum-types.c \
-	nm-enum-types.h nm-logging.c nm-logging.h nm-multi-index.c \
-	nm-multi-index.h nm-core-utils.c nm-core-utils.h \
+	nm-enum-types.h nm-multi-index.c nm-multi-index.h \
 	NetworkManagerUtils.c NetworkManagerUtils.h \
 	platform/wifi/wifi-utils-wext.c \
 	platform/wifi/wifi-utils-wext.h
 am_libnm_iface_helper_la_OBJECTS = nm-dhcp-client.lo nm-dhcp-utils.lo \
 	nm-dhcp-manager.lo nmp-netns.lo nmp-object.lo \
 	nm-platform-utils.lo nm-platform.lo nm-linux-platform.lo \
-	wifi-utils-nl80211.lo wifi-utils.lo nm-fake-rdisc.lo \
-	nm-lndp-rdisc.lo nm-rdisc.lo nm-route-manager.lo \
-	nm-exported-object.lo nm-ip4-config.lo nm-ip6-config.lo \
-	nm-enum-types.lo nm-logging.lo nm-multi-index.lo \
-	nm-core-utils.lo NetworkManagerUtils.lo $(am__objects_2)
+	wifi-utils-nl80211.lo wifi-utils.lo nm-lndp-rdisc.lo \
+	nm-rdisc.lo nm-route-manager.lo nm-exported-object.lo \
+	nm-ip4-config.lo nm-ip6-config.lo nm-enum-types.lo \
+	nm-multi-index.lo NetworkManagerUtils.lo $(am__objects_1)
 libnm_iface_helper_la_OBJECTS = $(am_libnm_iface_helper_la_OBJECTS)
 libsystemd_nm_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
 am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd.lo \
 	libsystemd_nm_la-nm-sd-adapt.lo libsystemd_nm_la-alloc-util.lo \
 	libsystemd_nm_la-escape.lo libsystemd_nm_la-ether-addr-util.lo \
-	libsystemd_nm_la-fd-util.lo libsystemd_nm_la-fileio.lo \
-	libsystemd_nm_la-fs-util.lo libsystemd_nm_la-hash-funcs.lo \
-	libsystemd_nm_la-hashmap.lo libsystemd_nm_la-hexdecoct.lo \
+	libsystemd_nm_la-extract-word.lo libsystemd_nm_la-fd-util.lo \
+	libsystemd_nm_la-fileio.lo libsystemd_nm_la-fs-util.lo \
+	libsystemd_nm_la-hash-funcs.lo libsystemd_nm_la-hashmap.lo \
+	libsystemd_nm_la-hexdecoct.lo \
 	libsystemd_nm_la-hostname-util.lo \
 	libsystemd_nm_la-in-addr-util.lo libsystemd_nm_la-io-util.lo \
 	libsystemd_nm_la-mempool.lo libsystemd_nm_la-parse-util.lo \
@@ -365,21 +350,22 @@ am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd.lo \
 	libsystemd_nm_la-sd-dhcp6-lease.lo \
 	libsystemd_nm_la-sd-ipv4acd.lo libsystemd_nm_la-sd-ipv4ll.lo \
 	libsystemd_nm_la-sd-lldp.lo libsystemd_nm_la-sd-event.lo \
-	libsystemd_nm_la-sd-id128.lo libsystemd_nm_la-dns-domain.lo
+	libsystemd_nm_la-id128-util.lo libsystemd_nm_la-sd-id128.lo \
+	libsystemd_nm_la-dns-domain.lo
 libsystemd_nm_la_OBJECTS = $(am_libsystemd_nm_la_OBJECTS)
 am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \
 	"$(DESTDIR)$(dbusservicedir)"
 PROGRAMS = $(libexec_PROGRAMS) $(sbin_PROGRAMS)
-am__objects_3 = nm-device-bond.$(OBJEXT) nm-device-bridge.$(OBJEXT) \
+am__objects_2 = nm-device-bond.$(OBJEXT) nm-device-bridge.$(OBJEXT) \
 	nm-device-ethernet.$(OBJEXT) nm-device-infiniband.$(OBJEXT) \
 	nm-device-ip-tunnel.$(OBJEXT) nm-device-macvlan.$(OBJEXT) \
 	nm-device-tun.$(OBJEXT) nm-device-veth.$(OBJEXT) \
 	nm-device-vlan.$(OBJEXT) nm-device-vxlan.$(OBJEXT)
-am__objects_4 = nm-dhcp-dhclient.$(OBJEXT) \
-	nm-dhcp-dhclient-utils.$(OBJEXT) nm-dhcp-dhcpcd.$(OBJEXT) \
+am__objects_3 =
+am__objects_4 = nm-dhcp-dhclient.$(OBJEXT) nm-dhcp-dhcpcd.$(OBJEXT) \
 	nm-dhcp-systemd.$(OBJEXT)
-am_NetworkManager_OBJECTS = $(am__objects_3) $(am__objects_1) \
-	$(am__objects_4) $(am__objects_1) main-utils.$(OBJEXT) \
+am_NetworkManager_OBJECTS = $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_3) main-utils.$(OBJEXT) \
 	main.$(OBJEXT)
 NetworkManager_OBJECTS = $(am_NetworkManager_OBJECTS)
 NetworkManager_DEPENDENCIES = libNetworkManager.la
@@ -391,8 +377,7 @@ am_nm_iface_helper_OBJECTS = nm-dhcp-systemd.$(OBJEXT) \
 	nm-iface-helper.$(OBJEXT) main-utils.$(OBJEXT)
 nm_iface_helper_OBJECTS = $(am_nm_iface_helper_OBJECTS)
 nm_iface_helper_DEPENDENCIES =  \
-	$(top_builddir)/libnm-core/libnm-core.la libsystemd-nm.la \
-	libnm-iface-helper.la $(am__DEPENDENCIES_1) \
+	$(top_builddir)/libnm-core/libnm-core.la libnm-iface-helper.la \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1)
@@ -434,12 +419,14 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libNetworkManager_base_la_SOURCES) \
-	$(libNetworkManager_la_SOURCES) \
+SOURCES = $(libNetworkManager_la_SOURCES) \
+	$(libNetworkManagerBase_la_SOURCES) \
+	$(libNetworkManagerTest_la_SOURCES) \
 	$(libnm_iface_helper_la_SOURCES) $(libsystemd_nm_la_SOURCES) \
 	$(NetworkManager_SOURCES) $(nm_iface_helper_SOURCES)
-DIST_SOURCES = $(am__libNetworkManager_base_la_SOURCES_DIST) \
-	$(am__libNetworkManager_la_SOURCES_DIST) \
+DIST_SOURCES = $(am__libNetworkManager_la_SOURCES_DIST) \
+	$(libNetworkManagerBase_la_SOURCES) \
+	$(libNetworkManagerTest_la_SOURCES) \
 	$(am__libnm_iface_helper_la_SOURCES_DIST) \
 	$(libsystemd_nm_la_SOURCES) $(NetworkManager_SOURCES) \
 	$(nm_iface_helper_SOURCES)
@@ -577,6 +564,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -633,13 +621,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -651,6 +640,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -711,6 +702,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -746,6 +739,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -810,49 +804,88 @@ with_valgrind = @with_valgrind@
 SUBDIRS = . dhcp-manager ppp-manager settings/plugins devices/adsl \
 	$(am__append_1) $(am__append_2) $(am__append_3) \
 	$(am__append_4)
-
-# add each subdirectory that contains a libNM source file. $(sort) is being used
-# primarily for its side effect of removing duplicates.
-AM_CPPFLAGS = -I$(top_srcdir)/shared -I$(top_builddir)/shared \
-	-I$(top_builddir)/introspection -I$(top_srcdir)/libnm-core \
-	-I$(top_builddir)/libnm-core -I$(top_srcdir)/callouts \
-	-DPREFIX=\"$(prefix)\" -DG_LOG_DOMAIN=\""NetworkManager"\" \
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/shared \
+	-I$(top_builddir)/shared \
+	-I$(top_builddir)/introspection \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	\
+	 $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d) \
+	\
+	-DPREFIX=\"$(prefix)\"          \
+	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	$(foreach d,$(sort $(dir \
-	$(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d) \
-	$(GLIB_CFLAGS) $(GUDEV_CFLAGS) $(LIBNL_CFLAGS) \
-	$(LIBNDP_CFLAGS) $(LIBSOUP_CFLAGS) $(SELINUX_CFLAGS) \
-	$(LIBAUDIT_CFLAGS) $(SYSTEMD_LOGIN_CFLAGS) \
-	$(SYSTEMD_JOURNAL_CFLAGS) $(SYSTEMD_NM_CFLAGS_PATHS) \
-	-DBINDIR=\"$(bindir)\" -DDATADIR=\"$(datadir)\" \
+	\
+	-DBINDIR=\"$(bindir)\" \
+	-DDATADIR=\"$(datadir)\" \
 	-DLIBEXECDIR=\"$(libexecdir)\" \
 	-DLOCALSTATEDIR=\"$(localstatedir)\" \
-	-DRUNSTATEDIR=\"$(runstatedir)\" -DSBINDIR=\"$(sbindir)\" \
-	-DSYSCONFDIR=\"$(sysconfdir)\" -DRUNDIR=\"$(rundir)\" \
+	-DRUNSTATEDIR=\"$(runstatedir)\" \
+	-DSBINDIR=\"$(sbindir)\" \
+	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DRUNDIR=\"$(rundir)\" \
+	\
 	-DNMCONFDIR=\"$(nmconfdir)\" \
 	-DNMLOCALEDIR=\"$(datadir)/locale\" \
-	-DNMPLUGINDIR=\"$(pkglibdir)\" -DNMRUNDIR=\"$(nmrundir)\" \
-	-DNMSTATEDIR=\"$(nmstatedir)\" -DNMLIBDIR=\"$(nmlibdir)\" \
+	-DNMPLUGINDIR=\"$(pkglibdir)\" \
+	-DNMRUNDIR=\"$(nmrundir)\" \
+	-DNMSTATEDIR=\"$(nmstatedir)\" \
+	-DNMLIBDIR=\"$(nmlibdir)\" \
+	\
 	-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
 	-DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \
-	-DPPPD_PLUGIN_DIR=\"$(PPPD_PLUGIN_DIR)\" $(NULL)
-noinst_LTLIBRARIES = libNetworkManager.la libnm-iface-helper.la \
-	libsystemd-nm.la $(am__append_5)
-
-######################
-# libsystemd-nm
-######################
-SYSTEMD_NM_CFLAGS_PATHS = \
-	-I$(top_srcdir)/src/systemd/src/systemd \
-	-I$(top_srcdir)/src/systemd/src/libsystemd-network \
-	-I$(top_srcdir)/src/systemd/src/basic \
-	-I$(top_srcdir)/src/systemd
+	-DPPPD_PLUGIN_DIR=\"$(PPPD_PLUGIN_DIR)\" \
+	\
+	$(GLIB_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(LIBNDP_CFLAGS) \
+	$(LIBSOUP_CFLAGS) \
+	$(SELINUX_CFLAGS) \
+	$(LIBAUDIT_CFLAGS) \
+	$(SYSTEMD_LOGIN_CFLAGS) \
+	$(SYSTEMD_JOURNAL_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS) \
+	\
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/ \
+	\
+	$(NULL)
+
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+noinst_LTLIBRARIES = \
+	libNetworkManagerBase.la \
+	libNetworkManager.la \
+	libNetworkManagerTest.la \
+	libnm-iface-helper.la \
+	libsystemd-nm.la
+
 
+###############################################################################
 libsystemd_nm_la_SOURCES = \
 	systemd/nm-sd.c \
 	systemd/nm-sd.h \
-	systemd/nm-sd-adapt.c \
-	systemd/nm-sd-adapt.h \
+	systemd/sd-adapt/nm-sd-adapt.c \
+	systemd/sd-adapt/nm-sd-adapt.h \
+	systemd/sd-adapt/build.h \
+	systemd/sd-adapt/cgroup-util.h \
+	systemd/sd-adapt/condition.h \
+	systemd/sd-adapt/conf-parser.h \
+	systemd/sd-adapt/def.h \
+	systemd/sd-adapt/dirent-util.h \
+	systemd/sd-adapt/formats-util.h \
+	systemd/sd-adapt/gunicode.h \
+	systemd/sd-adapt/libudev.h \
+	systemd/sd-adapt/missing.h \
+	systemd/sd-adapt/mkdir.h \
+	systemd/sd-adapt/process-util.h \
+	systemd/sd-adapt/sd-daemon.h \
+	systemd/sd-adapt/stat-util.h \
+	systemd/sd-adapt/udev-util.h \
+	systemd/sd-adapt/udev.h \
+	systemd/sd-adapt/user-util.h \
+	systemd/sd-adapt/virt.h \
 	systemd/src/basic/alloc-util.c \
 	systemd/src/basic/alloc-util.h \
 	systemd/src/basic/async.h \
@@ -860,6 +893,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/escape.h \
 	systemd/src/basic/ether-addr-util.c \
 	systemd/src/basic/ether-addr-util.h \
+	systemd/src/basic/extract-word.c \
+	systemd/src/basic/extract-word.h \
 	systemd/src/basic/fd-util.c \
 	systemd/src/basic/fd-util.h \
 	systemd/src/basic/fileio.c \
@@ -893,6 +928,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/random-util.h \
 	systemd/src/basic/refcnt.h \
 	systemd/src/basic/set.h \
+	systemd/src/basic/signal-util.h \
 	systemd/src/basic/siphash24.c \
 	systemd/src/basic/siphash24.h \
 	systemd/src/basic/socket-util.c \
@@ -943,6 +979,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/sd-ipv4ll.c \
 	systemd/src/libsystemd-network/sd-lldp.c \
 	systemd/src/libsystemd/sd-event/sd-event.c \
+	systemd/src/libsystemd/sd-id128/id128-util.c \
+	systemd/src/libsystemd/sd-id128/id128-util.h \
 	systemd/src/libsystemd/sd-id128/sd-id128.c \
 	systemd/src/shared/dns-domain.c \
 	systemd/src/shared/dns-domain.h \
@@ -959,49 +997,41 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/systemd/sd-ndisc.h
 
 libsystemd_nm_la_CPPFLAGS = \
-	-I$(top_srcdir)/shared \
-	-I$(top_builddir)/shared \
+	-I$(top_srcdir)/shared/ \
+	-I$(top_builddir)/shared/ \
 	-I$(top_srcdir)/libnm-core \
 	-I$(top_builddir)/libnm-core \
-	$(SYSTEMD_NM_CFLAGS_PATHS) \
-	-I$(top_srcdir)/src/systemd/src/shared \
+	-I$(top_srcdir)/src/systemd/sd-adapt/ \
+	-I$(top_srcdir)/src/systemd/src/systemd/ \
+	-I$(top_srcdir)/src/systemd/src/basic/ \
+	-I$(top_srcdir)/src/systemd/src/shared/ \
+	-I$(top_srcdir)/src/systemd/src/libsystemd-network/ \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD \
 	$(GLIB_CFLAGS)
 
 libsystemd_nm_la_LIBADD = \
 	$(GLIB_LIBS)
 
-@ENABLE_TESTS_TRUE@libNetworkManager_base_la_SOURCES = \
-@ENABLE_TESTS_TRUE@	nm-core-utils.c \
-@ENABLE_TESTS_TRUE@	nm-core-utils.h \
-@ENABLE_TESTS_TRUE@	nm-logging.c \
-@ENABLE_TESTS_TRUE@	nm-logging.h
-
-@ENABLE_TESTS_TRUE@libNetworkManager_base_la_CPPFLAGS = \
-@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/shared \
-@ENABLE_TESTS_TRUE@	-I$(top_builddir)/shared \
-@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/libnm-core \
-@ENABLE_TESTS_TRUE@	-I$(top_builddir)/libnm-core \
-@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/src/platform \
-@ENABLE_TESTS_TRUE@	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-@ENABLE_TESTS_TRUE@	-DNO_SYSTEMD_JOURNAL \
-@ENABLE_TESTS_TRUE@	-DPREFIX=\"$(prefix)\" \
-@ENABLE_TESTS_TRUE@	-DLOCALSTATEDIR=\"$(localstatedir)\" \
-@ENABLE_TESTS_TRUE@	-DNMSTATEDIR=\"$(nmstatedir)\" \
-@ENABLE_TESTS_TRUE@	$(GLIB_CFLAGS)
-
-@ENABLE_TESTS_TRUE@libNetworkManager_base_la_LIBADD = \
-@ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-core/libnm-core.la \
-@ENABLE_TESTS_TRUE@	$(GLIB_LIBS)
 
-NetworkManager_SOURCES = \
-	$(nm_device_sources) $(nm_device_headers) \
-	$(nm_dhcp_client_sources) $(nm_dhcp_client_headers) \
-	main-utils.c \
-	main-utils.h \
-	main.c
+###############################################################################
+libNetworkManagerBase_la_SOURCES = \
+	nm-core-utils.c \
+	nm-core-utils.h \
+	nm-logging.c \
+	nm-logging.h
+
+libNetworkManagerBase_la_LIBADD = \
+	$(top_builddir)/libnm-core/libnm-core.la \
+	$(GLIB_LIBS) \
+	$(SYSTEMD_JOURNAL_LIBS) \
+	$(NULL)
+
+
+###############################################################################
 
-NetworkManager_LDADD = libNetworkManager.la
+# These source files have a attribute((constructor)) to register their factories.
+# This gets stripped out from the resulting binary if we add them to libNetworkManager.la.
+# Instead, add them to the binary. An alternative would be to link with --as-needed.
 nm_device_sources = \
 	devices/nm-device-bond.c \
 	devices/nm-device-bridge.c \
@@ -1025,25 +1055,30 @@ nm_device_headers = \
 	devices/nm-device-tun.h \
 	devices/nm-device-veth.h \
 	devices/nm-device-vlan.h \
-	devices/nm-device-vxlan.h
+	devices/nm-device-vxlan.h \
+	$(NULL)
 
 nm_dhcp_client_sources = \
 	dhcp-manager/nm-dhcp-dhclient.c \
-	dhcp-manager/nm-dhcp-dhclient-utils.c \
 	dhcp-manager/nm-dhcp-dhcpcd.c \
-	dhcp-manager/nm-dhcp-systemd.c
+	dhcp-manager/nm-dhcp-systemd.c \
+	$(NULL)
 
 nm_dhcp_client_headers = \
 	dhcp-manager/nm-dhcp-dhclient.h \
-	dhcp-manager/nm-dhcp-dhclient-utils.h \
 	dhcp-manager/nm-dhcp-dhcpcd.h \
-	dhcp-manager/nm-dhcp-systemd.h
+	dhcp-manager/nm-dhcp-systemd.h \
+	$(NULL)
 
-libNetworkManager_la_SOURCES = $(nm_device_headers) \
-	$(nm_dhcp_client_headers) devices/nm-device.c \
-	devices/nm-device.h devices/nm-lldp-listener.c \
-	devices/nm-lldp-listener.h devices/nm-arping-manager.c \
-	devices/nm-arping-manager.h devices/nm-device-ethernet-utils.c \
+
+###############################################################################
+libNetworkManager_la_SOURCES = dhcp-manager/nm-dhcp-dhclient-utils.c \
+	dhcp-manager/nm-dhcp-dhclient-utils.h nm-checkpoint-manager.c \
+	nm-checkpoint-manager.h nm-checkpoint.c nm-checkpoint.h \
+	devices/nm-device.c devices/nm-device.h \
+	devices/nm-lldp-listener.c devices/nm-lldp-listener.h \
+	devices/nm-arping-manager.c devices/nm-arping-manager.h \
+	devices/nm-device-ethernet-utils.c \
 	devices/nm-device-ethernet-utils.h devices/nm-device-factory.c \
 	devices/nm-device-factory.h devices/nm-device-generic.c \
 	devices/nm-device-generic.h devices/nm-device-logging.h \
@@ -1066,12 +1101,11 @@ libNetworkManager_la_SOURCES = $(nm_device_headers) \
 	platform/nmp-object.h platform/nm-platform-utils.c \
 	platform/nm-platform-utils.h platform/nm-platform.c \
 	platform/nm-platform.h platform/nm-linux-platform.c \
-	platform/nm-linux-platform.h platform/nm-fake-platform.c \
-	platform/nm-fake-platform.h platform/wifi/wifi-utils-nl80211.c \
+	platform/nm-linux-platform.h \
+	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \
-	platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \
+	platform/wifi/wifi-utils.h rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \
 	rdisc/nm-rdisc-private.h ppp-manager/nm-ppp-manager.c \
 	ppp-manager/nm-ppp-manager.h ppp-manager/nm-ppp-status.h \
@@ -1107,47 +1141,63 @@ libNetworkManager_la_SOURCES = $(nm_device_headers) \
 	nm-activation-request.h nm-active-connection.c \
 	nm-active-connection.h nm-audit-manager.c nm-audit-manager.h \
 	nm-bus-manager.c nm-bus-manager.h nm-config.c nm-config.h \
-	nm-config-data.c nm-config-data.h nm-connection-provider.c \
-	nm-connection-provider.h nm-connectivity.c nm-connectivity.h \
-	nm-dcb.c nm-dcb.h nm-route-manager.c nm-route-manager.h \
-	nm-default-route-manager.c nm-default-route-manager.h \
-	nm-dhcp4-config.c nm-dhcp4-config.h nm-dhcp6-config.c \
-	nm-dhcp6-config.h nm-dispatcher.c nm-dispatcher.h \
-	nm-enum-types.c nm-enum-types.h nm-exported-object.c \
-	nm-exported-object.h nm-firewall-manager.c \
-	nm-firewall-manager.h nm-ip4-config.c nm-ip4-config.h \
-	nm-ip6-config.c nm-ip6-config.h nm-logging.c nm-logging.h \
+	nm-config-data.c nm-config-data.h nm-connectivity.c \
+	nm-connectivity.h nm-dcb.c nm-dcb.h nm-route-manager.c \
+	nm-route-manager.h nm-default-route-manager.c \
+	nm-default-route-manager.h nm-dhcp4-config.c nm-dhcp4-config.h \
+	nm-dhcp6-config.c nm-dhcp6-config.h nm-dispatcher.c \
+	nm-dispatcher.h nm-enum-types.c nm-enum-types.h \
+	nm-exported-object.c nm-exported-object.h \
+	nm-firewall-manager.c nm-firewall-manager.h nm-ip4-config.c \
+	nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h \
 	nm-auth-manager.c nm-auth-manager.h nm-auth-subject.c \
 	nm-auth-subject.h nm-auth-utils.c nm-auth-utils.h nm-manager.c \
 	nm-manager.h nm-multi-index.c nm-multi-index.h nm-policy.c \
 	nm-policy.h nm-rfkill-manager.c nm-rfkill-manager.h \
 	nm-session-monitor.h nm-session-monitor.c nm-sleep-monitor.c \
-	nm-sleep-monitor.h nm-types.h nm-core-utils.c nm-core-utils.h \
-	NetworkManagerUtils.c NetworkManagerUtils.h $(am__append_6)
-GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
-GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n'
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_enum_types_sources = $(filter-out \
-                              %/nm-device-private.h \
-                              %/nm-rdisc-private.h \
-                              %/wifi-utils-private.h \
-                              %/nm-dhcp-client-logging.h \
-                              %/nm-device-logging.h \
-                              , $(libNetworkManager_la_SOURCES))
-
-BUILT_SOURCES = $(GLIB_GENERATED)
+	nm-sleep-monitor.h nm-types.h NetworkManagerUtils.c \
+	NetworkManagerUtils.h $(NULL) $(am__append_5)
 libNetworkManager_la_LIBADD =  \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	$(top_builddir)/introspection/libnmdbus.la libsystemd-nm.la \
-	$(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(SYSTEMD_LOGIN_LIBS) \
-	$(SYSTEMD_JOURNAL_LIBS) $(LIBNDP_LIBS) $(LIBDL) $(LIBM) \
-	$(SELINUX_LIBS) $(LIBAUDIT_LIBS) $(am__append_7)
+	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la libsystemd-nm.la $(GLIB_LIBS) \
+	$(GUDEV_LIBS) $(LIBNL_LIBS) $(SYSTEMD_LOGIN_LIBS) \
+	$(LIBNDP_LIBS) $(DL_LIBS) $(LIBM) $(SELINUX_LIBS) \
+	$(LIBAUDIT_LIBS) $(am__append_6)
+
+###############################################################################
+libNetworkManagerTest_la_SOURCES = \
+	rdisc/nm-fake-rdisc.c \
+	rdisc/nm-fake-rdisc.h \
+	platform/nm-fake-platform.c \
+	platform/nm-fake-platform.h
+
+libNetworkManagerTest_la_LIBADD = \
+	libNetworkManager.la
+
+
+###############################################################################
+NetworkManager_SOURCES = \
+	\
+	$(nm_device_sources) \
+	$(nm_device_headers) \
+	\
+	$(nm_dhcp_client_sources) \
+	$(nm_dhcp_client_headers) \
+	\
+	main-utils.c \
+	main-utils.h \
+	main.c
+
+NetworkManager_LDADD = \
+	libNetworkManager.la
+
 NetworkManager_LDFLAGS = \
 	-rdynamic \
 	-Wl,--version-script="$(srcdir)/NetworkManager.ver"
 
 
-######################
+###############################################################################
 libnm_iface_helper_la_SOURCES = dhcp-manager/nm-dhcp-client.c \
 	dhcp-manager/nm-dhcp-client.h \
 	dhcp-manager/nm-dhcp-client-logging.h \
@@ -1161,24 +1211,23 @@ libnm_iface_helper_la_SOURCES = dhcp-manager/nm-dhcp-client.c \
 	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \
-	platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \
-	rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \
+	platform/wifi/wifi-utils.h rdisc/nm-lndp-rdisc.c \
 	rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \
 	nm-route-manager.c nm-route-manager.h nm-exported-object.c \
 	nm-exported-object.h nm-ip4-config.c nm-ip4-config.h \
 	nm-ip6-config.c nm-ip6-config.h nm-enum-types.c \
-	nm-enum-types.h nm-logging.c nm-logging.h nm-multi-index.c \
-	nm-multi-index.h nm-core-utils.c nm-core-utils.h \
-	NetworkManagerUtils.c NetworkManagerUtils.h $(am__append_8)
+	nm-enum-types.h nm-multi-index.c nm-multi-index.h \
+	NetworkManagerUtils.c NetworkManagerUtils.h $(am__append_7)
 libnm_iface_helper_la_LIBADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
 	$(top_builddir)/introspection/libnmdbus.la \
+	libNetworkManagerBase.la \
 	libsystemd-nm.la \
 	$(GLIB_LIBS) \
 	$(GUDEV_LIBS) \
 	$(LIBNL_LIBS) \
 	$(LIBNDP_LIBS) \
-	$(LIBDL) \
+	$(DL_LIBS) \
 	$(LIBM)
 
 nm_iface_helper_SOURCES = \
@@ -1190,23 +1239,38 @@ nm_iface_helper_SOURCES = \
 
 nm_iface_helper_LDADD = \
 	$(top_builddir)/libnm-core/libnm-core.la \
-	libsystemd-nm.la \
 	libnm-iface-helper.la \
 	$(GLIB_LIBS) \
 	$(GUDEV_LIBS) \
-	$(SYSTEMD_JOURNAL_LIBS) \
 	$(LIBNL_LIBS) \
 	$(LIBNDP_LIBS) \
 	$(LIBM)
 
-nm_iface_helper_LDFLAGS = -rdynamic
+nm_iface_helper_LDFLAGS = \
+	-rdynamic
+
 
-######################
+###############################################################################
+GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n'
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM --fhead '\#include "nm-default.h"\n'
+nm_enum_types_sources = $(filter-out \
+                              %/nm-device-private.h \
+                              %/nm-rdisc-private.h \
+                              %/wifi-utils-private.h \
+                              %/nm-dhcp-client-logging.h \
+                              %/nm-device-logging.h \
+                              , $(libNetworkManager_la_SOURCES))
+
+BUILT_SOURCES = $(GLIB_GENERATED)
+
+###############################################################################
 dbusservicedir = $(DBUS_SYS_DIR)
 dbusservice_DATA = org.freedesktop.NetworkManager.conf
 EXTRA_DIST = \
 	$(dbusservice_DATA) \
 	$(NetworkManager_DATA) \
+	nm-test-utils-core.h \
 	NetworkManager.ver
 
 rundir = $(runstatedir)/NetworkManager
@@ -1260,12 +1324,15 @@ clean-noinstLTLIBRARIES:
 	  rm -f $${locs}; \
 	}
 
-libNetworkManager-base.la: $(libNetworkManager_base_la_OBJECTS) $(libNetworkManager_base_la_DEPENDENCIES) $(EXTRA_libNetworkManager_base_la_DEPENDENCIES) 
-	$(AM_V_CCLD)$(LINK) $(am_libNetworkManager_base_la_rpath) $(libNetworkManager_base_la_OBJECTS) $(libNetworkManager_base_la_LIBADD) $(LIBS)
-
 libNetworkManager.la: $(libNetworkManager_la_OBJECTS) $(libNetworkManager_la_DEPENDENCIES) $(EXTRA_libNetworkManager_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(LINK)  $(libNetworkManager_la_OBJECTS) $(libNetworkManager_la_LIBADD) $(LIBS)
 
+libNetworkManagerBase.la: $(libNetworkManagerBase_la_OBJECTS) $(libNetworkManagerBase_la_DEPENDENCIES) $(EXTRA_libNetworkManagerBase_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(LINK)  $(libNetworkManagerBase_la_OBJECTS) $(libNetworkManagerBase_la_LIBADD) $(LIBS)
+
+libNetworkManagerTest.la: $(libNetworkManagerTest_la_OBJECTS) $(libNetworkManagerTest_la_DEPENDENCIES) $(EXTRA_libNetworkManagerTest_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(LINK)  $(libNetworkManagerTest_la_OBJECTS) $(libNetworkManagerTest_la_LIBADD) $(LIBS)
+
 libnm-iface-helper.la: $(libnm_iface_helper_la_OBJECTS) $(libnm_iface_helper_la_DEPENDENCIES) $(EXTRA_libnm_iface_helper_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(LINK)  $(libnm_iface_helper_la_OBJECTS) $(libnm_iface_helper_la_LIBADD) $(LIBS)
 
@@ -1385,8 +1452,6 @@ distclean-compile:
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetworkManagerUtils.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libNetworkManager_base_la-nm-core-utils.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libNetworkManager_base_la-nm-logging.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-alloc-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-arp-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-dhcp-identifier.Plo@am__quote@
@@ -1398,6 +1463,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-dns-domain.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-escape.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-ether-addr-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-extract-word.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fd-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fileio.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fs-util.Plo@am__quote@
@@ -1405,6 +1471,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-hashmap.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-hexdecoct.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-hostname-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-id128-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-in-addr-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-io-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-neighbor.Plo@am__quote@
@@ -1445,9 +1512,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-auth-subject.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-auth-utils.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-bus-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-checkpoint-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-checkpoint.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-config-data.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-config.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-connection-provider.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-connectivity.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-core-utils.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dcb.Plo@am__quote@
@@ -1467,7 +1535,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-vxlan.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-client.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-dhclient-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-dhclient-utils.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-dhclient.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-dhcpcd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-dhcp-listener.Plo@am__quote@
@@ -1549,19 +1617,12 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
-libNetworkManager_base_la-nm-core-utils.lo: nm-core-utils.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libNetworkManager_base_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libNetworkManager_base_la-nm-core-utils.lo -MD -MP -MF $(DEPDIR)/libNetworkManager_base_la-nm-core-utils.Tpo -c -o libNetworkManager_base_la-nm-core-utils.lo `test -f 'nm-core-utils.c' || echo '$(srcdir)/'`nm-core-utils.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libNetworkManager_base_la-nm-core-utils.Tpo $(DEPDIR)/libNetworkManager_base_la-nm-core-utils.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nm-core-utils.c' object='libNetworkManager_base_la-nm-core-utils.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libNetworkManager_base_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libNetworkManager_base_la-nm-core-utils.lo `test -f 'nm-core-utils.c' || echo '$(srcdir)/'`nm-core-utils.c
-
-libNetworkManager_base_la-nm-logging.lo: nm-logging.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libNetworkManager_base_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libNetworkManager_base_la-nm-logging.lo -MD -MP -MF $(DEPDIR)/libNetworkManager_base_la-nm-logging.Tpo -c -o libNetworkManager_base_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libNetworkManager_base_la-nm-logging.Tpo $(DEPDIR)/libNetworkManager_base_la-nm-logging.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nm-logging.c' object='libNetworkManager_base_la-nm-logging.lo' libtool=yes @AMDEPBACKSLASH@
+nm-dhcp-dhclient-utils.lo: dhcp-manager/nm-dhcp-dhclient-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhclient-utils.lo -MD -MP -MF $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo -c -o nm-dhcp-dhclient-utils.lo `test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`dhcp-manager/nm-dhcp-dhclient-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo $(DEPDIR)/nm-dhcp-dhclient-utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dhcp-manager/nm-dhcp-dhclient-utils.c' object='nm-dhcp-dhclient-utils.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libNetworkManager_base_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libNetworkManager_base_la-nm-logging.lo `test -f 'nm-logging.c' || echo '$(srcdir)/'`nm-logging.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient-utils.lo `test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`dhcp-manager/nm-dhcp-dhclient-utils.c
 
 nm-device.lo: devices/nm-device.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-device.lo -MD -MP -MF $(DEPDIR)/nm-device.Tpo -c -o nm-device.lo `test -f 'devices/nm-device.c' || echo '$(srcdir)/'`devices/nm-device.c
@@ -1710,13 +1771,6 @@ nm-linux-platform.lo: platform/nm-linux-platform.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-linux-platform.lo `test -f 'platform/nm-linux-platform.c' || echo '$(srcdir)/'`platform/nm-linux-platform.c
 
-nm-fake-platform.lo: platform/nm-fake-platform.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-platform.lo -MD -MP -MF $(DEPDIR)/nm-fake-platform.Tpo -c -o nm-fake-platform.lo `test -f 'platform/nm-fake-platform.c' || echo '$(srcdir)/'`platform/nm-fake-platform.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-platform.Tpo $(DEPDIR)/nm-fake-platform.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='platform/nm-fake-platform.c' object='nm-fake-platform.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-platform.lo `test -f 'platform/nm-fake-platform.c' || echo '$(srcdir)/'`platform/nm-fake-platform.c
-
 wifi-utils-nl80211.lo: platform/wifi/wifi-utils-nl80211.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wifi-utils-nl80211.lo -MD -MP -MF $(DEPDIR)/wifi-utils-nl80211.Tpo -c -o wifi-utils-nl80211.lo `test -f 'platform/wifi/wifi-utils-nl80211.c' || echo '$(srcdir)/'`platform/wifi/wifi-utils-nl80211.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/wifi-utils-nl80211.Tpo $(DEPDIR)/wifi-utils-nl80211.Plo
@@ -1731,13 +1785,6 @@ wifi-utils.lo: platform/wifi/wifi-utils.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wifi-utils.lo `test -f 'platform/wifi/wifi-utils.c' || echo '$(srcdir)/'`platform/wifi/wifi-utils.c
 
-nm-fake-rdisc.lo: rdisc/nm-fake-rdisc.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-rdisc.lo -MD -MP -MF $(DEPDIR)/nm-fake-rdisc.Tpo -c -o nm-fake-rdisc.lo `test -f 'rdisc/nm-fake-rdisc.c' || echo '$(srcdir)/'`rdisc/nm-fake-rdisc.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-rdisc.Tpo $(DEPDIR)/nm-fake-rdisc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdisc/nm-fake-rdisc.c' object='nm-fake-rdisc.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-rdisc.lo `test -f 'rdisc/nm-fake-rdisc.c' || echo '$(srcdir)/'`rdisc/nm-fake-rdisc.c
-
 nm-lndp-rdisc.lo: rdisc/nm-lndp-rdisc.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-lndp-rdisc.lo -MD -MP -MF $(DEPDIR)/nm-lndp-rdisc.Tpo -c -o nm-lndp-rdisc.lo `test -f 'rdisc/nm-lndp-rdisc.c' || echo '$(srcdir)/'`rdisc/nm-lndp-rdisc.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-lndp-rdisc.Tpo $(DEPDIR)/nm-lndp-rdisc.Plo
@@ -1885,6 +1932,20 @@ wifi-utils-wext.lo: platform/wifi/wifi-utils-wext.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wifi-utils-wext.lo `test -f 'platform/wifi/wifi-utils-wext.c' || echo '$(srcdir)/'`platform/wifi/wifi-utils-wext.c
 
+nm-fake-rdisc.lo: rdisc/nm-fake-rdisc.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-rdisc.lo -MD -MP -MF $(DEPDIR)/nm-fake-rdisc.Tpo -c -o nm-fake-rdisc.lo `test -f 'rdisc/nm-fake-rdisc.c' || echo '$(srcdir)/'`rdisc/nm-fake-rdisc.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-rdisc.Tpo $(DEPDIR)/nm-fake-rdisc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='rdisc/nm-fake-rdisc.c' object='nm-fake-rdisc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-rdisc.lo `test -f 'rdisc/nm-fake-rdisc.c' || echo '$(srcdir)/'`rdisc/nm-fake-rdisc.c
+
+nm-fake-platform.lo: platform/nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-platform.lo -MD -MP -MF $(DEPDIR)/nm-fake-platform.Tpo -c -o nm-fake-platform.lo `test -f 'platform/nm-fake-platform.c' || echo '$(srcdir)/'`platform/nm-fake-platform.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-platform.Tpo $(DEPDIR)/nm-fake-platform.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='platform/nm-fake-platform.c' object='nm-fake-platform.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-fake-platform.lo `test -f 'platform/nm-fake-platform.c' || echo '$(srcdir)/'`platform/nm-fake-platform.c
+
 libsystemd_nm_la-nm-sd.lo: systemd/nm-sd.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-nm-sd.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-nm-sd.Tpo -c -o libsystemd_nm_la-nm-sd.lo `test -f 'systemd/nm-sd.c' || echo '$(srcdir)/'`systemd/nm-sd.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-nm-sd.Tpo $(DEPDIR)/libsystemd_nm_la-nm-sd.Plo
@@ -1892,12 +1953,12 @@ libsystemd_nm_la-nm-sd.lo: systemd/nm-sd.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-nm-sd.lo `test -f 'systemd/nm-sd.c' || echo '$(srcdir)/'`systemd/nm-sd.c
 
-libsystemd_nm_la-nm-sd-adapt.lo: systemd/nm-sd-adapt.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-nm-sd-adapt.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Tpo -c -o libsystemd_nm_la-nm-sd-adapt.lo `test -f 'systemd/nm-sd-adapt.c' || echo '$(srcdir)/'`systemd/nm-sd-adapt.c
+libsystemd_nm_la-nm-sd-adapt.lo: systemd/sd-adapt/nm-sd-adapt.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-nm-sd-adapt.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Tpo -c -o libsystemd_nm_la-nm-sd-adapt.lo `test -f 'systemd/sd-adapt/nm-sd-adapt.c' || echo '$(srcdir)/'`systemd/sd-adapt/nm-sd-adapt.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Tpo $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/nm-sd-adapt.c' object='libsystemd_nm_la-nm-sd-adapt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/sd-adapt/nm-sd-adapt.c' object='libsystemd_nm_la-nm-sd-adapt.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-nm-sd-adapt.lo `test -f 'systemd/nm-sd-adapt.c' || echo '$(srcdir)/'`systemd/nm-sd-adapt.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-nm-sd-adapt.lo `test -f 'systemd/sd-adapt/nm-sd-adapt.c' || echo '$(srcdir)/'`systemd/sd-adapt/nm-sd-adapt.c
 
 libsystemd_nm_la-alloc-util.lo: systemd/src/basic/alloc-util.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-alloc-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-alloc-util.Tpo -c -o libsystemd_nm_la-alloc-util.lo `test -f 'systemd/src/basic/alloc-util.c' || echo '$(srcdir)/'`systemd/src/basic/alloc-util.c
@@ -1920,6 +1981,13 @@ libsystemd_nm_la-ether-addr-util.lo: systemd/src/basic/ether-addr-util.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-ether-addr-util.lo `test -f 'systemd/src/basic/ether-addr-util.c' || echo '$(srcdir)/'`systemd/src/basic/ether-addr-util.c
 
+libsystemd_nm_la-extract-word.lo: systemd/src/basic/extract-word.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-extract-word.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-extract-word.Tpo -c -o libsystemd_nm_la-extract-word.lo `test -f 'systemd/src/basic/extract-word.c' || echo '$(srcdir)/'`systemd/src/basic/extract-word.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-extract-word.Tpo $(DEPDIR)/libsystemd_nm_la-extract-word.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/basic/extract-word.c' object='libsystemd_nm_la-extract-word.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-extract-word.lo `test -f 'systemd/src/basic/extract-word.c' || echo '$(srcdir)/'`systemd/src/basic/extract-word.c
+
 libsystemd_nm_la-fd-util.lo: systemd/src/basic/fd-util.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-fd-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-fd-util.Tpo -c -o libsystemd_nm_la-fd-util.lo `test -f 'systemd/src/basic/fd-util.c' || echo '$(srcdir)/'`systemd/src/basic/fd-util.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-fd-util.Tpo $(DEPDIR)/libsystemd_nm_la-fd-util.Plo
@@ -2200,6 +2268,13 @@ libsystemd_nm_la-sd-event.lo: systemd/src/libsystemd/sd-event/sd-event.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-sd-event.lo `test -f 'systemd/src/libsystemd/sd-event/sd-event.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-event/sd-event.c
 
+libsystemd_nm_la-id128-util.lo: systemd/src/libsystemd/sd-id128/id128-util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-id128-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-id128-util.Tpo -c -o libsystemd_nm_la-id128-util.lo `test -f 'systemd/src/libsystemd/sd-id128/id128-util.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-id128/id128-util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-id128-util.Tpo $(DEPDIR)/libsystemd_nm_la-id128-util.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd/sd-id128/id128-util.c' object='libsystemd_nm_la-id128-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-id128-util.lo `test -f 'systemd/src/libsystemd/sd-id128/id128-util.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-id128/id128-util.c
+
 libsystemd_nm_la-sd-id128.lo: systemd/src/libsystemd/sd-id128/sd-id128.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-sd-id128.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-sd-id128.Tpo -c -o libsystemd_nm_la-sd-id128.lo `test -f 'systemd/src/libsystemd/sd-id128/sd-id128.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-id128/sd-id128.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-sd-id128.Tpo $(DEPDIR)/libsystemd_nm_la-sd-id128.Plo
@@ -2368,20 +2443,6 @@ nm-dhcp-dhclient.obj: dhcp-manager/nm-dhcp-dhclient.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient.obj `if test -f 'dhcp-manager/nm-dhcp-dhclient.c'; then $(CYGPATH_W) 'dhcp-manager/nm-dhcp-dhclient.c'; else $(CYGPATH_W) '$(srcdir)/dhcp-manager/nm-dhcp-dhclient.c'; fi`
 
-nm-dhcp-dhclient-utils.o: dhcp-manager/nm-dhcp-dhclient-utils.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhclient-utils.o -MD -MP -MF $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo -c -o nm-dhcp-dhclient-utils.o `test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`dhcp-manager/nm-dhcp-dhclient-utils.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo $(DEPDIR)/nm-dhcp-dhclient-utils.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dhcp-manager/nm-dhcp-dhclient-utils.c' object='nm-dhcp-dhclient-utils.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient-utils.o `test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c' || echo '$(srcdir)/'`dhcp-manager/nm-dhcp-dhclient-utils.c
-
-nm-dhcp-dhclient-utils.obj: dhcp-manager/nm-dhcp-dhclient-utils.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhclient-utils.obj -MD -MP -MF $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo -c -o nm-dhcp-dhclient-utils.obj `if test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c'; then $(CYGPATH_W) 'dhcp-manager/nm-dhcp-dhclient-utils.c'; else $(CYGPATH_W) '$(srcdir)/dhcp-manager/nm-dhcp-dhclient-utils.c'; fi`
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhclient-utils.Tpo $(DEPDIR)/nm-dhcp-dhclient-utils.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dhcp-manager/nm-dhcp-dhclient-utils.c' object='nm-dhcp-dhclient-utils.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dhcp-dhclient-utils.obj `if test -f 'dhcp-manager/nm-dhcp-dhclient-utils.c'; then $(CYGPATH_W) 'dhcp-manager/nm-dhcp-dhclient-utils.c'; else $(CYGPATH_W) '$(srcdir)/dhcp-manager/nm-dhcp-dhclient-utils.c'; fi`
-
 nm-dhcp-dhcpcd.o: dhcp-manager/nm-dhcp-dhcpcd.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-dhcp-dhcpcd.o -MD -MP -MF $(DEPDIR)/nm-dhcp-dhcpcd.Tpo -c -o nm-dhcp-dhcpcd.o `test -f 'dhcp-manager/nm-dhcp-dhcpcd.c' || echo '$(srcdir)/'`dhcp-manager/nm-dhcp-dhcpcd.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-dhcp-dhcpcd.Tpo $(DEPDIR)/nm-dhcp-dhcpcd.Po
diff --git a/src/NetworkManager.ver b/src/NetworkManager.ver
index b4397b9c..c91affcb 100644
--- a/src/NetworkManager.ver
+++ b/src/NetworkManager.ver
@@ -1,6 +1,7 @@
 {
 global:
-	nm*;
+	nm_*;
+	nmp_*;
 	_nm*;
 	NM*;
 	_NM*;
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 9894872b..7eb6cf16 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -529,8 +529,8 @@ check_connection_mac_address (NMConnection *orig,
 
 static gboolean
 check_connection_cloned_mac_address (NMConnection *orig,
-                              NMConnection *candidate,
-                              GHashTable *settings)
+                                     NMConnection *candidate,
+                                     GHashTable *settings)
 {
 	GHashTable *props;
 	const char *orig_mac = NULL, *cand_mac = NULL;
@@ -551,6 +551,12 @@ check_connection_cloned_mac_address (NMConnection *orig,
 	if (s_wired_cand)
 		cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand);
 
+	/* special cloned mac address entires are accepted. */
+	if (NM_CLONED_MAC_IS_SPECIAL (orig_mac))
+		orig_mac = NULL;
+	if (NM_CLONED_MAC_IS_SPECIAL (cand_mac))
+		cand_mac = NULL;
+
 	if (!orig_mac || !cand_mac) {
 		remove_from_hash (settings, props,
 		                  NM_SETTING_WIRED_SETTING_NAME,
diff --git a/src/devices/Makefile.in b/src/devices/Makefile.in
index d6848e53..4791015d 100644
--- a/src/devices/Makefile.in
+++ b/src/devices/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -225,6 +226,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -281,13 +283,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -299,6 +302,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -359,6 +364,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -394,6 +401,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/devices/adsl/Makefile.am b/src/devices/adsl/Makefile.am
index 5f981740..9075021c 100644
--- a/src/devices/adsl/Makefile.am
+++ b/src/devices/adsl/Makefile.am
@@ -40,6 +40,7 @@ if ENABLE_TESTS
 
 check-local:
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-adsl.so $(SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-adsl.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 endif
 
diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in
index b6805537..f3fbad4c 100644
--- a/src/devices/adsl/Makefile.in
+++ b/src/devices/adsl/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/adsl
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -252,6 +253,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -308,13 +310,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -326,6 +329,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -386,6 +391,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -421,6 +428,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -838,6 +846,7 @@ include $(GLIB_MAKEFILE)
 
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-adsl.so $(SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-adsl.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/adsl/nm-atm-manager.h b/src/devices/adsl/nm-atm-manager.h
index fe0bc14c..bcbc6c77 100644
--- a/src/devices/adsl/nm-atm-manager.h
+++ b/src/devices/adsl/nm-atm-manager.h
@@ -22,10 +22,6 @@
 #ifndef __NETWORKMANAGER_ATM_MANAGER_H__
 #define __NETWORKMANAGER_ATM_MANAGER_H__
 
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_ATM_MANAGER            (nm_atm_manager_get_type ())
 #define NM_ATM_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ATM_MANAGER, NMAtmManager))
 
diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h
index efcdaf69..cfde2b35 100644
--- a/src/devices/adsl/nm-device-adsl.h
+++ b/src/devices/adsl/nm-device-adsl.h
@@ -23,11 +23,8 @@
 #define __NETWORKMANAGER_DEVICE_ADSL_H__
 
 // Parent class
-#include "nm-default.h"
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_ADSL            (nm_device_adsl_get_type ())
 #define NM_DEVICE_ADSL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdsl))
 #define NM_DEVICE_ADSL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass))
@@ -52,6 +49,4 @@ NMDevice *nm_device_adsl_new (const char *udi,
                               const char *driver,
                               int atm_index);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_ADSL_H */
+#endif /* NM_DEVICE_ADSL_H */
diff --git a/src/devices/bluetooth/Makefile.am b/src/devices/bluetooth/Makefile.am
index 2ff9a572..8454702e 100644
--- a/src/devices/bluetooth/Makefile.am
+++ b/src/devices/bluetooth/Makefile.am
@@ -76,6 +76,7 @@ if ENABLE_TESTS
 
 check-local:
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-bluetooth.so $(SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-bluetooth.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 endif
 
diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in
index d2b0b865..c010f4c7 100644
--- a/src/devices/bluetooth/Makefile.in
+++ b/src/devices/bluetooth/Makefile.in
@@ -96,8 +96,9 @@ host_triplet = @host@
 @WITH_BLUEZ5_DUN_TRUE@am__append_3 = $(BLUEZ5_LIBS)
 subdir = src/devices/bluetooth
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -273,6 +274,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -329,13 +331,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -347,6 +350,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -407,6 +412,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -442,6 +449,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -872,6 +880,7 @@ include $(GLIB_MAKEFILE)
 
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-bluetooth.so $(SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-bluetooth.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index dcfa5eec..3463da6b 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -28,6 +28,7 @@
 #include "nm-bt-error.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
+#include "nm-settings.h"
 #include "nm-settings-connection.h"
 #include "NetworkManagerUtils.h"
 
@@ -67,7 +68,7 @@ typedef struct {
 	NMBluez5DunContext *b5_dun_context;
 #endif
 
-	NMConnectionProvider *provider;
+	NMSettings *settings;
 	GSList *connections;
 
 	NMConnection *pan_connection;
@@ -96,7 +97,7 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 
-static void cp_connection_added (NMConnectionProvider *provider,
+static void cp_connection_added (NMSettings *settings,
                                  NMConnection *connection, NMBluezDevice *self);
 static gboolean connection_compatible (NMBluezDevice *self, NMConnection *connection);
 
@@ -233,9 +234,9 @@ pan_connection_check_create (NMBluezDevice *self)
 	/* Adding a new connection raises a signal which eventually calls check_emit_usable (again)
 	 * which then already finds the suitable connection in priv->connections. This is confusing,
 	 * so block the signal. check_emit_usable will succeed after this function call returns. */
-	g_signal_handlers_block_by_func (priv->provider, cp_connection_added, self);
-	added = nm_connection_provider_add_connection (priv->provider, connection, FALSE, &error);
-	g_signal_handlers_unblock_by_func (priv->provider, cp_connection_added, self);
+	g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
+	added = NM_CONNECTION (nm_settings_add_connection (priv->settings, connection, FALSE, &error));
+	g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
 
 	if (added) {
 		g_assert (!g_slist_find (priv->connections, added));
@@ -367,7 +368,7 @@ _internal_track_connection (NMBluezDevice *self, NMConnection *connection, gbool
 }
 
 static void
-cp_connection_added (NMConnectionProvider *provider,
+cp_connection_added (NMSettings *settings,
                      NMConnection *connection,
                      NMBluezDevice *self)
 {
@@ -378,7 +379,7 @@ cp_connection_added (NMConnectionProvider *provider,
 }
 
 static void
-cp_connection_removed (NMConnectionProvider *provider,
+cp_connection_removed (NMSettings *settings,
                        NMConnection *connection,
                        NMBluezDevice *self)
 {
@@ -387,8 +388,9 @@ cp_connection_removed (NMConnectionProvider *provider,
 }
 
 static void
-cp_connection_updated (NMConnectionProvider *provider,
+cp_connection_updated (NMSettings *settings,
                        NMConnection *connection,
+                       gboolean by_user,
                        NMBluezDevice *self)
 {
 	if (_internal_track_connection (self, connection,
@@ -400,12 +402,13 @@ static void
 load_connections (NMBluezDevice *self)
 {
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	const GSList *connections, *iter;
+	NMSettingsConnection *const*connections;
+	guint i;
 	gboolean changed = FALSE;
 
-	connections = nm_connection_provider_get_connections (priv->provider);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *connection = iter->data;
+	connections = nm_settings_get_connections (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMConnection *connection = (NMConnection *) connections[i];
 
 		if (connection_compatible (self, connection))
 			changed |= _internal_track_connection (self, connection, TRUE);
@@ -1029,7 +1032,7 @@ on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
 NMBluezDevice *
 nm_bluez_device_new (const char *path,
                      const char *adapter_address,
-                     NMConnectionProvider *provider,
+                     NMSettings *settings,
                      int bluez_version)
 {
 	NMBluezDevice *self;
@@ -1037,7 +1040,7 @@ nm_bluez_device_new (const char *path,
 	const char *interface_name = NULL;
 
 	g_return_val_if_fail (path != NULL, NULL);
-	g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (provider), NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
 	g_return_val_if_fail (bluez_version == 4 || bluez_version == 5, NULL);
 
 	self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
@@ -1051,14 +1054,14 @@ nm_bluez_device_new (const char *path,
 	priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
 	priv->bluez_version = bluez_version;
-	priv->provider = g_object_ref (provider);
+	priv->settings = g_object_ref (settings);
 	g_return_val_if_fail (bluez_version == 5 || (bluez_version == 4 && adapter_address), NULL);
 	if (adapter_address)
 		set_adapter_address (self, adapter_address);
 
-	g_signal_connect (priv->provider, NM_CP_SIGNAL_CONNECTION_ADDED,   G_CALLBACK (cp_connection_added),   self);
-	g_signal_connect (priv->provider, NM_CP_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
-	g_signal_connect (priv->provider, NM_CP_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,   G_CALLBACK (cp_connection_added),   self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
 
 	g_bus_get (G_BUS_TYPE_SYSTEM,
 	           NULL,
@@ -1116,10 +1119,10 @@ dispose (GObject *object)
 	}
 #endif
 
-	if (priv->provider) {
-		g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_added, self);
-		g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_removed, self);
-		g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_updated, self);
+	if (priv->settings) {
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
 	}
 
 	g_slist_free_full (priv->connections, g_object_unref);
@@ -1137,7 +1140,7 @@ dispose (GObject *object)
 		g_object_unref (to_delete);
 	}
 
-	g_clear_object (&priv->provider);
+	g_clear_object (&priv->settings);
 }
 
 static void
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
index c9560540..66a349e0 100644
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -22,7 +22,6 @@
 #define __NETWORKMANAGER_BLUEZ_DEVICE_H__
 
 #include "nm-connection.h"
-#include "nm-connection-provider.h"
 
 #define NM_TYPE_BLUEZ_DEVICE            (nm_bluez_device_get_type ())
 #define NM_BLUEZ_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice))
@@ -59,7 +58,7 @@ GType nm_bluez_device_get_type (void);
 
 NMBluezDevice *nm_bluez_device_new (const char *path,
                                     const char *adapter_address,
-                                    NMConnectionProvider *provider,
+                                    NMSettings *settings,
                                     int bluez_version);
 
 const char *nm_bluez_device_get_path (NMBluezDevice *self);
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index 766ecc0a..0d2e302c 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -20,19 +20,20 @@
 
 #include "nm-default.h"
 
+#include "nm-bluez-manager.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
 #include <gmodule.h>
 
-#include "nm-bluez-manager.h"
 #include "nm-device-factory.h"
 #include "nm-setting-bluetooth.h"
+#include "nm-settings.h"
 #include "nm-bluez4-manager.h"
 #include "nm-bluez5-manager.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
-#include "nm-connection-provider.h"
 #include "nm-device-bt.h"
 #include "nm-core-internal.h"
 #include "nm-platform.h"
@@ -51,7 +52,7 @@
 typedef struct {
 	int bluez_version;
 
-	NMConnectionProvider *provider;
+	NMSettings *settings;
 	NMBluez4Manager *manager4;
 	NMBluez5Manager *manager5;
 
@@ -190,7 +191,7 @@ setup_bluez4 (NMBluezManager *self)
 	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
 
 	setup_version_number (self, 4);
-	priv->manager4 = manager = nm_bluez4_manager_new (priv->provider);
+	priv->manager4 = manager = nm_bluez4_manager_new (priv->settings);
 
 	g_signal_connect (manager,
 	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
@@ -209,7 +210,7 @@ setup_bluez5 (NMBluezManager *self)
 	g_return_if_fail (!priv->manager4 && !priv->manager5 && !priv->bluez_version);
 
 	setup_version_number (self, 5);
-	priv->manager5 = manager = nm_bluez5_manager_new (priv->provider);
+	priv->manager5 = manager = nm_bluez5_manager_new (priv->settings);
 
 	g_signal_connect (manager,
 	                  NM_BLUEZ_MANAGER_BDADDR_ADDED,
@@ -407,9 +408,9 @@ dispose (GObject *object)
 
 	priv->bluez_version = 0;
 
-	g_clear_object (&priv->provider);
-
 	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
+
+	g_clear_object (&priv->settings);
 }
 
 static void
@@ -417,7 +418,7 @@ nm_bluez_manager_init (NMBluezManager *self)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
-	priv->provider = g_object_ref (nm_connection_provider_get ());
+	priv->settings = g_object_ref (NM_SETTINGS_GET);
 }
 
 static NMDevice *
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
index 1f5597d8..d23b33a7 100644
--- a/src/devices/bluetooth/nm-bluez-manager.h
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -22,10 +22,6 @@
 #ifndef __NETWORKMANAGER_BLUEZ_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ_MANAGER_H__
 
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
 #define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
 
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index 9d8fd210..2ef071a8 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -20,13 +20,15 @@
 
 #include "nm-default.h"
 
+#include "nm-bluez4-adapter.h"
+
 #include <string.h>
 
 #include "nm-dbus-interface.h"
-#include "nm-bluez4-adapter.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 #include "nm-core-internal.h"
+#include "nm-settings.h"
 
 G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
 
@@ -41,7 +43,7 @@ typedef struct {
 	GHashTable *devices;
 
 	/* Cached for devices */
-	NMConnectionProvider *provider;
+	NMSettings *settings;
 } NMBluez4AdapterPrivate;
 
 
@@ -160,7 +162,7 @@ device_created (GDBusProxy *proxy, const char *path, gpointer user_data)
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	device = nm_bluez_device_new (path, priv->address, priv->provider, 4);
+	device = nm_bluez_device_new (path, priv->address, priv->settings, 4);
 	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
 	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
 	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
@@ -234,17 +236,19 @@ query_properties (NMBluez4Adapter *self)
 /***********************************************************/
 
 NMBluez4Adapter *
-nm_bluez4_adapter_new (const char *path, NMConnectionProvider *provider)
+nm_bluez4_adapter_new (const char *path, NMSettings *settings)
 {
 	NMBluez4Adapter *self;
 	NMBluez4AdapterPrivate *priv;
 
+	g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
+
 	self = (NMBluez4Adapter *) g_object_new (NM_TYPE_BLUEZ4_ADAPTER,
 	                                         NM_BLUEZ4_ADAPTER_PATH, path,
 	                                         NULL);
 	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 
-	priv->provider = provider;
+	priv->settings = g_object_ref (settings);
 
 	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
 	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
@@ -301,6 +305,8 @@ finalize (GObject *object)
 	g_object_unref (priv->proxy);
 
 	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->finalize (object);
+
+	g_object_unref (priv->settings);
 }
 
 static void
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
index a8a2bfb4..ee61112f 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.h
+++ b/src/devices/bluetooth/nm-bluez4-adapter.h
@@ -21,10 +21,7 @@
 #ifndef __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
 #define __NETWORKMANAGER_BLUEZ4_ADAPTER_H__
 
-
-#include "nm-default.h"
 #include "nm-bluez-device.h"
-#include "nm-connection-provider.h"
 
 #define NM_TYPE_BLUEZ4_ADAPTER            (nm_bluez4_adapter_get_type ())
 #define NM_BLUEZ4_ADAPTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4Adapter))
@@ -54,7 +51,7 @@ typedef struct {
 GType nm_bluez4_adapter_get_type (void);
 
 NMBluez4Adapter *nm_bluez4_adapter_new (const char *path,
-                                        NMConnectionProvider *provider);
+                                        NMSettings *settings);
 
 const char *nm_bluez4_adapter_get_path (NMBluez4Adapter *self);
 
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
index 146612a8..36bc30e5 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.c
+++ b/src/devices/bluetooth/nm-bluez4-manager.c
@@ -21,20 +21,22 @@
 
 #include "nm-default.h"
 
+#include "nm-bluez4-manager.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
 
-#include "nm-bluez-manager.h"
-#include "nm-bluez4-manager.h"
 #include "nm-bluez4-adapter.h"
+#include "nm-bluez-manager.h"
 #include "nm-bluez-common.h"
 #include "nm-core-internal.h"
+#include "nm-settings.h"
 
 typedef struct {
 	gulong name_owner_changed_id;
 
-	NMConnectionProvider *provider;
+	NMSettings *settings;
 
 	GDBusProxy *proxy;
 
@@ -155,7 +157,7 @@ default_adapter_changed (GDBusProxy *proxy, const char *path, NMBluez4Manager *s
 
 	/* Add the new default adapter */
 	if (path) {
-		priv->adapter = nm_bluez4_adapter_new (path, priv->provider);
+		priv->adapter = nm_bluez4_adapter_new (path, priv->settings);
 		g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self);
 	}
 }
@@ -223,12 +225,14 @@ name_owner_changed_cb (GObject *object,
 /****************************************************************/
 
 NMBluez4Manager *
-nm_bluez4_manager_new (NMConnectionProvider *provider)
+nm_bluez4_manager_new (NMSettings *settings)
 {
 	NMBluez4Manager *instance;
 
+	g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
+
 	instance = g_object_new (NM_TYPE_BLUEZ4_MANAGER, NULL);
-	NM_BLUEZ4_MANAGER_GET_PRIVATE (instance)->provider = provider;
+	NM_BLUEZ4_MANAGER_GET_PRIVATE (instance)->settings = g_object_ref (settings);
 	return instance;
 }
 
@@ -264,6 +268,8 @@ dispose (GObject *object)
 	g_clear_object (&priv->adapter);
 
 	G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object);
+
+	g_clear_object (&priv->settings);
 }
 
 static void
diff --git a/src/devices/bluetooth/nm-bluez4-manager.h b/src/devices/bluetooth/nm-bluez4-manager.h
index f6bf7658..8afe6924 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.h
+++ b/src/devices/bluetooth/nm-bluez4-manager.h
@@ -1,4 +1,3 @@
-/* -*- 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
@@ -22,11 +21,6 @@
 #ifndef __NETWORKMANAGER_BLUEZ4_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ4_MANAGER_H__
 
-#include "nm-default.h"
-#include "nm-connection-provider.h"
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_BLUEZ4_MANAGER            (nm_bluez4_manager_get_type ())
 #define NM_BLUEZ4_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ4_MANAGER, NMBluez4Manager))
 #define NM_BLUEZ4_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ4_MANAGER, NMBluez4ManagerClass))
@@ -51,7 +45,7 @@ typedef struct {
 
 GType nm_bluez4_manager_get_type (void);
 
-NMBluez4Manager *nm_bluez4_manager_new (NMConnectionProvider *provider);
+NMBluez4Manager *nm_bluez4_manager_new (NMSettings *settings);
 
 void nm_bluez4_manager_query_devices (NMBluez4Manager *manager);
 
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index c59cbb80..124c1a05 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -21,8 +21,6 @@
 #ifndef _NM_BLUEZ5_UTILS_H_
 #define _NM_BLUEZ5_UTILS_H_
 
-#include "nm-default.h"
-
 typedef struct _NMBluez5DunContext NMBluez5DunContext;
 
 typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 66d171bf..a014d0c1 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -22,19 +22,21 @@
 
 #include "nm-default.h"
 
+#include "nm-bluez5-manager.h"
+
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
 
+#include "nm-core-internal.h"
+
 #include "nm-bluez-manager.h"
-#include "nm-bluez5-manager.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
-
-#include "nm-core-internal.h"
+#include "nm-settings.h"
 
 typedef struct {
-	NMConnectionProvider *provider;
+	NMSettings *settings;
 
 	GDBusProxy *proxy;
 
@@ -140,7 +142,7 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	device = nm_bluez_device_new (path, NULL, priv->provider, 5);
+	device = nm_bluez_device_new (path, NULL, priv->settings, 5);
 	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
 	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
 	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
@@ -180,7 +182,7 @@ object_manager_interfaces_removed (GDBusProxy       *proxy,
                                    const char      **ifaces,
                                    NMBluez5Manager  *self)
 {
-	if (_nm_utils_string_in_list (BLUEZ5_DEVICE_INTERFACE, ifaces))
+	if (ifaces && g_strv_contains (ifaces, BLUEZ5_DEVICE_INTERFACE))
 		device_removed (proxy, path, self);
 }
 
@@ -309,12 +311,14 @@ bluez_cleanup (NMBluez5Manager *self, gboolean do_signal)
 /****************************************************************/
 
 NMBluez5Manager *
-nm_bluez5_manager_new (NMConnectionProvider *provider)
+nm_bluez5_manager_new (NMSettings *settings)
 {
 	NMBluez5Manager *instance = NULL;
 
+	g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
+
 	instance = g_object_new (NM_TYPE_BLUEZ5_MANAGER, NULL);
-	NM_BLUEZ5_MANAGER_GET_PRIVATE (instance)->provider = provider;
+	NM_BLUEZ5_MANAGER_GET_PRIVATE (instance)->settings = g_object_ref (settings);
 	return instance;
 }
 
@@ -347,6 +351,8 @@ finalize (GObject *object)
 	g_hash_table_destroy (priv->devices);
 
 	G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->finalize (object);
+
+	g_object_unref (priv->settings);
 }
 
 static void
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
index 1c531090..0e309d33 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ b/src/devices/bluetooth/nm-bluez5-manager.h
@@ -22,11 +22,6 @@
 #ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
 #define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
 
-#include "nm-default.h"
-#include "nm-connection-provider.h"
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_BLUEZ5_MANAGER            (nm_bluez5_manager_get_type ())
 #define NM_BLUEZ5_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5Manager))
 #define NM_BLUEZ5_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
@@ -51,7 +46,7 @@ typedef struct {
 
 GType nm_bluez5_manager_get_type (void);
 
-NMBluez5Manager *nm_bluez5_manager_new (NMConnectionProvider *provider);
+NMBluez5Manager *nm_bluez5_manager_new (NMSettings *settings);
 
 void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
 
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index eef4ed0a..67ae15bc 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -362,6 +362,7 @@ static void
 ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 
 	switch (nm_device_get_state (device)) {
 	case NM_DEVICE_STATE_PREPARE:
@@ -375,7 +376,18 @@ ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
 	case NM_DEVICE_STATE_ACTIVATED:
 		if (nm_device_activate_ip4_state_in_conf (device))
 			nm_device_activate_schedule_ip4_config_timeout (device);
-		else {
+		else if (nm_device_activate_ip6_state_in_conf (device))
+			nm_device_activate_schedule_ip6_config_timeout (device);
+		else if (nm_device_activate_ip4_state_done (device)) {
+			nm_device_ip_method_failed (device,
+			                            AF_INET,
+			                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		} else if (nm_device_activate_ip6_state_done (device)) {
+			nm_device_ip_method_failed (device,
+			                            AF_INET6,
+			                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		} else {
+			_LOGW (LOGD_MB, "PPP failure in unexpected state %u", (guint) nm_device_get_state (device));
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
@@ -502,8 +514,9 @@ modem_ip4_config_result (NMModem *modem,
 		_LOGW (LOGD_MB | LOGD_IP4 | LOGD_BT,
 		       "retrieving IP4 configuration failed: %s",
 		       error->message);
-
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		nm_device_ip_method_failed (device,
+		                            AF_INET,
+		                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else
 		nm_device_activate_schedule_ip4_config_result (device, config);
 }
@@ -989,7 +1002,7 @@ nm_device_bt_new (NMBluezDevice *bt_device,
 	                                  NM_DEVICE_UDI, udi,
 	                                  NM_DEVICE_IFACE, bdaddr,
 	                                  NM_DEVICE_DRIVER, "bluez",
-	                                  NM_DEVICE_HW_ADDRESS, bdaddr,
+	                                  NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
 	                                  NM_DEVICE_BT_DEVICE, bt_device,
 	                                  NM_DEVICE_BT_NAME, name,
 	                                  NM_DEVICE_BT_CAPABILITIES, capabilities,
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index c565745f..c97cca01 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -25,8 +25,6 @@
 #include "nm-bluez-device.h"
 #include "nm-modem.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_BT		(nm_device_bt_get_type ())
 #define NM_DEVICE_BT(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
 #define NM_DEVICE_BT_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
@@ -63,6 +61,4 @@ gboolean nm_device_bt_modem_added (NMDeviceBt *device,
                                    NMModem *modem,
                                    const char *driver);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_DEVICE_BT_H__ */
diff --git a/src/devices/nm-arping-manager.h b/src/devices/nm-arping-manager.h
index 4a4f8d4b..78965a0d 100644
--- a/src/devices/nm-arping-manager.h
+++ b/src/devices/nm-arping-manager.h
@@ -19,8 +19,6 @@
 
 #include <netinet/in.h>
 
-G_BEGIN_DECLS
-
 #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))
@@ -48,6 +46,4 @@ gboolean nm_arping_manager_check_address (NMArpingManager *self, in_addr_t addre
 void nm_arping_manager_announce_addresses (NMArpingManager *self);
 void nm_arping_manager_reset (NMArpingManager *self);
 
-G_END_DECLS
-
 #endif /* __NM_ARPING_MANAGER__ */
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 9c98c8ea..3f17849b 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2011 - 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -118,12 +118,15 @@ complete_connection (NMDevice *device,
 /******************************************************************/
 
 static gboolean
-set_bond_attr (NMDevice *device, const char *attr, const char *value)
+set_bond_attr (NMDevice *device, NMBondMode mode, const char *attr, const char *value)
 {
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	gboolean ret;
 	int ifindex = nm_device_get_ifindex (device);
 
+	if (!_nm_setting_bond_option_supported (attr, mode))
+		return FALSE;
+
 	ret = nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, attr, value);
 	if (!ret)
 		_LOGW (LOGD_HW, "failed to set bonding attribute '%s' to '%s'", attr, value);
@@ -134,10 +137,10 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value)
 static gboolean
 ignore_if_zero (const char *option, const char *value)
 {
-	if (strcmp (option, "arp_interval") &&
-	    strcmp (option, "miimon") &&
-	    strcmp (option, "downdelay") &&
-	    strcmp (option, "updelay"))
+	if (!NM_IN_STRSET (option, NM_SETTING_BOND_OPTION_ARP_INTERVAL,
+	                           NM_SETTING_BOND_OPTION_DOWNDELAY,
+	                           NM_SETTING_BOND_OPTION_MIIMON,
+	                           NM_SETTING_BOND_OPTION_UPDELAY))
 		return FALSE;
 
 	return g_strcmp0 (value, "0") == 0 ? TRUE : FALSE;
@@ -173,7 +176,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 		    && !ignore_if_zero (*options, value)
 		    && !nm_streq0 (value, defvalue)) {
 			/* Replace " " with "," for arp_ip_targets from the kernel */
-			if (strcmp (*options, "arp_ip_target") == 0) {
+			if (strcmp (*options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET) == 0) {
 				for (p = value; *p; p++) {
 					if (*p == ' ')
 						*p = ',';
@@ -201,6 +204,7 @@ master_update_slave_connection (NMDevice *self,
 
 static void
 set_arp_targets (NMDevice *device,
+                 NMBondMode mode,
                  const char *value,
                  const char *delim,
                  const char *prefix)
@@ -214,7 +218,7 @@ set_arp_targets (NMDevice *device,
 	for (iter = items; iter && *iter; iter++) {
 		if (*iter[0]) {
 			tmp = g_strdup_printf ("%s%s", prefix, *iter);
-			set_bond_attr (device, "arp_ip_target", tmp);
+			set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp);
 			g_free (tmp);
 		}
 	}
@@ -223,7 +227,7 @@ set_arp_targets (NMDevice *device,
 
 static void
 set_simple_option (NMDevice *device,
-                   const char *attr,
+                   NMBondMode mode,
                    NMSettingBond *s_bond,
                    const char *opt)
 {
@@ -232,18 +236,20 @@ set_simple_option (NMDevice *device,
 	value = nm_setting_bond_get_option_by_name (s_bond, opt);
 	if (!value)
 		value = nm_setting_bond_get_option_default (s_bond, opt);
-	set_bond_attr (device, attr, value);
+	set_bond_attr (device, mode, opt, value);
 }
 
 static NMActStageReturn
 apply_bonding_config (NMDevice *device)
 {
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	NMConnection *connection;
 	NMSettingBond *s_bond;
 	int ifindex = nm_device_get_ifindex (device);
-	const char *mode, *value;
+	const char *mode_str, *value;
 	char *contents;
 	gboolean set_arp_interval = TRUE;
+	NMBondMode mode;
 
 	/* Option restrictions:
 	 *
@@ -263,98 +269,90 @@ apply_bonding_config (NMDevice *device)
 	s_bond = nm_connection_get_setting_bond (connection);
 	g_assert (s_bond);
 
-	mode = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
-	if (mode == NULL)
-		mode = "balance-rr";
+	mode_str = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
+	if (!mode_str)
+		mode_str = "balance-rr";
+
+	mode = _nm_setting_bond_mode_from_string (mode_str);
+	if (mode == NM_BOND_MODE_UNKNOWN) {
+		_LOGW (LOGD_BOND, "unknown bond mode '%s'", mode_str);
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	/* Set mode first, as some other options (e.g. arp_interval) are valid
+	 * only for certain modes.
+	 */
+
+	set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_MODE, mode_str);
 
 	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON);
 	if (value && atoi (value)) {
 		/* clear arp interval */
-		set_bond_attr (device, "arp_interval", "0");
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0");
 		set_arp_interval = FALSE;
 
-		set_bond_attr (device, "miimon", value);
-		set_simple_option (device, "updelay", s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
-		set_simple_option (device, "downdelay", s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_MIIMON, value);
+		set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
+		set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
 	} else if (!value) {
-		/* If not given, and arp_interval is not given, default to 100 */
-		long int val_int;
-		char *end;
-
+		/* If not given, and arp_interval is not given or disabled, default to 100 */
 		value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
-		errno = 0;
-		val_int = strtol (value ? value : "0", &end, 10);
-		if (!value || (val_int == 0 && errno == 0 && *end == '\0'))
-			set_bond_attr (device, "miimon", "100");
+		if (_nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 0) == 0)
+			set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_MIIMON, "100");
 	}
 
-	/* The stuff after 'mode' requires the given mode or doesn't care */
-	set_bond_attr (device, "mode", mode);
-
-	/* arp_interval not compatible with ALB, TLB */
-	if (NM_IN_STRSET (mode, "balance-alb", "balance-tlb"))
-		set_arp_interval = FALSE;
-
 	if (set_arp_interval) {
-		set_simple_option (device, "arp_interval", s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
-
+		set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
 		/* Just let miimon get cleared automatically; even setting miimon to
 		 * 0 (disabled) clears arp_interval.
 		 */
 	}
 
+	/* ARP validate: value > 0 only valid in active-backup mode */
 	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE);
-	/* arp_validate > 0 only valid in active-backup mode */
 	if (   value
 	    && !nm_streq (value, "0")
 	    && !nm_streq (value, "none")
-	    && nm_streq (mode, "active-backup"))
-		set_bond_attr (device, "arp_validate", value);
+	    && mode == NM_BOND_MODE_ACTIVEBACKUP)
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_VALIDATE, value);
 	else
-		set_bond_attr (device, "arp_validate", "0");
-
-	if (NM_IN_STRSET (mode, "active-backup", "balance-alb", "balance-tlb")) {
-		value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
-		set_bond_attr (device, "primary", value ? value : "");
-		set_simple_option (device, "lp_interval", s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL);
-	}
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_VALIDATE, "0");
 
-	/* Clear ARP targets */
-	contents = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "arp_ip_target");
-	set_arp_targets (device, contents, " \n", "-");
-	g_free (contents);
+	/* Primary */
+	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
+	set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : "");
 
-	/* Add new ARP targets */
+	/* ARP targets: clear and initialize the list */
+	contents = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex,
+	                                                 NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+	set_arp_targets (device, mode, contents, " \n", "-");
 	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
-	set_arp_targets (device, value, ",", "+");
-
-	set_simple_option (device, "primary_reselect", s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
-	set_simple_option (device, "fail_over_mac", s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
-	set_simple_option (device, "use_carrier", s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
-	set_simple_option (device, "ad_select", s_bond, NM_SETTING_BOND_OPTION_AD_SELECT);
-	set_simple_option (device, "xmit_hash_policy", s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
-	set_simple_option (device, "resend_igmp", s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);
-	set_simple_option (device, "active_slave", s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
-	set_simple_option (device, "all_slaves_active", s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE);
-	set_simple_option (device, "num_grat_arp", s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
-	set_simple_option (device, "num_unsol_na", s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
-
-	if (nm_streq (mode, "802.3ad")) {
-		set_simple_option (device, "lacp_rate", s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
-		set_simple_option (device, "ad_actor_sys_prio", s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO);
-		set_simple_option (device, "ad_actor_system", s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM);
-		set_simple_option (device, "ad_user_port_key", s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY);
-		set_simple_option (device, "min_links", s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS);
-	}
-
-	if (nm_streq (mode, "active-backup"))
-		set_simple_option (device, "arp_all_targets", s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS);
-
-	if (nm_streq (mode, "balance-rr"))
-		set_simple_option (device, "packets_per_slave", s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE);
+	set_arp_targets (device, mode, value, ",", "+");
+	g_free (contents);
 
-	if (nm_streq (mode, "balance-tlb"))
-		set_simple_option (device, "tlb_dynamic_lb", s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB);
+	/* AD actor system: don't set if empty */
+	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM);
+	if (value)
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, value);
+
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_AD_SELECT);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
diff --git a/src/devices/nm-device-bond.h b/src/devices/nm-device-bond.h
index 9e86719e..02b6c8e5 100644
--- a/src/devices/nm-device-bond.h
+++ b/src/devices/nm-device-bond.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_BOND            (nm_device_bond_get_type ())
 #define NM_DEVICE_BOND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BOND, NMDeviceBond))
 #define NM_DEVICE_BOND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BOND, NMDeviceBondClass))
@@ -37,6 +35,4 @@ typedef NMDeviceClass NMDeviceBondClass;
 
 GType nm_device_bond_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_BOND_H */
+#endif /* NM_DEVICE_BOND_H */
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index 46482675..05da9ed7 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -21,11 +21,8 @@
 #ifndef __NETWORKMANAGER_DEVICE_BRIDGE_H__
 #define __NETWORKMANAGER_DEVICE_BRIDGE_H__
 
-#include "nm-default.h"
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_BRIDGE            (nm_device_bridge_get_type ())
 #define NM_DEVICE_BRIDGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridge))
 #define NM_DEVICE_BRIDGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass))
@@ -38,6 +35,4 @@ typedef NMDeviceClass NMDeviceBridgeClass;
 
 GType nm_device_bridge_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_BRIDGE_H */
+#endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
index d034ddfc..298e6dff 100644
--- a/src/devices/nm-device-ethernet-utils.c
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -25,30 +25,26 @@
 #include "nm-device-ethernet-utils.h"
 
 char *
-nm_device_ethernet_utils_get_default_wired_name (const GSList *connections)
+nm_device_ethernet_utils_get_default_wired_name (NMConnection *const *connections)
 {
-	const GSList *iter;
-	char *cname = NULL;
-	int i = 0;
+	char *temp;
+	guint j;
+	int i;
 
 	/* Find the next available unique connection name */
-	while (!cname && (i++ < 10000)) {
-		char *temp;
-		gboolean found = FALSE;
-
+	for (i = 1; i <= 10000; i++) {
 		temp = g_strdup_printf (_("Wired connection %d"), i);
-		for (iter = connections; iter; iter = iter->next) {
-			if (g_strcmp0 (nm_connection_get_id (NM_CONNECTION (iter->data)), temp) == 0) {
-				found = TRUE;
+		for (j = 0; connections[j]; j++) {
+			if (nm_streq0 (nm_connection_get_id (connections[j]), temp)) {
 				g_free (temp);
-				break;
+				goto next;
 			}
 		}
-
-		if (found == FALSE)
-			cname = temp;
+		return temp;
+next:
+		;
 	}
 
-	return cname;
+	return NULL;
 }
 
diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h
index 31d645d8..197d0a9e 100644
--- a/src/devices/nm-device-ethernet-utils.h
+++ b/src/devices/nm-device-ethernet-utils.h
@@ -19,8 +19,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 #define __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 
-#include "nm-default.h"
-
-char *nm_device_ethernet_utils_get_default_wired_name (const GSList *connections);
+char *nm_device_ethernet_utils_get_default_wired_name (NMConnection *const *connections);
 
 #endif  /* NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H */
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index d27f7dca..90d472da 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -21,6 +21,8 @@
 
 #include "nm-default.h"
 
+#include "nm-device-ethernet.h"
+
 #include <netinet/in.h>
 #include <string.h>
 #include <stdlib.h>
@@ -29,7 +31,6 @@
 
 #include <gudev/gudev.h>
 
-#include "nm-device-ethernet.h"
 #include "nm-device-private.h"
 #include "nm-activation-request.h"
 #include "NetworkManagerUtils.h"
@@ -44,7 +45,7 @@
 #include "nm-settings-connection.h"
 #include "nm-config.h"
 #include "nm-device-ethernet-utils.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
@@ -54,15 +55,15 @@
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceEthernet);
 
-G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
-
-#define NM_DEVICE_ETHERNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate))
+/*****************************************************************************/
 
 #define WIRED_SECRETS_TRIES "wired-secrets-tries"
 
 #define PPPOE_RECONNECT_DELAY 7
 #define PPPOE_ENCAP_OVERHEAD  8 /* 2 bytes for PPP, 6 for PPPoE */
 
+/*****************************************************************************/
+
 typedef struct Supplicant {
 	NMSupplicantManager *mgr;
 	NMSupplicantInterface *iface;
@@ -90,7 +91,7 @@ typedef enum {
 	DCB_WAIT_CARRIER_POSTCONFIG_UP,
 } DcbWait;
 
-typedef struct {
+typedef struct _NMDeviceEthernetPrivate {
 	guint32             speed;
 
 	Supplicant          supplicant;
@@ -117,15 +118,29 @@ typedef struct {
 	gulong        dcb_carrier_id;
 } NMDeviceEthernetPrivate;
 
-enum {
-	PROP_0,
-	PROP_PERM_HW_ADDRESS,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceEthernet,
 	PROP_SPEED,
 	PROP_S390_SUBCHANNELS,
+);
 
-	LAST_PROP
-};
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_ETHERNET_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMDeviceEthernet *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_DEVICE_ETHERNET (_self)); \
+		_self->_priv; \
+	})
 
+/*****************************************************************************/
 
 static char *
 get_link_basename (const char *parent_path, const char *name, GError **error)
@@ -147,21 +162,29 @@ static void
 _update_s390_subchannels (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	GUdevDevice *dev;
-	GUdevDevice *parent = NULL;
-	const char *parent_path, *item, *driver;
+	gs_unref_object GUdevDevice *dev = NULL;
+	gs_unref_object GUdevDevice *parent = NULL;
+	const char *parent_path, *item;
 	int ifindex;
 	GDir *dir;
 	GError *error = NULL;
 
-	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
-	dev = (GUdevDevice *) nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex);
+	if (priv->subchannels) {
+		/* only read the subchannels once. For one, we don't expect them to change
+		 * on multiple invocations. Second, we didn't implement proper reloading.
+		 * Proper reloading might also be complicated, because the subchannels are
+		 * used to match on devices based on a device-spec. Thus, it's not clear
+		 * what it means to change afterwards. */
+		return;
+	}
+
+	ifindex = nm_device_get_ifindex ((NMDevice *) self);
+	dev = (GUdevDevice *) nm_g_object_ref (nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex));
 	if (!dev) {
-		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device %d '%s' with udev",
-		       ifindex, nm_device_get_iface (NM_DEVICE (self)) ?: "(null)");
-		goto out;
+		/* we only call _update_s390_subchannels() when platform claims the device to be initialized.
+		 * Thus, we expect to successfully lookup a GUdevDevice. */
+		g_return_if_reached ();
 	}
-	g_object_ref (dev);
 
 	/* Try for the "ccwgroup" parent */
 	parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL);
@@ -169,17 +192,17 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 		/* FIXME: whatever 'lcs' devices' subsystem is here... */
 		if (!parent) {
 			/* Not an s390 device */
-			goto out;
+			return;
 		}
 	}
 
 	parent_path = g_udev_device_get_sysfs_path (parent);
 	dir = g_dir_open (parent_path, 0, &error);
 	if (!dir) {
-		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to open directory '%s': %s",
+		_LOGW (LOGD_DEVICE | LOGD_HW, "update-s390: failed to open directory '%s': %s",
 		       parent_path, error->message);
 		g_clear_error (&error);
-		goto out;
+		return;
 	}
 
 	while ((item = g_dir_read_name (dir))) {
@@ -206,11 +229,11 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 				g_hash_table_insert (priv->s390_options, g_strdup (item), value);
 				value = NULL;
 			} else
-				_LOGW (LOGD_DEVICE | LOGD_HW, "error reading %s", path);
+				_LOGW (LOGD_DEVICE | LOGD_HW, "update-s390: error reading %s", path);
 		}
 
 		if (error) {
-			_LOGW (LOGD_DEVICE | LOGD_HW, "%s", error->message);
+			_LOGW (LOGD_DEVICE | LOGD_HW, "update-s390: failed reading sysfs for %s (%s)", item, error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -235,42 +258,23 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	priv->subchannels_dbus[2] = g_strdup (priv->subchan3);
 	priv->subchannels_dbus[3] = NULL;
 
-	driver = nm_device_get_driver (NM_DEVICE (self));
-	_LOGI (LOGD_DEVICE | LOGD_HW, "found s390 '%s' subchannels [%s]",
-	       driver ? driver : "(unknown driver)", priv->subchannels);
+	_LOGI (LOGD_DEVICE | LOGD_HW, "update-s390: found s390 '%s' subchannels [%s]",
+	       nm_device_get_driver ((NMDevice *) self) ?: "(unknown driver)",
+	       priv->subchannels);
 
-out:
-	if (parent)
-		g_object_unref (parent);
-	if (dev)
-		g_object_unref (dev);
+	_notify (self, PROP_S390_SUBCHANNELS);
 }
 
-static GObject*
-constructor (GType type,
-             guint n_construct_params,
-             GObjectConstructParam *construct_params)
+static void
+constructed (GObject *object)
 {
-	GObject *object;
-
-	object = G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructor (type,
-	                                                                        n_construct_params,
-	                                                                        construct_params);
-	if (object) {
-#ifndef G_DISABLE_ASSERT
-		int ifindex = nm_device_get_ifindex (NM_DEVICE (object));
-		NMLinkType link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
-
-		g_assert (   link_type == NM_LINK_TYPE_ETHERNET
-		          || link_type == NM_LINK_TYPE_VETH
-		          || link_type == NM_LINK_TYPE_NONE);
-#endif
-
-		/* s390 stuff */
-		_update_s390_subchannels (NM_DEVICE_ETHERNET (object));
-	}
+	const NMPlatformLink *pllink;
+
+	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructed (object);
 
-	return object;
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, nm_device_get_ifindex ((NMDevice *) object));
+	if (pllink && pllink->initialized)
+		_update_s390_subchannels ((NMDeviceEthernet *) object);
 }
 
 static void
@@ -302,16 +306,12 @@ device_state_changed (NMDevice *device,
 static void
 nm_device_ethernet_init (NMDeviceEthernet *self)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-}
+	NMDeviceEthernetPrivate *priv;
 
-static void
-realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
-{
-	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->realize_start_notify (device, plink);
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate);
+	self->_priv = priv;
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
+	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
 static NMDeviceCapabilities
@@ -407,7 +407,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		if (!match_subchans (self, s_wired, &try_mac))
 			return FALSE;
 
-		perm_hw_addr = nm_device_get_permanent_hw_address (device);
+		perm_hw_addr = nm_device_get_permanent_hw_address (device, TRUE);
 		mac = nm_setting_wired_get_mac_address (s_wired);
 		if (perm_hw_addr) {
 			if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
@@ -812,21 +812,17 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	NMSettingWired *s_wired;
-	const char *cloned_mac;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason);
 	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
-		s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED);
-		if (s_wired) {
-			/* Set device MAC address if the connection wants to change it */
-			cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-			nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER);
-		}
+		if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE))
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
+	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
 		/* If we're re-activating a PPPoE connection a short while after
 		 * a previous PPPoE connection was torn down, wait a bit to allow the
 		 * remote side to handle the disconnection.  Otherwise the peer may
@@ -946,7 +942,7 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	g_assert (req);
 
-	s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_PPPOE);
+	s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE);
 	g_assert (s_pppoe);
 
 	priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (NM_DEVICE (self)));
@@ -972,22 +968,6 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reas
 
 /****************************************************************/
 
-static void
-dcb_timeout_cleanup (NMDevice *device)
-{
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-
-	nm_clear_g_source (&priv->dcb_timeout_id);
-}
-
-static void
-dcb_carrier_cleanup (NMDevice *device)
-{
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-
-	nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
-}
-
 static void dcb_state (NMDevice *device, gboolean timeout);
 
 static gboolean
@@ -1011,12 +991,12 @@ dcb_carrier_timeout (gpointer user_data)
 static gboolean
 dcb_configure (NMDevice *device)
 {
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernet *self = (NMDeviceEthernet *) device;
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingDcb *s_dcb;
 	GError *error = NULL;
 
-	dcb_timeout_cleanup (device);
+	nm_clear_g_source (&priv->dcb_timeout_id);
 
 	s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB);
 	g_assert (s_dcb);
@@ -1043,7 +1023,7 @@ dcb_enable (NMDevice *device)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	GError *error = NULL;
 
-	dcb_timeout_cleanup (device);
+	nm_clear_g_source (&priv->dcb_timeout_id);
 	if (!nm_dcb_enable (nm_device_get_iface (device), TRUE, &error)) {
 		_LOGW (LOGD_DCB, "Activation: (ethernet) failed to enable DCB/FCoE: %s",
 		       error->message);
@@ -1081,9 +1061,9 @@ dcb_state (NMDevice *device, gboolean timeout)
 	case DCB_WAIT_CARRIER_PREENABLE_UP:
 		if (timeout || carrier) {
 			_LOGD (LOGD_DCB, "dcb_state() enabling DCB");
-			dcb_timeout_cleanup (device);
+			nm_clear_g_source (&priv->dcb_timeout_id);
 			if (!dcb_enable (device)) {
-				dcb_carrier_cleanup (device);
+				nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 				nm_device_state_changed (device,
 				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
@@ -1091,7 +1071,7 @@ dcb_state (NMDevice *device, gboolean timeout)
 		}
 		break;
 	case DCB_WAIT_CARRIER_PRECONFIG_DOWN:
-		dcb_timeout_cleanup (device);
+		nm_clear_g_source (&priv->dcb_timeout_id);
 		priv->dcb_wait = DCB_WAIT_CARRIER_PRECONFIG_UP;
 
 		if (!carrier) {
@@ -1105,9 +1085,9 @@ dcb_state (NMDevice *device, gboolean timeout)
 	case DCB_WAIT_CARRIER_PRECONFIG_UP:
 		if (timeout || carrier) {
 			_LOGD (LOGD_DCB, "dcb_state() preconfig up configuring DCB");
-			dcb_timeout_cleanup (device);
+			nm_clear_g_source (&priv->dcb_timeout_id);
 			if (!dcb_configure (device)) {
-				dcb_carrier_cleanup (device);
+				nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 				nm_device_state_changed (device,
 				                         NM_DEVICE_STATE_FAILED,
 				                         NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
@@ -1115,7 +1095,7 @@ dcb_state (NMDevice *device, gboolean timeout)
 		}
 		break;
 	case DCB_WAIT_CARRIER_POSTCONFIG_DOWN:
-		dcb_timeout_cleanup (device);
+		nm_clear_g_source (&priv->dcb_timeout_id);
 		priv->dcb_wait = DCB_WAIT_CARRIER_POSTCONFIG_UP;
 
 		if (!carrier) {
@@ -1129,8 +1109,8 @@ dcb_state (NMDevice *device, gboolean timeout)
 	case DCB_WAIT_CARRIER_POSTCONFIG_UP:
 		if (timeout || carrier) {
 			_LOGD (LOGD_DCB, "dcb_state() postconfig up starting IP");
-			dcb_timeout_cleanup (device);
-			dcb_carrier_cleanup (device);
+			nm_clear_g_source (&priv->dcb_timeout_id);
+			nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 			priv->dcb_wait = DCB_WAIT_UNKNOWN;
 			nm_device_activate_schedule_stage3_ip_config_start (device);
 		}
@@ -1200,7 +1180,7 @@ found:
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernet *self = (NMDeviceEthernet *) device;
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
 	const char *connection_type;
@@ -1213,8 +1193,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	                                                              NM_TYPE_SETTING_CONNECTION));
 	g_assert (s_con);
 
-	dcb_timeout_cleanup (device);
-	dcb_carrier_cleanup (device);
+	nm_clear_g_source (&priv->dcb_timeout_id);
+	nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 
 	/* 802.1x has to run before any IP configuration since the 802.1x auth
 	 * process opens the port up for normal traffic.
@@ -1311,7 +1291,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	guint32 mtu;
 
 	/* MTU only set for plain ethernet */
-	if (NM_DEVICE_ETHERNET_GET_PRIVATE (device)->ppp_manager)
+	if (NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device)->ppp_manager)
 		return;
 
 	connection = nm_device_get_applied_connection (device);
@@ -1348,8 +1328,8 @@ deactivate (NMDevice *device)
 	supplicant_interface_release (self);
 
 	priv->dcb_wait = DCB_WAIT_UNKNOWN;
-	dcb_timeout_cleanup (device);
-	dcb_carrier_cleanup (device);
+	nm_clear_g_source (&priv->dcb_timeout_id);
+	nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
 
 	/* Tear down DCB/FCoE if it was enabled */
 	s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB);
@@ -1363,11 +1343,7 @@ deactivate (NMDevice *device)
 
 	/* Set last PPPoE connection time */
 	if (nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE))
-		NM_DEVICE_ETHERNET_GET_PRIVATE (device)->last_pppoe_time = nm_utils_get_monotonic_timestamp_s ();
-
-	/* Reset MAC address back to initial address */
-	if (nm_device_get_initial_hw_address (device))
-		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_ETHER);
+		priv->last_pppoe_time = nm_utils_get_monotonic_timestamp_s ();
 }
 
 static gboolean
@@ -1408,7 +1384,7 @@ complete_connection (NMDevice *device,
 		nm_connection_add_setting (connection, NM_SETTING (s_wired));
 	}
 
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 	if (perm_hw_addr) {
 		setting_mac = nm_setting_wired_get_mac_address (s_wired);
 		if (setting_mac) {
@@ -1435,9 +1411,9 @@ static NMConnection *
 new_default_connection (NMDevice *self)
 {
 	NMConnection *connection;
-	const GSList *connections;
+	NMSettingsConnection *const*connections;
 	NMSetting *setting;
-	const char *hw_address;
+	const char *perm_hw_addr;
 	gs_free char *defname = NULL;
 	gs_free char *uuid = NULL;
 	gs_free char *machine_id = NULL;
@@ -1445,16 +1421,16 @@ new_default_connection (NMDevice *self)
 	if (nm_config_get_no_auto_default_for_device (nm_config_get (), self))
 		return NULL;
 
-	hw_address = nm_device_get_hw_address (self);
-	if (!hw_address)
+	perm_hw_addr = nm_device_get_permanent_hw_address (self, TRUE);
+	if (!perm_hw_addr)
 		return NULL;
 
 	connection = nm_simple_connection_new ();
 	setting = nm_setting_connection_new ();
 	nm_connection_add_setting (connection, setting);
 
-	connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
-	defname = nm_device_ethernet_utils_get_default_wired_name (connections);
+	connections = nm_settings_get_connections (nm_device_get_settings (self), NULL);
+	defname = nm_device_ethernet_utils_get_default_wired_name ((NMConnection *const*) connections);
 	if (!defname)
 		return NULL;
 
@@ -1465,7 +1441,7 @@ new_default_connection (NMDevice *self)
 	uuid = _nm_utils_uuid_generate_from_strings ("default-wired",
 	                                             machine_id ?: "",
 	                                             defname,
-	                                             hw_address,
+	                                             perm_hw_addr,
 	                                             NULL);
 
 	g_object_set (setting,
@@ -1479,7 +1455,7 @@ new_default_connection (NMDevice *self)
 
 	/* Lock the connection to the device */
 	setting = nm_setting_wired_new ();
-	g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, hw_address, NULL);
+	g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL);
 	nm_connection_add_setting (connection, setting);
 
 	return connection;
@@ -1489,7 +1465,7 @@ static NMMatchSpecMatchType
 spec_match_list (NMDevice *device, const GSList *specs)
 {
 	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device);
 
 	if (priv->subchannels)
 		matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
@@ -1503,9 +1479,9 @@ spec_match_list (NMDevice *device, const GSList *specs)
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device);
 	NMSettingWired *s_wired = nm_connection_get_setting_wired (connection);
-	const char *perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	const char *perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 	const char *mac = nm_device_get_hw_address (device);
 	const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
 	GHashTableIter iter;
@@ -1562,7 +1538,7 @@ get_link_speed (NMDevice *device)
 		return;
 
 	priv->speed = speed;
-	g_object_notify (G_OBJECT (device), "speed");
+	_notify (self, PROP_SPEED);
 
 	_LOGD (LOGD_HW | LOGD_ETHER, "speed is now %d Mb/s", speed);
 }
@@ -1579,37 +1555,9 @@ carrier_changed (NMDevice *device, gboolean carrier)
 static void
 link_changed (NMDevice *device, NMPlatformLink *info)
 {
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	static const guint8 zero_hwaddr[ETH_ALEN];
-	const guint8 *hwaddr;
-	gsize hwaddrlen = 0;
-
 	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info);
-	if (!priv->subchan1 && info->initialized)
-		_update_s390_subchannels (self);
-
-	if (!nm_device_get_initial_hw_address (device)) {
-		hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET,
-		                                       nm_device_get_ifindex (self),
-		                                       &hwaddrlen);
-		if (!nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, ETH_ALEN)) {
-			_LOGD (LOGD_DEVICE, "device got a valid hw address");
-			nm_device_update_hw_address (self);
-			nm_device_update_initial_hw_address (self);
-			if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
-				/*
-				 * If the device is UNAVAILABLE, any previous try to
-				 * bring it up probably has failed because of the
-				 * invalid hardware address; try again.
-				 */
-				nm_device_bring_up (self, TRUE, NULL);
-				nm_device_queue_recheck_available (device,
-				                                   NM_DEVICE_STATE_REASON_NONE,
-				                                   NM_DEVICE_STATE_REASON_NONE);
-			}
-		}
-	}
+	if (info->initialized)
+		_update_s390_subchannels ((NMDeviceEthernet *) device);
 }
 
 static gboolean
@@ -1631,8 +1579,8 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&priv->pppoe_wait_id);
 
-	dcb_timeout_cleanup (NM_DEVICE (self));
-	dcb_carrier_cleanup (NM_DEVICE (self));
+	nm_clear_g_source (&priv->dcb_timeout_id);
+	nm_clear_g_signal_handler (self, &priv->dcb_carrier_id);
 
 	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
 }
@@ -1663,9 +1611,6 @@ get_property (GObject *object, guint prop_id,
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	switch (prop_id) {
-	case PROP_PERM_HW_ADDRESS:
-		g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object)));
-		break;
 	case PROP_SPEED:
 		g_value_set_uint (value, priv->speed);
 		break;
@@ -1700,14 +1645,13 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRED_SETTING_NAME, NM_LINK_TYPE_ETHERNET)
 
 	/* virtual methods */
-	object_class->constructor = constructor;
+	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
 	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
 	parent_class->new_default_connection = new_default_connection;
@@ -1725,27 +1669,19 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 
 	parent_class->state_changed = device_state_changed;
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_PERM_HW_ADDRESS,
-		 g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_SPEED,
-		 g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_S390_SUBCHANNELS,
-		 g_param_spec_boxed (NM_DEVICE_ETHERNET_S390_SUBCHANNELS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SPEED] =
+	    g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_S390_SUBCHANNELS] =
+	    g_param_spec_boxed (NM_DEVICE_ETHERNET_S390_SUBCHANNELS, "", "",
+	                        G_TYPE_STRV,
+	                        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_ETHERNET_SKELETON,
diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h
index 2d284822..3ce6f0ca 100644
--- a/src/devices/nm-device-ethernet.h
+++ b/src/devices/nm-device-ethernet.h
@@ -24,8 +24,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #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))
@@ -33,15 +31,20 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_ETHERNET_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_ETHERNET))
 #define NM_DEVICE_ETHERNET_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
 
-#define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address"
 #define NM_DEVICE_ETHERNET_SPEED "speed"
 #define NM_DEVICE_ETHERNET_S390_SUBCHANNELS "s390-subchannels"
 
-typedef NMDevice NMDeviceEthernet;
-typedef NMDeviceClass NMDeviceEthernetClass;
+struct _NMDeviceEthernetPrivate;
 
-GType nm_device_ethernet_get_type (void);
+typedef struct {
+	NMDevice parent;
+	struct _NMDeviceEthernetPrivate *_priv;
+} NMDeviceEthernet;
 
-G_END_DECLS
+typedef struct {
+	NMDeviceClass parent_class;
+} NMDeviceEthernetClass;
+
+GType nm_device_ethernet_get_type (void);
 
-#endif	/* NM_DEVICE_ETHERNET_H */
+#endif /* __NETWORKMANAGER_DEVICE_ETHERNET_H__ */
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index ae78968a..4a62468c 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -92,10 +92,10 @@ typedef struct {
 	 * @connection: the #NMConnection to return the parent name for, if supported
 	 *
 	 * Given a connection, returns the a parent interface name, parent connection
-	 * UUID, or parent device hardware address for @connection.
+	 * UUID, or parent device permanent hardware address for @connection.
 	 *
 	 * Returns: the parent interface name, parent connection UUID, parent
-	 *   device hardware address, or %NULL
+	 *   device permenent hardware address, or %NULL
 	 */
 	const char * (*get_connection_parent) (NMDeviceFactory *factory,
 	                                       NMConnection *connection);
diff --git a/src/devices/nm-device-generic.h b/src/devices/nm-device-generic.h
index 9303a278..9a5ff091 100644
--- a/src/devices/nm-device-generic.h
+++ b/src/devices/nm-device-generic.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_GENERIC            (nm_device_generic_get_type ())
 #define NM_DEVICE_GENERIC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGeneric))
 #define NM_DEVICE_GENERIC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass))
@@ -47,6 +45,4 @@ GType nm_device_generic_get_type (void);
 
 NMDevice *nm_device_generic_new (const NMPlatformLink *plink);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_GENERIC_H */
+#endif /* __NETWORKMANAGER_DEVICE_GENERIC_H__ */
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index ae9543e4..17f50ba2 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -147,10 +147,15 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 
 	if (nm_device_is_real (device)) {
 		const char *mac;
+		const char *hw_addr;
 
 		mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-		if (mac && !nm_utils_hwaddr_matches (mac, -1, nm_device_get_hw_address (device), -1))
-			return FALSE;
+		if (mac) {
+			hw_addr = nm_device_get_permanent_hw_address (device, TRUE);
+			if (   !hw_addr
+			    || !nm_utils_hwaddr_matches (mac, -1, hw_addr, -1))
+				return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -183,7 +188,7 @@ complete_connection (NMDevice *device,
 	}
 
 	setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-	hw_address = nm_device_get_hw_address (device);
+	hw_address = nm_device_get_permanent_hw_address (device, TRUE);
 	if (setting_mac) {
 		/* Make sure the setting MAC (if any) matches the device's MAC */
 		if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) {
@@ -209,7 +214,7 @@ static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection);
-	const char *mac = nm_device_get_hw_address (device);
+	const char *mac = nm_device_get_permanent_hw_address (device, TRUE);
 	const char *transport_mode = "datagram";
 	int ifindex;
 
diff --git a/src/devices/nm-device-infiniband.h b/src/devices/nm-device-infiniband.h
index cac9a79d..a74d07a5 100644
--- a/src/devices/nm-device-infiniband.h
+++ b/src/devices/nm-device-infiniband.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_INFINIBAND			(nm_device_infiniband_get_type ())
 #define NM_DEVICE_INFINIBAND(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfiniband))
 #define NM_DEVICE_INFINIBAND_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass))
@@ -37,6 +35,4 @@ typedef NMDeviceClass NMDeviceInfinibandClass;
 
 GType nm_device_infiniband_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_INFINIBAND_H */
+#endif /* __NETWORKMANAGER_DEVICE_INFINIBAND_H__ */
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 089b1035..785010eb 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -20,19 +20,20 @@
 
 #include "nm-default.h"
 
+#include "nm-device-ip-tunnel.h"
+
 #include <string.h>
 #include <netinet/in.h>
 #include <linux/if.h>
 #include <linux/ip.h>
 #include <linux/if_tunnel.h>
 
-#include "nm-device-ip-tunnel.h"
 #include "nm-device-private.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-activation-request.h"
 #include "nm-ip4-config.h"
 
@@ -385,8 +386,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 			NMConnection *parent_connection;
 
 			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (),
-			                                                                   setting_parent);
+			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device),
+			                                                                         setting_parent);
 			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
 				new_parent = NULL;
 		}
diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h
index 11cb5bce..94cc7375 100644
--- a/src/devices/nm-device-ip-tunnel.h
+++ b/src/devices/nm-device-ip-tunnel.h
@@ -24,8 +24,6 @@
 #include "nm-core-types.h"
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_IP_TUNNEL            (nm_device_ip_tunnel_get_type ())
 #define NM_DEVICE_IP_TUNNEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnel))
 #define NM_DEVICE_IP_TUNNEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_IP_TUNNEL, NMDeviceIPTunnelClass))
@@ -55,6 +53,4 @@ typedef struct {
 
 GType nm_device_ip_tunnel_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_IP_TUNNEL_H */
+#endif /* __NETWORKMANAGER_DEVICE_IP_TUNNEL_H__ */
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index 1be17073..fac92eac 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -21,7 +21,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_LOGGING_H__
 #define __NETWORKMANAGER_DEVICE_LOGGING_H__
 
-#include "nm-default.h"
 #include "nm-device.h"
 
 #define _LOG_DECLARE_SELF(t) \
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index c431fe89..2bfc65cb 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -20,11 +20,12 @@
 
 #include "nm-default.h"
 
+#include "nm-device-macvlan.h"
+
 #include <string.h>
 
-#include "nm-device-macvlan.h"
 #include "nm-device-private.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-activation-request.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
@@ -372,9 +373,8 @@ match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hw
 	if (!priv->parent)
 		return !fail_if_no_hwaddr;
 
-	parent_mac = nm_device_get_hw_address (priv->parent);
-
-	return nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1);
+	parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE);
+	return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1);
 }
 
 static gboolean
@@ -488,7 +488,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 			NMConnection *parent_connection;
 
 			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
+			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device), setting_parent);
 			if (parent_connection && nm_device_check_connection_compatible (priv->parent, parent_connection))
 				new_parent = NULL;
 		}
@@ -502,8 +502,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 static NMActStageReturn
 act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 {
-	NMSettingWired *s_wired;
-	const char *cloned_mac;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -512,14 +510,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED);
-	if (s_wired) {
-		/* Set device MAC address if the connection wants to change it */
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW);
-	}
-
-	return TRUE;
+	if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
 static void
@@ -548,16 +541,6 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
 	update_properties (device);
 }
 
-static void
-deactivate (NMDevice *device)
-{
-	/* Reset MAC address back to initial address */
-	if (nm_device_get_initial_hw_address (device)) {
-		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device),
-		                       "reset", LOGD_DEVICE);
-	}
-}
-
 /******************************************************************/
 
 static void
@@ -638,7 +621,6 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	device_class->complete_connection = complete_connection;
 	device_class->connection_type = NM_SETTING_MACVLAN_SETTING_NAME;
 	device_class->create_and_realize = create_and_realize;
-	device_class->deactivate = deactivate;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	device_class->is_available = is_available;
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index 89f670bd..5756f696 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_MACVLAN            (nm_device_macvlan_get_type ())
 #define NM_DEVICE_MACVLAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlan))
 #define NM_DEVICE_MACVLAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanClass))
@@ -42,6 +40,4 @@ typedef NMDeviceClass NMDeviceMacvlanClass;
 
 GType nm_device_macvlan_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_MACVLAN_H */
+#endif /* __NETWORKMANAGER_DEVICE_MACVLAN_H__ */
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 602c2c85..c85cb969 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -24,8 +24,6 @@
 
 #include "nm-device.h"
 
-#include "nm-default.h"
-
 /* This file should only be used by subclasses of NMDevice */
 
 enum NMActStageReturn {
@@ -42,6 +40,8 @@ enum NMActStageReturn {
 
 #define NM_DEVICE_CAP_INTERNAL_MASK 0xc0000000
 
+NMSettings *nm_device_get_settings (NMDevice *self);
+
 void nm_device_set_ip_iface (NMDevice *self, const char *iface);
 
 void nm_device_activate_schedule_stage3_ip_config_start (NMDevice *device);
@@ -54,8 +54,9 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar
 
 void nm_device_take_down (NMDevice *self, gboolean block);
 
-gboolean nm_device_set_hw_addr (NMDevice *device, const char *addr,
-                                const char *detail, guint64 hw_log_domain);
+gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr, const char *detail);
+gboolean nm_device_hw_addr_set_cloned (NMDevice *device, NMConnection *connection, gboolean is_wifi);
+gboolean nm_device_hw_addr_reset (NMDevice *device, const char *detail);
 
 void nm_device_set_firmware_missing (NMDevice *self, gboolean missing);
 
@@ -70,9 +71,11 @@ void nm_device_activate_schedule_ip6_config_timeout (NMDevice *device);
 
 gboolean nm_device_activate_ip4_state_in_conf (NMDevice *device);
 gboolean nm_device_activate_ip4_state_in_wait (NMDevice *device);
+gboolean nm_device_activate_ip4_state_done (NMDevice *device);
 
 gboolean nm_device_activate_ip6_state_in_conf (NMDevice *device);
 gboolean nm_device_activate_ip6_state_in_wait (NMDevice *device);
+gboolean nm_device_activate_ip6_state_done (NMDevice *device);
 
 void nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout);
 void nm_device_set_dhcp_anycast_address (NMDevice *device, const char *addr);
@@ -103,6 +106,10 @@ void nm_device_queue_recheck_available (NMDevice *device,
 void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config);
 void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config);
 
+gboolean nm_device_hw_addr_is_explict (NMDevice *device);
+
+void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason);
+
 gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value);
 
 #define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index 01cd8ea6..1f42d060 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -291,8 +291,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (device);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
-	NMSettingWired *s_wired;
-	const char *cloned_mac;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -305,12 +303,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	if (g_strcmp0 (priv->mode, "tap"))
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 
-	s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED);
-	if (s_wired) {
-		/* Set device MAC address if the connection wants to change it */
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE);
-	}
+	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
diff --git a/src/devices/nm-device-tun.h b/src/devices/nm-device-tun.h
index 32dcfd47..d62b0c37 100644
--- a/src/devices/nm-device-tun.h
+++ b/src/devices/nm-device-tun.h
@@ -23,8 +23,6 @@
 
 #include "nm-device-generic.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_TUN            (nm_device_tun_get_type ())
 #define NM_DEVICE_TUN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_TUN, NMDeviceTun))
 #define NM_DEVICE_TUN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_TUN, NMDeviceTunClass))
@@ -44,6 +42,4 @@ typedef NMDeviceClass NMDeviceTunClass;
 
 GType nm_device_tun_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_TUN_H */
+#endif /* __NETWORKMANAGER_DEVICE_TUN_H__ */
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index ff198a7d..cca86fbe 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -38,23 +38,44 @@
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceVeth);
 
-G_DEFINE_TYPE (NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET)
-
-#define NM_DEVICE_VETH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VETH, NMDeviceVethPrivate))
+/*****************************************************************************/
 
 typedef struct {
 	NMDevice *peer;
 	gboolean ever_had_peer;
 } NMDeviceVethPrivate;
 
-enum {
-	PROP_0,
-	PROP_PEER,
+struct _NMDeviceVeth {
+	NMDeviceEthernet parent;
+	NMDeviceVethPrivate _priv;
+};
 
-	LAST_PROP
+struct _NMDeviceVethClass {
+	NMDeviceEthernetClass parent_class;
 };
 
-/**************************************************************/
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceVeth,
+	PROP_PEER,
+);
+
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMDeviceVeth, nm_device_veth, NM_TYPE_DEVICE_ETHERNET)
+
+#define NM_DEVICE_VETH_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMDeviceVeth *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_DEVICE_VETH (_self)); \
+		&_self->_priv; \
+	})
+
+/*****************************************************************************/
 
 static void
 set_peer (NMDeviceVeth *self, NMDevice *peer)
@@ -66,7 +87,7 @@ set_peer (NMDeviceVeth *self, NMDevice *peer)
 		priv->peer = peer;
 		g_object_add_weak_pointer (G_OBJECT (peer), (gpointer *) &priv->peer);
 
-		g_object_notify (G_OBJECT (self), NM_DEVICE_VETH_PEER);
+		_notify (self, PROP_PEER);
 	}
 }
 
@@ -150,8 +171,6 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (NMDeviceVethPrivate));
-
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH)
 
 	object_class->get_property = get_property;
@@ -159,13 +178,13 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 
 	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
 
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_PEER,
-		 g_param_spec_string (NM_DEVICE_VETH_PEER, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PEER] =
+	    g_param_spec_string (NM_DEVICE_VETH_PEER, "", "",
+	                         NULL,
+	                         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_VETH_SKELETON,
diff --git a/src/devices/nm-device-veth.h b/src/devices/nm-device-veth.h
index c7b8ec3a..53ccf925 100644
--- a/src/devices/nm-device-veth.h
+++ b/src/devices/nm-device-veth.h
@@ -23,8 +23,6 @@
 
 #include "nm-device-ethernet.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_VETH            (nm_device_veth_get_type ())
 #define NM_DEVICE_VETH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_VETH, NMDeviceVeth))
 #define NM_DEVICE_VETH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_VETH, NMDeviceVethClass))
@@ -34,11 +32,9 @@ G_BEGIN_DECLS
 
 #define NM_DEVICE_VETH_PEER "peer"
 
-typedef NMDeviceEthernet NMDeviceVeth;
-typedef NMDeviceEthernetClass NMDeviceVethClass;
+typedef struct _NMDeviceVeth NMDeviceVeth;
+typedef struct _NMDeviceVethClass NMDeviceVethClass;
 
 GType nm_device_veth_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_VETH_H */
+#endif /* __NETWORKMANAGER_DEVICE_VETH_H__ */
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index eb6527de..16edc0d2 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -20,15 +20,16 @@
 
 #include "nm-default.h"
 
+#include "nm-device-vlan.h"
+
 #include <sys/socket.h>
 
-#include "nm-device-vlan.h"
 #include "nm-manager.h"
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
 #include "nm-enum-types.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-activation-request.h"
 #include "nm-ip4-config.h"
 #include "nm-platform.h"
@@ -86,7 +87,6 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
 {
 	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
 	NMConnection *connection;
-	NMSettingWired *s_wired;
 	const char *new_mac, *old_mac;
 	NMSettingIPConfig *s_ip6;
 
@@ -99,11 +99,8 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
 		return;
 
 	/* Update the VLAN MAC only if configuration does not specify one */
-	s_wired = nm_connection_get_setting_wired (connection);
-	if (s_wired) {
-		if (nm_setting_wired_get_cloned_mac_address (s_wired))
-			return;
-	}
+	if (nm_device_hw_addr_is_explict (self))
+		return;
 
 	old_mac = nm_device_get_hw_address (self);
 	new_mac = nm_device_get_hw_address (parent);
@@ -113,7 +110,7 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
 	_LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s",
 	       NM_PRINT_FMT_QUOTE_STRING (new_mac));
 	if (new_mac) {
-		nm_device_set_hw_addr (self, new_mac, "set", LOGD_VLAN);
+		nm_device_hw_addr_set (self, new_mac, "vlan-parent");
 		/* When changing the hw address the interface is taken down,
 		 * removing the IPv6 configuration; reapply it.
 		 */
@@ -377,21 +374,25 @@ match_parent (NMDeviceVlan *self, const char *parent)
 static gboolean
 match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr)
 {
-	  NMSettingWired *s_wired;
-	  const char *setting_mac;
-	  const char *device_mac;
+	NMDeviceVlanPrivate *priv;
+	NMSettingWired *s_wired;
+	const char *setting_mac;
+	const char *parent_mac;
 
-	  s_wired = nm_connection_get_setting_wired (connection);
-	  if (!s_wired)
-		  return !fail_if_no_hwaddr;
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (!s_wired)
+		return !fail_if_no_hwaddr;
 
-	  setting_mac = nm_setting_wired_get_mac_address (s_wired);
-	  if (!setting_mac)
-		  return !fail_if_no_hwaddr;
+	setting_mac = nm_setting_wired_get_mac_address (s_wired);
+	if (!setting_mac)
+		return !fail_if_no_hwaddr;
 
-	  device_mac = nm_device_get_hw_address (device);
+	priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
+	if (!priv->parent)
+		return !fail_if_no_hwaddr;
 
-	  return nm_utils_hwaddr_matches (setting_mac, -1, device_mac, -1);
+	parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE);
+	return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1);
 }
 
 static gboolean
@@ -515,7 +516,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 			NMConnection *parent_connection;
 
 			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
+			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device), setting_parent);
 			if (parent_connection && nm_device_check_connection_compatible (priv->parent, parent_connection))
 				new_parent = NULL;
 		}
@@ -549,8 +550,6 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 {
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (dev);
 	NMSettingVlan *s_vlan;
-	NMSettingWired *s_wired;
-	const char *cloned_mac;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -559,12 +558,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED);
-	if (s_wired) {
-		/* Set device MAC address if the connection wants to change it */
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN);
-	}
+	if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	/* Change MAC address to parent's one if needed */
 	if (priv->parent)
@@ -618,14 +613,6 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 	}
 }
 
-static void
-deactivate (NMDevice *device)
-{
-	/* Reset MAC address back to initial address */
-	if (nm_device_get_initial_hw_address (device))
-		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_VLAN);
-}
-
 /******************************************************************/
 
 static void
@@ -689,7 +676,6 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	parent_class->bring_up = bring_up;
 	parent_class->act_stage1_prepare = act_stage1_prepare;
 	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
-	parent_class->deactivate = deactivate;
 	parent_class->is_available = is_available;
 	parent_class->notify_new_device_added = notify_new_device_added;
 
diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h
index e31e171d..71ca8030 100644
--- a/src/devices/nm-device-vlan.h
+++ b/src/devices/nm-device-vlan.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_VLAN            (nm_device_vlan_get_type ())
 #define NM_DEVICE_VLAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlan))
 #define NM_DEVICE_VLAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass))
@@ -47,6 +45,4 @@ typedef NMDeviceClass NMDeviceVlanClass;
 
 GType nm_device_vlan_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_VLAN_H */
+#endif /* __NETWORKMANAGER_DEVICE_VLAN_H__ */
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index 45f33bdd..674c2a78 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -20,9 +20,10 @@
 
 #include "nm-default.h"
 
+#include "nm-device-vxlan.h"
+
 #include <string.h>
 
-#include "nm-device-vxlan.h"
 #include "nm-device-private.h"
 #include "nm-manager.h"
 #include "nm-platform.h"
@@ -30,7 +31,7 @@
 #include "nm-device-factory.h"
 #include "nm-setting-vxlan.h"
 #include "nm-setting-wired.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-activation-request.h"
 #include "nm-ip4-config.h"
 
@@ -417,8 +418,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 			NMConnection *parent_connection;
 
 			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (),
-			                                                                   setting_parent);
+			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device),
+			                                                                         setting_parent);
 			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
 				new_parent = NULL;
 		}
@@ -510,8 +511,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
-	NMSettingWired *s_wired;
-	const char *cloned_mac;
 	NMActStageReturn ret;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -520,12 +519,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED);
-	if (s_wired) {
-		/* Set device MAC address if the connection wants to change it */
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE);
-	}
+	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h
index 7de04a12..71204d3b 100644
--- a/src/devices/nm-device-vxlan.h
+++ b/src/devices/nm-device-vxlan.h
@@ -23,8 +23,6 @@
 
 #include "nm-device-generic.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_VXLAN            (nm_device_vxlan_get_type ())
 #define NM_DEVICE_VXLAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlan))
 #define NM_DEVICE_VXLAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_VXLAN, NMDeviceVxlanClass))
@@ -54,6 +52,4 @@ typedef NMDeviceClass NMDeviceVxlanClass;
 
 GType nm_device_vxlan_get_type (void);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_VXLAN_H */
+#endif /* __NETWORKMANAGER_DEVICE_VXLAN_H__ */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 62259bc4..199acc66 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -21,6 +21,8 @@
 
 #include "nm-default.h"
 
+#include "nm-device.h"
+
 #include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
@@ -52,7 +54,7 @@
 #include "nm-firewall-manager.h"
 #include "nm-enum-types.h"
 #include "nm-settings-connection.h"
-#include "nm-connection-provider.h"
+#include "nm-settings.h"
 #include "nm-auth-utils.h"
 #include "nm-dispatcher.h"
 #include "nm-config.h"
@@ -69,10 +71,22 @@
 _LOG_DECLARE_SELF (NMDevice);
 
 #include "nmdbus-device.h"
+#include "nmdbus-device-statistics.h"
 
 G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_DEVICE_GET_PRIVATE(o) ((o)->priv)
+#define NM_DEVICE_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMDevice *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_DEVICE (_self)); \
+		_self->priv; \
+	})
 
 enum {
 	STATE_CHANGED,
@@ -119,11 +133,15 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
 	PROP_IS_MASTER,
 	PROP_MASTER,
 	PROP_HW_ADDRESS,
+	PROP_PERM_HW_ADDRESS,
 	PROP_HAS_PENDING_ACTION,
 	PROP_METERED,
 	PROP_LLDP_NEIGHBORS,
 	PROP_REAL,
 	PROP_SLAVES,
+	PROP_REFRESH_RATE_MS,
+	PROP_TX_BYTES,
+	PROP_RX_BYTES,
 );
 
 #define DEFAULT_AUTOCONNECT TRUE
@@ -134,6 +152,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
 #define PENDING_ACTION_DHCP6 "dhcp6"
 #define PENDING_ACTION_AUTOCONF6 "autoconf6"
 
+#define DHCP_RESTART_TIMEOUT   120
+#define DHCP_NUM_TRIES_MAX     3
+
 typedef void (*ActivationHandleFunc) (NMDevice *self);
 
 typedef struct {
@@ -142,9 +163,9 @@ typedef struct {
 } ActivationHandleData;
 
 typedef enum {
-	CLEANUP_TYPE_DECONFIGURE,
 	CLEANUP_TYPE_KEEP,
 	CLEANUP_TYPE_REMOVED,
+	CLEANUP_TYPE_DECONFIGURE,
 } CleanupType;
 
 typedef enum {
@@ -163,9 +184,9 @@ typedef struct {
 
 typedef struct {
 	NMDevice *slave;
-	gboolean slave_is_enslaved;
-	gboolean configure;
 	gulong watch_id;
+	bool slave_is_enslaved;
+	bool configure;
 } SlaveInfo;
 
 typedef struct {
@@ -192,8 +213,15 @@ typedef struct {
 	NMIP4Config **configs;
 } ArpingData;
 
+typedef enum {
+	HW_ADDR_TYPE_UNSET = 0,
+	HW_ADDR_TYPE_PERMANENT,
+	HW_ADDR_TYPE_EXPLICIT,
+	HW_ADDR_TYPE_GENERATED,
+} HwAddrType;
+
 typedef struct _NMDevicePrivate {
-	gboolean in_state_changed;
+	bool in_state_changed;
 
 	guint device_link_changed_id;
 	guint device_ip_link_changed_id;
@@ -209,7 +237,12 @@ typedef struct _NMDevicePrivate {
 	char *        udi;
 	char *        iface;   /* may change, could be renamed by user */
 	int           ifindex;
-	gboolean      real;
+
+	guint         hw_addr_len;
+	guint8 /*HwAddrType*/ hw_addr_type;
+
+	bool          real;
+
 	char *        ip_iface;
 	int           ip_ifindex;
 	NMDeviceType  type;
@@ -221,19 +254,19 @@ typedef struct _NMDevicePrivate {
 	char *        driver_version;
 	char *        firmware_version;
 	RfKillType    rfkill_type;
-	gboolean      firmware_missing;
-	gboolean      nm_plugin_missing;
+	bool          firmware_missing:1;
+	bool          nm_plugin_missing:1;
+	bool          hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */
 	GHashTable *  available_connections;
 	char *        hw_addr;
-	guint         hw_addr_len;
-	char *        perm_hw_addr;
-	char *        initial_hw_addr;
+	char *        hw_addr_perm;
+	char *        hw_addr_initial;
 	char *        physical_port_id;
 	guint         dev_id;
 
 	NMUnmanagedFlags        unmanaged_mask;
 	NMUnmanagedFlags        unmanaged_flags;
-	gboolean                is_nm_owned; /* whether the device is a device owned and created by NM */
+	bool                    is_nm_owned; /* whether the device is a device owned and created by NM */
 	DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */
 
 	GCancellable *deactivating_cancellable;
@@ -241,7 +274,7 @@ typedef struct _NMDevicePrivate {
 	guint32         ip4_address;
 
 	NMActRequest *  queued_act_request;
-	gboolean        queued_act_request_is_waiting_for_carrier;
+	bool            queued_act_request_is_waiting_for_carrier;
 	NMActRequest *  act_request;
 	ActivationHandleData act_handle4; /* for layer2 and IPv4. */
 	ActivationHandleData act_handle6;
@@ -261,11 +294,12 @@ typedef struct _NMDevicePrivate {
 	guint           link_connected_id;
 	guint           link_disconnected_id;
 	guint           carrier_defer_id;
-	gboolean        carrier;
+	bool            carrier;
 	guint           carrier_wait_id;
-	gboolean        ignore_carrier;
+	bool            ignore_carrier;
+	gulong          ignore_carrier_id;
 	guint32         mtu;
-	gboolean        up;   /* IFF_UP */
+	bool            up;   /* IFF_UP */
 
 	/* Generic DHCP stuff */
 	guint32         dhcp_timeout;
@@ -288,14 +322,17 @@ typedef struct _NMDevicePrivate {
 		NMPlatformIP6Route v6;
 	} default_route;
 
-	gboolean v4_commit_first_time;
-	gboolean v6_commit_first_time;
+	bool v4_commit_first_time;
+	bool v6_commit_first_time;
 
 	/* DHCPv4 tracking */
-	NMDhcpClient *  dhcp4_client;
-	gulong          dhcp4_state_sigid;
-	NMDhcp4Config * dhcp4_config;
-	guint           dhcp4_restart_id;
+	struct {
+		NMDhcpClient *  client;
+		gulong          state_sigid;
+		NMDhcp4Config * config;
+		guint           restart_id;
+		guint           num_tries_left;
+	} dhcp4;
 
 	PingInfo        gw_ping;
 
@@ -304,7 +341,7 @@ typedef struct _NMDevicePrivate {
 	gulong            dnsmasq_state_id;
 
 	/* Firewall */
-	gboolean       fw_ready;
+	bool fw_ready;
 	NMFirewallManagerCallId fw_call;
 
 	/* IPv4LL stuff */
@@ -325,8 +362,9 @@ typedef struct _NMDevicePrivate {
 	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 */
-	gboolean       nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
+	bool           nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
 	guint32        ip6_mtu;
+	NMIP6Config *  dad6_ip6_config;
 
 	NMRDisc *      rdisc;
 	gulong         rdisc_changed_id;
@@ -340,35 +378,47 @@ typedef struct _NMDevicePrivate {
 
 	GHashTable *   ip6_saved_properties;
 
-	NMDhcpClient *  dhcp6_client;
-	NMRDiscDHCPLevel dhcp6_mode;
-	gulong          dhcp6_state_sigid;
-	NMDhcp6Config * dhcp6_config;
-	/* IP6 config from DHCP */
-	NMIP6Config *   dhcp6_ip6_config;
-	/* Event ID of the current IP6 config from DHCP */
-	char *          dhcp6_event_id;
-	guint           dhcp6_restart_id;
+	struct {
+		NMDhcpClient *   client;
+		NMRDiscDHCPLevel mode;
+		gulong           state_sigid;
+		NMDhcp6Config *  config;
+		/* IP6 config from DHCP */
+		NMIP6Config *    ip6_config;
+		/* Event ID of the current IP6 config from DHCP */
+		char *           event_id;
+		guint            restart_id;
+		guint            num_tries_left;
+	} dhcp6;
 
 	/* allow autoconnect feature */
-	gboolean        autoconnect;
+	bool autoconnect;
 
 	/* master interface for bridge/bond/team slave */
 	NMDevice *      master;
-	gboolean        is_enslaved;
-	gboolean        master_ready_handled;
+	bool            is_enslaved;
+	bool            master_ready_handled;
 	gulong          master_ready_id;
 
 	/* slave management */
-	gboolean        is_master;
+	bool            is_master;
 	GSList *        slaves;    /* list of SlaveInfo */
 
 	NMMetered       metered;
 
-	NMConnectionProvider *con_provider;
+	NMSettings *settings;
+
 	NMLldpListener *lldp_listener;
 
 	guint check_delete_unrealized_id;
+
+	struct {
+		guint timeout_id;
+		guint refresh_rate_ms;
+		guint64 tx_bytes;
+		guint64 rx_bytes;
+	} stats;
+
 } NMDevicePrivate;
 
 static gboolean nm_device_set_ip4_config (NMDevice *self,
@@ -417,6 +467,8 @@ static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceState
 static void nm_device_start_ip_check (NMDevice *self);
 static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink);
 static void nm_device_set_mtu (NMDevice *self, guint32 mtu);
+static void dhcp_schedule_restart (NMDevice *self, int family, const char *reason);
+static void _cancel_activation (NMDevice *self);
 
 /***********************************************************/
 
@@ -524,6 +576,12 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason,
 
 /***********************************************************/
 
+NMSettings *
+nm_device_get_settings (NMDevice *self)
+{
+	return NM_DEVICE_GET_PRIVATE (self)->settings;
+}
+
 static void
 init_ip4_config_dns_priority (NMDevice *self, NMIP4Config *config)
 {
@@ -570,6 +628,41 @@ nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps)
 	return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->capabilities, caps);
 }
 
+static void
+_add_capabilities (NMDevice *self, NMDeviceCapabilities capabilities)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!NM_FLAGS_ALL (priv->capabilities, capabilities)) {
+		priv->capabilities |= capabilities;
+		_notify (self, PROP_CAPABILITIES);
+	}
+}
+
+/***********************************************************/
+
+static const char *
+_get_stable_id (NMConnection *connection, NMUtilsStableType *out_stable_type)
+{
+	NMSettingConnection *s_con;
+	const char *stable_id;
+
+	nm_assert (NM_IS_CONNECTION (connection));
+	nm_assert (out_stable_type);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_return_val_if_fail (s_con, NULL);
+
+	stable_id = nm_setting_connection_get_stable_id (s_con);
+	if (!stable_id) {
+		*out_stable_type = NM_UTILS_STABLE_TYPE_UUID;
+		return nm_connection_get_uuid (connection);
+	}
+
+	*out_stable_type = NM_UTILS_STABLE_TYPE_STABLE_ID;
+	return stable_id;
+}
+
 /***********************************************************/
 
 const char *
@@ -688,43 +781,175 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	g_free (old_ip_iface);
 }
 
+/*****************************************************************************/
+
+static void
+_stats_update_counters (NMDevice *self,
+                        guint64 tx_bytes,
+                        guint64 rx_bytes)
+{
+	NMDevicePrivate *priv;
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->stats.tx_bytes != tx_bytes) {
+		priv->stats.tx_bytes = tx_bytes;
+		_notify (self, PROP_TX_BYTES);
+	}
+	if (priv->stats.rx_bytes != rx_bytes) {
+		priv->stats.rx_bytes = rx_bytes;
+		_notify (self, PROP_RX_BYTES);
+	}
+}
+
+static void
+_stats_update_counters_from_pllink (NMDevice *self, const NMPlatformLink *pllink)
+{
+	_stats_update_counters (self, pllink->tx_bytes, pllink->rx_bytes);
+}
+
+static gboolean
+_stats_timeout_cb (gpointer user_data)
+{
+	NMDevice *self = user_data;
+	int ifindex;
+
+	ifindex = nm_device_get_ip_ifindex (self);
+
+	_LOGT (LOGD_DEVICE, "stats: refresh %d", ifindex);
+
+	if (ifindex > 0)
+		nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+
+	return G_SOURCE_CONTINUE;
+}
+
+static guint
+_stats_refresh_rate_real (guint refresh_rate_ms)
+{
+	const guint STATS_REFRESH_RATE_MS_MIN = 200;
+
+	if (refresh_rate_ms == 0)
+		return 0;
+
+	if (refresh_rate_ms < STATS_REFRESH_RATE_MS_MIN) {
+		/* you cannot set the refresh-rate arbitrarly small. E.g.
+		 * setting to 1ms is just killing. Have a lowest number. */
+		return STATS_REFRESH_RATE_MS_MIN;
+	}
+
+	return refresh_rate_ms;
+}
+
+static void
+_stats_set_refresh_rate (NMDevice *self, guint refresh_rate_ms)
+{
+	NMDevicePrivate *priv;
+	int ifindex;
+	guint old_rate;
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->stats.refresh_rate_ms == refresh_rate_ms)
+		return;
+
+	old_rate = priv->stats.refresh_rate_ms;
+	priv->stats.refresh_rate_ms = refresh_rate_ms;
+	_notify (self, PROP_REFRESH_RATE_MS);
+
+	_LOGD (LOGD_DEVICE, "stats: set refresh to %u ms", priv->stats.refresh_rate_ms);
+
+	if (!nm_device_is_real (self))
+		return;
+
+	refresh_rate_ms = _stats_refresh_rate_real (refresh_rate_ms);
+	if (_stats_refresh_rate_real (old_rate) == refresh_rate_ms)
+		return;
+
+	nm_clear_g_source (&priv->stats.timeout_id);
+
+	if (!refresh_rate_ms)
+		return;
+
+	/* trigger an inital refresh of the data whenever the refresh-rate changes.
+	 * As we process the result in an idle handler with device_link_changed(),
+	 * we don't get the result right away. */
+	ifindex = nm_device_get_ip_ifindex (self);
+	if (ifindex > 0)
+		nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+
+	priv->stats.timeout_id = g_timeout_add (refresh_rate_ms, _stats_timeout_cb, self);
+}
+
+/*****************************************************************************/
+
 static gboolean
 get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMLinkType link_type;
-	const guint8 *hwaddr = NULL;
-	size_t hwaddr_len = 0;
+	const NMPlatformLink *pllink;
 	int ifindex;
 	gboolean success;
 
 	/* If we get here, we *must* have a kernel netdev, which implies an ifindex */
 	ifindex = nm_device_get_ip_ifindex (self);
-	g_assert (ifindex);
+	g_return_val_if_fail (ifindex > 0, FALSE);
 
-	link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex);
-	g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0);
+	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+	if (   !pllink
+	    || NM_IN_SET (pllink->type, NM_LINK_TYPE_NONE, NM_LINK_TYPE_UNKNOWN))
+		return FALSE;
 
-	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len);
-	if (!hwaddr_len)
+	if (pllink->addr.len <= 0)
 		return FALSE;
+	if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX)
+		g_return_val_if_reached (FALSE);
 
-	success = nm_utils_get_ipv6_interface_identifier (link_type,
-	                                                  hwaddr,
-	                                                  hwaddr_len,
+	success = nm_utils_get_ipv6_interface_identifier (pllink->type,
+	                                                  pllink->addr.data,
+	                                                  pllink->addr.len,
 	                                                  priv->dev_id,
 	                                                  out_iid);
 	if (!success) {
 		_LOGW (LOGD_HW, "failed to generate interface identifier "
-		       "for link type %u hwaddr_len %zu", link_type, hwaddr_len);
+		       "for link type %u hwaddr_len %u", pllink->type, (unsigned) pllink->addr.len);
 	}
 	return success;
 }
 
+/**
+ * nm_device_get_ip_iface_identifier:
+ * @self: an #NMDevice
+ * @iid: where to place the interface identifier
+ * @ignore_token: force creation of a non-tokenized address
+ *
+ * Return the interface's identifier for the EUI64 address generation mode.
+ * It's either a manually set token or and identifier generated in a
+ * hardware-specific way.
+ *
+ * Unless @ignore_token is set the token is preferred. That is the case
+ * for link-local addresses (to mimic kernel behavior).
+ *
+ * Returns: #TRUE if the @iid could be set
+ */
 static gboolean
-nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid)
+nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid, gboolean ignore_token)
 {
-	return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid);
+	NMSettingIP6Config *s_ip6;
+	const char *token = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (!ignore_token) {
+		s_ip6 = (NMSettingIP6Config *)
+			nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
+		g_return_val_if_fail (s_ip6, FALSE);
+		token = nm_setting_ip6_config_get_token (s_ip6);
+	}
+	if (token)
+		return nm_utils_ipv6_interface_identifier_get_from_token (iid, token);
+	else
+		return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid);
 }
 
 const char *
@@ -1029,7 +1254,11 @@ nm_device_get_settings_connection (NMDevice *self)
 NMConnection *
 nm_device_get_applied_connection (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL;
 }
@@ -1046,23 +1275,12 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla
 }
 
 NMSetting *
-nm_device_get_applied_setting (NMDevice *device, GType setting_type)
+nm_device_get_applied_setting (NMDevice *self, GType setting_type)
 {
-	NMActRequest *req;
-	NMSetting *setting = NULL;
-
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-
-	req = nm_device_get_act_request (device);
-	if (req) {
-		NMConnection *connection;
-
-		connection = nm_act_request_get_applied_connection (req);
-		if (connection)
-			setting = nm_connection_get_setting (connection, setting_type);
-	}
+	NMConnection *connection;
 
-	return setting;
+	connection = nm_device_get_applied_connection (self);
+	return connection ? nm_connection_get_setting (connection, setting_type) : NULL;
 }
 
 RfKillType
@@ -1355,7 +1573,7 @@ nm_device_update_dynamic_ip_setup (NMDevice *self)
 
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
-	if (priv->dhcp4_client) {
+	if (priv->dhcp4.client) {
 		if (!nm_device_dhcp4_renew (self, FALSE)) {
 			nm_device_state_changed (self,
 			                         NM_DEVICE_STATE_FAILED,
@@ -1363,7 +1581,7 @@ nm_device_update_dynamic_ip_setup (NMDevice *self)
 			return;
 		}
 	}
-	if (priv->dhcp6_client) {
+	if (priv->dhcp6.client) {
 		if (!nm_device_dhcp6_renew (self, FALSE)) {
 			nm_device_state_changed (self,
 			                         NM_DEVICE_STATE_FAILED,
@@ -1467,8 +1685,6 @@ link_disconnect_action_cb (gpointer user_data)
 
 	priv->carrier_defer_id = 0;
 
-	_LOGI (LOGD_DEVICE, "link disconnected (calling deferred action)");
-
 	NM_DEVICE_GET_CLASS (self)->carrier_changed (self, FALSE);
 
 	return FALSE;
@@ -1508,11 +1724,11 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 			nm_device_remove_pending_action (self, "carrier wait", TRUE);
 			_carrier_wait_check_queued_act_request (self);
 		}
-	} else if (state <= NM_DEVICE_STATE_DISCONNECTED) {
-		_LOGI (LOGD_DEVICE, "link disconnected");
+	} else if (   state <= NM_DEVICE_STATE_DISCONNECTED
+	           && !priv->queued_act_request) {
+		_LOGD (LOGD_DEVICE, "link disconnected");
 		klass->carrier_changed (self, FALSE);
 	} else {
-		_LOGI (LOGD_DEVICE, "link disconnected (deferring action for %d seconds)", LINK_DISCONNECT_DELAY);
 		priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY,
 		                                                link_disconnect_action_cb, self);
 		_LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)",
@@ -1563,13 +1779,14 @@ device_link_changed (NMDevice *self)
 {
 	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMUtilsIPv6IfaceId token_iid;
 	gboolean ip_ifname_changed = FALSE;
 	const char *udi;
 	NMPlatformLink info;
 	const NMPlatformLink *pllink;
 	int ifindex;
 	gboolean was_up;
+	gboolean update_unmanaged_specs = FALSE;
+	gboolean got_hw_addr = FALSE, had_hw_addr;
 
 	priv->device_link_changed_id = 0;
 
@@ -1607,6 +1824,14 @@ device_link_changed (NMDevice *self)
 		_notify (self, PROP_DRIVER);
 	}
 
+	if (ifindex == nm_device_get_ip_ifindex (self))
+		_stats_update_counters_from_pllink (self, &info);
+
+	had_hw_addr = (priv->hw_addr != NULL);
+	nm_device_update_hw_address (self);
+	got_hw_addr = (!had_hw_addr && priv->hw_addr);
+	nm_device_update_permanent_hw_address (self);
+
 	if (info.name[0] && strcmp (priv->iface, info.name) != 0) {
 		_LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'",
 		       priv->ifindex, priv->iface, info.name);
@@ -1616,6 +1841,11 @@ device_link_changed (NMDevice *self)
 		/* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */
 		ip_ifname_changed = !priv->ip_iface;
 
+		if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT))
+			nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings));
+		else
+			update_unmanaged_specs = TRUE;
+
 		_notify (self, PROP_IFACE);
 		if (ip_ifname_changed)
 			_notify (self, PROP_IP_IFACE);
@@ -1629,10 +1859,11 @@ device_link_changed (NMDevice *self)
 		nm_device_emit_recheck_auto_activate (self);
 	}
 
-	if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) {
-		_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
-		if (nm_rdisc_set_iid (priv->rdisc, token_iid))
+	if (priv->rdisc && info.inet6_token.id) {
+		if (nm_rdisc_set_iid (priv->rdisc, info.inet6_token)) {
+			_LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
 			nm_rdisc_start (priv->rdisc);
+		}
 	}
 
 	if (klass->link_changed)
@@ -1651,18 +1882,23 @@ device_link_changed (NMDevice *self)
 
 		nm_device_set_unmanaged_by_user_udev (self);
 
+		reason = NM_DEVICE_STATE_REASON_NOW_MANAGED;
+
 		/* If the device is a external-down candidated but no longer has external
 		 * down set, we must clear the platform-unmanaged flag with reason
 		 * "assumed". */
 		if (    nm_device_get_unmanaged_mask (self, NM_UNMANAGED_EXTERNAL_DOWN)
 		    && !nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN)) {
-			/* Ensure the assume check is queued before any queued state changes
-			 * from the transition to UNAVAILABLE.
-			 */
-			nm_device_queue_recheck_assume (self);
-			reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
-		} else
-			reason = NM_DEVICE_STATE_REASON_NOW_MANAGED;
+			/* actually, user-udev overwrites external-down. So we only assume the device,
+			 * when it is a external-down candidate, which is not managed via udev. */
+			if (!nm_device_get_unmanaged_mask (self, NM_UNMANAGED_USER_UDEV)) {
+				/* Ensure the assume check is queued before any queued state changes
+				 * from the transition to UNAVAILABLE.
+				 */
+				nm_device_queue_recheck_assume (self);
+				reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
+			}
+		}
 
 		nm_device_set_unmanaged_by_flags (self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason);
 	}
@@ -1684,6 +1920,23 @@ device_link_changed (NMDevice *self)
 		}
 	}
 
+	if (update_unmanaged_specs)
+		nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings));
+
+	if (   got_hw_addr
+	    && !priv->up
+	    && nm_device_get_state (self) == NM_DEVICE_STATE_UNAVAILABLE) {
+		/*
+		 * If the device is UNAVAILABLE, any previous try to
+		 * bring it up probably has failed because of the
+		 * invalid hardware address; try again.
+		 */
+		nm_device_bring_up (self, TRUE, NULL);
+		nm_device_queue_recheck_available (self,
+		                                   NM_DEVICE_STATE_REASON_NONE,
+		                                   NM_DEVICE_STATE_REASON_NONE);
+	}
+
 	return G_SOURCE_REMOVE;
 }
 
@@ -1702,6 +1955,8 @@ device_ip_link_changed (NMDevice *self)
 	if (!pllink)
 		return G_SOURCE_REMOVE;
 
+	_stats_update_counters_from_pllink (self, pllink);
+
 	if (pllink->name[0] && g_strcmp0 (priv->ip_iface, pllink->name)) {
 		_LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'",
 		       priv->ifindex, nm_device_get_ip_ifindex (self),
@@ -1712,6 +1967,7 @@ device_ip_link_changed (NMDevice *self)
 		_notify (self, PROP_IP_IFACE);
 		nm_device_update_dynamic_ip_setup (self);
 	}
+
 	return G_SOURCE_REMOVE;
 }
 
@@ -1817,6 +2073,8 @@ nm_device_realize_start (NMDevice *self,
                          gboolean *out_compatible,
                          GError **error)
 {
+	NMPlatformLink plink_copy;
+
 	NM_SET_OUT (out_compatible, TRUE);
 
 	if (plink) {
@@ -1831,6 +2089,10 @@ nm_device_realize_start (NMDevice *self,
 			return FALSE;
 	}
 
+	if (plink) {
+		plink_copy = *plink;
+		plink = &plink_copy;
+	}
 	realize_start_setup (self, plink);
 
 	return TRUE;
@@ -1962,6 +2224,9 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	NMDevicePrivate *priv;
 	NMDeviceClass *klass;
 	static guint32 id = 0;
+	NMDeviceCapabilities capabilities = 0;
+	NMConfig *config;
+	guint real_rate;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -1985,6 +2250,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	if (plink) {
 		g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL));
 		update_device_from_platform_link (self, plink);
+		_stats_update_counters_from_pllink (self, plink);
 	}
 
 	if (priv->ifindex > 0) {
@@ -1994,7 +2260,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
 
 		if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex))
-			priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
+			capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
 
 		priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
 		_notify (self, PROP_MTU);
@@ -2014,7 +2280,9 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	}
 
 	if (klass->get_generic_capabilities)
-		priv->capabilities |= klass->get_generic_capabilities (self);
+		capabilities |= klass->get_generic_capabilities (self);
+
+	_add_capabilities (self, capabilities);
 
 	if (!priv->udi) {
 		/* Use a placeholder UDI until we get a real one */
@@ -2028,17 +2296,19 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 
 	nm_device_update_hw_address (self);
 	nm_device_update_initial_hw_address (self);
+	nm_device_update_permanent_hw_address (self);
 
 	/* Note: initial hardware address must be read before calling get_ignore_carrier() */
-	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-		NMConfig *config = nm_config_get ();
-
-		priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
-		g_signal_connect (G_OBJECT (config),
-		                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
-		                  G_CALLBACK (config_changed_update_ignore_carrier),
-		                  self);
+	config = nm_config_get ();
+	priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
+	if (!priv->ignore_carrier_id) {
+		priv->ignore_carrier_id = g_signal_connect (config,
+		                                            NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+		                                            G_CALLBACK (config_changed_update_ignore_carrier),
+		                                            self);
+	}
 
+	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
 		check_carrier (self);
 		_LOGD (LOGD_HW,
 		       "carrier is %s%s",
@@ -2049,7 +2319,10 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		priv->carrier = TRUE;
 	}
 
-	_notify (self, PROP_CAPABILITIES);
+	nm_assert (!priv->stats.timeout_id);
+	real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms);
+	if (real_rate)
+		priv->stats.timeout_id = g_timeout_add (real_rate, _stats_timeout_cb, self);
 
 	klass->realize_start_notify (self, plink);
 
@@ -2214,6 +2487,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 		_notify (self, PROP_UDI);
 	}
 	if (priv->hw_addr) {
+		priv->hw_addr_len = 0;
 		g_clear_pointer (&priv->hw_addr, g_free);
 		_notify (self, PROP_HW_ADDRESS);
 	}
@@ -2222,14 +2496,21 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 		_notify (self, PROP_PHYSICAL_PORT_ID);
 	}
 
-	g_clear_pointer (&priv->perm_hw_addr, g_free);
-	g_clear_pointer (&priv->initial_hw_addr, g_free);
+	nm_clear_g_source (&priv->stats.timeout_id);
+	_stats_update_counters (self, 0, 0);
+
+	priv->hw_addr_type = HW_ADDR_TYPE_UNSET;
+	g_clear_pointer (&priv->hw_addr_perm, g_free);
+	_notify (self, PROP_PERM_HW_ADDRESS);
+	g_clear_pointer (&priv->hw_addr_initial, g_free);
 
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
 	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 	_notify (self, PROP_CAPABILITIES);
 
+	nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id);
+
 	priv->real = FALSE;
 	_notify (self, PROP_REAL);
 
@@ -2660,6 +2941,8 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 		       nm_connection_get_id (connection),
 		       master_status);
 
+		/* Cancel any pending activation sources */
+		_cancel_activation (self);
 		nm_device_queue_state (self, new_state, reason);
 	} else
 		_LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master));
@@ -2999,6 +3282,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 	gs_free char *uuid = NULL;
 	const char *ip4_method, *ip6_method;
 	GError *error = NULL;
+	const NMPlatformLink *pllink;
 
 	/* If update_connection() is not implemented, just fail. */
 	if (!klass->update_connection)
@@ -3045,6 +3329,15 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 
 		s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
 		nm_connection_add_setting (connection, s_ip6);
+
+		pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex);
+		if (pllink && pllink->inet6_token.id) {
+			_LOGD (LOGD_IP6, "IPv6 tokenized identifier present");
+			g_object_set (s_ip6,
+			              NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, NM_IN6_ADDR_GEN_MODE_EUI64,
+			              NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, NULL),
+			              NULL);
+		}
 	}
 
 	klass->update_connection (self, connection);
@@ -3202,7 +3495,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave
 static gboolean
 nm_device_can_assume_connections (NMDevice *self)
 {
-	return   !!NM_DEVICE_GET_CLASS (self)->update_connection;
+	return !!NM_DEVICE_GET_CLASS (self)->update_connection;
 }
 
 /**
@@ -3218,7 +3511,7 @@ nm_device_can_assume_connections (NMDevice *self)
  * if there is no active connection or the active connection cannot be
  * assumed.
  */
-gboolean
+static gboolean
 nm_device_can_assume_active_connection (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -3255,16 +3548,55 @@ nm_device_can_assume_active_connection (NMDevice *self)
 		return FALSE;
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	if (!_nm_utils_string_in_list (method, assumable_ip6_methods))
+	if (!g_strv_contains (assumable_ip6_methods, method))
 		return FALSE;
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
-	if (!_nm_utils_string_in_list (method, assumable_ip4_methods))
+	if (!g_strv_contains (assumable_ip4_methods, method))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+unmanaged_on_quit (NMDevice *self)
+{
+	/* Leave certain devices alone when quitting so their configuration
+	 * can be taken over when NM restarts.  This ensures connectivity while
+	 * NM is stopped.
+	 */
+	if (nm_device_uses_assumed_connection (self)) {
+		/* An assume connection must be left alone */
+		return FALSE;
+	}
+
+	if (!nm_device_get_act_request (self)) {
+		/* a device without any active connection is either UNAVAILABLE or DISCONNECTED
+		 * state. Since we don't know whether the device was upped by NetworkManager,
+		 * we must leave it up on exit.
+		 */
+		return FALSE;
+	}
+
+	if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (self))) {
+		/* The device has no layer 3 configuration. Leave it up. */
+		return FALSE;
+	}
+
+	if (nm_device_can_assume_active_connection (self))
 		return FALSE;
 
 	return TRUE;
 }
 
+gboolean
+nm_device_unmanage_on_quit (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	return NM_DEVICE_GET_CLASS (self)->unmanaged_on_quit (self);
+}
+
 static gboolean
 nm_device_emit_recheck_assume (gpointer user_data)
 {
@@ -3351,7 +3683,7 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us
 
 	switch (status) {
 	case NM_DNSMASQ_STATUS_DEAD:
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
+		nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
 		break;
 	default:
 		break;
@@ -3753,15 +4085,21 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 
 	if (!priv->master_ready_handled) {
 		NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
+		NMActiveConnection *master;
+
+		master = nm_active_connection_get_master (active);
 
-		if (!nm_active_connection_get_master (active)) {
+		if (!master) {
 			g_warn_if_fail (!priv->master_ready_id);
 			priv->master_ready_handled = TRUE;
 		} else {
 			/* If the master connection is ready for slaves, attach ourselves */
 			if (nm_active_connection_get_master_ready (active))
 				master_ready (self, active);
-			else {
+			else if (nm_active_connection_get_state (master) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) {
+				_LOGD (LOGD_DEVICE, "master connection is deactivating");
+				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+			} else {
 				_LOGD (LOGD_DEVICE, "waiting for master connection to become ready");
 
 				if (priv->master_ready_id == 0) {
@@ -3815,6 +4153,26 @@ check_ip_failed (NMDevice *self, gboolean may_fail)
 	                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 }
 
+void
+nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (family == AF_INET || family == AF_INET6);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (family == AF_INET)
+		priv->ip4_state = IP_FAIL;
+	else
+		priv->ip6_state = IP_FAIL;
+
+	if (get_ip_config_may_fail (self, family))
+		check_ip_failed (self, FALSE);
+	else
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+}
+
 /*
  * check_ip_done
  *
@@ -4050,14 +4408,14 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla)
 
 	memset (&address, 0, sizeof (address));
 	nm_platform_ip4_address_set_addr (&address, lla, 16);
-	address.source = NM_IP_CONFIG_SOURCE_IP4LL;
+	address.addr_source = NM_IP_CONFIG_SOURCE_IP4LL;
 	nm_ip4_config_add_address (config, &address);
 
 	/* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */
 	memset (&route, 0, sizeof (route));
 	route.network = htonl (0xE0000000L);
 	route.plen = 4;
-	route.source = NM_IP_CONFIG_SOURCE_IP4LL;
+	route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL;
 	route.metric = nm_device_get_ip4_route_metric (self);
 	nm_ip4_config_add_route (config, &route);
 
@@ -4094,23 +4452,20 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 		r = sd_ipv4ll_get_address (ll, &address);
 		if (r < 0) {
 			_LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r);
-			priv->ip4_state = IP_FAIL;
-			check_ip_failed (self, FALSE);
+			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED);
 			return;
 		}
 
 		if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
 			_LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr);
-			priv->ip4_state = IP_FAIL;
-			check_ip_failed (self, FALSE);
+			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 			return;
 		}
 
 		config = ipv4ll_get_ip4_config (self, address.s_addr);
 		if (config == NULL) {
 			_LOGE (LOGD_AUTOIP4, "failed to get IPv4LL config");
-			priv->ip4_state = IP_FAIL;
-			check_ip_failed (self, FALSE);
+			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
 			return;
 		}
 
@@ -4120,8 +4475,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 		} else if (priv->ip4_state == IP_DONE) {
 			if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) {
 				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
-				priv->ip4_state = IP_FAIL;
-				check_ip_failed (self, FALSE);
+				nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
 			}
 		} else
 			g_assert_not_reached ();
@@ -4130,8 +4484,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 		break;
 	default:
 		_LOGW (LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event);
-		priv->ip4_state = IP_FAIL;
-		check_ip_failed (self, FALSE);
+		nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
 	}
 }
 
@@ -4188,9 +4541,9 @@ ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason)
 		goto fail;
 	}
 
-	r = sd_ipv4ll_set_index (priv->ipv4ll, ifindex);
+	r = sd_ipv4ll_set_ifindex (priv->ipv4ll, ifindex);
 	if (r < 0) {
-		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_index() failed with error %d", r);
+		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r);
 		goto fail;
 	}
 
@@ -4325,23 +4678,23 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_clear_g_source (&priv->dhcp4_restart_id);
+	nm_clear_g_source (&priv->dhcp4.restart_id);
 
-	if (priv->dhcp4_client) {
+	if (priv->dhcp4.client) {
 		/* Stop any ongoing DHCP transaction on this device */
-		nm_clear_g_signal_handler (priv->dhcp4_client, &priv->dhcp4_state_sigid);
+		nm_clear_g_signal_handler (priv->dhcp4.client, &priv->dhcp4.state_sigid);
 
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
 
 		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
 		    || cleanup_type == CLEANUP_TYPE_REMOVED)
-			nm_dhcp_client_stop (priv->dhcp4_client, release);
+			nm_dhcp_client_stop (priv->dhcp4.client, release);
 
-		g_clear_object (&priv->dhcp4_client);
+		g_clear_object (&priv->dhcp4.client);
 	}
 
-	if (priv->dhcp4_config) {
-		nm_exported_object_clear_and_unexport (&priv->dhcp4_config);
+	if (priv->dhcp4.config) {
+		nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
 		_notify (self, PROP_DHCP4_CONFIG);
 	}
 }
@@ -4495,7 +4848,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 
 	priv->default_route.v4_has = TRUE;
 	memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4));
-	priv->default_route.v4.source = NM_IP_CONFIG_SOURCE_USER;
+	priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	priv->default_route.v4.gateway = gateway;
 	priv->default_route.v4.metric = default_route_metric;
 	priv->default_route.v4.mss = nm_ip4_config_get_mss (composite);
@@ -4541,28 +4894,30 @@ END_ADD_DEFAULT_ROUTE:
 	return success;
 }
 
-static void
+static gboolean
 dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 {
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
-	g_return_if_fail (config != NULL);
+	g_return_val_if_fail (config != NULL, FALSE);
 
 	if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) {
 		_LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change.");
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-	} else {
-		/* Notify dispatcher scripts of new DHCP4 config */
-		nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
-		                    nm_device_get_settings_connection (self),
-		                    nm_device_get_applied_connection (self),
-		                    self,
-		                    NULL,
-		                    NULL,
-		                    NULL);
-
-		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
+		return FALSE;
 	}
+
+	/* Notify dispatcher scripts of new DHCP4 config */
+	nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
+	                    nm_device_get_settings_connection (self),
+	                    nm_device_get_applied_connection (self),
+	                    self,
+	                    NULL,
+	                    NULL,
+	                    NULL);
+
+	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE);
+
+	return TRUE;
 }
 
 static gboolean
@@ -4576,11 +4931,11 @@ dhcp4_restart_cb (gpointer user_data)
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	priv->dhcp4_restart_id = 0;
+	priv->dhcp4.restart_id = 0;
 	connection = nm_device_get_applied_connection (self);
 
 	if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE)
-		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
+		dhcp_schedule_restart (self, AF_INET, NULL);
 
 	return FALSE;
 }
@@ -4590,6 +4945,9 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	_LOGD (LOGD_DHCP4, "DHCPv4 failed: timeout %d, num tries left %u",
+	       timeout, priv->dhcp4.num_tries_left);
+
 	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 
 	/* Don't fail if there are static addresses configured on
@@ -4598,8 +4956,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 	if (   priv->ip4_state == IP_DONE
 	    && priv->con_ip4_config
 	    && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) {
-		_LOGI (LOGD_DHCP4, "Scheduling DHCPv4 restart because device has IP addresses");
-		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
+		dhcp_schedule_restart (self, AF_INET, "device has IP addresses");
 		return;
 	}
 
@@ -4608,16 +4965,23 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 	 * retry DHCP again.
 	 */
 	if (nm_device_uses_assumed_connection (self)) {
-		_LOGI (LOGD_DHCP4, "Scheduling DHCPv4 restart because the connection is assumed");
-		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
+		dhcp_schedule_restart (self, AF_INET, "connection is assumed");
 		return;
 	}
 
-	if (timeout || (priv->ip4_state == IP_CONF))
+	if (   priv->dhcp4.num_tries_left == DHCP_NUM_TRIES_MAX
+	    && (timeout || (priv->ip4_state == IP_CONF)))
 		nm_device_activate_schedule_ip4_config_timeout (self);
-	else if (priv->ip4_state == IP_DONE)
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
-	else
+	else if (priv->ip4_state == IP_DONE) {
+		/* Don't fail immediately when the lease expires but try to
+		 * restart DHCP for a predefined number of times.
+		 */
+		if (priv->dhcp4.num_tries_left) {
+			priv->dhcp4.num_tries_left--;
+			dhcp_schedule_restart (self, AF_INET, "lease expired");
+		} else
+			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+	} else
 		g_warn_if_reached ();
 }
 
@@ -4654,14 +5018,13 @@ dhcp4_state_changed (NMDhcpClient *client,
 	case NM_DHCP_STATE_BOUND:
 		if (!ip4_config) {
 			_LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event.");
-			nm_device_state_changed (self,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+			dhcp4_fail (self, FALSE);
 			break;
 		}
 
-		nm_dhcp4_config_set_options (priv->dhcp4_config, options);
+		nm_dhcp4_config_set_options (priv->dhcp4.config, options);
 		_notify (self, PROP_DHCP4_CONFIG);
+		priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX;
 
 		if (priv->ip4_state == IP_CONF) {
 			connection = nm_device_get_applied_connection (self);
@@ -4678,8 +5041,10 @@ dhcp4_state_changed (NMDhcpClient *client,
 
 			ipv4_dad_start (self, configs, dhcp4_dad_cb);
 		} else if (priv->ip4_state == IP_DONE) {
-			dhcp4_lease_change (self, ip4_config);
-			nm_device_update_metered (self);
+			if (dhcp4_lease_change (self, ip4_config))
+				nm_device_update_metered (self);
+			else
+				dhcp4_fail (self, FALSE);
 		}
 		break;
 	case NM_DHCP_STATE_TIMEOUT:
@@ -4735,8 +5100,8 @@ dhcp4_start (NMDevice *self,
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
 	/* Clear old exported DHCP options */
-	nm_exported_object_clear_and_unexport (&priv->dhcp4_config);
-	priv->dhcp4_config = nm_dhcp4_config_new ();
+	nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
+	priv->dhcp4.config = nm_dhcp4_config_new ();
 
 	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
 	if (hw_addr_len) {
@@ -4745,8 +5110,8 @@ dhcp4_start (NMDevice *self,
 	}
 
 	/* 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 (),
+	g_warn_if_fail (priv->dhcp4.client == NULL);
+	priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
 	                                                tmp,
@@ -4763,12 +5128,12 @@ dhcp4_start (NMDevice *self,
 	if (tmp)
 		g_byte_array_free (tmp, TRUE);
 
-	if (!priv->dhcp4_client) {
+	if (!priv->dhcp4.client) {
 		*reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client,
+	priv->dhcp4.state_sigid = g_signal_connect (priv->dhcp4.client,
 	                                            NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
 	                                            G_CALLBACK (dhcp4_state_changed),
 	                                            self);
@@ -4787,7 +5152,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	NMDeviceStateReason reason;
 	NMConnection *connection;
 
-	g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE);
+	g_return_val_if_fail (priv->dhcp4.client != NULL, FALSE);
 
 	_LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested");
 
@@ -4862,7 +5227,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso
 	}
 
 	config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
-	address.source = NM_IP_CONFIG_SOURCE_SHARED;
+	address.addr_source = NM_IP_CONFIG_SOURCE_SHARED;
 	nm_ip4_config_add_address (config, &address);
 
 	/* Remove the address lock when the object gets disposed */
@@ -4888,7 +5253,7 @@ connection_ip4_method_requires_carrier (NMConnection *connection,
 
 	if (out_ip4_enabled)
 		*out_ip4_enabled = !!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED);
-	return _nm_utils_string_in_list (method, ip4_carrier_methods);
+	return g_strv_contains (ip4_carrier_methods, method);
 }
 
 static gboolean
@@ -4905,7 +5270,7 @@ connection_ip6_method_requires_carrier (NMConnection *connection,
 
 	if (out_ip6_enabled)
 		*out_ip6_enabled = !!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
-	return _nm_utils_string_in_list (method, ip6_carrier_methods);
+	return g_strv_contains (ip6_carrier_methods, method);
 }
 
 static gboolean
@@ -5013,6 +5378,7 @@ act_stage3_ip4_config_start (NMDevice *self,
 	}
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX;
 
 	/* Start IPv4 addressing based on the method requested */
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
@@ -5056,25 +5422,25 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
-	g_clear_object (&priv->dhcp6_ip6_config);
-	g_clear_pointer (&priv->dhcp6_event_id, g_free);
-	nm_clear_g_source (&priv->dhcp6_restart_id);
+	priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_NONE;
+	g_clear_object (&priv->dhcp6.ip6_config);
+	g_clear_pointer (&priv->dhcp6.event_id, g_free);
+	nm_clear_g_source (&priv->dhcp6.restart_id);
 
-	if (priv->dhcp6_client) {
-		nm_clear_g_signal_handler (priv->dhcp6_client, &priv->dhcp6_state_sigid);
+	if (priv->dhcp6.client) {
+		nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid);
 
 		if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
 		    || cleanup_type == CLEANUP_TYPE_REMOVED)
-			nm_dhcp_client_stop (priv->dhcp6_client, release);
+			nm_dhcp_client_stop (priv->dhcp6.client, release);
 
-		g_clear_object (&priv->dhcp6_client);
+		g_clear_object (&priv->dhcp6.client);
 	}
 
 	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
 
-	if (priv->dhcp6_config) {
-		nm_exported_object_clear_and_unexport (&priv->dhcp6_config);
+	if (priv->dhcp6.config) {
+		nm_exported_object_clear_and_unexport (&priv->dhcp6.config);
 		_notify (self, PROP_DHCP6_CONFIG);
 	}
 }
@@ -5104,6 +5470,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 	gboolean ignore_auto_routes = FALSE;
 	gboolean ignore_auto_dns = FALSE;
 	gboolean auto_method = FALSE;
+	const char *token = NULL;
 
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
 	connection = nm_device_get_applied_connection (self);
@@ -5111,9 +5478,14 @@ ip6_config_merge_and_apply (NMDevice *self,
 		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 (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);
+
 			if (NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6),
 			                  NM_SETTING_IP6_CONFIG_METHOD_AUTO,
 			                  NM_SETTING_IP6_CONFIG_METHOD_DHCP))
@@ -5136,8 +5508,8 @@ ip6_config_merge_and_apply (NMDevice *self,
 		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
 		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
-	if (priv->dhcp6_ip6_config) {
-		nm_ip6_config_merge (composite, priv->dhcp6_ip6_config,
+	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_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
@@ -5230,7 +5602,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 
 	priv->default_route.v6_has = TRUE;
 	memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6));
-	priv->default_route.v6.source = NM_IP_CONFIG_SOURCE_USER;
+	priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	priv->default_route.v6.gateway = *gateway;
 	priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self);
 	priv->default_route.v6.mss = nm_ip6_config_get_mss (composite);
@@ -5261,6 +5633,14 @@ END_ADD_DEFAULT_ROUTE:
 
 	/* 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_PLATFORM_GET,
+			                                 nm_device_get_ip_ifindex (self),
+			                                 iid);
+		}
+
 		if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit)
 			NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite);
 	}
@@ -5276,37 +5656,38 @@ END_ADD_DEFAULT_ROUTE:
 	return success;
 }
 
-static void
+static gboolean
 dhcp6_lease_change (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (priv->dhcp6_ip6_config == NULL) {
+	if (priv->dhcp6.ip6_config == NULL) {
 		_LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind");
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
-		return;
+		return FALSE;
 	}
 
-	g_assert (priv->dhcp6_client);  /* sanity check */
+	g_assert (priv->dhcp6.client);  /* sanity check */
 
 	settings_connection = nm_device_get_settings_connection (self);
 	g_assert (settings_connection);
 
 	/* Apply the updated config */
-	if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) {
-		_LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event.");
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-	} else {
-		/* Notify dispatcher scripts of new DHCPv6 config */
-		nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
-		                    settings_connection,
-		                    nm_device_get_applied_connection (self),
-		                    self, NULL, NULL, NULL);
-
-		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
+	if (!ip6_config_merge_and_apply (self, TRUE, &reason)) {
+		_LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event");
+		return FALSE;
 	}
+
+	/* Notify dispatcher scripts of new DHCPv6 config */
+	nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
+	                    settings_connection,
+	                    nm_device_get_applied_connection (self),
+	                    self, NULL, NULL, NULL);
+
+	nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
+
+	return TRUE;
 }
 
 static gboolean
@@ -5319,30 +5700,63 @@ dhcp6_restart_cb (gpointer user_data)
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	priv->dhcp6_restart_id = 0;
+	priv->dhcp6.restart_id = 0;
 
 	if (!dhcp6_start (self, FALSE, &reason))
-		priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self);
+		dhcp_schedule_restart (self, AF_INET6, NULL);
 
 	return FALSE;
 }
 
 static void
+dhcp_schedule_restart (NMDevice *self, int family, const char *reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean inet4;
+	guint tries_left;
+	gs_free char *tries_str = NULL;
+
+	g_return_if_fail (family == AF_INET || family == AF_INET6);
+	inet4 = family == AF_INET;
+
+	tries_left = inet4 ? priv->dhcp4.num_tries_left : priv->dhcp6.num_tries_left;
+	if (tries_left != DHCP_NUM_TRIES_MAX)
+		tries_str = g_strdup_printf (", %u tries left", tries_left + 1);
+
+	_LOGI (inet4 ? LOGD_DHCP4 : LOGD_DHCP6,
+	       "scheduling DHCPv%c restart in %u seconds%s%s%s%s",
+	       inet4 ? '4' : '6',
+	       DHCP_RESTART_TIMEOUT,
+	       tries_str ? tries_str : "",
+	       NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", ""));
+
+	if (inet4) {
+		priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT,
+		                                                dhcp4_restart_cb, self);
+	} else {
+		priv->dhcp6.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT,
+		                                                dhcp6_restart_cb, self);
+	}
+}
+
+static void
 dhcp6_fail (NMDevice *self, gboolean timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	_LOGD (LOGD_DHCP6, "DHCPv6 failed: timeout %d, num tries left %u",
+           timeout, priv->dhcp6.num_tries_left);
+
 	dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 
-	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
+	if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
 		/* Don't fail if there are static addresses configured on
 		 * the device, instead retry after some time.
 		 */
 		if (   priv->ip6_state == IP_DONE
 		    && priv->con_ip6_config
 		    && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) {
-			_LOGI (LOGD_DHCP6, "Scheduling DHCPv6 restart because device has IP addresses");
-			priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self);
+			dhcp_schedule_restart (self, AF_INET6, "device has IP addresses");
 			return;
 		}
 
@@ -5351,16 +5765,23 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 		 * retry DHCP again.
 		 */
 		if (nm_device_uses_assumed_connection (self)) {
-			_LOGI (LOGD_DHCP6, "Scheduling DHCPv6 restart because the connection is assumed");
-			priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self);
+			dhcp_schedule_restart (self, AF_INET6, "connection is assumed");
 			return;
 		}
 
-		if (timeout || (priv->ip6_state == IP_CONF))
+		if (   priv->dhcp6.num_tries_left == DHCP_NUM_TRIES_MAX
+		    && (timeout || (priv->ip6_state == IP_CONF)))
 			nm_device_activate_schedule_ip6_config_timeout (self);
-		else if (priv->ip6_state == IP_DONE)
-			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
-		else
+		else if (priv->ip6_state == IP_DONE) {
+			/* Don't fail immediately when the lease expires but try to
+			 * restart DHCP for a predefined number of times.
+			 */
+			if (priv->dhcp6.num_tries_left) {
+				priv->dhcp6.num_tries_left--;
+				dhcp_schedule_restart (self, AF_INET6, "lease expired");
+			} else
+				nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		} else
 			g_warn_if_reached ();
 	} else {
 		/* not a hard failure; just live with the RA info */
@@ -5374,7 +5795,7 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED)
+	if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED)
 		dhcp6_fail (self, TRUE);
 	else {
 		/* not a hard failure; just live with the RA info */
@@ -5409,32 +5830,34 @@ dhcp6_state_changed (NMDhcpClient *client,
 		 */
 		if (   ip6_config
 		    && event_id
-		    && priv->dhcp6_event_id
-		    && !strcmp (event_id, priv->dhcp6_event_id)) {
+		    && priv->dhcp6.event_id
+		    && !strcmp (event_id, priv->dhcp6.event_id)) {
 			for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
-				nm_ip6_config_add_address (priv->dhcp6_ip6_config,
+				nm_ip6_config_add_address (priv->dhcp6.ip6_config,
 				                           nm_ip6_config_get_address (ip6_config, i));
 			}
 		} else {
-			g_clear_object (&priv->dhcp6_ip6_config);
-			g_clear_pointer (&priv->dhcp6_event_id, g_free);
+			g_clear_object (&priv->dhcp6.ip6_config);
+			g_clear_pointer (&priv->dhcp6.event_id, g_free);
 			if (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);
+				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);
 			}
 		}
 
+		priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX;
+
 		if (priv->ip6_state == IP_CONF) {
-			if (priv->dhcp6_ip6_config == NULL) {
-				/* FIXME: Initial DHCP failed; should we fail IPv6 entirely then? */
-				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED);
+			if (priv->dhcp6.ip6_config == NULL) {
+				nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED);
 				break;
 			}
 			nm_device_activate_schedule_ip6_config_result (self);
 		} else if (priv->ip6_state == IP_DONE)
-			dhcp6_lease_change (self);
+			if (!dhcp6_lease_change (self))
+				dhcp6_fail (self, FALSE);
 		break;
 	case NM_DHCP_STATE_TIMEOUT:
 		dhcp6_timeout (self, client);
@@ -5449,7 +5872,7 @@ dhcp6_state_changed (NMDhcpClient *client,
 		 * may exit right after getting a response from the server.  That's
 		 * normal.  In that case we just ignore the exit.
 		 */
-		if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF)
+		if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_OTHERCONF)
 			break;
 		/* Otherwise, fall through */
 	case NM_DHCP_STATE_FAIL:
@@ -5485,7 +5908,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 
 	g_return_val_if_fail (ll_addr, FALSE);
 
-	priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
+	priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
 	                                                tmp,
@@ -5496,19 +5919,19 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip6),
 	                                                priv->dhcp_timeout,
 	                                                priv->dhcp_anycast_address,
-	                                                (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
+	                                                (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
 	                                                nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)));
 	if (tmp)
 		g_byte_array_free (tmp, TRUE);
 
-	if (priv->dhcp6_client) {
-		priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client,
+	if (priv->dhcp6.client) {
+		priv->dhcp6.state_sigid = g_signal_connect (priv->dhcp6.client,
 		                                            NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
 		                                            G_CALLBACK (dhcp6_state_changed),
 		                                            self);
 	}
 
-	return !!priv->dhcp6_client;
+	return !!priv->dhcp6.client;
 }
 
 static gboolean
@@ -5518,12 +5941,12 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip6;
 
-	nm_exported_object_clear_and_unexport (&priv->dhcp6_config);
-	priv->dhcp6_config = nm_dhcp6_config_new ();
+	nm_exported_object_clear_and_unexport (&priv->dhcp6.config);
+	priv->dhcp6.config = nm_dhcp6_config_new ();
 
-	g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
-	g_clear_object (&priv->dhcp6_ip6_config);
-	g_clear_pointer (&priv->dhcp6_event_id, g_free);
+	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);
 	g_assert (connection);
@@ -5559,7 +5982,7 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	g_return_val_if_fail (priv->dhcp6_client != NULL, FALSE);
+	g_return_val_if_fail (priv->dhcp6.client != NULL, FALSE);
 
 	_LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested");
 
@@ -5668,11 +6091,17 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 		s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
 
 	if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
-		if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr,
-		                                            nm_device_get_iface (self),
-		                                            nm_connection_get_uuid (connection),
-		                                            priv->linklocal6_dad_counter++,
-		                                            &error)) {
+		NMUtilsStableType stable_type;
+		const char *stable_id;
+
+		stable_id = _get_stable_id (connection, &stable_type);
+		if (   !stable_id
+		    || !nm_utils_ipv6_addr_set_stable_privacy (stable_type,
+		                                               &lladdr,
+		                                               nm_device_get_iface (self),
+		                                               stable_id,
+		                                               priv->linklocal6_dad_counter++,
+		                                               &error)) {
 			_LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message);
 			g_clear_error (&error);
 			linklocal6_failed (self);
@@ -5690,13 +6119,13 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 			return;
 		}
 
-		if (!nm_device_get_ip_iface_identifier (self, &iid)) {
+		if (!nm_device_get_ip_iface_identifier (self, &iid, TRUE)) {
 			_LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue");
 			return;
 		}
 		_LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address");
 
-		nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid);
+		nm_utils_ipv6_addr_set_interface_identifier (&lladdr, iid);
 	}
 
 	_LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
@@ -5799,8 +6228,9 @@ nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu)
 }
 
 static void
-rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
+rdisc_config_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, NMDevice *self)
 {
+	NMRDiscConfigMap changed = changed_int;
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int i;
 	int system_support;
@@ -5830,11 +6260,9 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 
 	if (changed & NM_RDISC_CONFIG_GATEWAYS) {
 		/* Use the first gateway as ordered in router discovery cache. */
-		if (rdisc->gateways->len) {
-			NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
-
-			nm_ip6_config_set_gateway (priv->ac_ip6_config, &gateway->address);
-		} else
+		if (rdata->gateways_n)
+			nm_ip6_config_set_gateway (priv->ac_ip6_config, &rdata->gateways[0].address);
+		else
 			nm_ip6_config_set_gateway (priv->ac_ip6_config, NULL);
 	}
 
@@ -5847,8 +6275,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 		 * also counts static and temporary addresses when checking
 		 * max_addresses.
 		 **/
-		for (i = 0; i < rdisc->addresses->len; i++) {
-			NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+		for (i = 0; i < rdata->addresses_n; i++) {
+			const NMRDiscAddress *discovered_address = &rdata->addresses[i];
 			NMPlatformIP6Address address;
 
 			memset (&address, 0, sizeof (address));
@@ -5859,7 +6287,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 			address.preferred = discovered_address->preferred;
 			if (address.preferred > address.lifetime)
 				address.preferred = address.lifetime;
-			address.source = NM_IP_CONFIG_SOURCE_RDISC;
+			address.addr_source = NM_IP_CONFIG_SOURCE_RDISC;
 			address.n_ifa_flags = ifa_flags;
 
 			nm_ip6_config_add_address (priv->ac_ip6_config, &address);
@@ -5870,25 +6298,17 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 		/* Rebuild route list from router discovery cache. */
 		nm_ip6_config_reset_routes (priv->ac_ip6_config);
 
-		for (i = 0; i < rdisc->routes->len; i++) {
-			NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
-			NMPlatformIP6Route route;
-
-			/* Only accept non-default routes.  The router has no idea what the
-			 * local configuration or user preferences are, so sending routes
-			 * with a prefix length of 0 is quite rude and thus ignored.
-			 */
-			if (discovered_route->plen > 0) {
-				memset (&route, 0, sizeof (route));
-				route.network = discovered_route->network;
-				nm_assert (discovered_route->plen <= 128);
-				route.plen = discovered_route->plen;
-				route.gateway = discovered_route->gateway;
-				route.source = NM_IP_CONFIG_SOURCE_RDISC;
-				route.metric = nm_device_get_ip6_route_metric (self);
-
-				nm_ip6_config_add_route (priv->ac_ip6_config, &route);
-			}
+		for (i = 0; i < rdata->routes_n; i++) {
+			const NMRDiscRoute *discovered_route = &rdata->routes[i];
+			const NMPlatformIP6Route route = {
+				.network    = discovered_route->network,
+				.plen       = discovered_route->plen,
+				.gateway    = discovered_route->gateway,
+				.rt_source  = NM_IP_CONFIG_SOURCE_RDISC,
+				.metric     = nm_device_get_ip6_route_metric (self),
+			};
+
+			nm_ip6_config_add_route (priv->ac_ip6_config, &route);
 		}
 	}
 
@@ -5896,36 +6316,30 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 		/* Rebuild DNS server list from router discovery cache. */
 		nm_ip6_config_reset_nameservers (priv->ac_ip6_config);
 
-		for (i = 0; i < rdisc->dns_servers->len; i++) {
-			NMRDiscDNSServer *discovered_server = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i);
-
-			nm_ip6_config_add_nameserver (priv->ac_ip6_config, &discovered_server->address);
-		}
+		for (i = 0; i < rdata->dns_servers_n; i++)
+			nm_ip6_config_add_nameserver (priv->ac_ip6_config, &rdata->dns_servers[i].address);
 	}
 
 	if (changed & NM_RDISC_CONFIG_DNS_DOMAINS) {
 		/* Rebuild domain list from router discovery cache. */
 		nm_ip6_config_reset_domains (priv->ac_ip6_config);
 
-		for (i = 0; i < rdisc->dns_domains->len; i++) {
-			NMRDiscDNSDomain *discovered_domain = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
-
-			nm_ip6_config_add_domain (priv->ac_ip6_config, discovered_domain->domain);
-		}
+		for (i = 0; i < rdata->dns_domains_n; i++)
+			nm_ip6_config_add_domain (priv->ac_ip6_config, rdata->dns_domains[i].domain);
 	}
 
 	if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
 		dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, TRUE);
 
-		priv->dhcp6_mode = rdisc->dhcp_level;
-		if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) {
+		priv->dhcp6.mode = rdata->dhcp_level;
+		if (priv->dhcp6.mode != NM_RDISC_DHCP_LEVEL_NONE) {
 			NMDeviceStateReason reason;
 
 			_LOGD (LOGD_DEVICE | LOGD_DHCP6,
 			       "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6"
 			       " as requested by IPv6 router...");
 			if (!dhcp6_start (self, FALSE, &reason)) {
-				if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
+				if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
 					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 					return;
 				}
@@ -5934,10 +6348,10 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
 	}
 
 	if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
-		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdisc->hop_limit);
+		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdata->hop_limit);
 
 	if (changed & NM_RDISC_CONFIG_MTU)
-		priv->ip6_mtu = rdisc->mtu;
+		priv->ip6_mtu = rdata->mtu;
 
 	nm_device_activate_schedule_ip6_config_result (self);
 }
@@ -5975,10 +6389,7 @@ addrconf6_start_with_link_ready (NMDevice *self)
 
 	g_assert (priv->rdisc);
 
-	if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) {
-		_LOGD (LOGD_IP6, "addrconf6: IPv6 tokenized identifier present");
-		nm_rdisc_set_iid (priv->rdisc, iid);
-	} else if (nm_device_get_ip_iface_identifier (self, &iid)) {
+	if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) {
 		_LOGD (LOGD_IP6, "addrconf6: using the device EUI-64 identifier");
 		nm_rdisc_set_iid (priv->rdisc, iid);
 	} else {
@@ -6017,6 +6428,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	NMActStageReturn ret;
 	NMSettingIP6Config *s_ip6 = NULL;
 	GError *error = NULL;
+	NMUtilsStableType stable_type;
+	const char *stable_id;
 
 	connection = nm_device_get_applied_connection (self);
 	g_assert (connection);
@@ -6030,12 +6443,16 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
 	g_assert (s_ip6);
 
-	priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET,
-	                                 nm_device_get_ip_ifindex (self),
-	                                 nm_device_get_ip_iface (self),
-	                                 nm_connection_get_uuid (connection),
-	                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
-	                                 &error);
+	stable_id = _get_stable_id (connection, &stable_type);
+	if (stable_id) {
+		priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET,
+		                                 nm_device_get_ip_ifindex (self),
+		                                 nm_device_get_ip_iface (self),
+		                                 stable_type,
+		                                 stable_id,
+		                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
+		                                 &error);
+	}
 	if (!priv->rdisc) {
 		_LOGE (LOGD_IP6, "addrconf6: failed to start router discovery: %s", error->message);
 		g_error_free (error);
@@ -6147,11 +6564,18 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 
 	priv->nm_ipv6ll = enable;
 	if (ifindex > 0) {
+		NMPlatformError plerr;
 		const char *detail = enable ? "enable" : "disable";
 
 		_LOGD (LOGD_IP6, "will %s userland IPv6LL", detail);
-		if (!nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable))
-			_LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail);
+		plerr = nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
+			_NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN,
+			        LOGD_IP6,
+			        "failed to %s userspace IPv6LL address handling (%s)",
+			        detail,
+			        nm_platform_error_to_string (plerr));
+		}
 
 		if (enable) {
 			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
@@ -6286,7 +6710,8 @@ act_stage3_ip6_config_start (NMDevice *self,
 		}
 	}
 
-	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
+	priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_NONE;
+	priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX;
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 
@@ -6334,7 +6759,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
 		ret = linklocal6_start (self);
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
-		priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_MANAGED;
+		priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_MANAGED;
 		if (!dhcp6_start (self, TRUE, reason)) {
 			/* IPv6 might be disabled; allow IPv4 to proceed */
 			ret = NM_ACT_STAGE_RETURN_STOP;
@@ -6611,7 +7036,6 @@ act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
-
 /*
  * nm_device_activate_stage4_ip4_config_timeout
  *
@@ -6639,7 +7063,6 @@ activate_stage4_ip4_config_timeout (NMDevice *self)
 	check_ip_failed (self, FALSE);
 }
 
-
 /*
  * nm_device_activate_schedule_ip4_config_timeout
  *
@@ -6659,7 +7082,6 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 	activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET);
 }
 
-
 static NMActStageReturn
 act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 {
@@ -6671,7 +7093,6 @@ act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
-
 /*
  * activate_stage4_ip6_config_timeout
  *
@@ -6699,7 +7120,6 @@ activate_stage4_ip6_config_timeout (NMDevice *self)
 	check_ip_failed (self, FALSE);
 }
 
-
 /*
  * nm_device_activate_schedule_ip6_config_timeout
  *
@@ -6747,19 +7167,12 @@ share_init (void)
 	return TRUE;
 }
 
-static void
-add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...)
-{
-	va_list args;
-	char *cmd;
-
-	va_start (args, fmt);
-	cmd = g_strdup_vprintf (fmt, args);
-	va_end (args);
-
-	nm_act_request_add_share_rule (req, table, cmd);
-	g_free (cmd);
-}
+#define add_share_rule(req, table, ...) \
+	G_STMT_START { \
+		char *_cmd = g_strdup_printf (__VA_ARGS__); \
+		nm_act_request_add_share_rule (req, table, _cmd); \
+		g_free (_cmd); \
+	} G_STMT_END
 
 static gboolean
 start_sharing (NMDevice *self, NMIP4Config *config)
@@ -6906,7 +7319,7 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 	/* NULL to use the existing priv->dev_ip4_config */
 	if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
 		_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed");
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_ip_method_failed (self, AF_INET, reason);
 		return;
 	}
 
@@ -6924,7 +7337,7 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 	/* If IPv4 wasn't the first to complete, and DHCP was used, then ensure
 	 * dispatcher scripts get the DHCP lease information.
 	 */
-	if (   priv->dhcp4_client
+	if (   priv->dhcp4.client
 	    && nm_device_activate_ip4_state_in_conf (self)
 	    && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) {
 		/* Notify dispatcher scripts of new DHCP4 config */
@@ -6975,6 +7388,64 @@ nm_device_activate_ip4_state_in_wait (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT;
 }
 
+gboolean
+nm_device_activate_ip4_state_done (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE;
+}
+
+/*
+ * Returns a NMIP6Config containing NM-configured addresses which
+ * have the tentative flag, or NULL if none is present.
+ */
+static NMIP6Config *
+dad6_get_pending_addresses (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	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;
+	guint i, j, num;
+	int ifindex;
+
+	ifindex = nm_device_get_ip_ifindex (self);
+	g_return_val_if_fail (ifindex > 0, NULL);
+
+	/* 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]) {
+			num = nm_ip6_config_get_num_addresses (confs[i]);
+			for (j = 0; j < num; j++) {
+				addr = nm_ip6_config_get_address (confs[i], j);
+				pl_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET,
+				                                       ifindex,
+				                                       addr->address,
+				                                       addr->plen);
+				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 = nm_ip6_config_new (ifindex);
+
+					nm_ip6_config_add_address (dad6_config, pl_addr);
+				}
+			}
+		}
+	}
+
+	return dad6_config;
+}
+
 static void
 activate_stage5_ip6_config_commit (NMDevice *self)
 {
@@ -6998,9 +7469,9 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 	}
 
 	if (ip6_config_merge_and_apply (self, TRUE, &reason)) {
-		if (   priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE
+		if (   priv->dhcp6.mode != NM_RDISC_DHCP_LEVEL_NONE
 		    && priv->ip6_state == IP_CONF) {
-			if (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.
 				 */
@@ -7016,16 +7487,23 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 				return;
 			}
 		}
-
 		nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE);
 		nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE);
 
-		/* Enter the IP_CHECK state if this is the first method to complete */
-		priv->ip6_state = IP_DONE;
-		check_ip_done (self);
+		/* Check if we have to wait for DAD */
+		if (priv->ip6_state == IP_CONF && !priv->dad6_ip6_config) {
+			priv->dad6_ip6_config = dad6_get_pending_addresses (self);
+			if (priv->dad6_ip6_config) {
+				_LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: waiting termination");
+			} else {
+				/* No tentative addresses, proceed right away */
+				priv->ip6_state = IP_DONE;
+				check_ip_done (self);
+			}
+		}
 	} else {
 		_LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed");
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_ip_method_failed (self, AF_INET6, reason);
 	}
 }
 
@@ -7059,6 +7537,13 @@ nm_device_activate_ip6_state_in_wait (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT;
 }
 
+gboolean
+nm_device_activate_ip6_state_done (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_DONE;
+}
+
 static void
 clear_act_request (NMDevice *self)
 {
@@ -7223,6 +7708,7 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type)
 	if (nm_clear_g_source (&priv->queued_ip6_config_id))
 		_LOGD (LOGD_DEVICE, "clearing queued IP6 config change");
 
+	g_clear_object (&priv->dad6_ip6_config);
 	dhcp6_cleanup (self, cleanup_type, FALSE);
 	linklocal6_cleanup (self);
 	addrconf6_cleanup (self);
@@ -7891,7 +8377,7 @@ nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	_LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.",
+	_LOGI (LOGD_DEVICE, "disconnecting connection '%s' for new activation request",
 	       nm_settings_connection_get_id (connection));
 
 	if (   priv->queued_act_request
@@ -7970,7 +8456,7 @@ nm_device_get_dhcp4_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config;
+	return NM_DEVICE_GET_PRIVATE (self)->dhcp4.config;
 }
 
 NMIP4Config *
@@ -8287,7 +8773,7 @@ nm_device_get_dhcp6_config (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config;
+	return NM_DEVICE_GET_PRIVATE (self)->dhcp6.config;
 }
 
 NMIP6Config *
@@ -8595,6 +9081,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean device_is_up = FALSE;
+	NMDeviceCapabilities capabilities;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
@@ -8605,6 +9092,10 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 			return FALSE;
 	}
 
+	/* Store carrier immediately. */
+	if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT))
+		check_carrier (self);
+
 	device_is_up = nm_device_is_up (self);
 	if (block && !device_is_up) {
 		int ifindex = nm_device_get_ip_ifindex (self);
@@ -8626,6 +9117,13 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 		return FALSE;
 	}
 
+	/* some ethernet devices fail to report capabilities unless the device
+	 * is up. Re-read the capabilities. */
+	capabilities = 0;
+	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
+		capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
+	_add_capabilities (self, capabilities);
+
 	/* Devices that support carrier detect must be IFF_UP to report carrier
 	 * changes; so after setting the device IFF_UP we must suppress startup
 	 * complete (via a pending action) until either the carrier turns on, or
@@ -8669,10 +9167,6 @@ bring_up (NMDevice *self, gboolean *no_firmware)
 
 	result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware);
 
-	/* Store carrier immediately. */
-	if (result && nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT))
-		check_carrier (self);
-
 	return result;
 }
 
@@ -8808,7 +9302,7 @@ capture_lease_config (NMDevice *self,
                       NMIP6Config **out_ip6_config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const GSList *connections, *citer;
+	NMSettingsConnection *const*connections;
 	guint i;
 	gboolean dhcp_used = FALSE;
 
@@ -8841,9 +9335,9 @@ capture_lease_config (NMDevice *self,
 	if (!dhcp_used)
 		return;
 
-	connections = nm_connection_provider_get_connections (priv->con_provider);
-	for (citer = connections; citer; citer = citer->next) {
-		NMConnection *candidate = citer->data;
+	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))
@@ -9016,8 +9510,8 @@ update_ip6_config (NMDevice *self, gboolean initial)
 			nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config);
 		if (priv->ac_ip6_config)
 			nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config);
-		if (priv->dhcp6_ip6_config)
-			nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config);
+		if (priv->dhcp6.ip6_config)
+			nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config);
 		if (priv->wwan_ip6_config)
 			nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config);
 		g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config);
@@ -9029,8 +9523,8 @@ update_ip6_config (NMDevice *self, gboolean initial)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config);
 		if (priv->ac_ip6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config);
-		if (priv->dhcp6_ip6_config)
-			nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config);
+		if (priv->dhcp6.ip6_config)
+			nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.ip6_config);
 		if (priv->wwan_ip6_config)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config);
 		g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config);
@@ -9070,9 +9564,7 @@ queued_ip4_config_change (gpointer user_data)
 		return TRUE;
 
 	priv->queued_ip4_config_id = 0;
-	g_object_ref (self);
 	update_ip4_config (self, FALSE);
-	g_object_unref (self);
 
 	set_unmanaged_external_down (self, TRUE);
 
@@ -9096,16 +9588,15 @@ queued_ip6_config_change (gpointer user_data)
 		return TRUE;
 
 	priv->queued_ip6_config_id = 0;
-	g_object_ref (self);
 	update_ip6_config (self, FALSE);
 
-	if (   priv->state < NM_DEVICE_STATE_DEACTIVATING
+	if (priv->state < NM_DEVICE_STATE_DEACTIVATING
 	    && nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex)) {
 		/* Handle DAD failures */
 		for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
 			NMPlatformIP6Address *addr = iter->data;
 
-			if (addr->source >= NM_IP_CONFIG_SOURCE_USER)
+			if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER)
 				continue;
 
 			_LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address",
@@ -9132,7 +9623,18 @@ queued_ip6_config_change (gpointer user_data)
 	g_slist_free_full (priv->dad6_failed_addrs, g_free);
 	priv->dad6_failed_addrs = NULL;
 
-	g_object_unref (self);
+	/* 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);
+			priv->ip6_state = IP_DONE;
+			check_ip_done (self);
+		}
+	}
 
 	set_unmanaged_external_down (self, TRUE);
 
@@ -9256,7 +9758,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo
 	 * Some flags are authoritative, meaning they always cause
 	 * the device to be unmanaged (e.g. @NM_UNMANAGED_PLATFORM_INIT).
 	 *
-	 * OTOH, some flags can be overwritten. For example NM_UNMANAGED_USER_SETTINGS
+	 * OTOH, some flags can be overwritten. For example NM_UNMANAGED_USER_UDEV
 	 * is ignored once NM_UNMANAGED_USER_EXPLICIT is set. The idea is that
 	 * the flag from the configuration has no effect once the user explicitly
 	 * touches the unmanaged flags. */
@@ -9290,6 +9792,9 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo
 		/* configuration from udev or nm-config overwrites the by-default flag
 		 * which is based on the device type. */
 		flags &= ~NM_UNMANAGED_BY_DEFAULT;
+
+		/* configuration from udev overwrites external-down */
+		flags &= ~NM_UNMANAGED_EXTERNAL_DOWN;
 	}
 
 	if (   NM_FLAGS_HAS (mask, NM_UNMANAGED_IS_SLAVE)
@@ -9528,7 +10033,7 @@ nm_device_set_unmanaged_by_flags_queue (NMDevice *self,
 }
 
 void
-nm_device_set_unmanaged_by_user_config (NMDevice *self, const GSList *unmanaged_specs)
+nm_device_set_unmanaged_by_user_settings (NMDevice *self, const GSList *unmanaged_specs)
 {
 	NMDevicePrivate *priv;
 	gboolean unmanaged;
@@ -9880,50 +10385,45 @@ void
 nm_device_recheck_available_connections (NMDevice *self)
 {
 	NMDevicePrivate *priv;
-	const GSList *connections, *iter;
+	NMSettingsConnection *const*connections;
 	gboolean changed = FALSE;
 	GHashTableIter h_iter;
 	NMConnection *connection;
+	guint i;
+	gs_unref_hashtable GHashTable *prune_list = NULL;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE(self);
 
-	if (priv->con_provider) {
-		gs_unref_hashtable GHashTable *prune_list = NULL;
-
-		if (g_hash_table_size (priv->available_connections) > 0) {
-			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);
-		}
-
-		connections = nm_connection_provider_get_connections (priv->con_provider);
-		for (iter = connections; iter; iter = g_slist_next (iter)) {
-			connection = NM_CONNECTION (iter->data);
-
-			if (nm_device_check_connection_available (self,
-			                                          connection,
-			                                          NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
-			                                          NULL)) {
-				if (available_connections_add (self, connection))
-					changed = TRUE;
-				if (prune_list)
-					g_hash_table_remove (prune_list, connection);
-			}
+	if (g_hash_table_size (priv->available_connections) > 0) {
+		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);
+	}
+
+	connections = nm_settings_get_connections (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		connection = (NMConnection *) connections[i];
+
+		if (nm_device_check_connection_available (self,
+		                                          connection,
+		                                          NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
+		                                          NULL)) {
+			if (available_connections_add (self, connection))
+				changed = TRUE;
+			if (prune_list)
+				g_hash_table_remove (prune_list, connection);
 		}
+	}
 
-		if (prune_list) {
-			g_hash_table_iter_init (&h_iter, prune_list);
-			while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) {
-				if (available_connections_del (self, connection))
-					changed = TRUE;
-			}
+	if (prune_list) {
+		g_hash_table_iter_init (&h_iter, prune_list);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) {
+			if (available_connections_del (self, connection))
+				changed = TRUE;
 		}
-	} else {
-		if (available_connections_del_all (self))
-			changed = TRUE;
 	}
 
 	if (changed)
@@ -9984,10 +10484,9 @@ nm_device_get_best_connection (NMDevice *self,
 }
 
 static void
-cp_connection_added_or_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
+cp_connection_added_or_updated (NMDevice *self, NMConnection *connection)
 {
 	gboolean changed;
-	NMDevice *self = user_data;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
@@ -10007,6 +10506,18 @@ cp_connection_added_or_updated (NMConnectionProvider *cp, NMConnection *connecti
 }
 
 static void
+cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
+{
+	cp_connection_added_or_updated (user_data, connection);
+}
+
+static void
+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 (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data)
 {
 	NMDevice *self = user_data;
@@ -10213,6 +10724,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	g_clear_object (&priv->ext_ip6_config_captured);
 	g_clear_object (&priv->wwan_ip6_config);
 	g_clear_object (&priv->ip6_config);
+	g_clear_object (&priv->dad6_ip6_config);
 
 	g_slist_free_full (priv->vpn4_configs, g_object_unref);
 	priv->vpn4_configs = NULL;
@@ -10276,44 +10788,48 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	if (NM_DEVICE_GET_CLASS (self)->deactivate)
 		NM_DEVICE_GET_CLASS (self)->deactivate (self);
 
-	/* master: release slaves */
-	nm_device_master_release_slaves (self);
+	if (cleanup_type != CLEANUP_TYPE_KEEP) {
+		/* master: release slaves */
+		nm_device_master_release_slaves (self);
 
-	/* slave: mark no longer enslaved */
-	if (   priv->master
-	    && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0)
-		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+		/* slave: mark no longer enslaved */
+		if (   priv->master
+		    && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0)
+			nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
-	/* Take out any entries in the routing table and any IP address the device had. */
-	ifindex = nm_device_get_ip_ifindex (self);
-	if (ifindex > 0) {
-		nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
-		nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
+		/* Take out any entries in the routing table and any IP address the device had. */
+		ifindex = nm_device_get_ip_ifindex (self);
+		if (ifindex > 0) {
+			nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
+			nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
+		}
 	}
 
 	if (priv->lldp_listener)
 		nm_lldp_listener_stop (priv->lldp_listener);
 
 	nm_device_update_metered (self);
+
+	/* during device cleanup, we want to reset the MAC address of the device
+	 * to the initial state.
+	 *
+	 * We certainly want to do that when reaching the UNMANAGED state... */
+	if (nm_device_get_state (self) <= NM_DEVICE_STATE_UNMANAGED)
+		nm_device_hw_addr_reset (self, "unmanage");
+	else {
+		/* for other device states (UNAVAILABLE, DISCONNECTED), allow the
+		 * device to overwrite the reset behavior, so that Wi-Fi can set
+		 * a randomized MAC address used during scanning. */
+		NM_DEVICE_GET_CLASS (self)->deactivate_reset_hw_addr (self);
+	}
+
 	_cleanup_generic_post (self, cleanup_type);
 }
 
-static char *
-bin2hexstr (const char *bytes, gsize len)
+static void
+deactivate_reset_hw_addr (NMDevice *self)
 {
-	GString *str;
-	int i;
-
-	g_return_val_if_fail (bytes != NULL, NULL);
-	g_return_val_if_fail (len > 0, NULL);
-
-	str = g_string_sized_new (len * 2 + 1);
-	for (i = 0; i < len; i++) {
-		if (str->len)
-			g_string_append_c (str, ':');
-		g_string_append_printf (str, "%02x", (guint8) bytes[i]);
-	}
-	return g_string_free (str, FALSE);
+	nm_device_hw_addr_reset (self, "deactivate");
 }
 
 static char *
@@ -10329,7 +10845,7 @@ find_dhcp4_address (NMDevice *self)
 	for (i = 0; i < n; i++) {
 		const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
 
-		if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
+		if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP)
 			return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
 	}
 	return NULL;
@@ -10346,6 +10862,8 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	GPtrArray *argv;
 	gs_free char *dhcp4_address = NULL;
 	char *logging_backend;
+	NMUtilsStableType stable_type;
+	const char *stable_id;
 
 	if (priv->state != NM_DEVICE_STATE_ACTIVATED)
 		return;
@@ -10364,6 +10882,12 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	g_ptr_array_add (argv, g_strdup ("--uuid"));
 	g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
 
+	stable_id = _get_stable_id (connection, &stable_type);
+	if (stable_id && stable_type != NM_UTILS_STABLE_TYPE_UUID) {
+		g_ptr_array_add (argv, g_strdup ("--stable-id"));
+		g_ptr_array_add (argv, g_strdup_printf ("%d %s", (int) stable_type, stable_id));
+	}
+
 	logging_backend = nm_config_get_is_debug (nm_config_get ())
 	                  ? g_strdup ("debug")
 	                  : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
@@ -10380,7 +10904,6 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
 		NMSettingIPConfig *s_ip4;
-		char *hex_client_id;
 
 		s_ip4 = nm_connection_get_setting_ip4_config (connection);
 		g_assert (s_ip4);
@@ -10393,25 +10916,26 @@ nm_device_spawn_iface_helper (NMDevice *self)
 		if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
 			g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
 
-		if (priv->dhcp4_client) {
+		if (priv->dhcp4.client) {
 			const char *hostname, *fqdn;
 			GBytes *client_id;
 
-			client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
+			client_id = nm_dhcp_client_get_client_id (priv->dhcp4.client);
 			if (client_id) {
 				g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
-				hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
-				                            g_bytes_get_size (client_id));
-				g_ptr_array_add (argv, hex_client_id);
+				g_ptr_array_add (argv,
+				                 _nm_utils_bin2str (g_bytes_get_data (client_id, NULL),
+				                                    g_bytes_get_size (client_id),
+				                                    FALSE));
 			}
 
-			hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
+			hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client);
 			if (hostname) {
 				g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
 				g_ptr_array_add (argv, g_strdup (hostname));
 			}
 
-			fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4_client);
+			fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4.client);
 			if (fqdn) {
 				g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn"));
 				g_ptr_array_add (argv, g_strdup (fqdn));
@@ -10424,7 +10948,6 @@ nm_device_spawn_iface_helper (NMDevice *self)
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 	if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
 		NMSettingIPConfig *s_ip6;
-		char *hex_iid;
 		NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
 
 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
@@ -10441,10 +10964,12 @@ nm_device_spawn_iface_helper (NMDevice *self)
 		g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
 		g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
 
-		if (nm_device_get_ip_iface_identifier (self, &iid)) {
+		if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) {
 			g_ptr_array_add (argv, g_strdup ("--iid"));
-			hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
-			g_ptr_array_add (argv, hex_iid);
+			g_ptr_array_add (argv,
+			                 _nm_utils_bin2str (iid.id_u8,
+			                                    sizeof (NMUtilsIPv6IfaceId),
+			                                    FALSE));
 		}
 
 		g_ptr_array_add (argv, g_strdup ("--addr-gen-mode"));
@@ -10654,11 +11179,14 @@ _set_state_full (NMDevice *self,
 		if (old_state > NM_DEVICE_STATE_UNMANAGED) {
 			if (reason == NM_DEVICE_STATE_REASON_REMOVED) {
 				nm_device_cleanup (self, reason, CLEANUP_TYPE_REMOVED);
+			} else if (reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+				nm_device_cleanup (self, reason, CLEANUP_TYPE_KEEP);
 			} else {
 				/* Clean up if the device is now unmanaged but was activated */
 				if (nm_device_get_act_request (self))
 					nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
 				nm_device_take_down (self, TRUE);
+				nm_device_hw_addr_reset (self, "unmanage");
 				set_nm_ipv6ll (self, FALSE);
 				restore_ip6_properties (self);
 			}
@@ -10706,6 +11234,9 @@ _set_state_full (NMDevice *self,
 			}
 		}
 		break;
+	case NM_DEVICE_STATE_PREPARE:
+		nm_device_update_initial_hw_address (self);
+		break;
 	case NM_DEVICE_STATE_NEED_AUTH:
 		if (old_state > NM_DEVICE_STATE_NEED_AUTH) {
 			/* Clean up any half-done IP operations if the device's layer2
@@ -10726,7 +11257,7 @@ _set_state_full (NMDevice *self,
 
 	_notify (self, PROP_STATE);
 	_notify (self, PROP_STATE_REASON);
-	g_signal_emit_by_name (self, NM_DEVICE_STATE_CHANGED, state, old_state, reason);
+	g_signal_emit (self, signals[STATE_CHANGED], 0, state, old_state, reason);
 
 	/* Post-process the event after internal notification */
 
@@ -10749,11 +11280,9 @@ _set_state_full (NMDevice *self,
 	case NM_DEVICE_STATE_DEACTIVATING:
 		_cancel_activation (self);
 
-		if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-			/* We cache the ignore_carrier state to not react on config-reloads while the connection
-			 * is active. But on deactivating, reset the ignore-carrier flag to the current state. */
-			priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self);
-		}
+		/* We cache the ignore_carrier state to not react on config-reloads while the connection
+		 * is active. But on deactivating, reset the ignore-carrier flag to the current state. */
+		priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self);
 
 		if (quitting) {
 			nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
@@ -11026,25 +11555,27 @@ nm_device_get_hw_address (NMDevice *self)
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return priv->hw_addr_len ? priv->hw_addr : NULL;
+	nm_assert ((!priv->hw_addr) ^ (priv->hw_addr_len > 0));
+
+	return priv->hw_addr;
 }
 
 void
 nm_device_update_hw_address (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (self);
+	NMDevicePrivate *priv;
 	const guint8 *hwaddr;
 	gsize hwaddrlen = 0;
-	static const guint8 zero_hwaddr[ETH_ALEN];
 
-	if (ifindex <= 0)
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->ifindex <= 0)
 		return;
 
-	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen);
+	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &hwaddrlen);
 
 	if (   priv->type == NM_DEVICE_TYPE_ETHERNET
-	    && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, sizeof (zero_hwaddr)))
+	    && hwaddr
+	    && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, nm_ip_addr_zero.addr_eth, sizeof (nm_ip_addr_zero.addr_eth)))
 		hwaddrlen = 0;
 
 	if (hwaddrlen) {
@@ -11053,17 +11584,28 @@ nm_device_update_hw_address (NMDevice *self)
 			g_free (priv->hw_addr);
 			priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen);
 
-			_LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr);
+			_LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr);
 			_notify (self, PROP_HW_ADDRESS);
+
+			if (   !priv->hw_addr_initial
+			    || (   priv->hw_addr_type == HW_ADDR_TYPE_UNSET
+			        && priv->state < NM_DEVICE_STATE_PREPARE
+			        && !nm_device_is_activating (self))) {
+				/* when we get a hw_addr the first time or while the device
+				 * is not activated (with no explict hw address set), always
+				 * update our inital hw-address as well. */
+				nm_device_update_initial_hw_address (self);
+			}
 		}
 	} else {
 		/* Invalid or no hardware address */
 		if (priv->hw_addr_len != 0) {
-			g_clear_pointer (&priv->hw_addr, g_free);
-			priv->hw_addr_len = 0;
 			_LOGD (LOGD_HW | LOGD_DEVICE,
-			       "previous hardware address is no longer valid");
-			_notify (self, PROP_HW_ADDRESS);
+			       "hw-addr: failed reading current MAC address (stay with %s)",
+			       priv->hw_addr);
+		} else {
+			_LOGD (LOGD_HW | LOGD_DEVICE,
+			       "hw-addr: failed reading current MAC address");
 		}
 	}
 }
@@ -11073,84 +11615,365 @@ nm_device_update_initial_hw_address (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->hw_addr_len) {
-		priv->initial_hw_addr = g_strdup (priv->hw_addr);
-		_LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr);
+	if (   priv->hw_addr
+	    && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) {
+		if (   priv->hw_addr_initial
+		    && priv->hw_addr_type != HW_ADDR_TYPE_UNSET) {
+			/* once we have the initial hw address set, we only allow
+			 * update if the currenty type is "unset". */
+			return;
+		}
+		g_free (priv->hw_addr_initial);
+		priv->hw_addr_initial = g_strdup (priv->hw_addr);
+		_LOGD (LOGD_DEVICE, "hw-addr: update initial MAC address %s",
+		       priv->hw_addr_initial);
+	}
+}
 
-		if (priv->ifindex > 0) {
-			guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
-			size_t len = 0;
+void
+nm_device_update_permanent_hw_address (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+	size_t len = 0;
+	gboolean success_read;
 
-			if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) {
-				g_warn_if_fail (len == priv->hw_addr_len);
-				priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
-				_LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s",
-				       priv->perm_hw_addr);
-			} else {
-				/* Fall back to current address */
-				_LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address");
-				priv->perm_hw_addr = g_strdup (priv->hw_addr);
+	if (priv->hw_addr_perm) {
+		/* the permanent hardware address is only read once and not
+		 * re-read later.
+		 *
+		 * Except during unrealize/realize cycles, where we clear the permanent
+		 * hardware address during unrealization. */
+		return;
+	}
+
+	if (priv->ifindex <= 0)
+		return;
+
+	if (!priv->hw_addr_len) {
+		nm_device_update_hw_address (self);
+		if (!priv->hw_addr_len)
+			return;
+	}
+
+	success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len);
+	if (!success_read || len != priv->hw_addr_len) {
+		/* Fall back to current address. We use the fake address and keep it
+		 * until the device unrealizes.
+		 *
+		 * In some cases it might be necessary to know whether this is a "real" or
+		 * a temporary address (fake). */
+		_LOGD (LOGD_HW | LOGD_ETHER, "hw-addr: %s (use current: %s)",
+		       success_read
+		           ? "read HW addr length of permanent MAC address differs"
+		           : "unable to read permanent MAC address",
+		       priv->hw_addr);
+		priv->hw_addr_perm_fake = TRUE;
+		priv->hw_addr_perm = g_strdup (priv->hw_addr);
+	} else {
+		priv->hw_addr_perm_fake = FALSE;
+		priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len);
+		_LOGD (LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'",
+		       priv->hw_addr_perm);
+	}
+	_notify (self, PROP_PERM_HW_ADDRESS);
+}
+
+static const char *
+_get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_addr)
+{
+	NMSetting *setting;
+	const char *addr = NULL;
+
+	nm_assert (out_addr && !*out_addr);
+
+	setting = nm_connection_get_setting (connection,
+	                                     is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED);
+	if (setting) {
+		addr = is_wifi
+		       ? nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) setting)
+		       : nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) setting);
+	}
+
+	if (!addr) {
+		gs_free char *a = NULL;
+
+		a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+		                                           is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address",
+		                                           self);
+		/* default is permanent. */
+		addr = NM_CLONED_MAC_PERMANENT;
+
+		if (!a) {
+			if (is_wifi) {
+				NMSettingMacRandomization v;
+
+				/* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */
+				a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+				                                           "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
+				                                           self);
+				v = _nm_utils_ascii_str_to_int64 (a, 10,
+				                                  NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
+				                                  NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
+				                                  NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
+				if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS)
+					addr = NM_CLONED_MAC_RANDOM;
 			}
-		}
+		} else if (   NM_CLONED_MAC_IS_SPECIAL (a)
+		           || nm_utils_hwaddr_valid (a, ETH_ALEN))
+			addr = *out_addr = g_steal_pointer (&a);
 	}
+
+	return addr;
+}
+
+static const char *
+_get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_value)
+{
+	NMSetting *setting;
+	const char *value = NULL;
+	char *a;
+
+	nm_assert (out_value && !*out_value);
+
+	setting = nm_connection_get_setting (connection,
+	                                     is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED);
+	if (setting) {
+		value = is_wifi
+		        ? nm_setting_wireless_get_generate_mac_address_mask ((NMSettingWireless *) setting)
+		        : nm_setting_wired_get_generate_mac_address_mask ((NMSettingWired *) setting);
+		if (value)
+			return value;
+	}
+
+	a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                           is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask",
+	                                           self);
+	if (!a)
+		return NULL;
+	*out_value = a;
+	return a;
 }
 
 gboolean
-nm_device_set_hw_addr (NMDevice *self, const char *addr,
-                       const char *detail, guint64 hw_log_domain)
+nm_device_hw_addr_is_explict (NMDevice *self)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	return !NM_IN_SET (priv->hw_addr_type, HW_ADDR_TYPE_PERMANENT, HW_ADDR_TYPE_UNSET);
+}
+
+static gboolean
+_hw_addr_set (NMDevice *self,
+              const char *addr,
+              const char *operation,
+              const char *detail)
+{
+	NMDevicePrivate *priv;
 	gboolean success = FALSE;
-	const char *cur_addr = nm_device_get_hw_address (self);
+	NMPlatformError plerr;
+	const char *cur_addr;
 	guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX];
+	guint hw_addr_len;
+	gboolean was_up;
 
-	/* Fall back to the permanent address */
-	if (!addr)
-		addr = priv->perm_hw_addr;
-	if (!addr)
-		return FALSE;
+	nm_assert (NM_IS_DEVICE (self));
+	nm_assert (addr);
+	nm_assert (operation);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	cur_addr = nm_device_get_hw_address (self);
 
 	/* Do nothing if current MAC is same */
 	if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) {
-		_LOGD (LOGD_DEVICE | hw_log_domain, "no MAC address change needed");
+		_LOGT (LOGD_DEVICE, "set-hw-addr: no MAC address change needed (%s)", cur_addr);
 		return TRUE;
 	}
-	if (!nm_utils_hwaddr_aton (addr, addr_bytes, priv->hw_addr_len)) {
-		_LOGW (LOGD_DEVICE | hw_log_domain, "invalid MAC address %s", addr);
-		return FALSE;
-	}
 
-	/* Can't change MAC address while device is up */
-	nm_device_take_down (self, FALSE);
+	hw_addr_len = priv->hw_addr_len;
+	if (!hw_addr_len)
+		hw_addr_len = _nm_utils_hwaddr_length (addr);
+	if (   !hw_addr_len
+	    || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len))
+		g_return_val_if_reached (FALSE);
 
-	success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len);
+	_LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail);
+
+	was_up = nm_device_is_up (self);
+	if (was_up) {
+		/* Can't change MAC address while device is up */
+		nm_device_take_down (self, FALSE);
+	}
+
+	plerr = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len);
+	success = (plerr == NM_PLATFORM_ERROR_SUCCESS);
 	if (success) {
 		/* MAC address succesfully changed; update the current MAC to match */
 		nm_device_update_hw_address (self);
 		cur_addr = nm_device_get_hw_address (self);
 		if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) {
-			_LOGI (LOGD_DEVICE | hw_log_domain, "%s MAC address to %s",
-			       detail, addr);
+			_LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)",
+			       operation, addr, detail);
 		} else {
-			_LOGW (LOGD_DEVICE | hw_log_domain,
-			       "new MAC address %s not successfully set", addr);
+			_LOGW (LOGD_DEVICE,
+			       "set-hw-addr: new MAC address %s not successfully set to %s (%s)",
+			       addr, operation, detail);
 			success = FALSE;
 		}
 	} else {
-		_LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s",
-		       detail, addr);
+		_NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN,
+		        LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)",
+		        operation, addr, detail,
+		        nm_platform_error_to_string (plerr));
+	}
+
+	if (was_up) {
+		if (!nm_device_bring_up (self, TRUE, NULL))
+			return FALSE;
 	}
-	nm_device_bring_up (self, TRUE, NULL);
 
 	return success;
 }
 
+gboolean
+nm_device_hw_addr_set (NMDevice *self, const char *addr, const char *detail)
+{
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!addr)
+		g_return_val_if_reached (FALSE);
+
+	/* this is called by NMDeviceVlan to take the MAC address from the parent
+	 * and by NMDeviceWifi to set a random MAC address during scanning.
+	 * In this case, it's like setting it to PERMANENT. */
+	priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT;
+
+	return _hw_addr_set (self, addr, "set", detail);
+}
+
+gboolean
+nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi)
+{
+	NMDevicePrivate *priv;
+	gs_free char *hw_addr_tmp = NULL;
+	gs_free char *hw_addr_generated = NULL;
+	gs_free char *generate_mac_address_mask_tmp = NULL;
+	const char *addr, *addr_setting;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!connection)
+		g_return_val_if_reached (FALSE);
+
+	addr = addr_setting = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp);
+
+	if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) {
+		/* "preserve" means to reset the initial MAC address. */
+		return nm_device_hw_addr_reset (self, addr_setting);
+	}
+
+	if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) {
+		addr = nm_device_get_permanent_hw_address (self, TRUE);
+		if (!addr)
+			return FALSE;
+		priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT;
+	} else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) {
+		if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) {
+			/* hm, we already use a generate MAC address. Most certainly, that is from the same
+			 * activation request, so we should not create a new random address, instead keep
+			 * the current. */
+			return TRUE;
+		}
+		hw_addr_generated = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (self),
+		                                                     _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp));
+		if (!hw_addr_generated) {
+			_LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random");
+			return FALSE;
+		}
+		priv->hw_addr_type = HW_ADDR_TYPE_GENERATED;
+		addr = hw_addr_generated;
+	} else if (NM_IN_STRSET (addr, NM_CLONED_MAC_STABLE)) {
+		NMUtilsStableType stable_type;
+		const char *stable_id;
+
+		if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) {
+			/* hm, we already use a generate MAC address. Most certainly, that is from the same
+			 * activation request, so let's skip creating the stable address anew. */
+			return TRUE;
+		}
+
+		stable_id = _get_stable_id (connection, &stable_type);
+		if (stable_id) {
+			hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id,
+			                                                     nm_device_get_ip_iface (self),
+			                                                     nm_device_get_initial_hw_address (self),
+			                                                     _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp));
+		}
+		if (!hw_addr_generated) {
+			_LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable");
+			return FALSE;
+		}
+		priv->hw_addr_type = HW_ADDR_TYPE_GENERATED;
+		addr = hw_addr_generated;
+	} else {
+		/* this must be a valid address. Otherwise, we shouldn't come here. */
+		if (_nm_utils_hwaddr_length (addr) <= 0) {
+			g_return_val_if_reached (FALSE);
+		}
+		priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT;
+	}
+
+	return _hw_addr_set (self, addr, "set-cloned", addr_setting);
+}
+
+gboolean
+nm_device_hw_addr_reset (NMDevice *self, const char *detail)
+{
+	NMDevicePrivate *priv;
+	const char *addr;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->hw_addr_type == HW_ADDR_TYPE_UNSET)
+		return TRUE;
+
+	priv->hw_addr_type = HW_ADDR_TYPE_UNSET;
+	addr = nm_device_get_initial_hw_address (self);
+	if (!addr) {
+		/* as hw_addr_type is not UNSET, we expect that we can get an
+		 * initial address to which to reset. */
+		g_return_val_if_reached (FALSE);
+	}
+
+	return _hw_addr_set (self, addr, "reset", detail);
+}
+
 const char *
-nm_device_get_permanent_hw_address (NMDevice *self)
+nm_device_get_permanent_hw_address (NMDevice *self, gboolean fallback_fake)
 {
+	NMDevicePrivate *priv;
+
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr;
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (!priv->hw_addr_perm)
+		return NULL;
+	if (   priv->hw_addr_perm_fake
+	    && !fallback_fake)
+		return NULL;
+	return priv->hw_addr_perm;
 }
 
 const char *
@@ -11158,7 +11981,7 @@ nm_device_get_initial_hw_address (NMDevice *self)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
-	return NM_DEVICE_GET_PRIVATE (self)->initial_hw_addr;
+	return NM_DEVICE_GET_PRIVATE (self)->hw_addr_initial;
 }
 
 /**
@@ -11196,9 +12019,9 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs)
 static NMMatchSpecMatchType
 spec_match_list (NMDevice *self, const GSList *specs)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m;
 	const GSList *iter;
+	const char *hw_addr_perm;
 
 	for (iter = specs; iter; iter = g_slist_next (iter)) {
 		if (!strcmp ((const char *) iter->data, "*")) {
@@ -11206,8 +12029,10 @@ spec_match_list (NMDevice *self, const GSList *specs)
 			break;
 		}
 	}
-	if (priv->hw_addr_len && priv->hw_addr) {
-		m = nm_match_spec_hwaddr (specs, priv->hw_addr);
+
+	hw_addr_perm = nm_device_get_permanent_hw_address (self, FALSE);
+	if (hw_addr_perm) {
+		m = nm_match_spec_hwaddr (specs, hw_addr_perm);
 		matched = MAX (matched, m);
 	}
 	if (matched != NM_MATCH_SPEC_NEG_MATCH) {
@@ -11290,7 +12115,8 @@ constructor (GType type,
 	self = NM_DEVICE (object);
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->iface) {
+	if (   priv->iface
+	    && G_LIKELY (!nm_utils_get_testing ())) {
 		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
 
 		if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) {
@@ -11299,6 +12125,17 @@ constructor (GType type,
 		}
 	}
 
+	if (priv->hw_addr_perm) {
+		priv->hw_addr_len = _nm_utils_hwaddr_length (priv->hw_addr_perm);
+		if (!priv->hw_addr_len) {
+			g_clear_pointer (&priv->hw_addr_perm, g_free);
+			g_return_val_if_reached (object);
+		}
+
+		priv->hw_addr = g_strdup (priv->hw_addr_perm);
+		_LOGT (LOGD_DEVICE, "hw-addr: has permanent hw-address '%s'", priv->hw_addr_perm);
+	}
+
 	return object;
 }
 
@@ -11309,33 +12146,31 @@ constructed (GObject *object)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMPlatform *platform;
 
-	platform = nm_platform_get ();
-
 	if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
 	/* Watch for external IP config changes */
+	platform = NM_PLATFORM_GET;
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
 
-	priv->con_provider = nm_connection_provider_get ();
-	g_assert (priv->con_provider);
-	g_signal_connect (priv->con_provider,
-	                  NM_CP_SIGNAL_CONNECTION_ADDED,
-	                  G_CALLBACK (cp_connection_added_or_updated),
-	                  self);
+	priv->settings = g_object_ref (NM_SETTINGS_GET);
+	g_assert (priv->settings);
 
-	g_signal_connect (priv->con_provider,
-	                  NM_CP_SIGNAL_CONNECTION_REMOVED,
-	                  G_CALLBACK (cp_connection_removed),
+	g_signal_connect (priv->settings,
+	                  NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
+	                  G_CALLBACK (cp_connection_added),
 	                  self);
-
-	g_signal_connect (priv->con_provider,
-	                  NM_CP_SIGNAL_CONNECTION_UPDATED,
-	                  G_CALLBACK (cp_connection_added_or_updated),
+	g_signal_connect (priv->settings,
+	                  NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
+	                  G_CALLBACK (cp_connection_updated),
+	                  self);
+	g_signal_connect (priv->settings,
+	                  NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
+	                  G_CALLBACK (cp_connection_removed),
 	                  self);
 
 	G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
@@ -11352,7 +12187,7 @@ dispose (GObject *object)
 
 	_LOGD (LOGD_DEVICE, "disposing");
 
-	platform = nm_platform_get ();
+	platform = NM_PLATFORM_GET;
 	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);
 
@@ -11361,7 +12196,7 @@ dispose (GObject *object)
 
 	arp_cleanup (self);
 
-	g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self);
+	nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id);
 
 	dispatcher_cleanup (self);
 
@@ -11382,12 +12217,14 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&priv->check_delete_unrealized_id);
 
+	nm_clear_g_source (&priv->stats.timeout_id);
+
 	link_disconnect_action_cancel (self);
 
-	if (priv->con_provider) {
-		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added_or_updated, self);
-		g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self);
-		priv->con_provider = NULL;
+	if (priv->settings) {
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
+		g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
 	}
 
 	available_connections_del_all (self);
@@ -11426,8 +12263,8 @@ finalize (GObject *object)
 	_LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self));
 
 	g_free (priv->hw_addr);
-	g_free (priv->perm_hw_addr);
-	g_free (priv->initial_hw_addr);
+	g_free (priv->hw_addr_perm);
+	g_free (priv->hw_addr_initial);
 	g_slist_free_full (priv->pending_actions, g_free);
 	g_slist_free_full (priv->dad6_failed_addrs, g_free);
 	g_clear_pointer (&priv->physical_port_id, g_free);
@@ -11445,16 +12282,19 @@ finalize (GObject *object)
 	g_hash_table_unref (priv->available_connections);
 
 	G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
+
+	/* for testing, NMDeviceTest does not invoke NMDevice::constructed,
+	 * and thus @settings might be unset. */
+	if (priv->settings)
+		g_object_unref (priv->settings);
 }
 
 static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
-	NMDevice *self = NM_DEVICE (object);
+	NMDevice *self = (NMDevice *) object;
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	const char *hw_addr, *p;
-	guint count;
 
 	switch (prop_id) {
 	case PROP_UDI:
@@ -11476,11 +12316,11 @@ set_property (GObject *object, guint prop_id,
 		break;
 	case PROP_DRIVER_VERSION:
 		g_free (priv->driver_version);
-		priv->driver_version = g_strdup (g_value_get_string (value));
+		priv->driver_version = g_value_dup_string (value);
 		break;
 	case PROP_FIRMWARE_VERSION:
 		g_free (priv->firmware_version);
-		priv->firmware_version = g_strdup (g_value_get_string (value));
+		priv->firmware_version = g_value_dup_string (value);
 		break;
 	case PROP_MTU:
 		priv->mtu = g_value_get_uint (value);
@@ -11532,32 +12372,12 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IS_MASTER:
 		priv->is_master = g_value_get_boolean (value);
 		break;
-	case PROP_HW_ADDRESS:
+	case PROP_PERM_HW_ADDRESS:
 		/* construct only */
-		p = hw_addr = g_value_get_string (value);
-
-		/* Hardware address length is the number of ':' plus 1 */
-		count = 1;
-		while (p && *p) {
-			if (*p++ == ':')
-				count++;
-		}
-		if (count < ETH_ALEN || count > NM_UTILS_HWADDR_LEN_MAX) {
-			if (hw_addr && *hw_addr) {
-				_LOGW (LOGD_DEVICE, "ignoring hardware address '%s' with unexpected length %d",
-				       hw_addr, count);
-			}
-			break;
-		}
-
-		priv->hw_addr_len = count;
-		g_free (priv->hw_addr);
-		if (nm_utils_hwaddr_valid (hw_addr, priv->hw_addr_len))
-			priv->hw_addr = g_strdup (hw_addr);
-		else {
-			_LOGW (LOGD_DEVICE, "could not parse hw-address '%s'", hw_addr);
-			priv->hw_addr = NULL;
-		}
+		priv->hw_addr_perm = g_value_dup_string (value);
+		break;
+	case PROP_REFRESH_RATE_MS:
+		_stats_set_refresh_rate (self, g_value_get_uint (value));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -11617,13 +12437,13 @@ get_property (GObject *object, guint prop_id,
 		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL);
 		break;
 	case PROP_DHCP4_CONFIG:
-		nm_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_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL);
 		break;
 	case PROP_DHCP6_CONFIG:
-		nm_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);
@@ -11680,6 +12500,10 @@ get_property (GObject *object, guint prop_id,
 	case PROP_HW_ADDRESS:
 		g_value_set_string (value, priv->hw_addr);
 		break;
+	case PROP_PERM_HW_ADDRESS:
+		/* this property is exposed on D-Bus for NMDeviceEthernet and NMDeviceWifi. */
+		g_value_set_string (value, nm_device_get_permanent_hw_address (self, FALSE));
+		break;
 	case PROP_HAS_PENDING_ACTION:
 		g_value_set_boolean (value, nm_device_has_pending_action (self));
 		break;
@@ -11717,6 +12541,15 @@ get_property (GObject *object, guint prop_id,
 		g_value_take_boxed (value, slave_list);
 		break;
 	}
+	case PROP_REFRESH_RATE_MS:
+		g_value_set_uint (value, priv->stats.refresh_rate_ms);
+		break;
+	case PROP_TX_BYTES:
+		g_value_set_uint64 (value, priv->stats.tx_bytes);
+		break;
+	case PROP_RX_BYTES:
+		g_value_set_uint64 (value, priv->stats.rx_bytes);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -11765,6 +12598,8 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->take_down = take_down;
 	klass->carrier_changed = carrier_changed;
 	klass->get_ip_iface_identifier = get_ip_iface_identifier;
+	klass->unmanaged_on_quit = unmanaged_on_quit;
+	klass->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
 
 	/* Properties */
 	obj_properties[PROP_UDI] =
@@ -11923,6 +12758,11 @@ nm_device_class_init (NMDeviceClass *klass)
 	obj_properties[PROP_HW_ADDRESS] =
 	    g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "",
 	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_PERM_HW_ADDRESS] =
+	    g_param_spec_string (NM_DEVICE_PERM_HW_ADDRESS, "", "",
+	                         NULL,
 	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_HAS_PENDING_ACTION] =
@@ -11960,6 +12800,23 @@ 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,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_TX_BYTES] =
+	    g_param_spec_uint64 (NM_DEVICE_STATISTICS_TX_BYTES, "", "",
+	                         0, UINT64_MAX, 0,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_RX_BYTES] =
+	    g_param_spec_uint64 (NM_DEVICE_STATISTICS_RX_BYTES, "", "",
+	                         0, UINT64_MAX, 0,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	/* Signals */
@@ -12030,4 +12887,8 @@ nm_device_class_init (NMDeviceClass *klass)
 	                                        "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 21804396..34d31cad 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -26,7 +26,6 @@
 
 #include "nm-exported-object.h"
 #include "nm-dbus-interface.h"
-#include "nm-default.h"
 #include "nm-connection.h"
 #include "nm-rfkill-manager.h"
 #include "NetworkManagerUtils.h"
@@ -58,6 +57,11 @@
 #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
 #define NM_DEVICE_MTU              "mtu"
 #define NM_DEVICE_HW_ADDRESS       "hw-address"
+
+/* "perm-hw-address" is exposed on D-Bus both for NMDeviceEthernet
+ * and NMDeviceWifi. */
+#define NM_DEVICE_PERM_HW_ADDRESS  "perm-hw-address"
+
 #define NM_DEVICE_METERED          "metered"
 #define NM_DEVICE_LLDP_NEIGHBORS  "lldp-neighbors"
 #define NM_DEVICE_REAL             "real"
@@ -84,7 +88,9 @@
 #define NM_DEVICE_STATE_CHANGED         "state-changed"
 #define NM_DEVICE_LINK_INITIALIZED      "link-initialized"
 
-G_BEGIN_DECLS
+#define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms"
+#define NM_DEVICE_STATISTICS_TX_BYTES        "tx-bytes"
+#define NM_DEVICE_STATISTICS_RX_BYTES        "rx-bytes"
 
 #define NM_TYPE_DEVICE            (nm_device_get_type ())
 #define NM_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
@@ -275,6 +281,8 @@ typedef struct {
 	                                             GAsyncResult *res,
 	                                             GError **error);
 
+	void            (* deactivate_reset_hw_addr) (NMDevice *self);
+
 	/* Sync deactivating (in the DISCONNECTED phase) */
 	void            (* deactivate) (NMDevice *self);
 
@@ -324,6 +332,8 @@ typedef struct {
 	gboolean        (* owns_iface) (NMDevice *self, const char *iface);
 
 	NMConnection *  (* new_default_connection) (NMDevice *self);
+
+	gboolean        (* unmanaged_on_quit) (NMDevice *self);
 } NMDeviceClass;
 
 typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
@@ -354,7 +364,8 @@ guint32         nm_device_get_ip4_route_metric  (NMDevice *dev);
 guint32         nm_device_get_ip6_route_metric  (NMDevice *dev);
 
 const char *    nm_device_get_hw_address        (NMDevice *dev);
-const char *    nm_device_get_permanent_hw_address (NMDevice *dev);
+const char *    nm_device_get_permanent_hw_address (NMDevice *dev,
+                                                    gboolean fallback_fake);
 const char *    nm_device_get_initial_hw_address (NMDevice *dev);
 
 NMDhcp4Config * nm_device_get_dhcp4_config      (NMDevice *dev);
@@ -412,7 +423,7 @@ gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnec
 
 gboolean nm_device_uses_assumed_connection (NMDevice *device);
 
-gboolean nm_device_can_assume_active_connection (NMDevice *device);
+gboolean nm_device_unmanage_on_quit (NMDevice *self);
 
 gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
 
@@ -440,7 +451,10 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device);
  *   (e.g. via a D-Bus command)
  * @NM_UNMANAGED_USER_SETTINGS: %TRUE when unmanaged by user decision via
  *   the settings plugin (for example keyfile.unmanaged-devices or ifcfg-rh's
- *   NM_CONTROLLED=no)
+ *   NM_CONTROLLED=no). Although this is user-configuration (provided from
+ *   the settings plugins, such as NM_CONTROLLED=no in ifcfg-rh), it cannot
+ *   be overruled and is authorative. That is because users may depend on
+ *   dropping a ifcfg-rh file to ensure the device is unmanaged.
  * @NM_UNMANAGED_BY_DEFAULT: %TRUE for certain device types where we unmanage
  *   them by default
  * @NM_UNMANAGED_USER_UDEV: %TRUE when unmanaged by user decision (via UDev rule)
@@ -493,7 +507,7 @@ void nm_device_set_unmanaged_by_flags_queue (NMDevice *self,
                                              NMUnmanagedFlags flags,
                                              NMUnmanFlagOp set_op,
                                              NMDeviceStateReason reason);
-void nm_device_set_unmanaged_by_user_config (NMDevice *self, const GSList *unmanaged_specs);
+void nm_device_set_unmanaged_by_user_settings (NMDevice *self, const GSList *unmanaged_specs);
 void nm_device_set_unmanaged_by_user_udev (NMDevice *self);
 void nm_device_set_unmanaged_by_quitting (NMDevice *device);
 
@@ -576,8 +590,7 @@ void nm_device_reactivate_ip6_config (NMDevice *device,
 
 void nm_device_update_hw_address (NMDevice *self);
 void nm_device_update_initial_hw_address (NMDevice *self);
+void nm_device_update_permanent_hw_address (NMDevice *self);
 void nm_device_update_dynamic_ip_setup (NMDevice *self);
 
-G_END_DECLS
-
-#endif /* NM_DEVICE_H */
+#endif /* __NETWORKMANAGER_DEVICE_H__ */
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index 9cc1d086..f4663405 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -725,13 +725,20 @@ nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error)
 		return FALSE;
 	}
 
-	ret = sd_lldp_new (&priv->lldp_handle, ifindex);
+	ret = sd_lldp_new (&priv->lldp_handle);
 	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		                     "initialization failed");
 		return FALSE;
 	}
 
+	ret = sd_lldp_set_ifindex (priv->lldp_handle, ifindex);
+	if (ret < 0) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		                     "failed setting ifindex");
+		goto err;
+	}
+
 	ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
 	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h
index df02fd63..870ca8a5 100644
--- a/src/devices/nm-lldp-listener.h
+++ b/src/devices/nm-lldp-listener.h
@@ -21,8 +21,6 @@
 #ifndef __NM_LLDP_LISTENER__
 #define __NM_LLDP_LISTENER__
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_LLDP_LISTENER            (nm_lldp_listener_get_type ())
 #define NM_LLDP_LISTENER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LLDP_LISTENER, NMLldpListener))
 #define NM_LLDP_LISTENER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_LLDP_LISTENER, NMLldpListenerClass))
@@ -48,6 +46,4 @@ gboolean nm_lldp_listener_is_running (NMLldpListener *self);
 
 GVariant *nm_lldp_listener_get_neighbors (NMLldpListener *self);
 
-G_END_DECLS
-
 #endif /* __NM_LLDP_LISTENER__ */
diff --git a/src/devices/team/Makefile.am b/src/devices/team/Makefile.am
index 3609ace4..db756b4c 100644
--- a/src/devices/team/Makefile.am
+++ b/src/devices/team/Makefile.am
@@ -49,6 +49,7 @@ if ENABLE_TESTS
 
 check-local:
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-team.so $(SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-team.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 endif
 
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
index 4ca3014f..922ec593 100644
--- a/src/devices/team/Makefile.in
+++ b/src/devices/team/Makefile.in
@@ -92,8 +92,9 @@ host_triplet = @host@
 @WITH_TEAMDCTL_TRUE@am__append_2 = $(LIBTEAMDCTL_LIBS)
 subdir = src/devices/team
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -256,6 +257,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -312,13 +314,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -330,6 +333,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -390,6 +395,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -425,6 +432,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -835,6 +843,7 @@ include $(GLIB_MAKEFILE)
 
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-team.so $(SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-team.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index b4b522d8..09eefe70 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -45,12 +45,18 @@ G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate))
 
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceTeam,
+	PROP_CONFIG,
+);
+
 typedef struct {
 	struct teamdctl *tdc;
 	GPid teamd_pid;
 	guint teamd_process_watch;
 	guint teamd_timeout;
+	guint teamd_read_timeout;
 	guint teamd_dbus_watch;
+	char *config;
 } NMDeviceTeamPrivate;
 
 static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
@@ -147,39 +153,74 @@ ensure_teamd_connection (NMDevice *device)
 	return !!priv->tdc;
 }
 
+static gboolean
+teamd_read_config (NMDevice *device)
+{
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	char *config = NULL;
+	int err;
+
+	if (priv->tdc) {
+		err = teamdctl_config_actual_get_raw_direct (priv->tdc, &config);
+		if (err)
+			return FALSE;
+	}
+
+	if (!nm_streq0 (config, priv->config)) {
+		g_free (priv->config);
+		priv->config = g_strdup (config);
+		_notify (self, PROP_CONFIG);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+teamd_read_timeout_cb (gpointer user_data)
+{
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (user_data);
+
+	teamd_read_config ((NMDevice *) user_data);
+	priv->teamd_read_timeout = 0;
+
+	return G_SOURCE_REMOVE;
+}
+
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMSettingTeam *s_team = nm_connection_get_setting_team (connection);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+	struct teamdctl *tdc = priv->tdc;
 
 	if (!s_team) {
 		s_team = (NMSettingTeam *) nm_setting_team_new ();
 		nm_connection_add_setting (connection, (NMSetting *) s_team);
 	}
-	g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, NULL, NULL);
 
-	if (priv->tdc) {
-		const char *config = NULL;
-		int err;
+	/* Read the configuration only if not already set */
+	if (!priv->config && ensure_teamd_connection (device))
+		teamd_read_config (device);
 
-		err = teamdctl_config_get_raw_direct (NM_DEVICE_TEAM_GET_PRIVATE (device)->tdc,
-		                                      (char **)&config);
-		if (err == 0)
-			g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, config, NULL);
-		else
-			_LOGE (LOGD_TEAM, "failed to read teamd config (err=%d)", err);
+	/* Restore previous tdc state */
+	if (priv->tdc && !tdc) {
+		teamdctl_disconnect (priv->tdc);
+		teamdctl_free (priv->tdc);
+		priv->tdc = NULL;
 	}
+
+	g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, priv->config, NULL);
 }
 
 /******************************************************************/
 
 static gboolean
 master_update_slave_connection (NMDevice *self,
-                                   NMDevice *slave,
-                                   NMConnection *connection,
-                                   GError **error)
+                                NMDevice *slave,
+                                NMConnection *connection,
+                                GError **error)
 {
 	NMSettingTeamPort *s_port;
 	char *port_config = NULL;
@@ -249,6 +290,7 @@ teamd_cleanup (NMDevice *device, gboolean free_tdc)
 
 	nm_clear_g_source (&priv->teamd_process_watch);
 	nm_clear_g_source (&priv->teamd_timeout);
+	nm_clear_g_source (&priv->teamd_read_timeout);
 
 	if (priv->teamd_pid > 0) {
 		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL);
@@ -279,6 +321,14 @@ teamd_timeout_cb (gpointer user_data)
 
 		g_warn_if_fail (nm_device_is_activating (device));
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	} else {
+		/* Read again the configuration after the timeout since it might
+		 * have changed.
+		 */
+		if (!teamd_read_config (device)) {
+			_LOGW (LOGD_TEAM, "failed to read teamd configuration");
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+		}
 	}
 
 	return G_SOURCE_REMOVE;
@@ -339,6 +389,8 @@ teamd_dbus_appeared (GDBusConnection *connection,
 	success = ensure_teamd_connection (device);
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) {
 		if (success)
+			success = teamd_read_config (device);
+		if (success)
 			nm_device_activate_schedule_stage2_device_config (device);
 		else if (!nm_device_uses_assumed_connection (device))
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
@@ -523,7 +575,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 		 * have a PID, then we must fail.
 		 */
 		cfg = teamdctl_config_get_raw (priv->tdc);
-		if (cfg && strcmp (cfg,  nm_setting_team_get_config (s_team)) == 0) {
+		if (cfg && nm_streq0 (cfg,  nm_setting_team_get_config (s_team))) {
 			_LOGD (LOGD_TEAM, "using existing matching teamd config");
 			return NM_ACT_STAGE_RETURN_SUCCESS;
 		}
@@ -626,6 +678,11 @@ enslave_slave (NMDevice *device,
 		if (!success)
 			return FALSE;
 
+		nm_clear_g_source (&priv->teamd_read_timeout);
+		priv->teamd_read_timeout = g_timeout_add_seconds (5,
+		                                                  teamd_read_timeout_cb,
+		                                                  self);
+
 		_LOGI (LOGD_TEAM, "enslaved team port %s", slave_iface);
 	} else
 		_LOGI (LOGD_TEAM, "team port %s was enslaved", slave_iface);
@@ -639,6 +696,7 @@ release_slave (NMDevice *device,
                gboolean configure)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 	gboolean success, no_firmware = FALSE;
 
 	if (configure) {
@@ -658,6 +716,11 @@ release_slave (NMDevice *device,
 		if (!nm_device_bring_up (slave, TRUE, &no_firmware))
 			_LOGW (LOGD_TEAM, "released team port %s could not be brought up",
 			       nm_device_get_ip_iface (slave));
+
+		nm_clear_g_source (&priv->teamd_read_timeout);
+		priv->teamd_read_timeout = g_timeout_add_seconds (5,
+		                                                  teamd_read_timeout_cb,
+		                                                  self);
 	} else
 		_LOGI (LOGD_TEAM, "team port %s was released", nm_device_get_ip_iface (slave));
 }
@@ -701,6 +764,23 @@ nm_device_team_new (const char *iface)
 }
 
 static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceTeam *self = NM_DEVICE_TEAM (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_CONFIG:
+		g_value_set_string (value, priv->config);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
 nm_device_team_init (NMDeviceTeam * self)
 {
 }
@@ -738,6 +818,7 @@ dispose (GObject *object)
 	}
 
 	teamd_cleanup (device, TRUE);
+	g_clear_pointer (&priv->config, g_free);
 
 	G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
 }
@@ -754,6 +835,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
+	object_class->get_property = get_property;
 
 	parent_class->create_and_realize = create_and_realize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
@@ -770,6 +852,14 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
 
+	obj_properties[PROP_CONFIG] =
+	    g_param_spec_string (NM_DEVICE_TEAM_CONFIG, "", "",
+	                         NULL,
+	                         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_TEAM_SKELETON,
 	                                        NULL);
diff --git a/src/devices/team/nm-device-team.h b/src/devices/team/nm-device-team.h
index 0e81afa7..43e8aab4 100644
--- a/src/devices/team/nm-device-team.h
+++ b/src/devices/team/nm-device-team.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_TEAM            (nm_device_team_get_type ())
 #define NM_DEVICE_TEAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam))
 #define NM_DEVICE_TEAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass))
@@ -32,6 +30,9 @@ G_BEGIN_DECLS
 #define NM_IS_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_TEAM))
 #define NM_DEVICE_TEAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass))
 
+/* Properties */
+#define NM_DEVICE_TEAM_CONFIG          "config"
+
 typedef NMDevice NMDeviceTeam;
 typedef NMDeviceClass NMDeviceTeamClass;
 
@@ -39,6 +40,4 @@ GType nm_device_team_get_type (void);
 
 NMDevice *nm_device_team_new (const char *iface);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_TEAM_H */
+#endif /* __NETWORKMANAGER_DEVICE_TEAM_H__ */
diff --git a/src/devices/team/nm-team-factory.h b/src/devices/team/nm-team-factory.h
index 9049478d..4155870c 100644
--- a/src/devices/team/nm-team-factory.h
+++ b/src/devices/team/nm-team-factory.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_TEAM_FACTORY_H__
 #define __NETWORKMANAGER_TEAM_FACTORY_H__
 
-#include "nm-default.h"
-
 #define NM_TYPE_TEAM_FACTORY (nm_team_factory_get_type ())
 #define NM_TEAM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TEAM_FACTORY, NMTeamFactory))
 
diff --git a/src/devices/tests/Makefile.in b/src/devices/tests/Makefile.in
index b07e6dfd..7c25dc11 100644
--- a/src/devices/tests/Makefile.in
+++ b/src/devices/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-lldp$(EXEEXT) test-arping$(EXEEXT)
 TESTS = test-lldp$(EXEEXT) test-arping$(EXEEXT)
 subdir = src/devices/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -428,6 +429,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -484,13 +486,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -502,6 +505,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -562,6 +567,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -597,6 +604,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index bff85e6c..ff6f42a9 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -33,7 +33,7 @@
 
 #include "test-common.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /*****************************************************************************/
 
diff --git a/src/devices/wifi/Makefile.am b/src/devices/wifi/Makefile.am
index e35a84b4..add0a007 100644
--- a/src/devices/wifi/Makefile.am
+++ b/src/devices/wifi/Makefile.am
@@ -52,6 +52,7 @@ if ENABLE_TESTS
 
 check-local:
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wifi.so $(SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-wifi.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 endif
 
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
index 34aa4b97..134c4012 100644
--- a/src/devices/wifi/Makefile.in
+++ b/src/devices/wifi/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/devices/wifi
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -295,6 +296,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -351,13 +353,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -369,6 +372,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -429,6 +434,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -464,6 +471,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -967,6 +975,7 @@ include $(GLIB_MAKEFILE)
 
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wifi.so $(SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-wifi.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h
index 63895a88..135226d5 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.h
+++ b/src/devices/wifi/nm-device-olpc-mesh.h
@@ -30,8 +30,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_DEVICE_OLPC_MESH            (nm_device_olpc_mesh_get_type ())
 #define NM_DEVICE_OLPC_MESH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh))
 #define NM_DEVICE_OLPC_MESH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
@@ -67,6 +65,4 @@ GType nm_device_olpc_mesh_get_type (void);
 
 NMDevice *nm_device_olpc_mesh_new (const char *iface);
 
-G_END_DECLS
-
-#endif  /* NM_DEVICE_OLPC_MESH_H */
+#endif /* __NETWORKMANAGER_DEVICE_OLPC_MESH_H__ */
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 82b12a11..49c380af 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -21,6 +21,8 @@
 
 #include "nm-default.h"
 
+#include "nm-device-wifi.h"
+
 #include <netinet/in.h>
 #include <string.h>
 #include <unistd.h>
@@ -28,7 +30,6 @@
 
 #include "nm-common-macros.h"
 #include "nm-device.h"
-#include "nm-device-wifi.h"
 #include "nm-device-private.h"
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
@@ -46,8 +47,8 @@
 #include "nm-platform.h"
 #include "nm-auth-utils.h"
 #include "nm-settings-connection.h"
+#include "nm-settings.h"
 #include "nm-enum-types.h"
-#include "nm-connection-provider.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
 
@@ -61,25 +62,20 @@ _LOG_DECLARE_SELF(NMDeviceWifi);
 #define SCAN_INTERVAL_STEP 20
 #define SCAN_INTERVAL_MAX 120
 
-#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
+#define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5
 
-G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
-
-#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
+#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
 
+/*****************************************************************************/
 
-enum {
-	PROP_0,
-	PROP_PERM_HW_ADDRESS,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi,
 	PROP_MODE,
 	PROP_BITRATE,
 	PROP_ACCESS_POINTS,
 	PROP_ACTIVE_ACCESS_POINT,
 	PROP_CAPABILITIES,
 	PROP_SCANNING,
-
-	LAST_PROP
-};
+);
 
 enum {
 	ACCESS_POINT_ADDED,
@@ -91,26 +87,26 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-struct _NMDeviceWifiPrivate {
+typedef struct {
 	gint8             invalid_strength_counter;
 
 	GHashTable *      aps;
 	NMAccessPoint *   current_ap;
 	guint32           rate;
-	gboolean          enabled; /* rfkilled or not */
+	bool              enabled:1; /* rfkilled or not */
+	bool              requested_scan:1;
+	bool              ssid_found:1;
 
 	gint32            last_scan;
 	gint32            scheduled_scan_time;
 	guint8            scan_interval; /* seconds */
 	guint             pending_scan_id;
 	guint             ap_dump_id;
-	bool              requested_scan;
 
 	NMSupplicantManager   *sup_mgr;
 	NMSupplicantInterface *sup_iface;
 	guint                  sup_timeout_id; /* supplicant association timeout */
 
-	bool              ssid_found;
 	NM80211Mode       mode;
 
 	guint             periodic_source_id;
@@ -119,8 +115,44 @@ struct _NMDeviceWifiPrivate {
 	guint             reacquire_iface_id;
 
 	NMDeviceWifiCapabilities capabilities;
+
+	gint32 hw_addr_scan_expire;
+	char *hw_addr_scan;
+} NMDeviceWifiPrivate;
+
+struct _NMDeviceWifi
+{
+	NMDevice parent;
+	NMDeviceWifiPrivate _priv;
 };
 
+struct _NMDeviceWifiClass
+{
+	NMDeviceClass parent;
+
+	/* Signals */
+	gboolean (*scanning_allowed) (NMDeviceWifi *device);
+};
+
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_WIFI_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMDeviceWifi *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_DEVICE_WIFI (_self)); \
+		&_self->_priv; \
+	})
+
+/*****************************************************************************/
+
 static gboolean check_scanning_allowed (NMDeviceWifi *self);
 
 static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
@@ -169,7 +201,9 @@ static void ap_add_remove (NMDeviceWifi *self,
 
 static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
 
-/*****************************************************************/
+static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset);
+
+/*****************************************************************************/
 
 static void
 constructed (GObject *object)
@@ -187,6 +221,18 @@ constructed (GObject *object)
 }
 
 static gboolean
+unmanaged_on_quit (NMDevice *self)
+{
+	/* Wi-Fi devices cannot be assumed and are always taken down.
+	 * However, also when being disconnected, we scan and thus
+	 * set the MAC address to a random value.
+	 *
+	 * We must restore the original MAC address when quitting, thus
+	 * signal to unmanage the device. */
+	return TRUE;
+}
+
+static gboolean
 supplicant_interface_acquire (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -238,6 +284,24 @@ supplicant_interface_acquire (NMDeviceWifi *self)
 }
 
 static void
+_requested_scan_set (NMDeviceWifi *self, gboolean value)
+{
+	NMDeviceWifiPrivate *priv;
+
+	value = !!value;
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	if (priv->requested_scan == value)
+		return;
+
+	priv->requested_scan = value;
+	if (value)
+		nm_device_add_pending_action ((NMDevice *) self, "scan", TRUE);
+	else
+		nm_device_remove_pending_action ((NMDevice *) self, "scan", TRUE);
+}
+
+static void
 supplicant_interface_release (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv;
@@ -246,6 +310,8 @@ supplicant_interface_release (NMDeviceWifi *self)
 
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
+	_requested_scan_set (self, FALSE);
+
 	nm_clear_g_source (&priv->pending_scan_id);
 
 	/* Reset the scan interval to be pretty frequent when disconnected */
@@ -340,7 +406,7 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
 		g_object_unref (old_ap);
 	}
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
+	_notify (self, PROP_ACTIVE_ACCESS_POINT);
 }
 
 static void
@@ -386,7 +452,7 @@ periodic_update (NMDeviceWifi *self)
 	new_rate = nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex);
 	if (new_rate != priv->rate) {
 		priv->rate = new_rate;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
+		_notify (self, PROP_BITRATE);
 	}
 }
 
@@ -397,18 +463,10 @@ periodic_update_cb (gpointer user_data)
 	return TRUE;
 }
 
-static void
-realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
-{
-	NM_DEVICE_CLASS (nm_device_wifi_parent_class)->realize_start_notify (device, plink);
-
-	g_object_notify (G_OBJECT (device), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
-}
-
 static gboolean
 bring_up (NMDevice *device, gboolean *no_firmware)
 {
-	if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled)
+	if (!NM_DEVICE_WIFI_GET_PRIVATE ((NMDeviceWifi *) device)->enabled)
 		return FALSE;
 
 	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->bring_up (device, no_firmware);
@@ -431,7 +489,7 @@ ap_add_remove (NMDeviceWifi *self,
 	}
 
 	g_signal_emit (self, signals[signum], 0, ap);
-	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACCESS_POINTS);
+	_notify (self, PROP_ACCESS_POINTS);
 
 	if (signum == ACCESS_POINT_REMOVED) {
 		g_hash_table_remove (priv->aps, nm_exported_object_get_path ((NMExportedObject *) ap));
@@ -485,10 +543,6 @@ deactivate (NMDevice *device)
 	/* Clear any critical protocol notification in the Wi-Fi stack */
 	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
 
-	/* Reset MAC address back to initial address */
-	if (nm_device_get_initial_hw_address (device))
-		nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_WIFI);
-
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
 	 */
@@ -500,7 +554,7 @@ deactivate (NMDevice *device)
 
 	if (priv->mode != NM_802_11_MODE_INFRA) {
 		priv->mode = NM_802_11_MODE_INFRA;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE);
+		_notify (self, PROP_MODE);
 	}
 
 	/* Ensure we trigger a scan after deactivating a Hotspot */
@@ -510,6 +564,12 @@ deactivate (NMDevice *device)
 	}
 }
 
+static void
+deactivate_reset_hw_addr (NMDevice *device)
+{
+	_hw_addr_set_scanning ((NMDeviceWifi *) device, TRUE);
+}
+
 static gboolean
 is_adhoc_wpa (NMConnection *connection)
 {
@@ -566,7 +626,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	if (!s_wireless)
 		return FALSE;
 
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 	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))
@@ -679,35 +739,31 @@ check_connection_available (NMDevice *device,
 	return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE);
 }
 
-/*
- * 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",
-};
-
-#define ARRAY_SIZE(a)  (sizeof (a) / sizeof (a[0]))
-
 static gboolean
 is_manf_default_ssid (const GByteArray *ssid)
 {
 	int i;
-
-	for (i = 0; i < ARRAY_SIZE (manf_defaults); 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;
@@ -862,7 +918,7 @@ complete_connection (NMDevice *device,
 	if (hidden)
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
 
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
+	perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 	if (perm_hw_addr) {
 		setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
 		if (setting_mac) {
@@ -1022,6 +1078,60 @@ impl_device_wifi_get_all_access_points (NMDeviceWifi *self,
 }
 
 static void
+_hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
+{
+	NMDevice *device = (NMDevice *) self;
+	NMDeviceWifiPrivate *priv;
+	guint32 now;
+	gboolean randomize;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	if (   nm_device_is_activating (device)
+	    || nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+		return;
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	randomize = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA,
+	                                                      "wifi.scan-rand-mac-address",
+	                                                      device,
+	                                                      TRUE, TRUE);
+
+	if (!randomize) {
+		g_clear_pointer (&priv->hw_addr_scan, g_free);
+		if (do_reset)
+			nm_device_hw_addr_reset (device, "scanning");
+		return;
+	}
+
+	now = nm_utils_get_monotonic_timestamp_s ();
+
+	if (   !priv->hw_addr_scan
+	    || now >= priv->hw_addr_scan_expire) {
+		gs_free char *generate_mac_address_mask = NULL;
+
+		/* the random MAC address for scanning expires after a while.
+		 *
+		 * We don't bother with to update the MAC address exactly when
+		 * it expires, instead on the next scan request, we will generate
+		 * a new one.*/
+		priv->hw_addr_scan_expire = now + (SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN * 60);
+
+		generate_mac_address_mask = nm_config_data_get_device_config (NM_CONFIG_GET_DATA,
+		                                                              "wifi.scan-generate-mac-address-mask",
+		                                                              device,
+		                                                              NULL);
+
+		g_free (priv->hw_addr_scan);
+		priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device),
+		                                                      generate_mac_address_mask);
+	}
+
+	nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning");
+}
+
+static void
 request_scan_cb (NMDevice *device,
                  GDBusMethodInvocation *context,
                  NMAuthSubject *subject,
@@ -1199,7 +1309,7 @@ check_scanning_allowed (NMDeviceWifi *self)
 }
 
 static gboolean
-hidden_filter_func (NMConnectionProvider *provider,
+hidden_filter_func (NMSettings *settings,
                     NMConnection *connection,
                     gpointer user_data)
 {
@@ -1226,12 +1336,12 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	if (G_UNLIKELY (nullssid == NULL))
 		nullssid = g_byte_array_new ();
 
-	connections = nm_connection_provider_get_best_connections (nm_connection_provider_get (),
-	                                                           max_scan_ssids - 1,
-	                                                           NM_SETTING_WIRELESS_SETTING_NAME,
-	                                                           NULL,
-	                                                           hidden_filter_func,
-	                                                           NULL);
+	connections = nm_settings_get_best_connections (nm_device_get_settings ((NMDevice *) self),
+	                                                max_scan_ssids - 1,
+	                                                NM_SETTING_WIRELESS_SETTING_NAME,
+	                                                NULL,
+	                                                hidden_filter_func,
+	                                                NULL);
 	if (connections && connections->data) {
 		ssids = g_ptr_array_new_full (max_scan_ssids - 1, (GDestroyNotify) g_byte_array_unref);
 		g_ptr_array_add (ssids, g_byte_array_ref (nullssid));  /* Add wildcard SSID */
@@ -1330,11 +1440,12 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options)
 				_LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan");
 		}
 
+		_hw_addr_set_scanning (self, FALSE);
+
 		if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
 			/* success */
 			backoff = TRUE;
-			priv->requested_scan = TRUE;
-			nm_device_add_pending_action (NM_DEVICE (self), "scan", TRUE);
+			_requested_scan_set (self, TRUE);
 		}
 
 		if (ssids)
@@ -1349,10 +1460,11 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options)
 static gboolean
 request_wireless_scan_periodic (gpointer user_data)
 {
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (user_data);
+	NMDeviceWifi *self = user_data;
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	priv->pending_scan_id = 0;
-	request_wireless_scan (user_data, NULL);
+	request_wireless_scan (self, NULL);
 	return FALSE;
 }
 
@@ -1416,10 +1528,7 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
 	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
 	schedule_scan (self, success);
 
-	if (priv->requested_scan) {
-		priv->requested_scan = FALSE;
-		nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE);
-	}
+	_requested_scan_set (self, FALSE);
 }
 
 /****************************************************************************
@@ -1458,10 +1567,12 @@ schedule_ap_list_dump (NMDeviceWifi *self)
 }
 
 static void
-try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
+try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
+                             NMAccessPoint *ap)
 {
 	const char *bssid;
-	const GSList *connections, *iter;
+	NMSettingsConnection *const*connections;
+	guint i;
 
 	g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
 
@@ -1470,9 +1581,9 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
 
 	/* Look for this AP's BSSID in the seen-bssids list of a connection,
 	 * and if a match is found, copy over the SSID */
-	connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *connection = NM_CONNECTION (iter->data);
+	connections = nm_settings_get_connections (nm_device_get_settings ((NMDevice *) self), NULL);
+	for (i = 0; connections[i]; i++) {
+		NMConnection *connection = (NMConnection *) connections[i];
 		NMSettingWireless *s_wifi;
 
 		s_wifi = nm_connection_get_setting_wireless (connection);
@@ -1522,7 +1633,7 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
 	ssid = nm_ap_get_ssid (ap);
 	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
 		/* Try to fill the SSID from the AP database */
-		try_fill_ssid_for_hidden_ap (ap);
+		try_fill_ssid_for_hidden_ap (self, ap);
 
 		ssid = nm_ap_get_ssid (ap);
 		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
@@ -1951,6 +2062,11 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 		else
 			_LOGI (LOGD_DEVICE | LOGD_WIFI, "supplicant interface keeps failing, giving up");
 		break;
+	case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
+		_requested_scan_set (self, FALSE);
+		nm_clear_g_source (&priv->pending_scan_id);
+		request_wireless_scan (self, NULL);
+		break;
 	default:
 		break;
 	}
@@ -1964,7 +2080,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	/* Signal scanning state changes */
 	if (   new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
 	    || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
-		g_object_notify (G_OBJECT (self), "scanning");
+		_notify (self, PROP_SCANNING);
 }
 
 static void
@@ -2008,7 +2124,7 @@ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
 	scanning = nm_supplicant_interface_get_scanning (iface);
 	_LOGD (LOGD_WIFI_SCAN, "now %s", scanning ? "scanning" : "idle");
 
-	g_object_notify (G_OBJECT (self), "scanning");
+	_notify (self, PROP_SCANNING);
 
 	/* Run a quick update of current AP when coming out of a scan */
 	state = nm_device_get_state (NM_DEVICE (self));
@@ -2197,9 +2313,6 @@ build_supplicant_config (NMDeviceWifi *self,
 	NMSupplicantConfig *config = NULL;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
-	NMSupplicantFeature mac_randomization_support;
-	NMSettingMacRandomization mac_randomization_fallback;
-	gs_free char *svalue = NULL;
 
 	g_return_val_if_fail (priv->sup_iface, NULL);
 
@@ -2214,20 +2327,9 @@ build_supplicant_config (NMDeviceWifi *self,
 		_LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
 	}
 
-	mac_randomization_support = nm_supplicant_interface_get_mac_randomization_support (priv->sup_iface);
-	svalue = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                                "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION,
-	                                                NM_DEVICE (self));
-	mac_randomization_fallback = _nm_utils_ascii_str_to_int64 (svalue, 10,
-	                                                           NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
-	                                                           NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
-	                                                           NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
-
 	if (!nm_supplicant_config_add_setting_wireless (config,
 	                                                s_wireless,
 	                                                fixed_freq,
-	                                                mac_randomization_support,
-	                                                mac_randomization_fallback,
 	                                                error)) {
 		g_prefix_error (error, "802-11-wireless: ");
 		goto error;
@@ -2277,7 +2379,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingWireless *s_wireless;
-	const char *cloned_mac;
 	const char *mode;
 	const char *ap_path;
 
@@ -2305,7 +2406,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 		/* Scanning not done in AP mode; clear the scan list */
 		remove_all_aps (self);
 	}
-	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE);
+	_notify (self, PROP_MODE);
 
 	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
 	 * and turns them into open networks.  It's been this way since at least
@@ -2317,9 +2418,12 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
+	/* forget the temporary MAC address used during scanning */
+	g_clear_pointer (&priv->hw_addr_scan, g_free);
+
 	/* Set spoof MAC to the interface */
-	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
-	nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_WIFI);
+	if (!nm_device_hw_addr_set_cloned (device, connection, TRUE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
 
 	/* AP mode never uses a specific object or existing scanned AP */
 	if (priv->mode != NM_802_11_MODE_AP) {
@@ -2859,6 +2963,8 @@ set_enabled (NMDevice *device, gboolean enabled)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDeviceState state;
 
+	enabled = !!enabled;
+
 	if (priv->enabled == enabled)
 		return;
 
@@ -2959,6 +3065,8 @@ finalize (GObject *object)
 
 	g_hash_table_unref (priv->aps);
 
+	g_free (priv->hw_addr_scan);
+
 	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
 }
 
@@ -2973,9 +3081,6 @@ get_property (GObject *object, guint prop_id,
 	GPtrArray *array;
 
 	switch (prop_id) {
-	case PROP_PERM_HW_ADDRESS:
-		g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device)));
-		break;
 	case PROP_MODE:
 		g_value_set_uint (value, priv->mode);
 		break;
@@ -3030,8 +3135,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
 
-	g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate));
-
 	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
 
 	object_class->constructed = constructed;
@@ -3040,7 +3143,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
-	parent_class->realize_start_notify = realize_start_notify;
 	parent_class->bring_up = bring_up;
 	parent_class->can_auto_connect = can_auto_connect;
 	parent_class->is_available = is_available;
@@ -3057,90 +3159,81 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
 	parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
 	parent_class->deactivate = deactivate;
+	parent_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
+	parent_class->unmanaged_on_quit = unmanaged_on_quit;
 
 	parent_class->state_changed = device_state_changed;
 
 	klass->scanning_allowed = scanning_allowed;
 
 	/* Properties */
-	g_object_class_install_property
-		(object_class, PROP_PERM_HW_ADDRESS,
-		 g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_MODE,
-		 g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "",
-		                    NM_802_11_MODE_UNKNOWN,
-		                    NM_802_11_MODE_AP,
-		                    NM_802_11_MODE_INFRA,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_BITRATE,
-		 g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_ACCESS_POINTS,
-		 g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_ACTIVE_ACCESS_POINT,
-		 g_param_spec_string (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_CAPABILITIES,
-		 g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, "", "",
-		                    0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT_ONLY |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_SCANNING,
-		 g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, "", "",
-		                       FALSE,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MODE] =
+	    g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "",
+	                       NM_802_11_MODE_UNKNOWN,
+	                       NM_802_11_MODE_AP,
+	                       NM_802_11_MODE_INFRA,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_BITRATE] =
+	    g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_ACCESS_POINTS] =
+	    g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_ACTIVE_ACCESS_POINT] =
+	    g_param_spec_string (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_CAPABILITIES] =
+	    g_param_spec_uint (NM_DEVICE_WIFI_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_WIFI_SCANNING, "", "",
+	                          FALSE,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	/* Signals */
 	signals[ACCESS_POINT_ADDED] =
-		g_signal_new ("access-point-added",
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added),
-		              NULL, NULL, NULL,
-		              G_TYPE_NONE, 1,
-		              NM_TYPE_AP);
+	    g_signal_new ("access-point-added",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1,
+	                  NM_TYPE_AP);
 
 	signals[ACCESS_POINT_REMOVED] =
-		g_signal_new ("access-point-removed",
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0,
-		              NULL, NULL, NULL,
-		              G_TYPE_NONE, 1,
-		              NM_TYPE_AP);
+	    g_signal_new ("access-point-removed",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1,
+	                  NM_TYPE_AP);
 
 	signals[SCANNING_ALLOWED] =
-		g_signal_new ("scanning-allowed",
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_LAST,
-		              G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed),
-		              scanning_allowed_accumulator, NULL, NULL,
-		              G_TYPE_BOOLEAN, 0);
+	    g_signal_new ("scanning-allowed",
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_LAST,
+	                  G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed),
+	                  scanning_allowed_accumulator, NULL, NULL,
+	                  G_TYPE_BOOLEAN, 0);
 
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
 	                                        NMDBUS_TYPE_DEVICE_WIFI_SKELETON,
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
index 7e0d06f5..ba9d60e2 100644
--- a/src/devices/wifi/nm-device-wifi.h
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2016 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -25,18 +25,13 @@
 #include "nm-device.h"
 #include "nm-wifi-ap.h"
 
-struct NMAccessPointList;
+#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))
+#define NM_DEVICE_WIFI_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+#define NM_IS_DEVICE_WIFI(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
+#define NM_IS_DEVICE_WIFI_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIFI))
+#define NM_DEVICE_WIFI_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
 
-G_BEGIN_DECLS
-
-#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))
-#define NM_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
-#define NM_IS_DEVICE_WIFI(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
-#define NM_IS_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIFI))
-#define NM_DEVICE_WIFI_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
-
-#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address"
 #define NM_DEVICE_WIFI_MODE                "mode"
 #define NM_DEVICE_WIFI_BITRATE             "bitrate"
 #define NM_DEVICE_WIFI_ACCESS_POINTS       "access-points"
@@ -44,37 +39,11 @@ G_BEGIN_DECLS
 #define NM_DEVICE_WIFI_CAPABILITIES        "wireless-capabilities"
 #define NM_DEVICE_WIFI_SCANNING            "scanning"
 
-#ifndef NM_DEVICE_WIFI_DEFINED
-#define NM_DEVICE_WIFI_DEFINED
 typedef struct _NMDeviceWifi NMDeviceWifi;
-#endif
-
 typedef struct _NMDeviceWifiClass NMDeviceWifiClass;
-typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate;
-
-struct _NMDeviceWifi
-{
-	NMDevice parent;
-
-	/*< private >*/
-	NMDeviceWifiPrivate *priv;
-};
-
-struct _NMDeviceWifiClass
-{
-	NMDeviceClass parent;
-
-	/* Signals */
-	void (*access_point_added)   (NMDeviceWifi *device, NMAccessPoint *ap);
-	void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap);
-	gboolean (*scanning_allowed) (NMDeviceWifi *device);
-};
-
 
 GType nm_device_wifi_get_type (void);
 
 NMDevice * nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_WIFI_H */
+#endif /* __NETWORKMANAGER_DEVICE_WIFI_H__ */
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index a0c25709..e1beb85b 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -42,29 +42,48 @@ typedef struct
 	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
 
 	/* Scanned or cached values */
-	GByteArray *	ssid;
-	char *          address;
-	NM80211Mode		mode;
-	guint8			strength;
-	guint32			freq;		/* Frequency in MHz; ie 2412 (== 2.412 GHz) */
-	guint32			max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
+	GByteArray *       ssid;
+	char *             address;
+	NM80211Mode        mode;
+	guint8             strength;
+	guint32            freq;        /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
+	guint32            max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
 
 	NM80211ApFlags         flags;      /* General flags */
 	NM80211ApSecurityFlags wpa_flags;  /* WPA-related flags */
 	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
 
 	/* Non-scanned attributes */
-	gboolean			fake;	/* Whether or not the AP is from a scan */
-	gboolean            hotspot;    /* Whether the AP is a local device's hotspot network */
+	bool                fake;       /* Whether or not the AP is from a scan */
+	bool                hotspot;    /* 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()) */
 } NMAccessPointPrivate;
 
-#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate))
+struct _NMAccessPoint {
+	NMExportedObject parent;
+	NMAccessPointPrivate _priv;
+};
+
+struct _NMAccessPointClass{
+	NMExportedObjectClass parent;
+};
+
+#define NM_AP_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMAccessPoint *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_AP (_self)); \
+		&_self->_priv; \
+	})
 
 G_DEFINE_TYPE (NMAccessPoint, nm_ap, NM_TYPE_EXPORTED_OBJECT)
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMAccessPoint,
 	PROP_FLAGS,
 	PROP_WPA_FLAGS,
 	PROP_RSN_FLAGS,
@@ -75,8 +94,7 @@ enum {
 	PROP_MAX_BITRATE,
 	PROP_STRENGTH,
 	PROP_LAST_SEEN,
-	LAST_PROP
-};
+);
 
 /*****************************************************************/
 
@@ -130,7 +148,7 @@ nm_ap_set_ssid (NMAccessPoint *ap, const guint8 *ssid, gsize len)
 		g_byte_array_append (priv->ssid, ssid, len);
 	}
 
-	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
+	_notify (ap, PROP_SSID);
 }
 
 static void
@@ -144,7 +162,7 @@ nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags)
 
 	if (priv->flags != flags) {
 		priv->flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_FLAGS);
+		_notify (ap, PROP_FLAGS);
 	}
 }
 
@@ -158,7 +176,7 @@ nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
 	priv = NM_AP_GET_PRIVATE (ap);
 	if (priv->wpa_flags != flags) {
 		priv->wpa_flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS);
+		_notify (ap, PROP_WPA_FLAGS);
 	}
 }
 
@@ -172,7 +190,7 @@ nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
 	priv = NM_AP_GET_PRIVATE (ap);
 	if (priv->rsn_flags != flags) {
 		priv->rsn_flags = flags;
-		g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS);
+		_notify (ap, PROP_RSN_FLAGS);
 	}
 }
 
@@ -198,7 +216,7 @@ nm_ap_set_address (NMAccessPoint *ap, const char *addr)
 	if (!priv->address || !nm_utils_hwaddr_matches (addr, -1, priv->address, -1)) {
 		g_free (priv->address);
 		priv->address = g_strdup (addr);
-		g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS);
+		_notify (ap, PROP_HW_ADDRESS);
 	}
 }
 
@@ -223,7 +241,7 @@ nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode)
 
 	if (priv->mode != mode) {
 		priv->mode = mode;
-		g_object_notify (G_OBJECT (ap), NM_AP_MODE);
+		_notify (ap, PROP_MODE);
 	}
 }
 
@@ -254,7 +272,7 @@ nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength)
 
 	if (priv->strength != strength) {
 		priv->strength = strength;
-		g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH);
+		_notify (ap, PROP_STRENGTH);
 	}
 }
 
@@ -278,7 +296,7 @@ nm_ap_set_freq (NMAccessPoint *ap,
 
 	if (priv->freq != freq) {
 		priv->freq = freq;
-		g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY);
+		_notify (ap, PROP_FREQUENCY);
 	}
 }
 
@@ -302,7 +320,7 @@ nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate)
 
 	if (priv->max_bitrate != bitrate) {
 		priv->max_bitrate = bitrate;
-		g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE);
+		_notify (ap, PROP_MAX_BITRATE);
 	}
 }
 
@@ -333,7 +351,7 @@ nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
 
 	if (priv->last_seen != last_seen) {
 		priv->last_seen = last_seen;
-		g_object_notify (G_OBJECT (ap), NM_AP_LAST_SEEN);
+		_notify (ap, PROP_LAST_SEEN);
 	}
 }
 
@@ -347,18 +365,20 @@ security_from_vardict (GVariant *security)
 
 	g_return_val_if_fail (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT), NM_802_11_AP_SEC_NONE);
 
-	if (g_variant_lookup (security, "KeyMgmt", "^a&s", &array)) {
-		if (_nm_utils_string_in_list ("wpa-psk", array))
+	if (   g_variant_lookup (security, "KeyMgmt", "^a&s", &array)
+	    && array) {
+		if (g_strv_contains (array, "wpa-psk"))
 			flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-		if (_nm_utils_string_in_list ("wpa-eap", array))
+		if (g_strv_contains (array, "wpa-eap"))
 			flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
 		g_free (array);
 	}
 
-	if (g_variant_lookup (security, "Pairwise", "^a&s", &array)) {
-		if (_nm_utils_string_in_list ("tkip", array))
+	if (   g_variant_lookup (security, "Pairwise", "^a&s", &array)
+	    && array) {
+		if (g_strv_contains (array, "tkip"))
 			flags |= NM_802_11_AP_SEC_PAIR_TKIP;
-		if (_nm_utils_string_in_list ("ccmp", array))
+		if (g_strv_contains (array, "ccmp"))
 			flags |= NM_802_11_AP_SEC_PAIR_CCMP;
 		g_free (array);
 	}
@@ -879,7 +899,7 @@ nm_ap_init (NMAccessPoint *ap)
 static void
 finalize (GObject *object)
 {
-	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE ((NMAccessPoint *) object);
 
 	g_free (priv->supplicant_path);
 	if (priv->ssid)
@@ -891,16 +911,16 @@ finalize (GObject *object)
 
 static void
 set_property (GObject *object, guint prop_id,
-		    const GValue *value, GParamSpec *pspec)
+              const GValue *value, GParamSpec *pspec)
 {
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
-	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE ((NMAccessPoint *) object);
 	GVariant *ssid;
 
 	switch (prop_id) {
@@ -965,8 +985,6 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
 	                                             | NM_802_11_AP_SEC_KEY_MGMT_PSK
 	                                             | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
 
-	g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
-
 	exported_object_class->export_path = NM_DBUS_PATH_ACCESS_POINT "/%u";
 
 	/* virtual methods */
@@ -975,72 +993,64 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
 	object_class->finalize = finalize;
 
 	/* properties */
-	g_object_class_install_property
-	    (object_class, PROP_FLAGS,
-	     g_param_spec_uint (NM_AP_FLAGS, "", "",
-	                        NM_802_11_AP_FLAGS_NONE,
-	                        NM_802_11_AP_FLAGS_PRIVACY,
-	                        NM_802_11_AP_FLAGS_NONE,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_WPA_FLAGS,
-	     g_param_spec_uint (NM_AP_WPA_FLAGS, "", "",
-	                        NM_802_11_AP_SEC_NONE,
-	                        all_sec_flags,
-	                        NM_802_11_AP_SEC_NONE,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_RSN_FLAGS,
-	     g_param_spec_uint (NM_AP_RSN_FLAGS, "", "",
-	                        NM_802_11_AP_SEC_NONE,
-	                        all_sec_flags,
-	                        NM_802_11_AP_SEC_NONE,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_SSID,
-	     g_param_spec_variant (NM_AP_SSID, "", "",
-	                           G_VARIANT_TYPE ("ay"),
-	                           NULL,
-	                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_FREQUENCY,
-	     g_param_spec_uint (NM_AP_FREQUENCY, "", "",
-	                        0, 10000, 0,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_HW_ADDRESS,
-	     g_param_spec_string (NM_AP_HW_ADDRESS, "", "",
+	obj_properties[PROP_FLAGS] =
+	    g_param_spec_uint (NM_AP_FLAGS, "", "",
+	                       NM_802_11_AP_FLAGS_NONE,
+	                       NM_802_11_AP_FLAGS_PRIVACY,
+	                       NM_802_11_AP_FLAGS_NONE,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_WPA_FLAGS] =
+	    g_param_spec_uint (NM_AP_WPA_FLAGS, "", "",
+	                       NM_802_11_AP_SEC_NONE,
+	                       all_sec_flags,
+	                       NM_802_11_AP_SEC_NONE,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_RSN_FLAGS] =
+	    g_param_spec_uint (NM_AP_RSN_FLAGS, "", "",
+	                       NM_802_11_AP_SEC_NONE,
+	                       all_sec_flags,
+	                       NM_802_11_AP_SEC_NONE,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_SSID] =
+	    g_param_spec_variant (NM_AP_SSID, "", "",
+	                          G_VARIANT_TYPE ("ay"),
 	                          NULL,
-	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_MODE,
-	     g_param_spec_uint (NM_AP_MODE, "", "",
-	                        NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_MAX_BITRATE,
-	     g_param_spec_uint (NM_AP_MAX_BITRATE, "", "",
-	                        0, G_MAXUINT16, 0,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_STRENGTH,
-	     g_param_spec_uchar (NM_AP_STRENGTH, "", "",
-	                         0, G_MAXINT8, 0,
-	                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-	    (object_class, PROP_LAST_SEEN,
-	     g_param_spec_int (NM_AP_LAST_SEEN, "", "",
-	                       -1, G_MAXINT, -1,
-	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_FREQUENCY] =
+	    g_param_spec_uint (NM_AP_FREQUENCY, "", "",
+	                       0, 10000, 0,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_HW_ADDRESS] =
+	    g_param_spec_string (NM_AP_HW_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_MODE] =
+	    g_param_spec_uint (NM_AP_MODE, "", "",
+	                       NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_MAX_BITRATE] =
+	    g_param_spec_uint (NM_AP_MAX_BITRATE, "", "",
+	                       0, G_MAXUINT16, 0,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_STRENGTH] =
+	    g_param_spec_uchar (NM_AP_STRENGTH, "", "",
+	                        0, G_MAXINT8, 0,
+	                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_LAST_SEEN] =
+	    g_param_spec_int (NM_AP_LAST_SEEN, "", "",
+	                      -1, G_MAXINT, -1,
+	                       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 (ap_class),
 	                                        NMDBUS_TYPE_ACCESS_POINT_SKELETON,
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 84aff983..035d1b5e 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -44,14 +44,8 @@
 #define NM_AP_STRENGTH "strength"
 #define NM_AP_LAST_SEEN "last-seen"
 
-typedef struct {
-	NMExportedObject parent;
-} NMAccessPoint;
-
-typedef struct {
-	NMExportedObjectClass parent;
-
-} NMAccessPointClass;
+typedef struct _NMAccessPoint NMAccessPoint;
+typedef struct _NMAccessPointClass NMAccessPointClass;
 
 GType nm_ap_get_type (void);
 
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
index 956730f8..64113a21 100644
--- a/src/devices/wifi/tests/Makefile.in
+++ b/src/devices/wifi/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-wifi-ap-utils$(EXEEXT)
 TESTS = test-wifi-ap-utils$(EXEEXT)
 subdir = src/devices/wifi/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -423,6 +424,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -479,13 +481,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -497,6 +500,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -557,6 +562,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -592,6 +599,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
index 838368c4..dbe185b3 100644
--- a/src/devices/wifi/tests/test-wifi-ap-utils.c
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -26,7 +26,7 @@
 
 #include "nm-core-internal.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 #define DEBUG 1
 
diff --git a/src/devices/wwan/Makefile.am b/src/devices/wwan/Makefile.am
index 1777eb36..0d061a79 100644
--- a/src/devices/wwan/Makefile.am
+++ b/src/devices/wwan/Makefile.am
@@ -16,6 +16,7 @@ AM_CPPFLAGS = \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	$(GLIB_CFLAGS) \
+	$(LIBSYSTEMD_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
 BUILT_SOURCES = $(null)
@@ -26,7 +27,7 @@ pkglib_LTLIBRARIES = libnm-wwan.la libnm-device-plugin-wwan.la
 
 GLIB_GENERATED = nm-modem-enum-types.h nm-modem-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM --fhead '\#include "nm-default.h"\n'
 nm_modem_enum_types_sources = $(srcdir)/nm-modem.h
 
 BUILT_SOURCES += $(GLIB_GENERATED)
@@ -41,6 +42,13 @@ libnm_wwan_la_SOURCES = \
 	\
 	$(GLIB_GENERATED)
 
+if WITH_OFONO
+libnm_wwan_la_SOURCES += \
+	nm-modem-ofono.c \
+	nm-modem-ofono.h \
+	$(NULL)
+endif
+
 WWAN_SYMBOL_VIS_FILE=$(srcdir)/wwan-exports.ver
 
 libnm_wwan_la_LDFLAGS = \
@@ -49,6 +57,7 @@ libnm_wwan_la_LDFLAGS = \
 libnm_wwan_la_LIBADD = \
 	$(top_builddir)/introspection/libnmdbus.la \
 	$(GLIB_LIBS) \
+	$(LIBSYSTEMD_LIBS) \
 	$(MM_GLIB_LIBS)
 
 ###########################################################
@@ -79,7 +88,9 @@ if ENABLE_TESTS
 
 check-local:
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wwan.so $(SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-wwan.so $(top_builddir)/src/NetworkManager --version >/dev/null
 	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-wwan.so $(WWAN_SYMBOL_VIS_FILE)
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-wwan.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 endif
 
diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in
index 89328665..6d395567 100644
--- a/src/devices/wwan/Makefile.in
+++ b/src/devices/wwan/Makefile.in
@@ -88,10 +88,16 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+@WITH_OFONO_TRUE@am__append_1 = \
+@WITH_OFONO_TRUE@	nm-modem-ofono.c \
+@WITH_OFONO_TRUE@	nm-modem-ofono.h \
+@WITH_OFONO_TRUE@	$(NULL)
+
 subdir = src/devices/wwan
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -157,10 +163,16 @@ libnm_device_plugin_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 	$(LDFLAGS) -o $@
 libnm_wwan_la_DEPENDENCIES =  \
 	$(top_builddir)/introspection/libnmdbus.la \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am__libnm_wwan_la_SOURCES_DIST = nm-modem-broadband.c \
+	nm-modem-broadband.h nm-modem-manager.c nm-modem-manager.h \
+	nm-modem.c nm-modem.h nm-modem-enum-types.h \
+	nm-modem-enum-types.c nm-modem-ofono.c nm-modem-ofono.h
 am__objects_1 = nm-modem-enum-types.lo
+@WITH_OFONO_TRUE@am__objects_2 = nm-modem-ofono.lo
 am_libnm_wwan_la_OBJECTS = nm-modem-broadband.lo nm-modem-manager.lo \
-	nm-modem.lo $(am__objects_1)
+	nm-modem.lo $(am__objects_1) $(am__objects_2)
 libnm_wwan_la_OBJECTS = $(am_libnm_wwan_la_OBJECTS)
 libnm_wwan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -202,7 +214,7 @@ am__v_CCLD_1 =
 SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \
 	$(libnm_wwan_la_SOURCES)
 DIST_SOURCES = $(libnm_device_plugin_wwan_la_SOURCES) \
-	$(libnm_wwan_la_SOURCES)
+	$(am__libnm_wwan_la_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -264,6 +276,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -320,13 +333,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -338,6 +352,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -398,6 +414,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -433,6 +451,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -508,6 +527,7 @@ AM_CPPFLAGS = \
 	-DG_LOG_DOMAIN=\""NetworkManager-wwan"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	$(GLIB_CFLAGS) \
+	$(LIBSYSTEMD_CFLAGS) \
 	$(MM_GLIB_CFLAGS)
 
 BUILT_SOURCES = $(null) $(GLIB_GENERATED)
@@ -516,18 +536,11 @@ pkglib_LTLIBRARIES = libnm-wwan.la libnm-device-plugin-wwan.la
 ###########################################################
 GLIB_GENERATED = nm-modem-enum-types.h nm-modem-enum-types.c
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
-GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM --fhead '\#include "nm-default.h"\n'
 nm_modem_enum_types_sources = $(srcdir)/nm-modem.h
-libnm_wwan_la_SOURCES = \
-	nm-modem-broadband.c \
-	nm-modem-broadband.h \
-	nm-modem-manager.c \
-	nm-modem-manager.h \
-	nm-modem.c \
-	nm-modem.h \
-	\
-	$(GLIB_GENERATED)
-
+libnm_wwan_la_SOURCES = nm-modem-broadband.c nm-modem-broadband.h \
+	nm-modem-manager.c nm-modem-manager.h nm-modem.c nm-modem.h \
+	$(GLIB_GENERATED) $(am__append_1)
 WWAN_SYMBOL_VIS_FILE = $(srcdir)/wwan-exports.ver
 libnm_wwan_la_LDFLAGS = \
 	-avoid-version \
@@ -536,6 +549,7 @@ libnm_wwan_la_LDFLAGS = \
 libnm_wwan_la_LIBADD = \
 	$(top_builddir)/introspection/libnmdbus.la \
 	$(GLIB_LIBS) \
+	$(LIBSYSTEMD_LIBS) \
 	$(MM_GLIB_LIBS)
 
 
@@ -646,6 +660,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-broadband.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-enum-types.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem-ofono.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-modem.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wwan-factory.Plo@am__quote@
 
@@ -897,7 +912,9 @@ include $(GLIB_MAKEFILE)
 
 @ENABLE_TESTS_TRUE@check-local:
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wwan.so $(SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-device-plugin-wwan.so $(top_builddir)/src/NetworkManager --version >/dev/null
 @ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-wwan.so $(WWAN_SYMBOL_VIS_FILE)
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/.libs/libnm-wwan.so $(top_builddir)/src/NetworkManager --version >/dev/null
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index a8361c49..0f96dafb 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -60,6 +60,7 @@ static void
 ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
 
 	switch (nm_device_get_state (device)) {
 	case NM_DEVICE_STATE_PREPARE:
@@ -73,7 +74,18 @@ ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
 	case NM_DEVICE_STATE_ACTIVATED:
 		if (nm_device_activate_ip4_state_in_conf (device))
 			nm_device_activate_schedule_ip4_config_timeout (device);
-		else {
+		else if (nm_device_activate_ip6_state_in_conf (device))
+			nm_device_activate_schedule_ip6_config_timeout (device);
+		else if (nm_device_activate_ip4_state_done (device)) {
+			nm_device_ip_method_failed (device,
+			                            AF_INET,
+			                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		} else if (nm_device_activate_ip6_state_done (device)) {
+			nm_device_ip_method_failed (device,
+			                            AF_INET6,
+			                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		} else {
+			_LOGW (LOGD_MB, "PPP failure in unexpected state %u", (guint) nm_device_get_state (device));
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
@@ -159,8 +171,9 @@ modem_ip4_config_result (NMModem *modem,
 	if (error) {
 		_LOGW (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: %s",
 		       error->message);
-
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		nm_device_ip_method_failed (device,
+		                            AF_INET,
+		                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else {
 		nm_device_set_wwan_ip4_config (device, config);
 		nm_device_activate_schedule_ip4_config_result (device, NULL);
@@ -184,9 +197,11 @@ modem_ip6_config_result (NMModem *modem,
 	g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE);
 
 	if (error) {
-		_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s", error->message);
-
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s",
+		       error->message);
+		nm_device_ip_method_failed (device,
+		                            AF_INET6,
+		                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return;
 	}
 
@@ -201,7 +216,9 @@ modem_ip6_config_result (NMModem *modem,
 			nm_device_activate_schedule_ip6_config_result (device);
 		else {
 			_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses");
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+			nm_device_ip_method_failed (device,
+			                            AF_INET6,
+			                            NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		}
 		return;
 	}
@@ -211,7 +228,7 @@ modem_ip6_config_result (NMModem *modem,
 	g_assert (ignored == NULL);
 	switch (ret) {
 	case NM_ACT_STAGE_RETURN_FAILURE:
-		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_ip_method_failed (device, AF_INET6, reason);
 		break;
 	case NM_ACT_STAGE_RETURN_STOP:
 		/* all done */
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 1a6e6b25..727c8409 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -30,6 +30,8 @@
 #include "nm-device-private.h"
 #include "nm-platform.h"
 
+#define NM_MODEM_BROADBAND_MODEM "modem"
+
 G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
 
 typedef enum {
@@ -89,8 +91,35 @@ enum {
 
 /*****************************************************************************/
 
+#define _NMLOG_DOMAIN      LOGD_MB
+#define _NMLOG_PREFIX_NAME "modem-broadband"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel _level = (level); \
+        \
+        if (nm_logging_enabled (_level, (_NMLOG_DOMAIN))) { \
+            NMModemBroadband *const __self = (self); \
+            char __prefix_name[128]; \
+            const char *__uid; \
+            \
+            _nm_log (_level, (_NMLOG_DOMAIN), 0, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     (__self \
+                         ? ({ \
+                                ((__uid = nm_modem_get_uid ((NMModem *) __self)) \
+                                    ? nm_sprintf_buf (__prefix_name, "[%s]", __uid) \
+                                    : "(null)"); \
+                            }) \
+                         : "") \
+                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*****************************************************************************/
+
 static NMDeviceStateReason
-translate_mm_error (GError *error)
+translate_mm_error (NMModemBroadband *self, GError *error)
 {
 	NMDeviceStateReason reason;
 
@@ -122,7 +151,7 @@ translate_mm_error (GError *error)
 		reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT;
 	else {
 		/* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */
-		nm_log_dbg (LOGD_MB, "unmapped error detected: '%s'", error->message);
+		_LOGD ("unmapped error detected: '%s'", error->message);
 		reason = NM_DEVICE_STATE_REASON_UNKNOWN;
 	}
 
@@ -288,7 +317,7 @@ connect_context_clear (NMModemBroadband *self)
 
 		g_clear_error (&ctx->first_error);
 		g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
-		g_clear_object (&ctx->cancellable);
+		nm_clear_g_cancellable (&ctx->cancellable);
 		g_clear_object (&ctx->connection);
 		g_clear_object (&ctx->connect_properties);
 		g_clear_object (&ctx->self);
@@ -360,8 +389,7 @@ connect_ready (MMModemSimple *simple_iface,
 
 	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
 	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
-		nm_log_warn (LOGD_MB, "(%s): failed to connect modem: invalid bearer IP configuration",
-		             nm_modem_get_uid (NM_MODEM (self)));
+		_LOGW ("failed to connect modem: invalid bearer IP configuration");
 		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		connect_context_clear (self);
 		return;
@@ -389,7 +417,7 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 		return;
 
 	if (!self->priv->ctx || self->priv->ctx->step != CONNECT_STEP_UNLOCK)
-		return;
+		g_return_if_reached ();
 
 	if (error) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
@@ -397,7 +425,7 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 		     mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
 			ask_for_pin (self);
 		} else {
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (self, error));
 		}
 		return;
 	}
@@ -459,9 +487,8 @@ connect_context_step (NMModemBroadband *self)
 		else if (MODEM_CAPS_3GPP2 (ctx->caps))
 			ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
 		else {
-			nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
-			             nm_modem_get_uid (NM_MODEM (self)),
-			             nm_connection_get_id (ctx->connection));
+			_LOGW ("failed to connect '%s': not a mobile broadband modem",
+			       nm_connection_get_id (ctx->connection));
 
 			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
 			connect_context_clear (self);
@@ -472,10 +499,9 @@ connect_context_step (NMModemBroadband *self)
 		/* Build up list of IP types that we need to use in the retries */
 		ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error);
 		if (!ctx->ip_types) {
-			nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
-			             nm_modem_get_uid (NM_MODEM (self)),
-			             nm_connection_get_id (ctx->connection),
-			             error->message);
+			_LOGW ("failed to connect '%s': %s",
+			       nm_connection_get_id (ctx->connection),
+			       error->message);
 			g_clear_error (&error);
 
 			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
@@ -502,10 +528,9 @@ connect_context_step (NMModemBroadband *self)
 			else
 				g_assert_not_reached ();
 
-			nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s' (try %d)",
-			            nm_modem_get_uid (NM_MODEM (self)),
-			            nm_modem_ip_type_to_string (current),
-			            ctx->ip_type_tries + 1);
+			_LOGD ("launching connection with ip type '%s' (try %d)",
+			       nm_modem_ip_type_to_string (current),
+			       ctx->ip_type_tries + 1);
 
 			mm_modem_simple_connect (self->priv->simple_iface,
 			                         ctx->connect_properties,
@@ -528,10 +553,9 @@ connect_context_step (NMModemBroadband *self)
 				                                        NM_DEVICE_ERROR_INVALID_CONNECTION,
 				                                        "invalid bearer IP configuration");
 
-			nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
-					     nm_modem_get_uid (NM_MODEM (self)),
-					     ctx->first_error->message);
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
+			_LOGW ("failed to connect modem: %s",
+			       ctx->first_error->message);
+			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (self, ctx->first_error));
 		}
 
 		connect_context_clear (self);
@@ -550,8 +574,7 @@ act_stage1_prepare (NMModem *_self,
 	if (!self->priv->simple_iface) {
 		self->priv->simple_iface = mm_object_get_modem_simple (self->priv->modem_object);
 		if (!self->priv->simple_iface) {
-			nm_log_warn (LOGD_MB, "(%s) cannot access the Simple mobile broadband modem interface",
-			             nm_modem_get_uid (NM_MODEM (self)));
+			_LOGW ("cannot access the Simple mobile broadband modem interface");
 			*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
 			return NM_ACT_STAGE_RETURN_FAILURE;
 		}
@@ -745,9 +768,8 @@ set_power_state_low_ready (MMModem *modem,
 
 	if (!mm_modem_set_power_state_finish (modem, result, &error)) {
 		/* Log but ignore errors; not all modems support low power state */
-		nm_log_dbg (LOGD_MB, "(%s): failed to set modem low power state: %s",
-		            nm_modem_get_uid (NM_MODEM (self)),
-		            NM_G_ERROR_MSG (error));
+		_LOGD ("failed to set modem low power state: %s",
+		       NM_G_ERROR_MSG (error));
 		g_clear_error (&error);
 	}
 
@@ -770,9 +792,8 @@ modem_disable_ready (MMModem *modem_iface,
 		                          (GAsyncReadyCallback) set_power_state_low_ready,
 		                          g_object_ref (self));
 	} else {
-		nm_log_warn (LOGD_MB, "(%s): failed to disable modem: %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             NM_G_ERROR_MSG (error));
+		_LOGW ("failed to disable modem: %s",
+		       NM_G_ERROR_MSG (error));
 		nm_modem_set_prev_state (NM_MODEM (self), "disable failed");
 		g_clear_error (&error);
 	}
@@ -789,9 +810,8 @@ modem_enable_ready (MMModem *modem_iface,
 	GError *error = NULL;
 
 	if (!mm_modem_enable_finish (modem_iface, res, &error)) {
-		nm_log_warn (LOGD_MB, "(%s) failed to enable modem: %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             NM_G_ERROR_MSG (error));
+		_LOGW ("failed to enable modem: %s",
+		       NM_G_ERROR_MSG (error));
 		nm_modem_set_prev_state (NM_MODEM (self), "enable failed");
 		g_clear_error (&error);
 	}
@@ -854,8 +874,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	g_assert (self->priv->ipv4_config);
 	g_assert (self->priv->bearer);
 
-	nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:",
-	             nm_modem_get_uid (NM_MODEM (self)));
+	_LOGI ("IPv4 static configuration:");
 
 	/* Fully fail if invalid IP address retrieved */
 	address_string = mm_bearer_ip_config_get_address (self->priv->ipv4_config);
@@ -880,15 +899,15 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	address.address = address_network;
 	address.peer_address = address_network;
 	address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
-	address.source = NM_IP_CONFIG_SOURCE_WWAN;
+	address.addr_source = NM_IP_CONFIG_SOURCE_WWAN;
 	if (address.plen <= 32)
 		nm_ip4_config_add_address (config, &address);
 
-	nm_log_info (LOGD_MB, "  address %s/%d", address_string, address.plen);
+	_LOGI ("  address %s/%d", address_string, address.plen);
 
 	if (gw) {
 		nm_ip4_config_set_gateway (config, gw);
-		nm_log_info (LOGD_MB, "  gateway %s", gw_string);
+		_LOGI ("  gateway %s", gw_string);
 	}
 
 	/* DNS servers */
@@ -897,7 +916,7 @@ static_stage3_ip4_done (NMModemBroadband *self)
 		if (   ip4_string_to_num (dns[i], &address_network)
 		    && address_network > 0) {
 			nm_ip4_config_add_nameserver (config, address_network);
-			nm_log_info (LOGD_MB, "  DNS %s", dns[i]);
+			_LOGI ("  DNS %s", dns[i]);
 		}
 	}
 
@@ -964,8 +983,7 @@ stage3_ip6_done (NMModemBroadband *self)
 		goto out;
 	}
 
-	nm_log_info (LOGD_MB, "(%s): IPv6 base configuration:",
-	             nm_modem_get_uid (NM_MODEM (self)));
+	_LOGI ("IPv6 base configuration:");
 
 	data_port = mm_bearer_get_interface (self->priv->bearer);
 	g_assert (data_port);
@@ -975,7 +993,7 @@ stage3_ip6_done (NMModemBroadband *self)
 	if (address.plen <= 128)
 		nm_ip6_config_add_address (config, &address);
 
-	nm_log_info (LOGD_MB, "  address %s/%d", address_string, address.plen);
+	_LOGI ("  address %s/%d", address_string, address.plen);
 
 	address_string = mm_bearer_ip_config_get_gateway (self->priv->ipv6_config);
 	if (address_string) {
@@ -987,7 +1005,7 @@ stage3_ip6_done (NMModemBroadband *self)
 			                     address_string);
 			goto out;
 		}
-		nm_log_info (LOGD_MB, "  gateway %s", address_string);
+		_LOGI ("  gateway %s", address_string);
 		nm_ip6_config_set_gateway (config, &address.address);
 	} else if (ip_method == NM_MODEM_IP_METHOD_STATIC) {
 		/* Gateway required for the 'static' method */
@@ -1005,7 +1023,7 @@ stage3_ip6_done (NMModemBroadband *self)
 
 		if (inet_pton (AF_INET6, dns[i], &addr)) {
 			nm_ip6_config_add_nameserver (config, &addr);
-			nm_log_info (LOGD_MB, "  DNS %s", dns[i]);
+			_LOGI ("  DNS %s", dns[i]);
 		}
 	}
 
@@ -1050,19 +1068,6 @@ disconnect_context_complete (DisconnectContext *ctx)
 }
 
 static gboolean
-disconnect_context_complete_if_cancelled (DisconnectContext *ctx)
-{
-	GError *error = NULL;
-
-	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
-		g_simple_async_result_take_error (ctx->result, error);
-		disconnect_context_complete (ctx);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
 disconnect_finish (NMModem *self,
                    GAsyncResult *res,
                    GError **error)
@@ -1079,9 +1084,10 @@ simple_disconnect_ready (MMModemSimple *modem_iface,
 
 	if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) {
 		if (ctx->warn && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
-			nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: %s",
-			             nm_modem_get_uid (NM_MODEM (ctx->self)),
-			             error->message);
+			NMModemBroadband *self = ctx->self;
+
+			_LOGW ("failed to disconnect modem: %s",
+			       error->message);
 		}
 		g_simple_async_result_take_error (ctx->result, error);
 	}
@@ -1098,9 +1104,11 @@ disconnect (NMModem *modem,
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (modem);
 	DisconnectContext *ctx;
+	GError *error = NULL;
 
 	connect_context_clear (self);
 	ctx = g_slice_new (DisconnectContext);
+	ctx->cancellable = NULL;
 	ctx->self = g_object_ref (self);
 	ctx->result = g_simple_async_result_new (G_OBJECT (self),
 	                                         callback,
@@ -1109,10 +1117,12 @@ disconnect (NMModem *modem,
 	/* Don't bother warning on FAILED since the modem is already gone */
 	ctx->warn = warn;
 
-	/* Setup cancellable */
-	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-	if (disconnect_context_complete_if_cancelled (ctx))
+	/* Already cancelled? */
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		disconnect_context_complete (ctx);
 		return;
+	}
 
 	/* If no simple iface, we're done */
 	if (!ctx->self->priv->simple_iface) {
@@ -1120,14 +1130,13 @@ disconnect (NMModem *modem,
 		return;
 	}
 
-	nm_log_dbg (LOGD_MB, "(%s): notifying ModemManager about the modem disconnection",
-	            nm_modem_get_uid (NM_MODEM (ctx->self)));
-	mm_modem_simple_disconnect (
-		ctx->self->priv->simple_iface,
-		NULL, /* bearer path; if NULL given ALL get disconnected */
-		cancellable,
-		(GAsyncReadyCallback)simple_disconnect_ready,
-		ctx);
+	_LOGD ("notifying ModemManager about the modem disconnection");
+	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	mm_modem_simple_disconnect (ctx->self->priv->simple_iface,
+	                            NULL, /* bearer path; if NULL given ALL get disconnected */
+	                            cancellable,
+	                            (GAsyncReadyCallback) simple_disconnect_ready,
+	                            ctx);
 }
 
 /*****************************************************************************/
@@ -1274,9 +1283,10 @@ get_sim_ready (MMModem *modem,
 		if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_SIM)
 			connect_context_step (self);
 	} else {
-		nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s",
-		             nm_modem_get_uid (NM_MODEM (self)),
-		             NM_G_ERROR_MSG (error));
+		_NMLOG (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)
+		            ? LOGL_INFO : LOGL_WARN,
+		        "failed to retrieve SIM object: %s",
+		        NM_G_ERROR_MSG (error));
 	}
 	g_clear_error (&error);
 	g_object_unref (self);
diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h
index a54e63a6..6607f7f5 100644
--- a/src/devices/wwan/nm-modem-broadband.h
+++ b/src/devices/wwan/nm-modem-broadband.h
@@ -23,8 +23,6 @@
 
 #include "nm-modem.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_MODEM_BROADBAND            (nm_modem_broadband_get_type ())
 #define NM_MODEM_BROADBAND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_BROADBAND, NMModemBroadband))
 #define NM_MODEM_BROADBAND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass))
@@ -32,8 +30,6 @@ G_BEGIN_DECLS
 #define NM_IS_MODEM_BROADBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_MODEM_BROADBAND))
 #define NM_MODEM_BROADBAND_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass))
 
-#define NM_MODEM_BROADBAND_MODEM "modem"
-
 typedef struct _NMModemBroadband        NMModemBroadband;
 typedef struct _NMModemBroadbandClass   NMModemBroadbandClass;
 typedef struct _NMModemBroadbandPrivate NMModemBroadbandPrivate;
@@ -51,6 +47,4 @@ GType nm_modem_broadband_get_type (void);
 
 NMModem *nm_modem_broadband_new (GObject *object, GError **error);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_MODEM_BROADBAND_H__ */
diff --git a/src/devices/wwan/nm-modem-enum-types.c b/src/devices/wwan/nm-modem-enum-types.c
index 2d02ede9..f999b084 100644
--- a/src/devices/wwan/nm-modem-enum-types.c
+++ b/src/devices/wwan/nm-modem-enum-types.c
@@ -6,6 +6,7 @@
 #include "config.h"
 
 #include "nm-modem-enum-types.h"
+#include "nm-default.h"
 
 #include "nm-modem.h"
 
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index e26321b0..964485fc 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -17,20 +17,30 @@
  *
  * Copyright (C) 2009 - 2014 Red Hat, Inc.
  * Copyright (C) 2009 Novell, Inc.
- * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009 - 2013 Canonical Ltd.
  */
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-modem-manager.h"
 
+#include <string.h>
 #include <libmm-glib.h>
 
-#include "nm-modem-manager.h"
+#if HAVE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#else
+#define sd_booted() FALSE
+#endif
+
 #include "nm-dbus-compat.h"
 #include "nm-modem.h"
 #include "nm-modem-broadband.h"
 
+#if WITH_OFONO
+#include "nm-modem-ofono.h"
+#endif
+
 #define MODEM_POKE_INTERVAL 120
 
 G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT)
@@ -39,9 +49,13 @@ struct _NMModemManagerPrivate {
 	GDBusConnection *dbus_connection;
 	MMManager *modem_manager;
 	guint mm_launch_id;
-	guint mm_name_owner_changed_id;
-	guint mm_object_added_id;
-	guint mm_object_removed_id;
+	gulong mm_name_owner_changed_id;
+	gulong mm_object_added_id;
+	gulong mm_object_removed_id;
+
+#if WITH_OFONO
+	GDBusProxy *ofono_proxy;
+#endif
 
 	/* Common */
 	GHashTable *modems;
@@ -79,34 +93,14 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data)
 }
 
 static void
-modem_manager_clear_signals (NMModemManager *self)
+clear_modem_manager (NMModemManager *self)
 {
 	if (!self->priv->modem_manager)
 		return;
-
-	if (self->priv->mm_name_owner_changed_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager,
-		                                   self->priv->mm_name_owner_changed_id))
-			g_signal_handler_disconnect (self->priv->modem_manager,
-			                             self->priv->mm_name_owner_changed_id);
-		self->priv->mm_name_owner_changed_id = 0;
-	}
-
-	if (self->priv->mm_object_added_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager,
-		                                   self->priv->mm_object_added_id))
-			g_signal_handler_disconnect (self->priv->modem_manager,
-			                             self->priv->mm_object_added_id);
-		self->priv->mm_object_added_id = 0;
-	}
-
-	if (self->priv->mm_object_removed_id) {
-		if (g_signal_handler_is_connected (self->priv->modem_manager,
-		                                   self->priv->mm_object_removed_id))
-			g_signal_handler_disconnect (self->priv->modem_manager,
-			                             self->priv->mm_object_removed_id);
-		self->priv->mm_object_removed_id = 0;
-	}
+	nm_clear_g_signal_handler (self->priv->modem_manager, &self->priv->mm_name_owner_changed_id);
+	nm_clear_g_signal_handler (self->priv->modem_manager, &self->priv->mm_object_added_id);
+	nm_clear_g_signal_handler (self->priv->modem_manager, &self->priv->mm_object_removed_id);
+	g_clear_object (&self->priv->modem_manager);
 }
 
 static void
@@ -183,7 +177,7 @@ modem_manager_available (NMModemManager *self)
 
 static void schedule_modem_manager_relaunch (NMModemManager *self,
                                              guint n_seconds);
-static void ensure_client                   (NMModemManager *self);
+static void ensure_modem_manager (NMModemManager *self);
 
 static void
 modem_manager_name_owner_changed (MMManager *modem_manager,
@@ -199,10 +193,9 @@ modem_manager_name_owner_changed (MMManager *modem_manager,
 	if (!name_owner) {
 		nm_log_info (LOGD_MB, "ModemManager disappeared from bus");
 
-#if !HAVE_SYSTEMD
 		/* If not managed by systemd, schedule relaunch */
-		schedule_modem_manager_relaunch (self, 0);
-#endif
+		if (!sd_booted ())
+			schedule_modem_manager_relaunch (self, 0);
 
 		return;
 	}
@@ -214,16 +207,173 @@ modem_manager_name_owner_changed (MMManager *modem_manager,
 	 * nor 'object-removed' if it was created while there was no ModemManager in
 	 * the bus. This hack avoids this issue until we get a GIO with the fix
 	 * included... */
-	modem_manager_clear_signals (self);
-	g_clear_object (&self->priv->modem_manager);
-	ensure_client (self);
+	clear_modem_manager (self);
+	ensure_modem_manager (self);
 
 	/* Whenever GDBusObjectManagerClient is fixed, we can just do the following:
 	 * modem_manager_available (self);
 	 */
 }
 
-#if !HAVE_SYSTEMD
+#if WITH_OFONO
+static void
+ofono_create_modem (NMModemManager *self, const char *path)
+{
+	NMModem *modem = NULL;
+
+	/* Ensure duplicate modems aren't created.  Because we're not using the
+	 * ObjectManager interface there's a race during oFono startup where we
+	 * receive ModemAdded signals before GetModems() returns, so some of the
+	 * modems returned from GetModems() may already have been created.
+	 */
+	if (!g_hash_table_lookup (self->priv->modems, path)) {
+		modem = nm_modem_ofono_new (path);
+		if (modem)
+			handle_new_modem (self, modem);
+		else
+			nm_log_warn (LOGD_MB, "Failed to create oFono modem for %s", path);
+	}
+}
+
+static void
+ofono_signal_cb (GDBusProxy *proxy,
+                 gchar *sender_name,
+                 gchar *signal_name,
+                 GVariant *parameters,
+                 gpointer user_data)
+{
+	NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	gchar *object_path;
+	NMModem *modem;
+
+	if (g_strcmp0 (signal_name, "ModemAdded") == 0) {
+		g_variant_get (parameters, "(oa{sv})", &object_path, NULL);
+		nm_log_info (LOGD_MB, "oFono modem appeared: %s", object_path);
+
+		ofono_create_modem (NM_MODEM_MANAGER (user_data), object_path);
+		g_free (object_path);
+	} else if (g_strcmp0 (signal_name, "ModemRemoved") == 0) {
+		g_variant_get (parameters, "(o)", &object_path);
+		nm_log_info (LOGD_MB, "oFono modem removed: %s", object_path);
+
+		modem = (NMModem *) g_hash_table_lookup (self->priv->modems, object_path);
+		if (modem) {
+			nm_modem_emit_removed (modem);
+			g_hash_table_remove (self->priv->modems, object_path);
+		} else {
+			nm_log_warn (LOGD_MB, "could not remove modem %s, not found in table",
+			             object_path);
+		}
+		g_free (object_path);
+	}
+}
+
+static void
+ofono_enumerate_devices_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
+{
+	NMModemManager *manager = NM_MODEM_MANAGER (user_data);
+	gs_free_error GError *error = NULL;
+	GVariant *results;
+	GVariantIter *iter;
+	const char *path;
+
+	results = g_dbus_proxy_call_finish (proxy, res, &error);
+	if (results) {
+		g_variant_get (results, "(a(oa{sv}))", &iter);
+		while (g_variant_iter_loop (iter, "(&oa{sv})", &path, NULL))
+			ofono_create_modem (manager, path);
+		g_variant_iter_free (iter);
+		g_variant_unref (results);
+	}
+
+	if (error) {
+		nm_log_warn (LOGD_MB, "failed to enumerate oFono devices: %s",
+		             error->message);
+	}
+}
+
+static void
+ofono_check_name_owner (NMModemManager *self)
+{
+	gs_free char *name_owner = NULL;
+
+	name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (self->priv->ofono_proxy));
+	if (name_owner) {
+		nm_log_info (LOGD_MB, "oFono is now available");
+
+		g_dbus_proxy_call (self->priv->ofono_proxy,
+		                   "GetModems",
+		                   NULL,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   NULL,
+		                   (GAsyncReadyCallback) ofono_enumerate_devices_done,
+		                   g_object_ref (self));
+	} else {
+		GHashTableIter iter;
+		NMModem *modem;
+
+		nm_log_info (LOGD_MB, "oFono disappeared from bus");
+
+		/* Remove any oFono modems that might be left around */
+		g_hash_table_iter_init (&iter, self->priv->modems);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &modem)) {
+			if (NM_IS_MODEM_OFONO (modem)) {
+				nm_modem_emit_removed (modem);
+				g_hash_table_iter_remove (&iter);
+			}
+		}
+	}
+}
+
+static void
+ofono_name_owner_changed (GDBusProxy *ofono_proxy,
+                          GParamSpec *pspec,
+                          NMModemManager *self)
+{
+	ofono_check_name_owner (self);
+}
+
+static void
+ofono_proxy_new_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+	gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	gs_free_error GError *error = NULL;
+
+	self->priv->ofono_proxy = g_dbus_proxy_new_finish (res, &error);
+	if (error) {
+		nm_log_warn (LOGD_MB, "error getting oFono bus proxy: %s", error->message);
+		return;
+	}
+
+	g_signal_connect (self->priv->ofono_proxy,
+	                  "notify::g-name-owner",
+	                  G_CALLBACK (ofono_name_owner_changed),
+	                  self);
+
+	g_signal_connect (self->priv->ofono_proxy,
+	                  "g-signal",
+	                  G_CALLBACK (ofono_signal_cb),
+	                  self);
+
+	ofono_check_name_owner (self);
+}
+
+static void
+ensure_ofono_client (NMModemManager *self)
+{
+	g_assert (self->priv->dbus_connection);
+	g_dbus_proxy_new (self->priv->dbus_connection,
+	                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                  NULL,
+	                  OFONO_DBUS_SERVICE,
+	                  OFONO_DBUS_PATH,
+	                  OFONO_DBUS_INTERFACE,
+	                  NULL,
+	                  (GAsyncReadyCallback) ofono_proxy_new_cb,
+	                  g_object_ref (self));
+}
+#endif
 
 static void
 modem_manager_poke_cb (GDBusConnection *connection,
@@ -235,19 +385,18 @@ modem_manager_poke_cb (GDBusConnection *connection,
 
 	result = g_dbus_connection_call_finish (connection, res, &error);
 	if (error) {
-		/* Ignore common errors when MM is not installed and such */
-		if (   !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_EXEC_FAILED)
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FORK_FAILED)
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FAILED)
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMEOUT)
-		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
-			nm_log_dbg (LOGD_MB, "error poking ModemManager: %s", error->message);
+		nm_log_warn (LOGD_MB, "error poking ModemManager: %s",
+					error ? error->message : "");
+
+		/* Don't reschedule poke is MM service doesn't exist. */
+		if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)
+			&& !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
+
+			/* Setup timeout to relaunch */
+			schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
 		}
-		g_error_free (error);
 
-		/* Setup timeout to relaunch */
-		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
+		g_error_free (error);
 	} else
 		g_variant_unref (result);
 
@@ -273,25 +422,21 @@ modem_manager_poke (NMModemManager *self)
 	                        g_object_ref (self)); /* user_data */
 }
 
-#endif /* HAVE_SYSTEMD */
-
 static void
 modem_manager_check_name_owner (NMModemManager *self)
 {
-	gchar *name_owner;
+	gs_free gchar *name_owner = NULL;
 
 	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (self->priv->modem_manager));
 	if (name_owner) {
 		/* Available! */
 		modem_manager_available (self);
-		g_free (name_owner);
 		return;
 	}
 
-#if !HAVE_SYSTEMD
 	/* If the lifecycle is not managed by systemd, poke */
-	modem_manager_poke (self);
-#endif
+	if (!sd_booted ())
+		modem_manager_poke (self);
 }
 
 static void
@@ -303,7 +448,8 @@ manager_new_ready (GObject *source,
 
 	GError *error = NULL;
 
-	g_assert (!self->priv->modem_manager);
+	g_return_if_fail (!self->priv->modem_manager);
+
 	self->priv->modem_manager = mm_manager_new_finish (res, &error);
 	if (!self->priv->modem_manager) {
 		/* We're not really supposed to get any error here. If we do get one,
@@ -316,20 +462,20 @@ manager_new_ready (GObject *source,
 	} else {
 		/* Setup signals in the GDBusObjectManagerClient */
 		self->priv->mm_name_owner_changed_id =
-			g_signal_connect (self->priv->modem_manager,
-			                  "notify::name-owner",
-			                  G_CALLBACK (modem_manager_name_owner_changed),
-			                  self);
+		    g_signal_connect (self->priv->modem_manager,
+		                      "notify::name-owner",
+		                      G_CALLBACK (modem_manager_name_owner_changed),
+		                      self);
 		self->priv->mm_object_added_id =
-			g_signal_connect (self->priv->modem_manager,
-			                  "object-added",
-			                  G_CALLBACK (modem_object_added),
-			                  self);
+		    g_signal_connect (self->priv->modem_manager,
+		                      "object-added",
+		                      G_CALLBACK (modem_object_added),
+		                      self);
 		self->priv->mm_object_removed_id =
-			g_signal_connect (self->priv->modem_manager,
-			                  "object-removed",
-			                  G_CALLBACK (modem_object_removed),
-			                  self);
+		    g_signal_connect (self->priv->modem_manager,
+		                      "object-removed",
+		                      G_CALLBACK (modem_object_removed),
+		                      self);
 
 		modem_manager_check_name_owner (self);
 	}
@@ -339,7 +485,7 @@ manager_new_ready (GObject *source,
 }
 
 static void
-ensure_client (NMModemManager *self)
+ensure_modem_manager (NMModemManager *self)
 {
 	g_assert (self->priv->dbus_connection);
 
@@ -359,46 +505,12 @@ ensure_client (NMModemManager *self)
 	modem_manager_check_name_owner (self);
 }
 
-static void
-bus_get_ready (GObject *source,
-               GAsyncResult *res,
-               NMModemManager *self)
-{
-	/* Note we always get an extra reference to self here */
-
-	GError *error = NULL;
-
-	self->priv->dbus_connection = g_bus_get_finish (res, &error);
-	if (!self->priv->dbus_connection) {
-		nm_log_warn (LOGD_CORE, "error getting bus connection: %s", error->message);
-		g_error_free (error);
-		/* Setup timeout to relaunch */
-		schedule_modem_manager_relaunch (self, MODEM_POKE_INTERVAL);
-	} else {
-		/* Got the bus, ensure client */
-		ensure_client (self);
-	}
-
-	/* Balance refcount */
-	g_object_unref (self);
-}
-
 static gboolean
-ensure_bus (NMModemManager *self)
+mm_launch_cb (NMModemManager *self)
 {
-	/* Clear launch ID */
 	self->priv->mm_launch_id = 0;
-
-	if (!self->priv->dbus_connection)
-		g_bus_get (G_BUS_TYPE_SYSTEM,
-		           NULL,
-		           (GAsyncReadyCallback)bus_get_ready,
-		           g_object_ref (self));
-	else
-		/* If bus is already available, ensure client */
-		ensure_client (self);
-
-	return FALSE;
+	ensure_modem_manager (self);
+	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -407,11 +519,31 @@ schedule_modem_manager_relaunch (NMModemManager *self,
 {
 	/* No need to pass an extra reference to self; timeout/idle will be
 	 * cancelled if the object gets disposed. */
-
 	if (n_seconds)
-		self->priv->mm_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)ensure_bus, self);
+		self->priv->mm_launch_id = g_timeout_add_seconds (n_seconds, (GSourceFunc)mm_launch_cb, self);
 	else
-		self->priv->mm_launch_id = g_idle_add ((GSourceFunc)ensure_bus, self);
+		self->priv->mm_launch_id = g_idle_add ((GSourceFunc)mm_launch_cb, self);
+}
+
+static void
+bus_get_ready (GObject *source,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+	gs_unref_object NMModemManager *self = NM_MODEM_MANAGER (user_data);
+	gs_free_error GError *error = NULL;
+
+	self->priv->dbus_connection = g_bus_get_finish (res, &error);
+	if (!self->priv->dbus_connection) {
+		nm_log_warn (LOGD_MB, "error getting bus connection: %s", error->message);
+		return;
+	}
+
+	/* Got the bus, ensure clients */
+	ensure_modem_manager (self);
+#if WITH_OFONO
+	ensure_ofono_client (self);
+#endif
 }
 
 /************************************************************************/
@@ -423,7 +555,11 @@ nm_modem_manager_init (NMModemManager *self)
 
 	self->priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
-	schedule_modem_manager_relaunch (self, 0);
+	// FIXME: this doesn't handle bus-daemon restart
+	g_bus_get (G_BUS_TYPE_SYSTEM,
+	           NULL,
+	           (GAsyncReadyCallback)bus_get_ready,
+	           g_object_ref (self));
 }
 
 static void
@@ -433,8 +569,16 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&self->priv->mm_launch_id);
 
-	modem_manager_clear_signals (self);
-	g_clear_object (&self->priv->modem_manager);
+	clear_modem_manager (self);
+
+#if WITH_OFONO
+	if (self->priv->ofono_proxy) {
+		g_signal_handlers_disconnect_by_func (self->priv->ofono_proxy, ofono_name_owner_changed, self);
+		g_signal_handlers_disconnect_by_func (self->priv->ofono_proxy, ofono_signal_cb, self);
+		g_clear_object (&self->priv->ofono_proxy);
+	}
+#endif
+
 	g_clear_object (&self->priv->dbus_connection);
 
 	if (self->priv->modems) {
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
new file mode 100644
index 00000000..86492d3d
--- /dev/null
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -0,0 +1,1202 @@
+/* -*- 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) 2013 - 2016 Canonical Ltd.
+ */
+
+#include "nm-default.h"
+
+#include "nm-modem-ofono.h"
+
+#include <string.h>
+
+#include "nm-core-internal.h"
+#include "nm-device-private.h"
+#include "nm-modem.h"
+#include "nm-platform.h"
+
+G_DEFINE_TYPE (NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM)
+
+#define NM_MODEM_OFONO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_OFONO, NMModemOfonoPrivate))
+
+#define VARIANT_IS_OF_TYPE_BOOLEAN(v)      ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_BOOLEAN) ))
+#define VARIANT_IS_OF_TYPE_STRING(v)       ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING) ))
+#define VARIANT_IS_OF_TYPE_OBJECT_PATH(v)  ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_OBJECT_PATH) ))
+#define VARIANT_IS_OF_TYPE_STRING_ARRAY(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING_ARRAY) ))
+#define VARIANT_IS_OF_TYPE_DICTIONARY(v)   ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_DICTIONARY) ))
+
+typedef struct {
+	GHashTable *connect_properties;
+
+	GDBusProxy *modem_proxy;
+	GDBusProxy *connman_proxy;
+	GDBusProxy *context_proxy;
+	GDBusProxy *sim_proxy;
+
+	GError *property_error;
+
+	char *context_path;
+	char *imsi;
+
+	gboolean modem_online;
+	gboolean gprs_attached;
+
+	NMIP4Config *ip4_config;
+} NMModemOfonoPrivate;
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_MB
+#define _NMLOG_PREFIX_NAME "modem-ofono"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel _level = (level); \
+        \
+        if (nm_logging_enabled (_level, (_NMLOG_DOMAIN))) { \
+            NMModemOfono *const __self = (self); \
+            char __prefix_name[128]; \
+            const char *__uid; \
+            \
+            _nm_log (_level, (_NMLOG_DOMAIN), 0, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     (__self \
+                         ? ({ \
+                                ((__uid = nm_modem_get_uid ((NMModem *) __self)) \
+                                    ? nm_sprintf_buf (__prefix_name, "[%s]", __uid) \
+                                    : "(null)"); \
+                            }) \
+                         : "") \
+                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*****************************************************************************/
+
+static gboolean
+ip_string_to_network_address (const gchar *str,
+                              guint32 *out)
+{
+	guint32 addr = 0;
+	gboolean success = FALSE;
+
+	if (!str || inet_pton (AF_INET, str, &addr) != 1)
+		addr = 0;
+	else
+		success = TRUE;
+
+	*out = (guint32)addr;
+	return success;
+}
+
+static void
+get_capabilities (NMModem *_self,
+                  NMDeviceModemCapabilities *modem_caps,
+                  NMDeviceModemCapabilities *current_caps)
+{
+	/* FIXME: auto-detect capabilities to allow LTE */
+	*modem_caps = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
+	*current_caps = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
+}
+
+static void
+update_modem_state (NMModemOfono *self)
+{
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	NMModemState state = nm_modem_get_state (NM_MODEM (self));
+	NMModemState new_state = NM_MODEM_STATE_DISABLED;
+	const char *reason = NULL;
+
+	_LOGI ("'Attached': %s 'Online': %s 'IMSI': %s",
+	       priv->gprs_attached ? "true" : "false",
+	       priv->modem_online ? "true" : "false",
+	       priv->imsi);
+
+	if (priv->modem_online == FALSE) {
+		reason = "modem 'Online=false'";
+	} else if (priv->imsi == NULL && state != NM_MODEM_STATE_ENABLING) {
+		reason = "modem not ready";
+	} else if (priv->gprs_attached == FALSE) {
+		new_state = NM_MODEM_STATE_SEARCHING;
+		reason = "modem searching";
+	} else {
+		new_state = NM_MODEM_STATE_REGISTERED;
+		reason = "modem ready";
+	}
+
+	if (state != new_state)
+		nm_modem_set_state (NM_MODEM (self), new_state, reason);
+}
+
+/* Disconnect */
+typedef struct {
+	NMModemOfono *self;
+	GSimpleAsyncResult *result;
+	GCancellable *cancellable;
+	gboolean warn;
+} DisconnectContext;
+
+static void
+disconnect_context_complete (DisconnectContext *ctx)
+{
+	g_simple_async_result_complete_in_idle (ctx->result);
+	if (ctx->cancellable)
+		g_object_unref (ctx->cancellable);
+	g_object_unref (ctx->result);
+	g_object_unref (ctx->self);
+	g_slice_free (DisconnectContext, ctx);
+}
+
+static gboolean
+disconnect_context_complete_if_cancelled (DisconnectContext *ctx)
+{
+	GError *error = NULL;
+
+	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		disconnect_context_complete (ctx);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+disconnect_finish (NMModem *self,
+                   GAsyncResult *result,
+                   GError **error)
+{
+	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+disconnect_done (GDBusProxy *proxy,
+				 GAsyncResult *result,
+				 gpointer user_data)
+{
+	DisconnectContext *ctx = (DisconnectContext*) user_data;
+	NMModemOfono *self = ctx->self;
+	GError *error = NULL;
+
+	g_dbus_proxy_call_finish (proxy, result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		_LOGD ("disconnect cancelled");
+		return;
+	}
+
+	if (error) {
+		if (ctx->warn)
+			_LOGW ("failed to disconnect modem: %s", error->message);
+		g_clear_error (&error);
+	}
+
+	_LOGD ("modem disconnected");
+
+	update_modem_state (self);
+	disconnect_context_complete (ctx);
+}
+
+static void
+disconnect (NMModem *modem,
+            gboolean warn,
+            GCancellable *cancellable,
+            GAsyncReadyCallback callback,
+            gpointer user_data)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (modem);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	DisconnectContext *ctx;
+	NMModemState state = nm_modem_get_state (NM_MODEM (self));
+
+	_LOGD ("warn: %s modem_state: %s",
+	       warn ? "TRUE" : "FALSE",
+	       nm_modem_state_to_string (state));
+
+	if (state != NM_MODEM_STATE_CONNECTED)
+		return;
+
+	ctx = g_slice_new (DisconnectContext);
+	ctx->self = g_object_ref (self);
+	ctx->warn = warn;
+
+	if (callback) {
+		ctx->result = g_simple_async_result_new (G_OBJECT (self),
+		                                         callback,
+		                                         user_data,
+		                                         disconnect);
+	}
+	/* Setup cancellable */
+	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	if (disconnect_context_complete_if_cancelled (ctx))
+		return;
+
+	nm_modem_set_state (NM_MODEM (self),
+	                    NM_MODEM_STATE_DISCONNECTING,
+	                    nm_modem_state_to_string (NM_MODEM_STATE_DISCONNECTING));
+
+	g_dbus_proxy_call (priv->context_proxy,
+	                   "SetProperty",
+	                   g_variant_new ("(sv)",
+	                                  "Active",
+	                                  g_variant_new ("b", warn)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   20000,
+	                   NULL,
+	                   (GAsyncReadyCallback) disconnect_done,
+	                   ctx);
+}
+
+static void
+deactivate_cleanup (NMModem *_self, NMDevice *device)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (_self);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	/* TODO: cancel SimpleConnect() if any */
+
+	g_clear_object (&priv->ip4_config);
+
+	/* Chain up parent's */
+	NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (_self, device);
+}
+
+
+static gboolean
+check_connection_compatible (NMModem *modem,
+                             NMConnection *connection)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (modem);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+	NMSettingGsm *s_gsm;
+	const char *uuid;
+	const char *id;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	uuid = nm_connection_get_uuid (connection);
+	id = nm_connection_get_id (connection);
+
+	s_gsm = nm_connection_get_setting_gsm (connection);
+	if (!s_gsm)
+		return FALSE;
+
+	if (!priv->imsi) {
+		_LOGW ("skipping %s/%s: no IMSI", uuid, id);
+		return FALSE;
+	}
+
+	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) {
+		_LOGD ("skipping %s/%s: not GSM", uuid, id);
+		return FALSE;
+	}
+
+	if (!g_strrstr (id, "/context")) {
+		_LOGD ("skipping %s/%s: unexpected ID", uuid, id);
+		return FALSE;
+	}
+
+	if (!g_strrstr (id, priv->imsi)) {
+		_LOGD ("skipping %s/%s: ID doesn't contain IMSI", uuid, id);
+		return FALSE;
+	}
+
+	_LOGD ("%s/%s compatible with IMSI %s", uuid, id, priv->imsi);
+	return TRUE;
+}
+
+static void
+handle_sim_property (GDBusProxy *proxy,
+                     const char *property,
+                     GVariant *v,
+                     gpointer user_data)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (g_strcmp0 (property, "SubscriberIdentity") == 0 && VARIANT_IS_OF_TYPE_STRING (v)) {
+		gsize length;
+		const char *value_str = g_variant_get_string (v, &length);
+
+		_LOGD ("SubscriberIdentify found");
+
+		/* Check for empty DBus string value */
+		if (length &&
+			g_strcmp0 (value_str, "(null)") != 0 &&
+			g_strcmp0 (value_str, priv->imsi) != 0) {
+
+			if (priv->imsi != NULL) {
+				_LOGW ("SimManager:'SubscriberIdentity' changed: %s", priv->imsi);
+				g_free(priv->imsi);
+			}
+
+			priv->imsi = g_strdup (value_str);
+			update_modem_state (self);
+		}
+	}
+}
+
+static void
+sim_property_changed (GDBusProxy *proxy,
+                      const char *property,
+                      GVariant *v,
+                      gpointer user_data)
+{
+	GVariant *v_child = g_variant_get_child_value (v, 0);
+
+	handle_sim_property (proxy, property, v_child, user_data);
+	g_variant_unref (v_child);
+}
+
+static void
+sim_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	GError *error = NULL;
+	GVariant *v_properties, *v_dict, *v;
+	GVariantIter i;
+	const char *property;
+
+	v_properties = _nm_dbus_proxy_call_finish (proxy,
+	                                           result,
+	                                           G_VARIANT_TYPE ("(a{sv})"),
+	                                           &error);
+	if (!v_properties) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("error getting sim properties: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	_LOGD ("sim v_properties is type: %s", g_variant_get_type_string (v_properties));
+
+	v_dict = g_variant_get_child_value (v_properties, 0);
+	if (!v_dict) {
+		_LOGW ("error getting sim properties: no v_dict");
+		return;
+	}
+
+	_LOGD ("sim v_dict is type: %s", g_variant_get_type_string (v_dict));
+
+	/*
+	 * TODO:
+	 * 1) optimize by looking up properties ( Online, Interfaces ), instead
+	 *    of iterating
+	 *
+	 * 2) reduce code duplication between all of the get_properties_done
+	 *    functions in this class.
+	 */
+
+	g_variant_iter_init (&i, v_dict);
+	while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
+		handle_sim_property (NULL, property, v, self);
+		g_variant_unref (v);
+	}
+
+	g_variant_unref (v_dict);
+	g_variant_unref (v_properties);
+}
+
+static void
+handle_sim_iface (NMModemOfono *self, gboolean found)
+{
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	_LOGD ("SimManager interface %sfound", found ? "" : "not ");
+
+	if (!found && priv->sim_proxy) {
+		_LOGI ("SimManager interface disappeared");
+		g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self));
+		g_clear_object (&priv->sim_proxy);
+		g_clear_pointer (&priv->imsi, g_free);
+		update_modem_state (self);
+	} else if (found && !priv->sim_proxy) {
+		GError *error = NULL;
+
+		_LOGI ("found new SimManager interface");
+
+		priv->sim_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+		                                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+		                                                 | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                                                 NULL, /* GDBusInterfaceInfo */
+		                                                 OFONO_DBUS_SERVICE,
+		                                                 nm_modem_get_path (NM_MODEM (self)),
+		                                                 OFONO_DBUS_INTERFACE_SIM_MANAGER,
+		                                                 NULL, /* GCancellable */
+		                                                 &error);
+		if (priv->sim_proxy == NULL) {
+			_LOGW ("failed to create SimManager proxy: %s", error->message);
+			g_error_free (error);
+			return;
+		}
+
+		/* Watch for custom ofono PropertyChanged signals */
+		_nm_dbus_signal_connect (priv->sim_proxy,
+		                         "PropertyChanged",
+		                         G_VARIANT_TYPE ("(sv)"),
+		                         G_CALLBACK (sim_property_changed),
+		                         self);
+
+		g_dbus_proxy_call (priv->sim_proxy,
+		                   "GetProperties",
+		                   NULL,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   20000,
+		                   NULL,
+		                   (GAsyncReadyCallback) sim_get_properties_done,
+		                   g_object_ref (self));
+	}
+}
+
+static void
+handle_connman_property (GDBusProxy *proxy,
+                         const char *property,
+                         GVariant *v,
+                         gpointer user_data)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (g_strcmp0 (property, "Attached") == 0 && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
+		gboolean attached = g_variant_get_boolean (v);
+		gboolean old_attached = priv->gprs_attached;
+
+		_LOGD ("Attached: %s", attached ? "True" : "False");
+
+		if (priv->gprs_attached != attached) {
+			priv->gprs_attached = attached;
+
+			_LOGI ("Attached %s -> %s",
+			       old_attached ? "true" : "false",
+			       attached ? "true" : "false");
+
+			update_modem_state (self);
+		}
+	}
+}
+
+static void
+connman_property_changed (GDBusProxy *proxy,
+                        const char *property,
+                        GVariant *v,
+                        gpointer user_data)
+{
+	GVariant *v_child = g_variant_get_child_value (v, 0);
+
+	handle_connman_property (proxy, property, v_child, user_data);
+	g_variant_unref (v_child);
+}
+
+static void
+connman_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	GError *error = NULL;
+	GVariant *v_properties, *v_dict, *v;
+	GVariantIter i;
+	const char *property;
+
+	v_properties = _nm_dbus_proxy_call_finish (proxy,
+		                                       result,
+		                                       G_VARIANT_TYPE ("(a{sv})"),
+		                                       &error);
+	if (!v_properties) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("error getting connman properties: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	v_dict = g_variant_get_child_value (v_properties, 0);
+
+	/*
+	 * TODO:
+	 * 1) optimize by looking up properties ( Online, Interfaces ), instead
+	 *    of iterating
+	 *
+	 * 2) reduce code duplication between all of the get_properties_done
+	 *    functions in this class.
+	 */
+
+	g_variant_iter_init (&i, v_dict);
+	while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
+		handle_connman_property (NULL, property, v, self);
+		g_variant_unref (v);
+	}
+
+	g_variant_unref (v_dict);
+	g_variant_unref (v_properties);
+}
+
+static void
+handle_connman_iface (NMModemOfono *self, gboolean found)
+{
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	_LOGD ("ConnectionManager interface %sfound", found ? "" : "not ");
+
+	if (!found && priv->connman_proxy) {
+		_LOGI ("ConnectionManager interface disappeared");
+
+		g_signal_handlers_disconnect_by_data (priv->connman_proxy, NM_MODEM_OFONO (self));
+		g_clear_object (&priv->connman_proxy);
+
+		/* The connection manager proxy disappeared, we should
+		 * consider the modem disabled.
+		 */
+		priv->gprs_attached = FALSE;
+
+		update_modem_state (self);
+	} else if (found && !priv->connman_proxy) {
+		GError *error = NULL;
+
+		_LOGI ("found new ConnectionManager interface");
+
+		priv->connman_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+		                                                     G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+		                                                     | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+		                                                     NULL, /* GDBusInterfaceInfo */
+		                                                     OFONO_DBUS_SERVICE,
+		                                                     nm_modem_get_path (NM_MODEM (self)),
+		                                                     OFONO_DBUS_INTERFACE_CONNECTION_MANAGER,
+		                                                     NULL, /* GCancellable */
+		                                                     &error);
+		if (priv->connman_proxy == NULL) {
+			_LOGW ("failed to create ConnectionManager proxy: %s", error->message);
+			g_error_free (error);
+			return;
+		}
+
+		/* Watch for custom ofono PropertyChanged signals */
+		_nm_dbus_signal_connect (priv->connman_proxy,
+		                         "PropertyChanged",
+		                         G_VARIANT_TYPE ("(sv)"),
+		                         G_CALLBACK (connman_property_changed),
+		                         self);
+
+		g_dbus_proxy_call (priv->connman_proxy,
+		                   "GetProperties",
+		                   NULL,
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   20000,
+		                   NULL,
+		                   (GAsyncReadyCallback) connman_get_properties_done,
+		                   g_object_ref (self));
+	}
+}
+
+static void
+handle_modem_property (GDBusProxy *proxy,
+                       const char *property,
+                       GVariant *v,
+                       gpointer user_data)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if ((g_strcmp0 (property, "Online") == 0) && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
+		gboolean online = g_variant_get_boolean (v);
+
+		_LOGD ("Online: %s", online ? "True" : "False");
+
+		if (online != priv->modem_online) {
+			priv->modem_online = online;
+			_LOGI ("modem is now %s", online ? "Online" : "Offline");
+			update_modem_state (self);
+		}
+
+	} else if ((g_strcmp0 (property, "Interfaces") == 0) && VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
+		const char **array, **iter;
+		gboolean found_connman = FALSE;
+		gboolean found_sim = FALSE;
+
+		_LOGD ("Interfaces found");
+
+		array = g_variant_get_strv (v, NULL);
+		if (array) {
+			for (iter = array; *iter; iter++) {
+				if (g_strcmp0 (OFONO_DBUS_INTERFACE_SIM_MANAGER, *iter) == 0)
+					found_sim = TRUE;
+				else if (g_strcmp0 (OFONO_DBUS_INTERFACE_CONNECTION_MANAGER, *iter) == 0)
+					found_connman = TRUE;
+			}
+			g_free (array);
+		}
+
+		handle_sim_iface (self, found_sim);
+		handle_connman_iface (self, found_connman);
+	}
+}
+
+static void
+modem_property_changed (GDBusProxy *proxy,
+                        const char *property,
+                        GVariant *v,
+                        gpointer user_data)
+{
+	GVariant *v_child = g_variant_get_child_value (v, 0);
+
+	handle_modem_property (proxy, property, v_child, user_data);
+	g_variant_unref (v_child);
+}
+
+static void
+modem_get_properties_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	GError *error = NULL;
+	GVariant *v_properties, *v_dict, *v;
+	GVariantIter i;
+	const char *property;
+
+	v_properties = _nm_dbus_proxy_call_finish (proxy,
+	                                           result,
+	                                           G_VARIANT_TYPE ("(a{sv})"),
+	                                           &error);
+	if (!v_properties) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("error getting modem properties: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	v_dict = g_variant_get_child_value (v_properties, 0);
+	if (!v_dict) {
+		_LOGW ("error getting modem properties: no v_dict");
+		return;
+	}
+
+	/*
+	 * TODO:
+	 * 1) optimize by looking up properties ( Online, Interfaces ), instead
+	 *    of iterating
+	 *
+	 * 2) reduce code duplication between all of the get_properties_done
+	 *    functions in this class.
+	 */
+
+	g_variant_iter_init (&i, v_dict);
+	while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
+		handle_modem_property (NULL, property, v, self);
+		g_variant_unref (v);
+	}
+
+	g_variant_unref (v_dict);
+	g_variant_unref (v_properties);
+}
+
+NMModem *
+nm_modem_ofono_new (const char *path)
+{
+	gs_free char *basename = NULL;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	nm_log_info (LOGD_MB, "ofono: creating new Ofono modem path %s", path);
+
+	/* Use short modem name (not its object path) as the NM device name (which
+	 * comes from NM_MODEM_UID)and the device ID.
+	 */
+	basename = g_path_get_basename (path);
+
+	return (NMModem *) g_object_new (NM_TYPE_MODEM_OFONO,
+	                                 NM_MODEM_PATH, path,
+	                                 NM_MODEM_UID, basename,
+	                                 NM_MODEM_DEVICE_ID, basename,
+	                                 NM_MODEM_CONTROL_PORT, "ofono", /* mandatory */
+	                                 NM_MODEM_DRIVER, "ofono",
+	                                 NM_MODEM_STATE, NM_MODEM_STATE_INITIALIZING,
+	                                 NULL);
+}
+
+static void
+stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	g_clear_pointer (&priv->connect_properties, g_hash_table_destroy);
+
+	g_dbus_proxy_call_finish (proxy, result, &error);
+	if (error) {
+		_LOGW ("connection failed: %s", error->message);
+
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
+		                       NM_DEVICE_STATE_REASON_MODEM_BUSY);
+		/*
+		 * FIXME: add code to check for InProgress so that the
+		 * connection doesn't continue to try and activate,
+		 * leading to the connection being disabled, and a 5m
+		 * timeout...
+		 */
+
+		g_clear_error (&error);
+	}
+}
+
+static void
+context_property_changed (GDBusProxy *proxy,
+                          const char *property,
+                          GVariant *v,
+                          gpointer user_data)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMPlatformIP4Address addr;
+	gboolean ret = FALSE;
+	GVariant *v_dict;
+	const gchar *s, *addr_s;
+	const gchar **array, **iter;
+	guint32 address_network, gateway_network;
+	guint prefix = 0;
+
+	_LOGD ("PropertyChanged: %s", property);
+
+	/*
+	 * TODO: might be a good idea and re-factor this to mimic bluez-device,
+	 * ie. have this function just check the key, and call a sub-func to
+	 * handle the action.
+	 */
+
+	if (g_strcmp0 (property, "Settings") != 0)
+		return;
+
+	v_dict = g_variant_get_child_value (v, 0);
+	if (!v_dict) {
+		_LOGW ("error getting IPv4 Settings: no v_dict");
+		goto out;
+	}
+
+	_LOGI ("IPv4 static Settings:");
+
+	if (g_variant_lookup (v_dict, "Interface", "&s", &s)) {
+		if (s && strlen (s)) {
+			_LOGD ("Interface: %s", s);
+			g_object_set (self,
+			              NM_MODEM_DATA_PORT, g_strdup (s),
+			              NM_MODEM_IP4_METHOD, NM_MODEM_IP_METHOD_STATIC,
+			              NULL);
+		} else {
+			_LOGW ("Settings 'Interface'; empty");
+			goto out;
+		}
+
+	} else {
+		_LOGW ("Settings 'Interface' missing");
+		goto out;
+	}
+
+	/* TODO: verify handling of ip4_config; check other places it's used... */
+	g_clear_object (&priv->ip4_config);
+
+	memset (&addr, 0, sizeof (addr));
+
+	/*
+	 * TODO:
+	 *
+	 * NM 1.2 changed the NMIP4Config constructor to take an ifindex
+	 * ( vs. void pre 1.2 ), to tie config instance to a specific
+	 * platform interface.
+	 *
+	 * This doesn't work for ofono, as the devices are created
+	 * dynamically ( eg. ril_0, ril_1 ) in NMModemManager.  The
+	 * device created doesn't really map directly to a platform
+	 * link.  The closest would be one of the devices owned by
+	 * rild ( eg. ccmin0 ), which is passed to us above as
+	 * 'Interface'.
+	 *
+	 * This needs discussion with upstream.
+	 */
+	priv->ip4_config = nm_ip4_config_new (0);
+
+	/* TODO: simply if/else error logic! */
+
+	if (g_variant_lookup (v_dict, "Address", "&s", &addr_s)) {
+		_LOGD ("Address: %s", addr_s);
+
+		if (ip_string_to_network_address (addr_s, &address_network)) {
+			addr.address = address_network;
+			addr.addr_source = NM_IP_CONFIG_SOURCE_WWAN;
+		} else {
+			_LOGW ("can't convert 'Address' %s to addr", s);
+			goto out;
+		}
+
+	} else {
+		_LOGW ("Settings 'Address' missing");
+		goto out;
+	}
+
+	if (g_variant_lookup (v_dict, "Netmask", "&s", &s)) {
+		_LOGD ("Netmask: %s", s);
+
+		if (s && ip_string_to_network_address (s, &address_network)) {
+			prefix = nm_utils_ip4_netmask_to_prefix (address_network);
+			if (prefix > 0)
+				addr.plen = prefix;
+		} else {
+			_LOGW ("invalid 'Netmask': %s", s);
+			goto out;
+		}
+	} else {
+		_LOGW ("Settings 'Netmask' missing");
+		goto out;
+	}
+
+	_LOGI ("Address: %s/%d", addr_s, prefix);
+
+	nm_ip4_config_add_address (priv->ip4_config, &addr);
+
+	if (g_variant_lookup (v_dict, "Gateway", "&s", &s)) {
+		if (s && ip_string_to_network_address (s, &gateway_network)) {
+			_LOGI ("Gateway: %s", s);
+			nm_ip4_config_set_gateway (priv->ip4_config, gateway_network);
+		} else {
+			_LOGW ("invalid 'Gateway': %s", s);
+			goto out;
+		}
+		nm_ip4_config_set_gateway (priv->ip4_config, gateway_network);
+	} else {
+		_LOGW ("Settings 'Gateway' missing");
+		goto out;
+	}
+
+	if (g_variant_lookup (v_dict, "DomainNameServers", "^a&s", &array)) {
+		if (array) {
+			for (iter = array; *iter; iter++) {
+				if (ip_string_to_network_address (*iter, &address_network) && address_network > 0) {
+					_LOGI ("DNS: %s", *iter);
+					nm_ip4_config_add_nameserver (priv->ip4_config, address_network);
+				} else {
+					_LOGW ("invalid NameServer: %s", *iter);
+				}
+			}
+
+			if (iter == array) {
+				_LOGW ("Settings: 'DomainNameServers': none specified");
+				g_free (array);
+				goto out;
+			}
+			g_free (array);
+		}
+	} else {
+		_LOGW ("Settings 'DomainNameServers' missing");
+		goto out;
+	}
+
+	if (g_variant_lookup (v_dict, "MessageProxy", "&s", &s)) {
+		_LOGI ("MessageProxy: %s", s);
+		if (s && ip_string_to_network_address (s, &address_network)) {
+			NMPlatformIP4Route mms_route;
+
+			mms_route.network = address_network;
+			mms_route.plen = 32;
+			mms_route.gateway = gateway_network;
+
+			mms_route.metric = 1;
+
+			nm_ip4_config_add_route (priv->ip4_config, &mms_route);
+		} else {
+			_LOGW ("invalid MessageProxy: %s", s);
+		}
+	}
+
+	ret = TRUE;
+
+out:
+	if (nm_modem_get_state (NM_MODEM (self)) != NM_MODEM_STATE_CONNECTED) {
+		_LOGI ("emitting PREPARE_RESULT: %s", ret ? "TRUE" : "FALSE");
+		if (!ret)
+			reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, ret, reason);
+	} else {
+		_LOGW ("MODEM_PPP_FAILED");
+		g_signal_emit_by_name (self, NM_MODEM_PPP_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
+	}
+}
+
+static NMActStageReturn
+static_stage3_ip4_config_start (NMModem *_self,
+                                NMActRequest *req,
+                                NMDeviceStateReason *reason)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (_self);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	GError *error = NULL;
+
+	if (priv->ip4_config) {
+		_LOGD ("IP4 config is done; setting modem_state -> CONNECTED");
+		g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, priv->ip4_config, error);
+
+		/* Signal listener takes ownership of the IP4Config */
+		priv->ip4_config = NULL;
+
+		nm_modem_set_state (NM_MODEM (self),
+		                    NM_MODEM_STATE_CONNECTED,
+		                    nm_modem_state_to_string (NM_MODEM_STATE_CONNECTED));
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	return ret;
+}
+
+static void
+context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->context_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (error) {
+		_LOGE ("failed to create ofono ConnectionContext DBus proxy: %s", error->message);
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
+		                       NM_DEVICE_STATE_REASON_MODEM_BUSY);
+		return;
+	}
+
+	if (!priv->gprs_attached) {
+		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
+		                       NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+		return;
+	}
+
+	/* We have an old copy of the settings from a previous activation,
+	 * clear it so that we can gate getting the IP config from oFono
+	 * on whether or not we have already received them
+	 */
+	g_clear_object (&priv->ip4_config);
+
+	/* Watch for custom ofono PropertyChanged signals */
+	_nm_dbus_signal_connect (priv->context_proxy,
+	                         "PropertyChanged",
+	                         G_VARIANT_TYPE ("(sv)"),
+	                         G_CALLBACK (context_property_changed),
+	                         self);
+
+	g_dbus_proxy_call (priv->context_proxy,
+	                   "SetProperty",
+	                   g_variant_new ("(sv)",
+	                                  "Active",
+	                                   g_variant_new ("b", TRUE)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   20000,
+	                   NULL,
+	                   (GAsyncReadyCallback) stage1_prepare_done,
+	                   g_object_ref (self));
+}
+
+static void
+do_context_activate (NMModemOfono *self)
+{
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	g_return_if_fail (NM_IS_MODEM_OFONO (self));
+
+	g_clear_object (&priv->context_proxy);
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                          NULL,
+	                          OFONO_DBUS_SERVICE,
+	                          priv->context_path,
+	                          OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT,
+	                          NULL,
+	                          (GAsyncReadyCallback) context_proxy_new_cb,
+	                          g_object_ref (self));
+}
+
+static GHashTable *
+create_connect_properties (NMConnection *connection)
+{
+	NMSettingGsm *setting;
+	GHashTable *properties;
+	const char *str;
+
+	setting = nm_connection_get_setting_gsm (connection);
+	properties = g_hash_table_new (g_str_hash, g_str_equal);
+
+	str = nm_setting_gsm_get_apn (setting);
+	if (str)
+		g_hash_table_insert (properties, "AccessPointName", g_strdup (str));
+
+	str = nm_setting_gsm_get_username (setting);
+	if (str)
+		g_hash_table_insert (properties, "Username", g_strdup (str));
+
+	str = nm_setting_gsm_get_password (setting);
+	if (str)
+		g_hash_table_insert (properties, "Password", g_strdup (str));
+
+	return properties;
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMModem *modem,
+                    NMConnection *connection,
+                    NMDeviceStateReason *reason)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (modem);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	const char *context_id;
+	char **id = NULL;
+
+	context_id = nm_connection_get_id (connection);
+	id = g_strsplit (context_id, "/", 0);
+	g_assert (id[2]);
+
+	_LOGD ("trying %s %s", id[1], id[2]);
+
+	g_free (priv->context_path);
+	priv->context_path = g_strdup_printf ("%s/%s",
+	                                      nm_modem_get_path (modem),
+	                                      id[2]);
+	g_strfreev (id);
+
+	if (!priv->context_path) {
+		*reason = NM_DEVICE_STATE_REASON_GSM_APN_FAILED;
+			return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	if (priv->connect_properties)
+		g_hash_table_destroy (priv->connect_properties);
+
+	priv->connect_properties = create_connect_properties (connection);
+
+	_LOGI ("activating context %s", priv->context_path);
+
+	if (nm_modem_get_state (modem) == NM_MODEM_STATE_REGISTERED) {
+		do_context_activate (self);
+	} else {
+		_LOGW ("could not activate context: modem is not registered.");
+		*reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+static void
+modem_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	gs_unref_object NMModemOfono *self = NM_MODEM_OFONO (user_data);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	priv->modem_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (error) {
+		_LOGE ("failed to create ofono modem DBus proxy: %s", error->message);
+		return;
+	}
+
+	/* Watch for custom ofono PropertyChanged signals */
+	_nm_dbus_signal_connect (priv->modem_proxy,
+	                         "PropertyChanged",
+	                         G_VARIANT_TYPE ("(sv)"),
+	                         G_CALLBACK (modem_property_changed),
+	                         self);
+
+	g_dbus_proxy_call (priv->modem_proxy,
+	                   "GetProperties",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   20000,
+	                   NULL,
+	                   (GAsyncReadyCallback) modem_get_properties_done,
+	                   g_object_ref (self));
+}
+
+static void
+nm_modem_ofono_init (NMModemOfono *self)
+{
+}
+
+static void
+constructed (GObject *object)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (object);
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+	                          NULL,
+	                          OFONO_DBUS_SERVICE,
+	                          nm_modem_get_path (NM_MODEM (self)),
+	                          OFONO_DBUS_INTERFACE_MODEM,
+	                          NULL,
+	                          (GAsyncReadyCallback) modem_proxy_new_cb,
+	                          g_object_ref (self));
+}
+
+static void
+dispose (GObject *object)
+{
+	NMModemOfono *self = NM_MODEM_OFONO (object);
+	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
+
+	if (priv->connect_properties) {
+		g_hash_table_destroy (priv->connect_properties);
+		priv->connect_properties = NULL;
+	}
+
+	g_clear_object (&priv->ip4_config);
+
+	if (priv->modem_proxy) {
+		g_signal_handlers_disconnect_by_data (priv->modem_proxy, NM_MODEM_OFONO (self));
+		g_clear_object (&priv->modem_proxy);
+	}
+
+	g_clear_object (&priv->connman_proxy);
+	g_clear_object (&priv->context_proxy);
+
+	if (priv->sim_proxy) {
+		g_signal_handlers_disconnect_by_data (priv->sim_proxy, NM_MODEM_OFONO (self));
+		g_clear_object (&priv->sim_proxy);
+	}
+
+	g_free (priv->imsi);
+	priv->imsi = NULL;
+
+	G_OBJECT_CLASS (nm_modem_ofono_parent_class)->dispose (object);
+}
+
+static void
+nm_modem_ofono_class_init (NMModemOfonoClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMModemClass *modem_class = NM_MODEM_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMModemOfonoPrivate));
+
+	/* Virtual methods */
+	object_class->constructed = constructed;
+	object_class->dispose = dispose;
+
+	modem_class->get_capabilities = get_capabilities;
+	modem_class->disconnect = disconnect;
+	modem_class->disconnect_finish = disconnect_finish;
+	modem_class->deactivate_cleanup = deactivate_cleanup;
+	modem_class->check_connection_compatible = check_connection_compatible;
+
+	/* same as nm-modem-broadband */
+	modem_class->act_stage1_prepare = act_stage1_prepare;
+
+	/* same as nm-modem-broadband */
+	modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
+}
diff --git a/src/devices/wwan/nm-modem-ofono.h b/src/devices/wwan/nm-modem-ofono.h
new file mode 100644
index 00000000..5965526d
--- /dev/null
+++ b/src/devices/wwan/nm-modem-ofono.h
@@ -0,0 +1,53 @@
+/* -*- 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) 2013 - Canonical Ltd.
+ */
+
+#ifndef NM_MODEM_OFONO_H
+#define NM_MODEM_OFONO_H
+
+#include "nm-modem.h"
+
+#define NM_TYPE_MODEM_OFONO            (nm_modem_ofono_get_type ())
+#define NM_MODEM_OFONO(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_OFONO, NMModemOfono))
+#define NM_IS_MODEM_OFONO(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_OFONO))
+#define NM_MODEM_OFONO_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_MODEM_OFONO, NMModemOfonoClass))
+#define NM_IS_MODEM_OFONO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_MODEM_OFONO))
+#define NM_MODEM_OFONO_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_MODEM_OFONO, NMModemOfonoClass))
+
+#define OFONO_DBUS_SERVICE                      "org.ofono"
+#define OFONO_DBUS_PATH                         "/"
+#define OFONO_DBUS_INTERFACE                    "org.ofono.Manager"
+#define OFONO_DBUS_INTERFACE_MODEM              "org.ofono.Modem"
+#define OFONO_DBUS_INTERFACE_CONNECTION_MANAGER "org.ofono.ConnectionManager"
+#define OFONO_DBUS_INTERFACE_CONNECTION_CONTEXT "org.ofono.ConnectionContext"
+#define OFONO_DBUS_INTERFACE_SIM_MANAGER        "org.ofono.SimManager"
+
+typedef struct {
+	NMModem parent;
+} NMModemOfono;
+
+typedef struct {
+	NMModemClass parent;
+} NMModemOfonoClass;
+
+GType nm_modem_ofono_get_type (void);
+
+NMModem *nm_modem_ofono_new (const char *path);
+
+#endif /* NM_MODEM_OFONO_H */
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index a6c77007..2e3d63bb 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -202,7 +202,9 @@ nm_modem_set_mm_enabled (NMModem *self,
 		return;
 	}
 
-	NM_MODEM_GET_CLASS (self)->set_mm_enabled (self, enabled);
+	/* Not all modem classes support set_mm_enabled */
+	if (NM_MODEM_GET_CLASS (self)->set_mm_enabled)
+		NM_MODEM_GET_CLASS (self)->set_mm_enabled (self, enabled);
 
 	/* Pre-empt the state change signal */
 	nm_modem_set_state (self,
@@ -573,6 +575,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 	const char *method;
 	NMActStageReturn ret;
 
+	nm_log_dbg (LOGD_MB, "ip4_config_start");
+
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE);
@@ -602,9 +606,11 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 		ret = ppp_stage3_ip_config_start (self, req, reason);
 		break;
 	case NM_MODEM_IP_METHOD_STATIC:
+		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_STATIC");
 		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
 		break;
 	case NM_MODEM_IP_METHOD_AUTO:
+		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_AUTO");
 		ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
 		break;
 	default:
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 60e5589b..8f797d44 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -22,12 +22,9 @@
 #ifndef __NETWORKMANAGER_MODEM_H__
 #define __NETWORKMANAGER_MODEM_H__
 
-#include "nm-default.h"
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_MODEM            (nm_modem_get_type ())
 #define NM_MODEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM, NMModem))
 #define NM_MODEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_MODEM, NMModemClass))
@@ -268,6 +265,4 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
 
 const gchar *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index 805aea69..507935fc 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -90,7 +90,7 @@ modem_added_cb (NMModemManager *manager,
 
 
 NM_DEVICE_FACTORY_DECLARE_TYPES (
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WWAN_ETHERNET)
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES    (NM_LINK_TYPE_WWAN_NET)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_GSM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME)
 )
 
@@ -102,7 +102,7 @@ create_device (NMDeviceFactory *factory,
                gboolean *out_ignore)
 {
 	g_return_val_if_fail (plink, NULL);
-	g_return_val_if_fail (plink->type == NM_LINK_TYPE_WWAN_ETHERNET, NULL);
+	g_return_val_if_fail (plink->type == NM_LINK_TYPE_WWAN_NET, NULL);
 	*out_ignore = TRUE;
 	return NULL;
 }
diff --git a/src/devices/wwan/nm-wwan-factory.h b/src/devices/wwan/nm-wwan-factory.h
index 78031e60..61bdcb1e 100644
--- a/src/devices/wwan/nm-wwan-factory.h
+++ b/src/devices/wwan/nm-wwan-factory.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_WWAN_FACTORY_H__
 #define __NETWORKMANAGER_WWAN_FACTORY_H__
 
-#include "nm-default.h"
-
 #define NM_TYPE_WWAN_FACTORY (nm_wwan_factory_get_type ())
 #define NM_WWAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WWAN_FACTORY, NMWwanFactory))
 
diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in
index 063489cb..dcb45183 100644
--- a/src/dhcp-manager/Makefile.in
+++ b/src/dhcp-manager/Makefile.in
@@ -91,8 +91,9 @@ host_triplet = @host@
 libexec_PROGRAMS = nm-dhcp-helper$(EXEEXT)
 subdir = src/dhcp-manager
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -218,6 +219,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -274,13 +276,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -292,6 +295,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -352,6 +357,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -387,6 +394,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/dhcp-manager/nm-dhcp-client-logging.h b/src/dhcp-manager/nm-dhcp-client-logging.h
index bb4f2f4a..8dd18bf2 100644
--- a/src/dhcp-manager/nm-dhcp-client-logging.h
+++ b/src/dhcp-manager/nm-dhcp-client-logging.h
@@ -21,7 +21,6 @@
 #ifndef __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__
 #define __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__
 
-#include "nm-default.h"
 #include "nm-dhcp-client.h"
 
 #define _NMLOG_PREFIX_NAME    "dhcp"
@@ -52,4 +51,27 @@
         } \
     } G_STMT_END
 
+#define _NMLOG2(level, domain, ifname, ...) \
+    G_STMT_START { \
+        const NMLogLevel _level = (level); \
+        const NMLogDomain _domain = (domain); \
+        \
+        /* we check first for LOGD_DHCP instead of the correct domain.
+         * In the worst case, we guess wrong and enter the block.
+         *
+         * Same for the _NMLOG_ENABLED() macro. Probably it would be more
+         * expensive to determine the correct value then what we could
+         * safe. */ \
+        if (nm_logging_enabled (_level, _domain)) { \
+            const char *__ifname = (ifname); \
+            \
+            nm_log (_level, _domain, \
+                    "%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                    _NMLOG_PREFIX_NAME, \
+                    (_domain == LOGD_DHCP4 ? "4" : (_domain == LOGD_DHCP6 ? "6" : "")), \
+                    NM_PRINT_FMT_QUOTED (__ifname, " (", __ifname, ")", "") \
+                    _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
 #endif /* __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index c1025817..3be2ea03 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -448,6 +448,7 @@ generate_duid_from_machine_id (void)
 	GRand *generator;
 	guint i;
 	gs_free char *machine_id_s = NULL;
+	gs_free char *str = NULL;
 
 	machine_id_s = nm_utils_machine_id_read ();
 	if (nm_utils_machine_id_parse (machine_id_s, uuid)) {
@@ -457,7 +458,7 @@ generate_duid_from_machine_id (void)
 		g_checksum_get_digest (sum, buffer, &sumlen);
 		g_checksum_free (sum);
 	} else {
-		nm_log_warn (LOGD_DHCP6, "dhcp6: failed to read " SYSCONFDIR "/machine-id "
+		nm_log_warn (LOGD_DHCP, "dhcp: failed to read " SYSCONFDIR "/machine-id "
 		             "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate "
 		             "DHCPv6 DUID; creating non-persistent random DUID.");
 
@@ -481,6 +482,8 @@ generate_duid_from_machine_id (void)
 	 */
 	g_byte_array_append (duid, buffer, 16);
 
+	nm_log_dbg (LOGD_DHCP, "dhcp: generated DUID %s",
+	            (str = nm_dhcp_utils_duid_to_string (duid)));
 	return duid;
 }
 
@@ -489,17 +492,10 @@ get_duid (NMDhcpClient *self)
 {
 	static GByteArray *duid = NULL;
 	GByteArray *copy = NULL;
-	char *str;
 
 	if (G_UNLIKELY (duid == NULL)) {
 		duid = generate_duid_from_machine_id ();
 		g_assert (duid);
-
-		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
-			str = nm_dhcp_utils_duid_to_string (duid);
-			_LOGD ("generated DUID %s", str);
-			g_free (str);
-		}
 	}
 
 	if (G_LIKELY (duid)) {
@@ -519,7 +515,7 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
                           NMSettingIP6ConfigPrivacy privacy)
 {
 	NMDhcpClientPrivate *priv;
-	char *str;
+	gs_free char *str = NULL;
 
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
@@ -534,11 +530,7 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 	if (!priv->duid)
 		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
-		str = nm_dhcp_utils_duid_to_string (priv->duid);
-		_LOGD ("DUID is '%s'", str);
-		g_free (str);
-	}
+	_LOGD ("DUID is '%s'", (str = nm_dhcp_utils_duid_to_string (priv->duid)));
 
 	g_clear_pointer (&priv->hostname, g_free);
 	priv->hostname = g_strdup (hostname);
@@ -559,51 +551,49 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 void
 nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 {
-	char *pid_contents = NULL, *proc_contents = NULL, *proc_path = NULL;
-	long int tmp;
+	guint64 start_time;
+	pid_t pid, ppid;
+	const char *exe;
+	char proc_path[NM_STRLEN ("/proc/%lu/cmdline") + 100];
+	gs_free char *pid_contents = NULL, *proc_contents = NULL;
 
 	/* Check for an existing instance and stop it */
 	if (!g_file_get_contents (pid_file, &pid_contents, NULL, NULL))
 		return;
 
-	errno = 0;
-	tmp = strtol (pid_contents, NULL, 10);
-	if ((errno == 0) && (tmp > 1)) {
-		guint64 start_time;
-		const char *exe;
-		pid_t ppid;
-
-		/* Ensure the process is a DHCP client */
-		start_time = nm_utils_get_start_time_for_pid (tmp, NULL, &ppid);
-		proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp);
-		if (   start_time
-		    && g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) {
-			exe = strrchr (proc_contents, '/');
-			if (exe)
-				exe++;
-			else
-				exe = proc_contents;
-
-			if (!strcmp (exe, binary_name)) {
-				if (ppid == getpid ()) {
-					/* the process is our own child. */
-					nm_utils_kill_child_sync (tmp, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20);
-				} else {
-					nm_utils_kill_process_sync (tmp, start_time, SIGTERM, LOGD_DHCP,
-					                            "dhcp-client", 1000 / 2, 1000 / 20, 2000);
-				}
-			}
-		}
+	pid = _nm_utils_ascii_str_to_int64 (pid_contents, 10, 1, G_MAXINT64, 0);
+	if (pid <= 0)
+		goto out;
+
+	start_time = nm_utils_get_start_time_for_pid (pid, NULL, &ppid);
+	if (start_time == 0)
+		goto out;
+
+	nm_sprintf_buf (proc_path, "/proc/%lu/cmdline", (long unsigned) pid);
+	if (!g_file_get_contents (proc_path, &proc_contents, NULL, NULL))
+		goto out;
+
+	exe = strrchr (proc_contents, '/');
+	if (exe)
+		exe++;
+	else
+		exe = proc_contents;
+	if (!nm_streq0 (exe, binary_name))
+		goto out;
+
+	if (ppid == getpid ()) {
+		/* the process is our own child. */
+		nm_utils_kill_child_sync (pid, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20);
+	} else {
+		nm_utils_kill_process_sync (pid, start_time, SIGTERM, LOGD_DHCP,
+		                            "dhcp-client", 1000 / 2, 1000 / 20, 2000);
 	}
 
+out:
 	if (remove (pid_file) == -1) {
 		nm_log_dbg (LOGD_DHCP, "dhcp: could not remove pid file \"%s\": %d (%s)",
 		            pid_file, errno, g_strerror (errno));
 	}
-
-	g_free (proc_path);
-	g_free (pid_contents);
-	g_free (proc_contents);
 }
 
 void
@@ -631,7 +621,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
 /********************************************/
 
 static char *
-bytearray_variant_to_string (GVariant *value, const char *key)
+bytearray_variant_to_string (NMDhcpClient *self, GVariant *value, const char *key)
 {
 	const guint8 *array;
 	gsize length;
@@ -662,7 +652,7 @@ bytearray_variant_to_string (GVariant *value, const char *key)
 
 	converted = str->str;
 	if (!g_utf8_validate (converted, -1, NULL))
-		nm_log_warn (LOGD_DHCP, "dhcp: option '%s' couldn't be converted to UTF-8", key);
+		_LOGW ("option '%s' couldn't be converted to UTF-8", key);
 	g_string_free (str, FALSE);
 	return converted;
 }
@@ -671,7 +661,8 @@ bytearray_variant_to_string (GVariant *value, const char *key)
 #define NEW_TAG "new_"
 
 static void
-maybe_add_option (GHashTable *hash,
+maybe_add_option (NMDhcpClient *self,
+                  GHashTable *hash,
                   const char *key,
                   GVariant *value)
 {
@@ -701,7 +692,7 @@ maybe_add_option (GHashTable *hash,
 	if (!key[0])
 		return;
 
-	str_value = bytearray_variant_to_string (value, key);
+	str_value = bytearray_variant_to_string (self, value, key);
 	if (str_value)
 		g_hash_table_insert (hash, g_strdup (key), str_value);
 }
@@ -747,7 +738,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 		str_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 		g_variant_iter_init (&iter, options);
 		while (g_variant_iter_next (&iter, "{&sv}", &name, &value)) {
-			maybe_add_option (str_options, name, value);
+			maybe_add_option (self, str_options, name, value);
 			g_variant_unref (value);
 		}
 
@@ -836,7 +827,7 @@ set_property (GObject *object, guint prop_id,
 	switch (prop_id) {
 	case PROP_IFACE:
 		/* construct-only */
-		priv->iface = g_strdup (g_value_get_string (value));
+		priv->iface = g_value_dup_string (value);
 		break;
 	case PROP_IFINDEX:
 		/* construct-only */
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
index 1c78c5b1..5779d4ed 100644
--- a/src/dhcp-manager/nm-dhcp-client.h
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -24,8 +24,6 @@
 #include <nm-ip4-config.h>
 #include <nm-ip6-config.h>
 
-#include "nm-default.h"
-
 #define NM_TYPE_DHCP_CLIENT            (nm_dhcp_client_get_type ())
 #define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
 #define NM_DHCP_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
index 008f8af2..8d4c54ae 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -720,7 +720,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
 
 		address.timestamp = now_monotonic_ts;
 		address.lifetime = address.preferred = expiry;
-		address.source = NM_IP_CONFIG_SOURCE_DHCP;
+		address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
 
 		ip4 = nm_ip4_config_new (ifindex);
 		nm_ip4_config_add_address (ip4, &address);
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
index 0e255027..83d5a23d 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
@@ -22,8 +22,6 @@
 #include <nm-setting-ip4-config.h>
 #include <nm-setting-ip6-config.h>
 
-#include "nm-default.h"
-
 char *nm_dhcp_dhclient_create_config (const char *interface,
                                       gboolean is_ip6,
                                       GBytes *client_id,
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index b35921ec..bd020624 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -18,7 +18,8 @@
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
-#include "config.h"
+#include <config.h>
+#define __CONFIG_H__
 
 #define _XOPEN_SOURCE
 #include <time.h>
@@ -178,6 +179,9 @@ merge_dhclient_config (NMDhcpDhclient *self,
 		}
 	}
 
+	if (is_ip6 && hostname && !strchr (hostname, '.'))
+		_LOGW ("hostname is not a FQDN, it will be ignored");
+
 	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, fqdn, orig_path, orig, out_new_client_id);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
index 3e8d9c5d..7f0c855a 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -19,7 +19,6 @@
 #ifndef __NETWORKMANAGER_DHCP_DHCLIENT_H__
 #define __NETWORKMANAGER_DHCP_DHCLIENT_H__
 
-#include "nm-default.h"
 #include "nm-dhcp-client.h"
 
 #define NM_TYPE_DHCP_DHCLIENT            (nm_dhcp_dhclient_get_type ())
diff --git a/src/dhcp-manager/nm-dhcp-listener.c b/src/dhcp-manager/nm-dhcp-listener.c
index 595b2097..eadff3ec 100644
--- a/src/dhcp-manager/nm-dhcp-listener.c
+++ b/src/dhcp-manager/nm-dhcp-listener.c
@@ -111,20 +111,20 @@ handle_event (GDBusConnection  *connection,
 
 	iface = get_option (options, "interface");
 	if (iface == NULL) {
-		nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
+		nm_log_warn (LOGD_DHCP, "dhcp-event: didn't have associated interface.");
 		goto out;
 	}
 
 	pid_str = get_option (options, "pid");
 	pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
 	if (pid == -1) {
-		nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
+		nm_log_warn (LOGD_DHCP, "dhcp-event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
 		goto out;
 	}
 
 	reason = get_option (options, "reason");
 	if (reason == NULL) {
-		nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", pid);
+		nm_log_warn (LOGD_DHCP, "dhcp-event: (pid %d) DHCP event didn't have a reason", pid);
 		goto out;
 	}
 
@@ -132,9 +132,9 @@ handle_event (GDBusConnection  *connection,
 	if (!handled) {
 		if (g_ascii_strcasecmp (reason, "RELEASE") == 0) {
 			/* Ignore event when the dhcp client gets killed and we receive its last message */
-			nm_log_dbg (LOGD_DHCP, "(pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
+			nm_log_dbg (LOGD_DHCP, "dhcp-event: (pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
 		} else
-			nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", pid, iface);
+			nm_log_warn (LOGD_DHCP, "dhcp-event: (pid %d) unhandled DHCP event for interface %s", pid, iface);
 	}
 
 out:
diff --git a/src/dhcp-manager/nm-dhcp-listener.h b/src/dhcp-manager/nm-dhcp-listener.h
index b2631acc..ff31fe34 100644
--- a/src/dhcp-manager/nm-dhcp-listener.h
+++ b/src/dhcp-manager/nm-dhcp-listener.h
@@ -19,8 +19,6 @@
 #ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
 #define __NETWORKMANAGER_DHCP_LISTENER_H__
 
-#include "nm-default.h"
-
 #define NM_TYPE_DHCP_LISTENER           (nm_dhcp_listener_get_type ())
 #define NM_DHCP_LISTENER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
 #define NM_IS_DHCP_LISTENER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_LISTENER))
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index fe723e96..50469c5c 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -372,7 +372,7 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	for (iter = client_descs; iter; iter = iter->next) {
 		ClientDesc *desc = iter->data;
 
-		nm_log_dbg (LOGD_DHCP, "Registered DHCP client '%s' (%s)",
+		nm_log_dbg (LOGD_DHCP, "dhcp-init: Registered DHCP client '%s' (%s)",
 		            desc->name, g_type_name (desc->gtype));
 	}
 
@@ -380,7 +380,7 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	client = nm_config_get_dhcp_client (config);
 	if (nm_config_get_configure_and_quit (config)) {
 		if (g_strcmp0 (client, "internal") != 0)
-			nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
+			nm_log_warn (LOGD_DHCP, "dhcp-init: Using internal DHCP client since configure-and-quit is set.");
 		client = "internal";
 	}
 
@@ -389,7 +389,7 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 
 	if (type == G_TYPE_INVALID) {
 		if (client)
-			nm_log_warn (LOGD_DHCP, "DHCP client '%s' not available", client);
+			nm_log_warn (LOGD_DHCP, "dhcp-init: DHCP client '%s' not available", client);
 
 		type = is_client_enabled ("dhclient");
 		if (type == G_TYPE_INVALID)
@@ -399,9 +399,9 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	}
 
 	if (type == G_TYPE_INVALID)
-		nm_log_warn (LOGD_DHCP, "No usable DHCP client found! DHCP configurations will fail");
+		nm_log_warn (LOGD_DHCP, "dhcp-init: No usable DHCP client found! DHCP configurations will fail");
 	else
-		nm_log_info (LOGD_DHCP, "Using DHCP client '%s'", find_client_desc (NULL, type)->name);
+		nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", find_client_desc (NULL, type)->name);
 
 	priv->client_type = type;
 	priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index 42d968ab..df92c91c 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -22,8 +22,6 @@
 #ifndef __NETWORKMANAGER_DHCP_MANAGER_H__
 #define __NETWORKMANAGER_DHCP_MANAGER_H__
 
-
-#include "nm-default.h"
 #include "nm-dhcp-client.h"
 #include "nm-ip4-config.h"
 #include "nm-dhcp4-config.h"
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index 82cb3350..ac8eb60a 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -34,13 +34,11 @@
 #include "NetworkManagerUtils.h"
 #include "nm-platform.h"
 #include "nm-dhcp-client-logging.h"
+#include "nm-sd.h"
 
 #include "sd-dhcp-client.h"
 #include "sd-dhcp6-client.h"
 
-#include "nm-sd-adapt.h"
-#include "dhcp-lease-internal.h"
-
 G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
 
 #define NM_DHCP_SYSTEMD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdPrivate))
@@ -193,7 +191,7 @@ add_requests_to_options (GHashTable *options, const ReqOption *requests)
 #define LOG_LEASE(domain, ...) \
 G_STMT_START { \
 	if (log_lease) { \
-		nm_log (LOGL_INFO, (domain), __VA_ARGS__); \
+		_LOG2I ((domain), (iface), __VA_ARGS__); \
 	} \
 } G_STMT_END
 
@@ -256,7 +254,7 @@ lease_to_ip4_config (const char *iface,
 	                SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
 	                end_time);
 
-	address.source = NM_IP_CONFIG_SOURCE_DHCP;
+	address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
 	nm_ip4_config_add_address (ip4_config, &address);
 
 	/* DNS Servers */
@@ -323,7 +321,7 @@ lease_to_ip4_config (const char *iface,
 			route.gateway = a.s_addr;
 
 			if (route.plen) {
-				route.source = NM_IP_CONFIG_SOURCE_DHCP;
+				route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
 				route.metric = default_priority;
 				nm_ip4_config_add_route (ip4_config, &route);
 
@@ -578,6 +576,8 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 		return FALSE;
 	}
 
+	_LOGT ("dhcp-client4: set %p", priv->client4);
+
 	r = sd_dhcp_client_attach_event (priv->client4, NULL, 0);
 	if (r < 0) {
 		_LOGW ("failed to attach event (%d)", r);
@@ -602,9 +602,9 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 		}
 	}
 
-	r = sd_dhcp_client_set_index (priv->client4, nm_dhcp_client_get_ifindex (client));
+	r = sd_dhcp_client_set_ifindex (priv->client4, nm_dhcp_client_get_ifindex (client));
 	if (r < 0) {
-		_LOGW ("failed to set ifindex (%d)", r);
+		_LOGW ("failed to set ififindex (%d)", r);
 		goto error;
 	}
 
@@ -741,7 +741,7 @@ lease_to_ip6_config (const char *iface,
 			.timestamp = ts,
 			.lifetime = lft_valid,
 			.preferred = lft_pref,
-			.source = NM_IP_CONFIG_SOURCE_DHCP,
+			.addr_source = NM_IP_CONFIG_SOURCE_DHCP,
 		};
 
 		nm_ip6_config_add_address (ip6_config, &address);
@@ -895,6 +895,8 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
+	_LOGT ("dhcp-client6: set %p", priv->client4);
+
 	if (info_only)
 	    sd_dhcp6_client_set_information_request (priv->client6, 1);
 
@@ -928,7 +930,7 @@ ip6_start (NMDhcpClient *client,
 		}
 	}
 
-	r = sd_dhcp6_client_set_index (priv->client6, nm_dhcp_client_get_ifindex (client));
+	r = sd_dhcp6_client_set_ifindex (priv->client6, nm_dhcp_client_get_ifindex (client));
 	if (r < 0) {
 		_LOGW ("failed to set ifindex (%d)", r);
 		goto error;
@@ -958,6 +960,8 @@ ip6_start (NMDhcpClient *client,
 		goto error;
 	}
 
+	nm_dhcp_client_start_timeout (client);
+
 	return TRUE;
 
 error:
@@ -973,6 +977,10 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	int r = 0;
 
+	_LOGT ("dhcp-client%d: stop %p",
+	       priv->client4 ? '4' : '6',
+	       priv->client4 ? (gpointer) priv->client4 : (gpointer) priv->client6);
+
 	if (priv->client4) {
 		sd_dhcp_client_set_callback (priv->client4, NULL, NULL);
 		r = sd_dhcp_client_stop (priv->client4);
diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c
index a88a7e6d..593d8c5d 100644
--- a/src/dhcp-manager/nm-dhcp-utils.c
+++ b/src/dhcp-manager/nm-dhcp-utils.c
@@ -28,11 +28,14 @@
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-platform.h"
+#include "nm-dhcp-client-logging.h"
+#include "nm-core-internal.h"
 
 /********************************************/
 
 static gboolean
-ip4_process_dhcpcd_rfc3442_routes (const char *str,
+ip4_process_dhcpcd_rfc3442_routes (const char *iface,
+                                   const char *str,
                                    guint32 priority,
                                    NMIP4Config *ip4_config,
                                    guint32 *gwaddr)
@@ -45,7 +48,7 @@ ip4_process_dhcpcd_rfc3442_routes (const char *str,
 		goto out;
 
 	if ((g_strv_length (routes) % 2) != 0) {
-		nm_log_warn (LOGD_DHCP4, "  classless static routes provided, but invalid");
+		_LOG2W (LOGD_DHCP4, iface, "  classless static routes provided, but invalid");
 		goto out;
 	}
 
@@ -61,16 +64,16 @@ ip4_process_dhcpcd_rfc3442_routes (const char *str,
 			errno = 0;
 			rt_cidr = strtol (slash + 1, NULL, 10);
 			if (errno || rt_cidr > 32) {
-				nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
+				_LOG2W (LOGD_DHCP4, iface, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
 				continue;
 			}
 		}
 		if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
-			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
+			_LOG2W (LOGD_DHCP4, iface, "DHCP provided invalid classless static route address: '%s'", *r);
 			continue;
 		}
 		if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
-			nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
+			_LOG2W (LOGD_DHCP4, iface, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
 			continue;
 		}
 
@@ -79,12 +82,12 @@ ip4_process_dhcpcd_rfc3442_routes (const char *str,
 			/* FIXME: how to handle multiple routers? */
 			*gwaddr = rt_route;
 		} else {
-			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
+			_LOG2I (LOGD_DHCP4, iface, "  classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
 			memset (&route, 0, sizeof (route));
 			route.network = rt_addr;
 			route.plen = rt_cidr;
 			route.gateway = rt_route;
-			route.source = NM_IP_CONFIG_SOURCE_DHCP;
+			route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
 			route.metric = priority;
 			nm_ip4_config_add_route (ip4_config, &route);
 		}
@@ -160,7 +163,8 @@ error:
 }
 
 static gboolean
-ip4_process_dhclient_rfc3442_routes (const char *str,
+ip4_process_dhclient_rfc3442_routes (const char *iface,
+                                     const char *str,
                                      guint32 priority,
                                      NMIP4Config *ip4_config,
                                      guint32 *gwaddr)
@@ -172,7 +176,7 @@ ip4_process_dhclient_rfc3442_routes (const char *str,
 
 	o = octets = g_strsplit_set (str, " .", 0);
 	if (g_strv_length (octets) < 5) {
-		nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+		_LOG2W (LOGD_DHCP4, iface, "ignoring invalid classless static routes '%s'", str);
 		goto out;
 	}
 
@@ -180,7 +184,7 @@ ip4_process_dhclient_rfc3442_routes (const char *str,
 		memset (&route, 0, sizeof (route));
 		o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route, &success);
 		if (!success) {
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
+			_LOG2W (LOGD_DHCP4, iface, "ignoring invalid classless static routes");
 			break;
 		}
 
@@ -192,13 +196,13 @@ ip4_process_dhclient_rfc3442_routes (const char *str,
 			char addr[INET_ADDRSTRLEN];
 
 			/* normal route */
-			route.source = NM_IP_CONFIG_SOURCE_DHCP;
+			route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
 			route.metric = priority;
 			nm_ip4_config_add_route (ip4_config, &route);
 
-			nm_log_info (LOGD_DHCP4, "  classless static route %s/%d gw %s",
-			             nm_utils_inet4_ntop (route.network, addr), route.plen,
-			             nm_utils_inet4_ntop (route.gateway, NULL));
+			_LOG2I (LOGD_DHCP4, iface, "  classless static route %s/%d gw %s",
+			        nm_utils_inet4_ntop (route.network, addr), route.plen,
+			        nm_utils_inet4_ntop (route.gateway, NULL));
 		}
 	}
 
@@ -208,7 +212,8 @@ out:
 }
 
 static gboolean
-ip4_process_classless_routes (GHashTable *options,
+ip4_process_classless_routes (const char *iface,
+                              GHashTable *options,
                               guint32 priority,
                               NMIP4Config *ip4_config,
                               guint32 *gwaddr)
@@ -257,7 +262,7 @@ ip4_process_classless_routes (GHashTable *options,
 	p = str;
 	while (*p) {
 		if (!g_ascii_isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+			_LOG2W (LOGD_DHCP4, iface, "ignoring invalid classless static routes '%s'", str);
 			return FALSE;
 		}
 		p++;
@@ -265,14 +270,17 @@ ip4_process_classless_routes (GHashTable *options,
 
 	if (strchr (str, '/')) {
 		/* dhcpcd format */
-		return ip4_process_dhcpcd_rfc3442_routes (str, priority, ip4_config, gwaddr);
+		return ip4_process_dhcpcd_rfc3442_routes (iface, str, priority, ip4_config, gwaddr);
 	}
 
-	return ip4_process_dhclient_rfc3442_routes (str, priority, ip4_config, gwaddr);
+	return ip4_process_dhclient_rfc3442_routes (iface, str, priority, ip4_config, gwaddr);
 }
 
 static void
-process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4_config)
+process_classful_routes (const char *iface,
+                         GHashTable *options,
+                         guint32 priority,
+                         NMIP4Config *ip4_config)
 {
 	const char *str;
 	char **searches, **s;
@@ -283,7 +291,7 @@ process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4
 
 	searches = g_strsplit (str, " ", 0);
 	if ((g_strv_length (searches) % 2)) {
-		nm_log_info (LOGD_DHCP, "  static routes provided, but invalid");
+		_LOG2I (LOGD_DHCP, iface, "  static routes provided, but invalid");
 		goto out;
 	}
 
@@ -292,11 +300,11 @@ process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4
 		guint32 rt_addr, rt_route;
 
 		if (inet_pton (AF_INET, *s, &rt_addr) <= 0) {
-			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route address: '%s'", *s);
+			_LOG2W (LOGD_DHCP, iface, "DHCP provided invalid static route address: '%s'", *s);
 			continue;
 		}
 		if (inet_pton (AF_INET, *(s + 1), &rt_route) <= 0) {
-			nm_log_warn (LOGD_DHCP, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
+			_LOG2W (LOGD_DHCP, iface, "DHCP provided invalid static route gateway: '%s'", *(s + 1));
 			continue;
 		}
 
@@ -314,11 +322,11 @@ process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4
 			route.plen = 32;
 		}
 		route.gateway = rt_route;
-		route.source = NM_IP_CONFIG_SOURCE_DHCP;
+		route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
 		route.metric = priority;
 
 		nm_ip4_config_add_route (ip4_config, &route);
-		nm_log_info (LOGD_DHCP, "  static route %s",
+		_LOG2I (LOGD_DHCP, iface, "  static route %s",
 		             nm_platform_ip4_route_to_string (&route, NULL, 0));
 	}
 
@@ -327,7 +335,10 @@ out:
 }
 
 static void
-process_domain_search (const char *str, GFunc add_func, gpointer user_data)
+process_domain_search (const char *iface,
+                       const char *str,
+                       GFunc add_func,
+                       gpointer user_data)
 {
 	char **searches, **s;
 	char *unescaped, *p;
@@ -348,14 +359,14 @@ process_domain_search (const char *str, GFunc add_func, gpointer user_data)
 	} while (*p++);
 
 	if (strchr (unescaped, '\\')) {
-		nm_log_warn (LOGD_DHCP, "  invalid domain search: '%s'", unescaped);
+		_LOG2W (LOGD_DHCP, iface, "  invalid domain search: '%s'", unescaped);
 		goto out;
 	}
 
 	searches = g_strsplit (unescaped, " ", 0);
 	for (s = searches; *s; s++) {
 		if (strlen (*s)) {
-			nm_log_info (LOGD_DHCP, "  domain search '%s'", *s);
+			_LOG2I (LOGD_DHCP, iface, "  domain search '%s'", *s);
 			add_func (*s, user_data);
 		}
 	}
@@ -393,29 +404,29 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 
 	str = g_hash_table_lookup (options, "ip_address");
 	if (str && (inet_pton (AF_INET, str, &addr) > 0))
-		nm_log_info (LOGD_DHCP4, "  address %s", str);
+		_LOG2I (LOGD_DHCP4, iface, "  address %s", str);
 	else
 		goto error;
 
 	str = g_hash_table_lookup (options, "subnet_mask");
 	if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) {
 		plen = nm_utils_ip4_netmask_to_prefix (tmp_addr);
-		nm_log_info (LOGD_DHCP4, "  plen %d (%s)", plen, str);
+		_LOG2I (LOGD_DHCP4, iface, "  plen %d (%s)", plen, str);
 	} else {
 		/* Get default netmask for the IP according to appropriate class. */
 		plen = nm_utils_ip4_get_default_prefix (addr);
-		nm_log_info (LOGD_DHCP4, "  plen %d (default)", plen);
+		_LOG2I (LOGD_DHCP4, iface, "  plen %d (default)", plen);
 	}
 	nm_platform_ip4_address_set_addr (&address, addr, plen);
 
 	/* Routes: if the server returns classless static routes, we MUST ignore
 	 * the 'static_routes' option.
 	 */
-	if (!ip4_process_classless_routes (options, priority, ip4_config, &gwaddr))
-		process_classful_routes (options, priority, ip4_config);
+	if (!ip4_process_classless_routes (iface, options, priority, ip4_config, &gwaddr))
+		process_classful_routes (iface, options, priority, ip4_config);
 
 	if (gwaddr) {
-		nm_log_info (LOGD_DHCP4, "  gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
+		_LOG2I (LOGD_DHCP4, iface, "  gateway %s", nm_utils_inet4_ntop (gwaddr, NULL));
 		nm_ip4_config_set_gateway (ip4_config, gwaddr);
 	} else {
 		/* If the gateway wasn't provided as a classless static route with a
@@ -430,10 +441,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 				/* FIXME: how to handle multiple routers? */
 				if (inet_pton (AF_INET, *s, &gwaddr) > 0) {
 					nm_ip4_config_set_gateway (ip4_config, gwaddr);
-					nm_log_info (LOGD_DHCP4, "  gateway %s", *s);
+					_LOG2I (LOGD_DHCP4, iface, "  gateway %s", *s);
 					break;
 				} else
-					nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s);
+					_LOG2W (LOGD_DHCP4, iface, "ignoring invalid gateway '%s'", *s);
 			}
 			g_strfreev (routers);
 		}
@@ -455,7 +466,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 	if (str) {
 		if (inet_pton (AF_INET, str, &tmp_addr) > 0) {
 
-			nm_log_info (LOGD_DHCP4, "  server identifier %s", str);
+			_LOG2I (LOGD_DHCP4, iface, "  server identifier %s", str);
 			if (   nm_utils_ip4_address_clear_host_address(tmp_addr, address.plen) != nm_utils_ip4_address_clear_host_address(address.address, address.plen)
 			    && !nm_ip4_config_get_direct_route_for_host (ip4_config, tmp_addr)) {
 				/* DHCP server not on assigned subnet and the no direct route was returned. Add route */
@@ -465,29 +476,29 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 				route.plen = 32;
 				/* this will be a device route if gwaddr is 0 */
 				route.gateway = gwaddr;
-				route.source = NM_IP_CONFIG_SOURCE_DHCP;
+				route.rt_source = NM_IP_CONFIG_SOURCE_DHCP;
 				route.metric = priority;
 				nm_ip4_config_add_route (ip4_config, &route);
-				nm_log_dbg (LOGD_IP, "adding route for server identifier: %s",
-				                      nm_platform_ip4_route_to_string (&route, NULL, 0));
+				_LOG2D (LOGD_IP, iface, "adding route for server identifier: %s",
+				        nm_platform_ip4_route_to_string (&route, NULL, 0));
 			}
 		}
 		else
-			nm_log_warn (LOGD_DHCP4, "ignoring invalid server identifier '%s'", str);
+			_LOG2W (LOGD_DHCP4, iface, "ignoring invalid server identifier '%s'", str);
 	}
 
 	str = g_hash_table_lookup (options, "dhcp_lease_time");
 	if (str) {
 		address.lifetime = address.preferred = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP4, "  lease time %u", address.lifetime);
+		_LOG2I (LOGD_DHCP4, iface, "  lease time %u", address.lifetime);
 	}
 
-	address.source = NM_IP_CONFIG_SOURCE_DHCP;
+	address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
 	nm_ip4_config_add_address (ip4_config, &address);
 
 	str = g_hash_table_lookup (options, "host_name");
 	if (str)
-		nm_log_info (LOGD_DHCP4, "  hostname '%s'", str);
+		_LOG2I (LOGD_DHCP4, iface, "  hostname '%s'", str);
 
 	str = g_hash_table_lookup (options, "domain_name_servers");
 	if (str) {
@@ -498,10 +509,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
 				if (tmp_addr) {
 					nm_ip4_config_add_nameserver (ip4_config, tmp_addr);
-					nm_log_info (LOGD_DHCP4, "  nameserver '%s'", *s);
+					_LOG2I (LOGD_DHCP4, iface, "  nameserver '%s'", *s);
 				}
 			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s);
+				_LOG2W (LOGD_DHCP4, iface, "ignoring invalid nameserver '%s'", *s);
 		}
 		g_strfreev (dns);
 	}
@@ -512,7 +523,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 		char **s;
 
 		for (s = domains; *s; s++) {
-			nm_log_info (LOGD_DHCP4, "  domain name '%s'", *s);
+			_LOG2I (LOGD_DHCP4, iface, "  domain name '%s'", *s);
 			nm_ip4_config_add_domain (ip4_config, *s);
 		}
 		g_strfreev (domains);
@@ -520,7 +531,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 
 	str = g_hash_table_lookup (options, "domain_search");
 	if (str)
-		process_domain_search (str, ip4_add_domain_search, ip4_config);
+		process_domain_search (iface, str, ip4_add_domain_search, ip4_config);
 
 	str = g_hash_table_lookup (options, "netbios_name_servers");
 	if (str) {
@@ -531,10 +542,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
 				if (tmp_addr) {
 					nm_ip4_config_add_wins (ip4_config, tmp_addr);
-					nm_log_info (LOGD_DHCP4, "  wins '%s'", *s);
+					_LOG2I (LOGD_DHCP4, iface, "  wins '%s'", *s);
 				}
 			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s);
+				_LOG2W (LOGD_DHCP4, iface, "ignoring invalid WINS server '%s'", *s);
 		}
 		g_strfreev (nbns);
 	}
@@ -554,7 +565,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 
 	str = g_hash_table_lookup (options, "nis_domain");
 	if (str) {
-		nm_log_info (LOGD_DHCP4, "  NIS domain '%s'", str);
+		_LOG2I (LOGD_DHCP4, iface, "  NIS domain '%s'", str);
 		nm_ip4_config_set_nis_domain (ip4_config, str);
 	}
 
@@ -567,10 +578,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex,
 			if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
 				if (tmp_addr) {
 					nm_ip4_config_add_nis_server (ip4_config, tmp_addr);
-					nm_log_info (LOGD_DHCP4, "  nis '%s'", *s);
+					_LOG2I (LOGD_DHCP4, iface, "  nis '%s'", *s);
 				}
 			} else
-				nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
+				_LOG2W (LOGD_DHCP4, iface, "ignoring invalid NIS server '%s'", *s);
 		}
 		g_strfreev (nis);
 	}
@@ -615,8 +626,8 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex,
 
 	g_hash_table_iter_init (&iter, options);
 	while (g_hash_table_iter_next (&iter, &key, &value)) {
-		nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'",
-		            iface, (const char *) key, (const char *) value);
+		_LOG2D (LOGD_DHCP6, iface, "(%s): option '%s'=>'%s'",
+		        iface, (const char *) key, (const char *) value);
 	}
 
 	ip6_config = nm_ip6_config_new (ifindex);
@@ -624,27 +635,27 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex,
 	str = g_hash_table_lookup (options, "max_life");
 	if (str) {
 		address.lifetime = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP6, "  valid_lft %u", address.lifetime);
+		_LOG2I (LOGD_DHCP6, iface, "  valid_lft %u", address.lifetime);
 	}
 
 	str = g_hash_table_lookup (options, "preferred_life");
 	if (str) {
 		address.preferred = strtoul (str, NULL, 10);
-		nm_log_info (LOGD_DHCP6, "  preferred_lft %u", address.preferred);
+		_LOG2I (LOGD_DHCP6, iface, "  preferred_lft %u", address.preferred);
 	}
 
 	str = g_hash_table_lookup (options, "ip6_address");
 	if (str) {
 		if (!inet_pton (AF_INET6, str, &tmp_addr)) {
-			nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'",
-			             iface, str);
+			_LOG2W (LOGD_DHCP6, iface, "(%s): DHCP returned invalid address '%s'",
+			        iface, str);
 			goto error;
 		}
 
 		address.address = tmp_addr;
-		address.source = NM_IP_CONFIG_SOURCE_DHCP;
+		address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
 		nm_ip6_config_add_address (ip6_config, &address);
-		nm_log_info (LOGD_DHCP6, "  address %s", str);
+		_LOG2I (LOGD_DHCP6, iface, "  address %s", str);
 	} else if (info_only == FALSE) {
 		/* No address in Managed mode is a hard error */
 		goto error;
@@ -652,7 +663,7 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex,
 
 	str = g_hash_table_lookup (options, "host_name");
 	if (str)
-		nm_log_info (LOGD_DHCP6, "  hostname '%s'", str);
+		_LOG2I (LOGD_DHCP6, iface, "  hostname '%s'", str);
 
 	str = g_hash_table_lookup (options, "dhcp6_name_servers");
 	if (str) {
@@ -663,17 +674,17 @@ nm_dhcp_utils_ip6_config_from_options (int ifindex,
 			if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) {
 				if (!IN6_IS_ADDR_UNSPECIFIED (&tmp_addr)) {
 					nm_ip6_config_add_nameserver (ip6_config, &tmp_addr);
-					nm_log_info (LOGD_DHCP6, "  nameserver '%s'", *s);
+					_LOG2I (LOGD_DHCP6, iface, "  nameserver '%s'", *s);
 				}
 			} else
-				nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s);
+				_LOG2W (LOGD_DHCP6, iface, "ignoring invalid nameserver '%s'", *s);
 		}
 		g_strfreev (dns);
 	}
 
 	str = g_hash_table_lookup (options, "dhcp6_domain_search");
 	if (str)
-		process_domain_search (str, ip6_add_domain_search, ip6_config);
+		process_domain_search (iface, str, ip6_add_domain_search, ip6_config);
 
 	return ip6_config;
 
@@ -685,18 +696,9 @@ error:
 char *
 nm_dhcp_utils_duid_to_string (const GByteArray *duid)
 {
-	guint32 i = 0;
-	GString *s;
-
 	g_return_val_if_fail (duid != NULL, NULL);
 
-	s = g_string_sized_new (MIN (duid->len * 3, 50));
-	while (i < duid->len) {
-		if (s->len)
-			g_string_append_c (s, ':');
-		g_string_append_printf (s, "%02x", duid->data[i++]);
-	}
-	return g_string_free (s, FALSE);
+	return _nm_utils_bin2str (duid->data, duid->len, FALSE);
 }
 
 /**
diff --git a/src/dhcp-manager/nm-dhcp-utils.h b/src/dhcp-manager/nm-dhcp-utils.h
index 056982c3..6540b1ff 100644
--- a/src/dhcp-manager/nm-dhcp-utils.h
+++ b/src/dhcp-manager/nm-dhcp-utils.h
@@ -24,8 +24,6 @@
 #include <nm-ip4-config.h>
 #include <nm-ip6-config.h>
 
-#include "nm-default.h"
-
 NMIP4Config *nm_dhcp_utils_ip4_config_from_options (int ifindex,
                                                     const char *iface,
                                                     GHashTable *options,
diff --git a/src/dhcp-manager/tests/Makefile.in b/src/dhcp-manager/tests/Makefile.in
index 2528b3c5..1ce288ba 100644
--- a/src/dhcp-manager/tests/Makefile.in
+++ b/src/dhcp-manager/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-dhcp-dhclient$(EXEEXT) test-dhcp-utils$(EXEEXT)
 TESTS = test-dhcp-dhclient$(EXEEXT) test-dhcp-utils$(EXEEXT)
 subdir = src/dhcp-manager/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -428,6 +429,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -484,13 +486,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -502,6 +505,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -562,6 +567,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -597,6 +604,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c
index 046dd7e2..77849c8c 100644
--- a/src/dhcp-manager/tests/test-dhcp-dhclient.c
+++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c
@@ -31,7 +31,7 @@
 #include "nm-ip4-config.h"
 #include "nm-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 #define DEBUG 1
 
diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c
index f477c061..89d693ed 100644
--- a/src/dhcp-manager/tests/test-dhcp-utils.c
+++ b/src/dhcp-manager/tests/test-dhcp-utils.c
@@ -28,7 +28,7 @@
 #include "nm-dhcp-utils.h"
 #include "nm-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 typedef struct {
 	const char *name;
diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h
index 50f050e5..3deda582 100644
--- a/src/dns-manager/nm-dns-manager.h
+++ b/src/dns-manager/nm-dns-manager.h
@@ -24,12 +24,9 @@
 #ifndef __NETWORKMANAGER_DNS_MANAGER_H__
 #define __NETWORKMANAGER_DNS_MANAGER_H__
 
-#include "nm-default.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 
-G_BEGIN_DECLS
-
 typedef enum {
 	NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0,
 	NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE,
@@ -117,6 +114,4 @@ typedef enum {
 
 gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */
diff --git a/src/dns-manager/nm-dns-plugin.h b/src/dns-manager/nm-dns-plugin.h
index 4c84b145..e24bc173 100644
--- a/src/dns-manager/nm-dns-plugin.h
+++ b/src/dns-manager/nm-dns-plugin.h
@@ -19,9 +19,7 @@
 #ifndef __NETWORKMANAGER_DNS_PLUGIN_H__
 #define __NETWORKMANAGER_DNS_PLUGIN_H__
 
-#include "nm-default.h"
 #include "nm-dns-manager.h"
-
 #include "nm-config-data.h"
 
 #define NM_TYPE_DNS_PLUGIN            (nm_dns_plugin_get_type ())
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c
index 12395db6..7b725944 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-manager.c
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c
@@ -65,60 +65,6 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-static void
-nm_dnsmasq_manager_init (NMDnsMasqManager *manager)
-{
-}
-
-static void
-finalize (GObject *object)
-{
-	NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object);
-
-	nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object));
-
-	g_free (priv->iface);
-	g_free (priv->pidfile);
-
-	G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object);
-}
-
-static void
-nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
-
-	g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate));
-
-	object_class->finalize = finalize;
-
-	/* signals */
-	signals[STATE_CHANGED] =
-	     g_signal_new (NM_DNS_MASQ_MANAGER_STATE_CHANGED,
-	                   G_OBJECT_CLASS_TYPE (object_class),
-	                   G_SIGNAL_RUN_FIRST,
-	                   G_STRUCT_OFFSET (NMDnsMasqManagerClass, state_changed),
-	                   NULL, NULL,
-	                   g_cclosure_marshal_VOID__UINT,
-	                   G_TYPE_NONE, 1,
-	                   G_TYPE_UINT);
-}
-
-NMDnsMasqManager *
-nm_dnsmasq_manager_new (const char *iface)
-{
-	NMDnsMasqManager *manager;
-	NMDnsMasqManagerPrivate *priv;
-
-	manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL);
-
-	priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
-	priv->iface = g_strdup (iface);
-	priv->pidfile = g_strdup_printf (RUNSTATEDIR "/nm-dnsmasq-%s.pid", iface);
-
-	return manager;
-}
-
 typedef struct {
 	GPtrArray *array;
 	GStringChunk *chunk;
@@ -387,3 +333,56 @@ nm_dnsmasq_manager_stop (NMDnsMasqManager *manager)
 
 	unlink (priv->pidfile);
 }
+
+NMDnsMasqManager *
+nm_dnsmasq_manager_new (const char *iface)
+{
+	NMDnsMasqManager *manager;
+	NMDnsMasqManagerPrivate *priv;
+
+	manager = (NMDnsMasqManager *) g_object_new (NM_TYPE_DNSMASQ_MANAGER, NULL);
+
+	priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (manager);
+	priv->iface = g_strdup (iface);
+	priv->pidfile = g_strdup_printf (RUNSTATEDIR "/nm-dnsmasq-%s.pid", iface);
+
+	return manager;
+}
+
+static void
+nm_dnsmasq_manager_init (NMDnsMasqManager *manager)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	NMDnsMasqManagerPrivate *priv = NM_DNSMASQ_MANAGER_GET_PRIVATE (object);
+
+	nm_dnsmasq_manager_stop (NM_DNSMASQ_MANAGER (object));
+
+	g_free (priv->iface);
+	g_free (priv->pidfile);
+
+	G_OBJECT_CLASS (nm_dnsmasq_manager_parent_class)->finalize (object);
+}
+
+static void
+nm_dnsmasq_manager_class_init (NMDnsMasqManagerClass *manager_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+	g_type_class_add_private (manager_class, sizeof (NMDnsMasqManagerPrivate));
+
+	object_class->finalize = finalize;
+
+	/* signals */
+	signals[STATE_CHANGED] =
+	     g_signal_new (NM_DNS_MASQ_MANAGER_STATE_CHANGED,
+	                   G_OBJECT_CLASS_TYPE (object_class),
+	                   G_SIGNAL_RUN_FIRST,
+	                   0, NULL, NULL,
+	                   g_cclosure_marshal_VOID__UINT,
+	                   G_TYPE_NONE, 1,
+	                   G_TYPE_UINT);
+}
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.h b/src/dnsmasq-manager/nm-dnsmasq-manager.h
index 47c88e6e..b610b498 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-manager.h
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_DNSMASQ_MANAGER_H__
 #define __NETWORKMANAGER_DNSMASQ_MANAGER_H__
 
-
-#include "nm-default.h"
 #include "nm-ip4-config.h"
 
 #define NM_TYPE_DNSMASQ_MANAGER            (nm_dnsmasq_manager_get_type ())
@@ -48,9 +46,6 @@ typedef struct {
 
 typedef struct {
 	GObjectClass parent;
-
-	/* Signals */
-	void (*state_changed) (NMDnsMasqManager *manager, NMDnsMasqStatus status);
 } NMDnsMasqManagerClass;
 
 GType nm_dnsmasq_manager_get_type (void);
diff --git a/src/dnsmasq-manager/nm-dnsmasq-utils.h b/src/dnsmasq-manager/nm-dnsmasq-utils.h
index 43d0b9a3..459dda04 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-utils.h
+++ b/src/dnsmasq-manager/nm-dnsmasq-utils.h
@@ -21,7 +21,6 @@
 #ifndef __NETWORKMANAGER_DNSMASQ_UTILS_H__
 #define __NETWORKMANAGER_DNSMASQ_UTILS_H__
 
-#include "nm-default.h"
 #include "nm-platform.h"
 
 gboolean nm_dnsmasq_utils_get_range (const NMPlatformIP4Address *addr,
diff --git a/src/dnsmasq-manager/tests/Makefile.in b/src/dnsmasq-manager/tests/Makefile.in
index 38344a8b..70f1ba1d 100644
--- a/src/dnsmasq-manager/tests/Makefile.in
+++ b/src/dnsmasq-manager/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-dnsmasq-utils$(EXEEXT)
 TESTS = test-dnsmasq-utils$(EXEEXT)
 subdir = src/dnsmasq-manager/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -422,6 +423,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -478,13 +480,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -496,6 +499,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -556,6 +561,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -591,6 +598,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/dnsmasq-manager/tests/test-dnsmasq-utils.c b/src/dnsmasq-manager/tests/test-dnsmasq-utils.c
index aeec5879..cf6dab50 100644
--- a/src/dnsmasq-manager/tests/test-dnsmasq-utils.c
+++ b/src/dnsmasq-manager/tests/test-dnsmasq-utils.c
@@ -24,7 +24,7 @@
 
 #include "nm-dnsmasq-utils.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static void
 test_address_ranges (void)
diff --git a/src/main-utils.c b/src/main-utils.c
index 86242802..c2ed9d1c 100644
--- a/src/main-utils.c
+++ b/src/main-utils.c
@@ -31,7 +31,6 @@
 
 #include <glib/gstdio.h>
 #include <glib-unix.h>
-#include <gmodule.h>
 
 #include "main-utils.h"
 #include "NetworkManagerUtils.h"
@@ -243,6 +242,7 @@ nm_main_utils_early_setup (const char *progname,
 	textdomain (GETTEXT_PACKAGE);
 
 	for (i = 0; options[i].long_name; i++) {
+		NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral")
 		if (!strcmp (options[i].long_name, "log-level")) {
 			opt_fmt_log_level = options[i].description;
 			opt_loc_log_level = &options[i].description;
@@ -252,6 +252,7 @@ nm_main_utils_early_setup (const char *progname,
 			opt_loc_log_domains = &options[i].description;
 			options[i].description = g_strdup_printf (options[i].description, nm_logging_all_domains_to_string ());
 		}
+		NM_PRAGMA_WARNING_REENABLE
 	}
 
 	/* Parse options */
diff --git a/src/main-utils.h b/src/main-utils.h
index 10ba3135..dbbdb85f 100644
--- a/src/main-utils.h
+++ b/src/main-utils.h
@@ -21,8 +21,6 @@
 #ifndef __MAIN_UTILS_H__
 #define __MAIN_UTILS_H__
 
-#include "nm-default.h"
-
 void     nm_main_utils_ensure_root (void);
 
 void     nm_main_utils_setup_signals (GMainLoop *main_loop);
diff --git a/src/main.c b/src/main.c
index e243baa8..490dcfe9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,7 +31,6 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <gmodule.h>
 #include <string.h>
 #include <sys/resource.h>
 
@@ -71,81 +70,10 @@ static struct {
 	char *opt_log_level;
 	char *opt_log_domains;
 	char *pidfile;
-	char *state_file;
 } global_opt = {
 	.become_daemon = TRUE,
 };
 
-static gboolean
-parse_state_file (const char *filename,
-                  gboolean *net_enabled,
-                  gboolean *wifi_enabled,
-                  gboolean *wwan_enabled,
-                  GError **error)
-{
-	GKeyFile *state_file;
-	GError *tmp_error = NULL;
-	gboolean wifi, net, wwan;
-
-	g_return_val_if_fail (net_enabled != NULL, FALSE);
-	g_return_val_if_fail (wifi_enabled != NULL, FALSE);
-	g_return_val_if_fail (wwan_enabled != NULL, FALSE);
-
-	state_file = g_key_file_new ();
-	g_key_file_set_list_separator (state_file, ',');
-	if (!g_key_file_load_from_file (state_file, filename, G_KEY_FILE_KEEP_COMMENTS, &tmp_error)) {
-		gboolean ret = FALSE;
-
-		/* This is kinda ugly; create the file and directory if it doesn't
-		 * exist yet.  We can't rely on distros necessarily creating the
-		 * /var/lib/NetworkManager for us since we have to ensure that
-		 * users upgrading NM get this working too.
-		 */
-		if (g_error_matches (tmp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
-			char *data;
-			gsize len = 0;
-
-			g_clear_error (&tmp_error);
-
-			/* Write out the initial state to the state file */
-			g_key_file_set_boolean (state_file, "main", "NetworkingEnabled", *net_enabled);
-			g_key_file_set_boolean (state_file, "main", "WirelessEnabled", *wifi_enabled);
-			g_key_file_set_boolean (state_file, "main", "WWANEnabled", *wwan_enabled);
-
-			data = g_key_file_to_data (state_file, &len, NULL);
-			if (data)
-				ret = g_file_set_contents (filename, data, len, error);
-			g_free (data);
-		} else {
-			/* the error is not "No such file or directory" - propagate the error */
-			g_propagate_error (error, tmp_error);
-		}
-
-		return ret;
-	}
-
-	/* Reading state bits of NetworkManager; an error leaves the passed-in state
-	 * value unchanged.
-	 */
-	net = g_key_file_get_boolean (state_file, "main", "NetworkingEnabled", &tmp_error);
-	if (tmp_error == NULL)
-		*net_enabled = net;
-	g_clear_error (&tmp_error);
-
-	wifi = g_key_file_get_boolean (state_file, "main", "WirelessEnabled", &tmp_error);
-	if (tmp_error == NULL)
-		*wifi_enabled = wifi;
-	g_clear_error (&tmp_error);
-
-	wwan = g_key_file_get_boolean (state_file, "main", "WWANEnabled", &tmp_error);
-	if (tmp_error == NULL)
-		*wwan_enabled = wwan;
-	g_clear_error (&tmp_error);
-
-	g_key_file_free (state_file);
-	return TRUE;
-}
-
 static void
 _set_g_fatal_warnings (void)
 {
@@ -256,7 +184,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
 		  "PLATFORM,RFKILL,WIFI" },
 		{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
 		{ "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_(NM_DEFAULT_PID_FILE) },
-		{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_(NM_DEFAULT_SYSTEM_STATE_FILE) },
 		{ "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL },
 		{ "print-config", 0, 0, G_OPTION_ARG_NONE, &global_opt.print_config, N_("Print NetworkManager configuration and exit"), NULL },
 		{NULL}
@@ -272,7 +199,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
 		exit (1);
 
 	global_opt.pidfile = global_opt.pidfile ? global_opt.pidfile : g_strdup (NM_DEFAULT_PID_FILE);
-	global_opt.state_file = global_opt.state_file ? global_opt.state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE);
 }
 
 /*
@@ -282,7 +208,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
 int
 main (int argc, char *argv[])
 {
-	gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE;
 	gboolean success = FALSE;
 	NMConfig *config;
 	GError *error = NULL;
@@ -423,17 +348,12 @@ main (int argc, char *argv[])
 
 	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");
 
-	/* Parse the state file */
-	if (!parse_state_file (global_opt.state_file, &net_enabled, &wifi_enabled, &wwan_enabled, &error)) {
-		nm_log_err (LOGD_CORE, "State file %s parsing failed: %s",
-		            global_opt.state_file,
-		            error->message);
-		/* Not a hard failure */
-	}
-	g_clear_error (&error);
-
 	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
 	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
+
+	/* the first access to State causes the file to be read (and possibly print a warning) */
+	nm_config_state_get (config);
+
 	nm_log_dbg (LOGD_CORE, "WEXT support is %s",
 #if HAVE_WEXT
 	             "enabled"
@@ -444,10 +364,7 @@ main (int argc, char *argv[])
 
 	nm_auth_manager_setup (nm_config_get_auth_polkit (config));
 
-	nm_manager_setup (global_opt.state_file,
-	                  net_enabled,
-	                  wifi_enabled,
-	                  wwan_enabled);
+	nm_manager_setup ();
 
 	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");
@@ -499,6 +416,8 @@ done:
 
 	nm_manager_stop (nm_manager_get ());
 
+	nm_config_state_set (config, TRUE, TRUE);
+
 	if (global_opt.pidfile && wrote_pidfile)
 		unlink (global_opt.pidfile);
 
diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c
index acc3e4ca..22fa5b69 100644
--- a/src/nm-activation-request.c
+++ b/src/nm-activation-request.c
@@ -445,9 +445,9 @@ master_failed (NMActiveConnection *self)
 	if (device) {
 		device_state = nm_device_get_state (device);
 		if (nm_device_is_activating (device) || (device_state == NM_DEVICE_STATE_ACTIVATED)) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+			nm_device_queue_state (device,
+			                       NM_DEVICE_STATE_FAILED,
+			                       NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
 			return;
 		}
 	}
diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h
index 929ce195..1bd00499 100644
--- a/src/nm-activation-request.h
+++ b/src/nm-activation-request.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_ACTIVATION_REQUEST_H__
 #define __NETWORKMANAGER_ACTIVATION_REQUEST_H__
 
-
-#include "nm-default.h"
 #include "nm-connection.h"
 #include "nm-active-connection.h"
 
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 2b7847b2..1844587c 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -546,7 +546,7 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
 
 		g_signal_connect (device, NM_DEVICE_STATE_CHANGED,
 		                  G_CALLBACK (device_state_changed), self);
-		g_signal_connect (device, "notify::master",
+		g_signal_connect (device, "notify::" NM_DEVICE_MASTER,
 		                  G_CALLBACK (device_master_changed), self);
 		g_signal_connect (device, "notify::" NM_DEVICE_METERED,
 		                  G_CALLBACK (device_metered_changed), self);
@@ -719,14 +719,14 @@ nm_active_connection_get_assumed (NMActiveConnection *self)
 
 /****************************************************************/
 
-static void unwatch_parent (NMActiveConnection *self);
+static void unwatch_parent (NMActiveConnection *self, gboolean unref);
 
 static void
 parent_destroyed (gpointer user_data, GObject *parent)
 {
 	NMActiveConnection *self = user_data;
 
-	unwatch_parent (self);
+	unwatch_parent (self, FALSE);
 	g_signal_emit (self, signals[PARENT_ACTIVE], 0, NULL);
 }
 
@@ -741,19 +741,20 @@ parent_state_cb (NMActiveConnection *parent_ac,
 	if (parent_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
 		return;
 
-	unwatch_parent (self);
+	unwatch_parent (self, TRUE);
 	g_signal_emit (self, signals[PARENT_ACTIVE], 0, parent_ac);
 }
 
 static void
-unwatch_parent (NMActiveConnection *self)
+unwatch_parent (NMActiveConnection *self, gboolean unref)
 {
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
 	g_signal_handlers_disconnect_by_func (priv->parent,
 	                                      (GCallback) parent_state_cb,
 	                                      self);
-	g_object_weak_unref ((GObject *) priv->parent, parent_destroyed, self);
+	if (unref)
+		g_object_weak_unref ((GObject *) priv->parent, parent_destroyed, self);
 	priv->parent = NULL;
 }
 
@@ -1135,7 +1136,7 @@ dispose (GObject *object)
 	g_clear_object (&priv->master);
 
 	if (priv->parent)
-		unwatch_parent (self);
+		unwatch_parent (self, TRUE);
 
 	g_clear_object (&priv->subject);
 
diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c
index 728575f6..9e884096 100644
--- a/src/nm-audit-manager.c
+++ b/src/nm-audit-manager.c
@@ -233,10 +233,10 @@ nm_audit_manager_audit_enabled (NMAuditManager *self)
 void
 _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, guint line,
                                      const char *func, const char *op, NMSettingsConnection *connection,
-                                     gboolean result, gpointer subject_context, const char *reason)
+                                     gboolean result, const char *args, gpointer subject_context, const char *reason)
 {
 	gs_unref_ptrarray GPtrArray *fields = NULL;
-	AuditField uuid_field = { }, name_field = { };
+	AuditField uuid_field = { }, name_field = { }, args_field = { };
 
 	g_return_if_fail (op);
 
@@ -252,11 +252,16 @@ _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, gui
 		g_ptr_array_add (fields, &name_field);
 	}
 
+	if (args) {
+		_audit_field_init_string (&args_field, "args", args, FALSE, BACKEND_ALL);
+		g_ptr_array_add (fields, &args_field);
+	}
+
 	_audit_log_helper (self, fields, file, line, func, op, result, subject_context, reason);
 }
 
 void
-_nm_audit_manager_log_control_op (NMAuditManager *self, const char *file, guint line,
+_nm_audit_manager_log_generic_op (NMAuditManager *self, const char *file, guint line,
                                   const char *func, const char *op, const char *arg,
                                   gboolean result, gpointer subject_context,
                                   const char *reason)
diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h
index 9aef417f..af8a2037 100644
--- a/src/nm-audit-manager.h
+++ b/src/nm-audit-manager.h
@@ -21,15 +21,10 @@
 #ifndef __NM_AUDIT_MANAGER_H__
 #define __NM_AUDIT_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
-
 #include "nm-connection.h"
 #include "nm-device.h"
 #include "nm-types.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_AUDIT_MANAGER            (nm_audit_manager_get_type ())
 #define NM_AUDIT_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AUDIT_MANAGER, NMAuditManager))
 #define NM_AUDIT_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_AUDIT_MANAGER, NMAuditManagerClass))
@@ -57,6 +52,7 @@ typedef struct {
 #define NM_AUDIT_OP_SLEEP_CONTROL           "sleep-control"
 #define NM_AUDIT_OP_NET_CONTROL             "networking-control"
 #define NM_AUDIT_OP_RADIO_CONTROL           "radio-control"
+#define NM_AUDIT_OP_STATISTICS              "statistics"
 
 #define NM_AUDIT_OP_DEVICE_AUTOCONNECT      "device-autoconnect"
 #define NM_AUDIT_OP_DEVICE_DISCONNECT       "device-disconnect"
@@ -64,17 +60,21 @@ typedef struct {
 #define NM_AUDIT_OP_DEVICE_MANAGED          "device-managed"
 #define NM_AUDIT_OP_DEVICE_REAPPLY          "device-reapply"
 
+#define NM_AUDIT_OP_CHECKPOINT_CREATE       "checkpoint-create"
+#define NM_AUDIT_OP_CHECKPOINT_ROLLBACK     "checkpoint-rollback"
+#define NM_AUDIT_OP_CHECKPOINT_DESTROY      "checkpoint-destroy"
+
 GType nm_audit_manager_get_type (void);
 NMAuditManager *nm_audit_manager_get (void);
 gboolean nm_audit_manager_audit_enabled (NMAuditManager *self);
 
-#define nm_audit_log_connection_op(op, connection, result, subject_context, reason) \
+#define nm_audit_log_connection_op(op, connection, result, args, subject_context, reason) \
 	G_STMT_START { \
 		NMAuditManager *_audit = nm_audit_manager_get (); \
 		\
 		if (nm_audit_manager_audit_enabled (_audit)) { \
 			_nm_audit_manager_log_connection_op (_audit, __FILE__, __LINE__, G_STRFUNC, \
-			                                     (op), (connection), (result), (subject_context), \
+			                                     (op), (connection), (result), (args), (subject_context), \
 			                                     (reason)); \
 		} \
 	} G_STMT_END
@@ -84,7 +84,7 @@ gboolean nm_audit_manager_audit_enabled (NMAuditManager *self);
 		NMAuditManager *_audit = nm_audit_manager_get (); \
 		\
 		if (nm_audit_manager_audit_enabled (_audit)) { \
-			_nm_audit_manager_log_control_op (_audit, __FILE__, __LINE__, G_STRFUNC, \
+			_nm_audit_manager_log_generic_op (_audit, __FILE__, __LINE__, G_STRFUNC, \
 			                                  (op), (arg), (result), (subject_context), (reason)); \
 		} \
 	} G_STMT_END
@@ -99,17 +99,27 @@ gboolean nm_audit_manager_audit_enabled (NMAuditManager *self);
 		} \
 	} G_STMT_END
 
+#define nm_audit_log_checkpoint_op(op, arg, result, subject_context, reason) \
+	G_STMT_START { \
+		NMAuditManager *_audit = nm_audit_manager_get (); \
+		\
+		if (nm_audit_manager_audit_enabled (_audit)) { \
+			_nm_audit_manager_log_generic_op (_audit, __FILE__, __LINE__, G_STRFUNC, \
+			                                  (op), (arg), (result), (subject_context), (reason)); \
+		} \
+	} G_STMT_END
+
 void _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, guint line,
                                           const char *func, const char *op, NMSettingsConnection *connection,
-                                          gboolean result, gpointer subject_context, const char *reason);
+                                          gboolean result, const char *args, gpointer subject_context,
+                                          const char *reason);
 
-void _nm_audit_manager_log_control_op    (NMAuditManager *self, const char *file, guint line,
+void _nm_audit_manager_log_generic_op    (NMAuditManager *self, const char *file, guint line,
                                           const char *func, const char *op, const char *arg,
                                           gboolean result, gpointer subject_context, const char *reason);
 
 void _nm_audit_manager_log_device_op     (NMAuditManager *self, const char *file, guint line,
                                           const char *func, const char *op, NMDevice *device,
                                           gboolean result, gpointer subject_context, const char *reason);
-G_END_DECLS
 
 #endif /* __NM_AUDIT_MANAGER_H__ */
diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h
index 2875cccb..9251b334 100644
--- a/src/nm-auth-manager.h
+++ b/src/nm-auth-manager.h
@@ -21,13 +21,8 @@
 #ifndef NM_AUTH_MANAGER_H
 #define NM_AUTH_MANAGER_H
 
-
-#include "nm-default.h"
 #include "nm-auth-subject.h"
 
-G_BEGIN_DECLS
-
-
 #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))
@@ -72,7 +67,5 @@ gboolean nm_auth_manager_polkit_authority_check_authorization_finish (NMAuthMana
 
 #endif
 
-G_END_DECLS
-
 #endif /* NM_AUTH_MANAGER_H */
 
diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c
index 86b873f0..eb496b28 100644
--- a/src/nm-auth-subject.c
+++ b/src/nm-auth-subject.c
@@ -37,10 +37,6 @@
 #include "nm-enum-types.h"
 #include "NetworkManagerUtils.h"
 
-G_DEFINE_TYPE (NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT)
-
-#define NM_AUTH_SUBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AUTH_SUBJECT, NMAuthSubjectPrivate))
-
 enum {
 	PROP_0,
 	PROP_SUBJECT_TYPE,
@@ -61,6 +57,30 @@ typedef struct {
 	} unix_process;
 } NMAuthSubjectPrivate;
 
+struct _NMAuthSubject {
+	GObject parent;
+	NMAuthSubjectPrivate _priv;
+};
+
+struct _NMAuthSubjectClass {
+	GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMAuthSubject, nm_auth_subject, G_TYPE_OBJECT)
+
+#define NM_AUTH_SUBJECT_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMAuthSubject *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_AUTH_SUBJECT (_self)); \
+		&_self->_priv; \
+	})
+
 /**************************************************************/
 
 #define CHECK_SUBJECT(self, error_value) \
@@ -252,7 +272,7 @@ nm_auth_subject_new_internal (void)
 static void
 get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
-	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object);
+	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE ((NMAuthSubject *) object);
 
 	switch (prop_id) {
 	case PROP_SUBJECT_TYPE:
@@ -276,7 +296,7 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 static void
 set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
-	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object);
+	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE ((NMAuthSubject *) object);
 	NMAuthSubjectType subject_type;
 	const char *str;
 	gulong id;
@@ -317,8 +337,10 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 }
 
 static void
-_clear_private (NMAuthSubjectPrivate *priv)
+_clear_private (NMAuthSubject *self)
 {
+	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (self);
+
 	priv->subject_type = NM_AUTH_SUBJECT_TYPE_INVALID;
 	priv->unix_process.pid = G_MAXULONG;
 	priv->unix_process.uid = G_MAXULONG;
@@ -328,7 +350,7 @@ _clear_private (NMAuthSubjectPrivate *priv)
 static void
 nm_auth_subject_init (NMAuthSubject *self)
 {
-	_clear_private (NM_AUTH_SUBJECT_GET_PRIVATE (self));
+	_clear_private (self);
 }
 
 static void
@@ -363,7 +385,7 @@ constructed (GObject *object)
 			/* Is the process already gone? Then fail creation of the auth subject
 			 * by clearing the type. */
 			if (kill (priv->unix_process.pid, 0) != 0)
-				_clear_private (priv);
+				_clear_private (self);
 
 			/* Otherwise, although we didn't detect a start_time, the process is still around.
 			 * That could be due to procfs mounted with hidepid. So just accept the request.
@@ -378,16 +400,14 @@ constructed (GObject *object)
 		break;
 	}
 
-	_clear_private (priv);
+	_clear_private (self);
 	g_return_if_reached ();
 }
 
 static void
 finalize (GObject *object)
 {
-	NMAuthSubjectPrivate *priv = NM_AUTH_SUBJECT_GET_PRIVATE (object);
-
-	_clear_private (priv);
+	_clear_private ((NMAuthSubject *) object);
 
 	G_OBJECT_CLASS (nm_auth_subject_parent_class)->finalize (object);
 }
@@ -397,8 +417,6 @@ nm_auth_subject_class_init (NMAuthSubjectClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
 
-	g_type_class_add_private (config_class, sizeof (NMAuthSubjectPrivate));
-
 	/* virtual methods */
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h
index 5f507058..a0b6d14b 100644
--- a/src/nm-auth-subject.h
+++ b/src/nm-auth-subject.h
@@ -39,13 +39,7 @@ typedef enum {
 #define NM_AUTH_SUBJECT_UNIX_PROCESS_PID           "unix-process-pid"
 #define NM_AUTH_SUBJECT_UNIX_PROCESS_UID           "unix-process-uid"
 
-struct _NMAuthSubject {
-	GObject parent;
-};
-
-typedef struct {
-	GObjectClass parent;
-} NMAuthSubjectClass;
+typedef struct _NMAuthSubjectClass NMAuthSubjectClass;
 
 GType nm_auth_subject_get_type (void);
 
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c
index e1761cab..a6bc506c 100644
--- a/src/nm-auth-utils.c
+++ b/src/nm-auth-utils.c
@@ -399,8 +399,8 @@ nm_auth_chain_add_call (NMAuthChain *self,
 		                                                      call);
 #else
 		if (!call->chain->error) {
-			call->chain->error = g_error_new_literal (DBUS_GERROR,
-			                                          DBUS_GERROR_FAILED,
+			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);
diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c
index 656e7dbf..449de4e6 100644
--- a/src/nm-bus-manager.c
+++ b/src/nm-bus-manager.c
@@ -533,6 +533,54 @@ nm_bus_manager_get_caller_info_from_message (NMBusManager *self,
 	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,
diff --git a/src/nm-bus-manager.h b/src/nm-bus-manager.h
index 49579d46..bc23d2ce 100644
--- a/src/nm-bus-manager.h
+++ b/src/nm-bus-manager.h
@@ -22,8 +22,6 @@
 #ifndef __NM_BUS_MANAGER_H__
 #define __NM_BUS_MANAGER_H__
 
-G_BEGIN_DECLS
-
 #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))
@@ -68,6 +66,12 @@ gboolean nm_bus_manager_get_caller_info (NMBusManager *self,
                                          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);
 
@@ -102,6 +106,4 @@ GDBusProxy *nm_bus_manager_new_proxy (NMBusManager *self,
                                       const char *path,
                                       const char *iface);
 
-G_END_DECLS
-
 #endif /* __NM_BUS_MANAGER_H__ */
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
new file mode 100644
index 00000000..254dc31e
--- /dev/null
+++ b/src/nm-checkpoint-manager.c
@@ -0,0 +1,298 @@
+/* -*- 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) 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-checkpoint-manager.h"
+
+#include "nm-checkpoint.h"
+#include "nm-connection.h"
+#include "nm-core-utils.h"
+#include "nm-device.h"
+#include "nm-exported-object.h"
+#include "nm-manager.h"
+#include "nm-utils.h"
+
+/*****************************************************************************/
+
+struct _NMCheckpointManager {
+	NMManager *_manager;
+	GHashTable *checkpoints;
+	guint rollback_timeout_id;
+};
+
+#define GET_MANAGER(self) \
+	({ \
+		typeof (self) _self = (self); \
+		\
+		_nm_unused NMCheckpointManager *_self2 = _self; \
+		\
+		nm_assert (_self); \
+		nm_assert (NM_IS_MANAGER (_self->_manager)); \
+		_self->_manager; \
+	})
+
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME                "checkpoint"
+#define _NMLOG_DOMAIN                     LOGD_CORE
+
+#define _NMLOG(level, ...) \
+	nm_log (level, _NMLOG_DOMAIN, \
+	        "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+	        _NMLOG_PREFIX_NAME \
+	        _NM_UTILS_MACRO_REST(__VA_ARGS__))
+
+/*****************************************************************************/
+
+static void update_rollback_timeout (NMCheckpointManager *self);
+
+static void
+checkpoint_destroy (gpointer checkpoint)
+{
+	nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint));
+	g_object_unref (G_OBJECT (checkpoint));
+}
+
+static gboolean
+rollback_timeout_cb (NMCheckpointManager *self)
+{
+	NMCheckpoint *checkpoint;
+	GHashTableIter iter;
+	GVariant *result;
+	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);
+		}
+	}
+
+	self->rollback_timeout_id = 0;
+	update_rollback_timeout (self);
+
+	return G_SOURCE_REMOVE;
+}
+
+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;
+	}
+
+	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 NMCheckpoint *
+find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
+{
+	GHashTableIter iter;
+	NMCheckpoint *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 *
+nm_checkpoint_manager_create (NMCheckpointManager *self,
+                              const char *const *device_paths,
+                              guint32 rollback_timeout,
+                              NMCheckpointCreateFlags flags,
+                              GError **error)
+{
+	NMCheckpoint *checkpoint;
+	const char * const *path;
+	gs_unref_ptrarray GPtrArray *devices = NULL;
+	NMDevice *device;
+	const char *checkpoint_path;
+	guint i;
+
+	g_return_val_if_fail (self, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	devices = g_ptr_array_new ();
+	for (path = device_paths; *path; path++) {
+		device = nm_manager_get_device_by_path (GET_MANAGER (self), *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)) {
+		for (i = 0; i < devices->len; i++) {
+			device = devices->pdata[i];
+			if (find_checkpoint_for_device (self, device)) {
+				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+				             "a checkpoint for device '%s' already exists",
+				             nm_device_get_iface (device));
+				return NULL;
+			}
+		}
+	}
+
+	checkpoint = nm_checkpoint_new (GET_MANAGER (self), devices,
+	                                rollback_timeout, 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));
+
+	if (!nm_g_hash_table_insert (self->checkpoints,
+	                             (gpointer) checkpoint_path,
+	                             checkpoint))
+		g_return_val_if_reached (NULL);
+
+	update_rollback_timeout (self);
+
+	return checkpoint;
+}
+
+gboolean
+nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
+                                   GError **error)
+{
+	g_return_val_if_fail (self, FALSE);
+
+	g_hash_table_remove_all (self->checkpoints);
+
+	return TRUE;
+}
+
+gboolean
+nm_checkpoint_manager_destroy (NMCheckpointManager *self,
+                               const char *checkpoint_path,
+                               GError **error)
+{
+	gboolean ret;
+
+	g_return_val_if_fail (self, FALSE);
+	g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	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 *checkpoint_path,
+                                GVariant **results,
+                                GError **error)
+{
+	NMCheckpoint *cp;
+
+	g_return_val_if_fail (self, 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);
+
+	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 = nm_checkpoint_rollback (cp);
+	g_hash_table_remove (self->checkpoints, checkpoint_path);
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+NMCheckpointManager *
+nm_checkpoint_manager_new (NMManager *manager)
+{
+	NMCheckpointManager *self;
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), FALSE);
+
+	self = g_slice_new0 (NMCheckpointManager);
+
+	/* the NMCheckpointManager instance is actually owned by NMManager.
+	 * Thus, we cannot take a reference to it, and we also don't bother
+	 * taking a weak-reference. Instead let GET_MANAGER() assert that
+	 * self->_manager is alive -- which we always expect as the lifetime
+	 * of NMManager shall surpass the lifetime of the NMCheckpointManager
+	 * instance. */
+	self->_manager = manager;
+	self->checkpoints = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                           NULL, checkpoint_destroy);
+
+	return self;
+}
+
+void
+nm_checkpoint_manager_unref (NMCheckpointManager *self)
+{
+	if (!self)
+		return;
+
+	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
new file mode 100644
index 00000000..022b1a80
--- /dev/null
+++ b/src/nm-checkpoint-manager.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager
+ *
+ * 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) 2016 Red Hat, Inc.
+ */
+
+#ifndef __NM_CHECKPOINT_MANAGER_H__
+#define __NM_CHECKPOINT_MANAGER_H__
+
+#include "nm-dbus-interface.h"
+#include "nm-checkpoint.h"
+
+typedef struct _NMCheckpointManager NMCheckpointManager;
+
+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,
+                                            guint32 rollback_timeout,
+                                            NMCheckpointCreateFlags flags,
+                                            GError **error);
+
+gboolean nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
+                                            GError **error);
+
+gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self,
+                                        const char *checkpoint_path,
+                                        GError **error);
+gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self,
+                                         const char *checkpoint_path,
+                                         GVariant **results,
+                                         GError **error);
+
+#endif /* __NM_CHECKPOINT_MANAGER_H__ */
+
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
new file mode 100644
index 00000000..cb1adc39
--- /dev/null
+++ b/src/nm-checkpoint.c
@@ -0,0 +1,433 @@
+/* -*- 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) 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+#include "nm-checkpoint.h"
+
+#include <string.h>
+
+#include "nm-auth-subject.h"
+#include "nm-core-utils.h"
+#include "nm-dbus-interface.h"
+#include "nm-device.h"
+#include "nm-manager.h"
+#include "nm-settings.h"
+#include "nm-settings-connection.h"
+#include "nm-simple-connection.h"
+#include "nm-utils.h"
+#include "nmdbus-checkpoint.h"
+
+#define _NMLOG_PREFIX_NAME                "checkpoint"
+#define _NMLOG_DOMAIN                     LOGD_CORE
+
+#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, \
+			          "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+			          __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+		} \
+	} G_STMT_END
+
+typedef struct {
+	char *original_dev_path;
+	NMDevice *device;
+	NMConnection *connection;
+} DeviceCheckpoint;
+
+typedef struct {
+	/* properties */
+	GHashTable *devices;
+	gint64 created;
+	guint32 rollback_timeout;
+	/* private members */
+	NMManager *manager;
+	gint64 rollback_ts;
+} NMCheckpointPrivate;
+
+struct _NMCheckpoint {
+	NMExportedObject parent;
+	NMCheckpointPrivate priv;
+};
+
+typedef struct {
+	NMExportedObjectClass parent;
+} NMCheckpointClass;
+
+G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT)
+
+#define NM_CHECKPOINT_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMCheckpoint *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_CHECKPOINT (_self)); \
+		&_self->priv; \
+	})
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_DEVICES,
+	PROP_CREATED,
+	PROP_ROLLBACK_TIMEOUT,
+);
+
+guint64
+nm_checkpoint_get_rollback_ts (NMCheckpoint *self)
+{
+	g_return_val_if_fail (NM_IS_CHECKPOINT (self), 0);
+
+	return NM_CHECKPOINT_GET_PRIVATE (self)->rollback_ts;
+}
+
+gboolean
+nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
+{
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+
+	return g_hash_table_contains (priv->devices, device);
+}
+
+GVariant *
+nm_checkpoint_rollback (NMCheckpoint *self)
+{
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+	DeviceCheckpoint *dev_checkpoint;
+	GHashTableIter iter;
+	NMSettingsConnection *connection;
+	NMDevice *device;
+	GError *local_error = NULL;
+	GVariantBuilder builder;
+
+	_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 */
+	g_hash_table_iter_init (&iter, priv->devices);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
+		gs_unref_object NMAuthSubject *subject = NULL;
+		guint32 result = NM_ROLLBACK_RESULT_OK;
+		const char *con_path;
+
+		_LOGD ("rollback: restoring state of device %s", nm_device_get_iface (device));
+
+		if (!nm_device_is_real (device)) {
+			result = NM_ROLLBACK_RESULT_ERR_NO_DEVICE;
+			_LOGD ("rollback: device is not realized");
+			goto next_dev;
+		}
+
+		if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
+			result = NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED;
+			_LOGD ("rollback: device is unmanaged");
+			goto next_dev;
+		}
+
+		if (dev_checkpoint->connection) {
+			/* The device had an active connection, check if the
+			 * connection still exists
+			 * */
+			con_path = nm_connection_get_path (dev_checkpoint->connection);
+			connection = nm_settings_get_connection_by_path (nm_settings_get(), con_path);
+
+			if (connection) {
+				/* If the connection is still there, restore its content
+				 * and save it
+				 * */
+				_LOGD ("rollback: connection %s still exists", con_path);
+
+				nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
+				                                                dev_checkpoint->connection);
+				nm_settings_connection_commit_changes (connection,
+				                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+				                                       NULL,
+				                                       NULL);
+			} else {
+				/* The connection was deleted, recreate it */
+				_LOGD ("rollback: adding connection %s again", con_path);
+
+				connection = nm_settings_add_connection (nm_settings_get (),
+				                                         dev_checkpoint->connection,
+				                                         TRUE,
+				                                         &local_error);
+				if (!connection) {
+					_LOGD ("rollback: connection add failure: %s", local_error->message);
+					g_clear_error (&local_error);
+					result = NM_ROLLBACK_RESULT_ERR_FAILED;
+					goto next_dev;
+				}
+			}
+
+			/* Now re-activate the connection */
+			subject = nm_auth_subject_new_internal ();
+			if (!nm_manager_activate_connection (priv->manager,
+			                                     connection,
+			                                     NULL,
+			                                     device,
+			                                     subject,
+			                                     &local_error)) {
+				_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
+				       nm_connection_get_id ((NMConnection *) connection),
+				       nm_connection_get_uuid ((NMConnection *	) connection),
+				       local_error->message);
+				g_clear_error (&local_error);
+				result = NM_ROLLBACK_RESULT_ERR_FAILED;
+				goto next_dev;
+			}
+		} else {
+			/* The device was initially disconnected, deactivate any existing connection */
+			_LOGD ("rollback: disconnecting device");
+
+			if (   nm_device_get_state (device) > NM_DEVICE_STATE_DISCONNECTED
+			    && nm_device_get_state (device) < NM_DEVICE_STATE_DEACTIVATING) {
+				nm_device_state_changed (device,
+				                         NM_DEVICE_STATE_DEACTIVATING,
+				                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			}
+		}
+
+next_dev:
+		g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result);
+	}
+
+	return g_variant_new ("(a{su})", &builder);
+}
+
+static DeviceCheckpoint *
+device_checkpoint_create (NMDevice *device,
+                          GError **error)
+{
+	DeviceCheckpoint *dev_checkpoint;
+	NMConnection *connection;
+	const char *path;
+
+	if (!nm_device_is_real (device)) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+		             "device '%s' is not realized",
+		             nm_device_get_iface (device));
+		return NULL;
+	}
+
+	if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+		             "device '%s' is unmanaged",
+		             nm_device_get_iface (device));
+		return NULL;
+	}
+
+	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
+
+	dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
+	dev_checkpoint->device = g_object_ref (device);
+	dev_checkpoint->original_dev_path = g_strdup (path);
+
+	connection = nm_device_get_applied_connection (device);
+	if (connection)
+		dev_checkpoint->connection = nm_simple_connection_new_clone (connection);
+
+	return dev_checkpoint;
+}
+
+static void
+device_checkpoint_destroy (gpointer data)
+{
+	DeviceCheckpoint *dev_checkpoint = data;
+
+	g_clear_object (&dev_checkpoint->connection);
+	g_clear_object (&dev_checkpoint->device);
+	g_free (dev_checkpoint->original_dev_path);
+
+	g_slice_free (DeviceCheckpoint, dev_checkpoint);
+}
+
+static void
+nm_checkpoint_init (NMCheckpoint *self)
+{
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+
+	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                       NULL, device_checkpoint_destroy);
+}
+
+static void
+get_all_devices (NMManager *manager, GPtrArray *devices)
+{
+	const GSList *list, *iter;
+	NMDevice *dev;
+
+	list = nm_manager_get_devices (manager);
+
+	for (iter = list; iter; iter = g_slist_next (iter)) {
+		dev = iter->data;
+
+		if (!nm_device_is_real (dev))
+			continue;
+		if (nm_device_get_state (dev) <= NM_DEVICE_STATE_UNMANAGED)
+			continue;
+		/* We never touch assumed connections, unless told explicitly */
+		if (nm_device_uses_assumed_connection (dev))
+			continue;
+
+		g_ptr_array_add (devices, dev);
+	}
+}
+
+NMCheckpoint *
+nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
+                   GError **error)
+{
+	NMCheckpoint *self;
+	NMCheckpointPrivate *priv;
+	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 (!error || !*error, NULL);
+
+	if (!devices->len)
+		get_all_devices (manager, devices);
+
+	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->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;
+
+	for (i = 0; i < devices->len; i++) {
+		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;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMCheckpoint *self = NM_CHECKPOINT (object);
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+
+	g_clear_pointer (&priv->devices, g_hash_table_unref);
+
+	G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	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:
+		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, priv->created);
+		break;
+	case PROP_ROLLBACK_TIMEOUT:
+		g_value_set_uint (value, priv->rollback_timeout);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class);
+	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (checkpoint_class);
+
+	g_type_class_add_private (checkpoint_class, sizeof (NMCheckpointPrivate));
+
+	exported_object_class->export_path = NM_DBUS_PATH "/Checkpoint/%u";
+	exported_object_class->export_on_construction = FALSE;
+
+	/* virtual methods */
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+
+	/* properties */
+	obj_properties[PROP_DEVICES] =
+	     g_param_spec_boxed (NM_CHECKPOINT_DEVICES, "", "",
+	                         G_TYPE_STRV,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_CREATED] =
+	    g_param_spec_int64 (NM_CHECKPOINT_CREATED, "", "",
+	                        G_MININT64, G_MAXINT64, 0,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_ROLLBACK_TIMEOUT] =
+	    g_param_spec_uint (NM_CHECKPOINT_ROLLBACK_TIMEOUT, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       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 (checkpoint_class),
+	                                        NMDBUS_TYPE_CHECKPOINT_SKELETON,
+	                                        NULL);
+}
diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h
new file mode 100644
index 00000000..c7d2d42f
--- /dev/null
+++ b/src/nm-checkpoint.h
@@ -0,0 +1,49 @@
+/* -*- 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) 2016 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_CHECKPOINT_H__
+#define __NETWORKMANAGER_CHECKPOINT_H__
+
+#include "nm-exported-object.h"
+#include "nm-dbus-interface.h"
+
+#define NM_TYPE_CHECKPOINT            (nm_checkpoint_get_type ())
+#define NM_CHECKPOINT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CHECKPOINT, NMCheckpoint))
+#define NM_CHECKPOINT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CHECKPOINT, NMCheckpointClass))
+#define NM_IS_CHECKPOINT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CHECKPOINT))
+#define NM_IS_CHECKPOINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CHECKPOINT))
+#define NM_CHECKPOINT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CHECKPOINT, NMCheckpointClass))
+
+typedef struct _NMCheckpoint NMCheckpoint;
+
+#define NM_CHECKPOINT_DEVICES "devices"
+#define NM_CHECKPOINT_CREATED "created"
+#define NM_CHECKPOINT_ROLLBACK_TIMEOUT "rollback-timeout"
+
+GType nm_checkpoint_get_type (void);
+
+NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
+                                 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);
+
+#endif /* __NETWORKMANAGER_CHECKPOINT_H__ */
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 28057b47..23c4cecd 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -40,7 +40,7 @@ typedef struct {
 		gboolean has;
 		GSList *spec;
 	} match_device;
-} ConnectionInfo;
+} MatchSectionInfo;
 
 typedef struct {
 	char *config_main_file;
@@ -52,7 +52,11 @@ typedef struct {
 
 	/* A zero-terminated list of pre-processed information from the
 	 * [connection] sections. This is to speed up lookup. */
-	ConnectionInfo *connection_infos;
+	MatchSectionInfo *connection_infos;
+
+	/* A zero-terminated list of pre-processed information from the
+	 * [device] sections. This is to speed up lookup. */
+	MatchSectionInfo *device_infos;
 
 	struct {
 		char *uri;
@@ -268,9 +272,16 @@ nm_config_data_get_rc_manager (const NMConfigData *self)
 gboolean
 nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device)
 {
+	gs_free char *value = NULL;
+	gboolean has_match;
+
 	g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
 	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
 
+	value = nm_config_data_get_device_config (self, NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, device, &has_match);
+	if (has_match)
+		return nm_config_parse_boolean (value, FALSE);
+
 	return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->ignore_carrier);
 }
 
@@ -451,6 +462,7 @@ static int
 _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy)
 {
 	gboolean a_is_connection, b_is_connection;
+	gboolean a_is_device, b_is_device;
 	gboolean a_is_intern, b_is_intern;
 	const char *a = *pa;
 	const char *b = *pb;
@@ -488,6 +500,28 @@ _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy)
 	if (b_is_connection && !a_is_connection)
 		return -1;
 
+	/* we sort device groups before connection groups (to the end). */
+	a_is_device = a && g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
+	b_is_device = b && g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
+
+	if (a_is_device && b_is_device) {
+		/* if both are device groups, we want the explicit [device] group first. */
+		a_is_device = a[NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE)] == '\0';
+		b_is_device = b[NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE)] == '\0';
+
+		if (a_is_device != b_is_device) {
+			if (a_is_device)
+				return -1;
+			return 1;
+		}
+		/* the sections are ordered lowest-priority first. Reverse their order. */
+		return pa < pb ? 1 : -1;
+	}
+	if (a_is_device && !b_is_device)
+		return 1;
+	if (b_is_device && !a_is_device)
+		return -1;
+
 	/* no reordering. */
 	return 0;
 }
@@ -1049,25 +1083,18 @@ global_dns_equal (NMGlobalDnsConfig *old, NMGlobalDnsConfig *new)
 
 /************************************************************************/
 
-char *
-nm_config_data_get_connection_default (const NMConfigData *self,
-                                       const char *property,
-                                       NMDevice *device)
+static const MatchSectionInfo *
+_match_section_infos_lookup (const MatchSectionInfo *match_section_infos,
+                             GKeyFile *keyfile,
+                             const char *property,
+                             NMDevice *device,
+                             char **out_value)
 {
-	NMConfigDataPrivate *priv;
-	const ConnectionInfo *connection_info;
-
-	g_return_val_if_fail (self, NULL);
-	g_return_val_if_fail (property && *property, NULL);
-	g_return_val_if_fail (strchr (property, '.'), NULL);
-
-	priv = NM_CONFIG_DATA_GET_PRIVATE (self);
-
-	if (!priv->connection_infos)
+	if (!match_section_infos)
 		return NULL;
 
-	for (connection_info = &priv->connection_infos[0]; connection_info->group_name; connection_info++) {
-		char *value;
+	for (; match_section_infos->group_name; match_section_infos++) {
+		char *value = NULL;
 		gboolean match;
 
 		/* FIXME: Here we use g_key_file_get_string(). This should be in sync with what keyfile-reader
@@ -1077,23 +1104,87 @@ nm_config_data_get_connection_default (const NMConfigData *self,
 		 * string_to_value(keyfile_to_string(keyfile)) in one. Optimally, keyfile library would
 		 * expose both functions, and we would return here keyfile_to_string(keyfile).
 		 * The caller then could convert the string to the proper value via string_to_value(value). */
-		value = g_key_file_get_string (priv->keyfile, connection_info->group_name, property, NULL);
-		if (!value && !connection_info->stop_match)
+		value = g_key_file_get_string (keyfile, match_section_infos->group_name, property, NULL);
+		if (!value && !match_section_infos->stop_match)
 			continue;
 
 		match = TRUE;
-		if (connection_info->match_device.has)
-			match = device && nm_device_spec_match_list (device, connection_info->match_device.spec);
+		if (match_section_infos->match_device.has)
+			match = device && nm_device_spec_match_list (device, match_section_infos->match_device.spec);
 
-		if (match)
-			return value;
+		if (match) {
+			*out_value = value;
+			return match_section_infos;
+		}
 		g_free (value);
 	}
 	return NULL;
 }
 
+char *
+nm_config_data_get_device_config (const NMConfigData *self,
+                                  const char *property,
+                                  NMDevice *device,
+                                  gboolean *has_match)
+{
+	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,
+	                                               device,
+	                                               &value);
+	NM_SET_OUT (has_match, !!connection_info);
+	return value;
+}
+
+gboolean
+nm_config_data_get_device_config_boolean (const NMConfigData *self,
+                                          const char *property,
+                                          NMDevice *device,
+                                          gint val_no_match,
+                                          gint val_invalid)
+{
+	gs_free char *value = NULL;
+	gboolean has_match;
+
+	value = nm_config_data_get_device_config (self, property, device, &has_match);
+	if (!has_match)
+		return val_no_match;
+	return nm_config_parse_boolean (value, val_invalid);
+}
+
+char *
+nm_config_data_get_connection_default (const NMConfigData *self,
+                                       const char *property,
+                                       NMDevice *device)
+{
+	NMConfigDataPrivate *priv;
+	char *value = NULL;
+
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (property && *property, NULL);
+	g_return_val_if_fail (strchr (property, '.'), NULL);
+
+	priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+
+	_match_section_infos_lookup (&priv->connection_infos[0],
+	                             priv->keyfile,
+	                             property,
+	                             device,
+	                             &value);
+	return value;
+}
+
 static void
-_get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, char *group)
+_get_connection_info_init (MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group)
 {
 	/* pass ownership of @group on... */
 	connection_info->group_name = group;
@@ -1105,27 +1196,43 @@ _get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, c
 	connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, group, "stop-match", FALSE);
 }
 
-static ConnectionInfo *
-_get_connection_infos (GKeyFile *keyfile)
+static void
+_match_section_infos_free (MatchSectionInfo *match_section_infos)
+{
+	guint i;
+
+	if (!match_section_infos)
+		return;
+	for (i = 0; match_section_infos[i].group_name; i++) {
+		g_free (match_section_infos[i].group_name);
+		g_slist_free_full (match_section_infos[i].match_device.spec, g_free);
+	}
+	g_free (match_section_infos);
+}
+
+static MatchSectionInfo *
+_match_section_infos_construct (GKeyFile *keyfile, const char *prefix)
 {
 	char **groups;
 	gsize i, j, ngroups;
 	char *connection_tag = NULL;
-	ConnectionInfo *connection_infos = NULL;
+	MatchSectionInfo *match_section_infos = NULL;
 
-	/* get the list of existing [connection.\+] sections that we consider
-	 * for nm_config_data_get_connection_default().
+	/* get the list of existing [connection.\+]/[device.\+] sections.
 	 *
 	 * We expect the sections in their right order, with lowest priority
 	 * first. Only exception is the (literal) [connection] section, which
 	 * we will always reorder to the end. */
 	groups = g_key_file_get_groups (keyfile, &ngroups);
 	if (!groups)
-		ngroups = 0;
-	else if (ngroups > 0) {
+		return NULL;
+
+	if (ngroups > 0) {
+		gsize l = strlen (prefix);
+
 		for (i = 0, j = 0; i < ngroups; i++) {
-			if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)) {
-				if (groups[i][NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0')
+			if (g_str_has_prefix (groups[i], prefix)) {
+				if (groups[i][l] == '\0')
 					connection_tag = groups[i];
 				else
 					groups[j++] = groups[i];
@@ -1135,18 +1242,23 @@ _get_connection_infos (GKeyFile *keyfile)
 		ngroups = j;
 	}
 
-	connection_infos = g_new0 (ConnectionInfo, ngroups + 1 + (connection_tag ? 1 : 0));
+	if (ngroups == 0 && !connection_tag) {
+		g_free (groups);
+		return NULL;
+	}
+
+	match_section_infos = g_new0 (MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0));
 	for (i = 0; i < ngroups; i++) {
 		/* pass ownership of @group on... */
-		_get_connection_info_init (&connection_infos[i], keyfile, groups[ngroups - i - 1]);
+		_get_connection_info_init (&match_section_infos[i], keyfile, groups[ngroups - i - 1]);
 	}
 	if (connection_tag) {
 		/* pass ownership of @connection_tag on... */
-		_get_connection_info_init (&connection_infos[i], keyfile, connection_tag);
+		_get_connection_info_init (&match_section_infos[i], keyfile, connection_tag);
 	}
 	g_free (groups);
 
-	return connection_infos;
+	return match_section_infos;
 }
 
 /************************************************************************/
@@ -1306,7 +1418,6 @@ static void
 finalize (GObject *gobject)
 {
 	NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (gobject);
-	guint i;
 
 	g_free (priv->config_main_file);
 	g_free (priv->config_description);
@@ -1326,13 +1437,8 @@ finalize (GObject *gobject)
 
 	nm_global_dns_config_free (priv->global_dns);
 
-	if (priv->connection_infos) {
-		for (i = 0; priv->connection_infos[i].group_name; i++) {
-			g_free (priv->connection_infos[i].group_name);
-			g_slist_free_full (priv->connection_infos[i].match_device.spec, g_free);
-		}
-		g_free (priv->connection_infos);
-	}
+	_match_section_infos_free (priv->connection_infos);
+	_match_section_infos_free (priv->device_infos);
 
 	g_key_file_unref (priv->keyfile);
 	if (priv->keyfile_user)
@@ -1359,7 +1465,8 @@ constructed (GObject *object)
 
 	priv->keyfile = _merge_keyfiles (priv->keyfile_user, priv->keyfile_intern);
 
-	priv->connection_infos = _get_connection_infos (priv->keyfile);
+	priv->connection_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
+	priv->device_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
 
 	priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL));
 	priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL);
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index e01ad6ea..e63b4590 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -183,6 +183,17 @@ char *nm_config_data_get_connection_default (const NMConfigData *self,
                                              const char *property,
                                              NMDevice *device);
 
+char *nm_config_data_get_device_config (const NMConfigData *self,
+                                        const char *property,
+                                        NMDevice *device,
+                                        gboolean *has_match);
+
+gboolean nm_config_data_get_device_config_boolean (const NMConfigData *self,
+                                                   const char *property,
+                                                   NMDevice *device,
+                                                   gint val_no_match,
+                                                   gint val_invalid);
+
 char **nm_config_data_get_groups (const NMConfigData *self);
 char **nm_config_data_get_keys (const NMConfigData *self, const char *group);
 gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const char *group);
diff --git a/src/nm-config.c b/src/nm-config.c
index 247b9a89..4bc4f483 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -39,12 +39,27 @@
 #define DEFAULT_SYSTEM_CONFIG_DIR       NMLIBDIR  "/conf.d"
 #define DEFAULT_NO_AUTO_DEFAULT_FILE    NMSTATEDIR "/no-auto-default.state"
 #define DEFAULT_INTERN_CONFIG_FILE      NMSTATEDIR "/NetworkManager-intern.conf"
+#define DEFAULT_STATE_FILE              NMSTATEDIR "/NetworkManager.state"
+
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME                "config"
+#define _NMLOG_DOMAIN                     LOGD_CORE
+
+#define _NMLOG(level, ...) \
+	nm_log (level, _NMLOG_DOMAIN, \
+	        "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+	        _NMLOG_PREFIX_NAME \
+	        _NM_UTILS_MACRO_REST(__VA_ARGS__))
+
+/*****************************************************************************/
 
 struct NMConfigCmdLineOptions {
 	char *config_main_file;
 	char *intern_config_file;
 	char *config_dir;
 	char *system_config_dir;
+	char *state_file;
 	char *no_auto_default_file;
 	char *plugins;
 	gboolean configure_and_quit;
@@ -59,6 +74,10 @@ struct NMConfigCmdLineOptions {
 };
 
 typedef struct {
+	NMConfigState p;
+} State;
+
+typedef struct {
 	NMConfigCmdLineOptions cli;
 
 	NMConfigData *config_data;
@@ -82,6 +101,19 @@ typedef struct {
 	gboolean configure_and_quit;
 
 	char **atomic_section_prefixes;
+
+	/* The state. This is actually a mutable data member and it makes sense:
+	 * The regular config is immutable (NMConfigData) and can old be swapped
+	 * as a whole (via nm_config_set_values() or during reload). Thus, it can
+	 * be changed, but it is still immutable and is swapped atomically as a
+	 * whole. Also, we emit a config-changed signal on that occasion.
+	 *
+	 * For state, there are no events. You can query it and set it.
+	 * It only gets read *once* at startup, and later is cached and only
+	 * written out to disk. Hence, no need for the immutable dance here
+	 * because the state changes only on explicit actions from the daemon
+	 * itself. */
+	State *state;
 } NMConfigPrivate;
 
 enum {
@@ -367,7 +399,9 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
 
 	priv = NM_CONFIG_GET_PRIVATE (self);
 
-	hw_address = nm_device_get_hw_address (device);
+	hw_address = nm_device_get_permanent_hw_address (device, FALSE);
+	if (!hw_address)
+		return;
 
 	no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data);
 
@@ -407,6 +441,7 @@ _nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli)
 	g_clear_pointer (&cli->system_config_dir, g_free);
 	g_clear_pointer (&cli->no_auto_default_file, g_free);
 	g_clear_pointer (&cli->intern_config_file, g_free);
+	g_clear_pointer (&cli->state_file, g_free);
 	g_clear_pointer (&cli->plugins, g_free);
 	cli->configure_and_quit = FALSE;
 	cli->is_debug = FALSE;
@@ -428,6 +463,7 @@ _nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmd
 	dst->config_main_file = g_strdup (cli->config_main_file);
 	dst->no_auto_default_file = g_strdup (cli->no_auto_default_file);
 	dst->intern_config_file = g_strdup (cli->intern_config_file);
+	dst->state_file = g_strdup (cli->state_file);
 	dst->plugins = g_strdup (cli->plugins);
 	dst->configure_and_quit = cli->configure_and_quit;
 	dst->is_debug = cli->is_debug;
@@ -467,6 +503,7 @@ nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
 			{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) },
 			{ "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), N_(DEFAULT_SYSTEM_CONFIG_DIR) },
 			{ "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), N_(DEFAULT_INTERN_CONFIG_FILE) },
+			{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), N_(DEFAULT_STATE_FILE) },
 			{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) },
 			{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) },
 			{ "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL },
@@ -541,14 +578,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 {
 	const char *a, *b;
 	gboolean a_is_connection, b_is_connection;
-
-	/* basic NULL checking... */
-	if (pa == pb)
-		return 0;
-	if (!pa)
-		return -1;
-	if (!pb)
-		return 1;
+	gboolean a_is_device, b_is_device;
 
 	a = *pa;
 	b = *pb;
@@ -563,16 +593,34 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 			return 1;
 		return -1;
 	}
-	if (!a_is_connection) {
-		/* both are non-connection entries. Don't reorder. */
-		return 0;
+	if (a_is_connection) {
+		/* both are [connection.\+] entires. Reverse their order.
+		 * One of the sections might be literally [connection]. That section
+		 * 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;
 	}
 
-	/* both are [connection.\+] entires. Reverse their order.
-	 * One of the sections might be literally [connection]. That section
-	 * 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;
+	a_is_device = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
+	b_is_device = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
+
+	if (a_is_device != b_is_device) {
+		/* one is a [device*] entry, the other not. We sort [device*] entires
+		 * after.  */
+		if (a_is_device)
+			return 1;
+		return -1;
+	}
+	if (a_is_device) {
+		/* both are [device.\+] entires. Reverse their order.
+		 * One of the sections might be literally [device]. That section
+		 * 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;
+	}
+
+	/* don't reorder the rest. */
+	return 0;
 }
 
 void
@@ -595,7 +643,8 @@ _setting_is_device_spec (const char *group, const char *key)
 	       || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier")
 	       || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only")
 	       || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices")
-	       || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device"));
+	       || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device"))
+	       || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE    ) && !strcmp (key, "match-device"));
 }
 
 static gboolean
@@ -845,15 +894,6 @@ read_base_config (GKeyFile *keyfile,
 	return TRUE;
 }
 
-static int
-sort_asciibetically (gconstpointer a, gconstpointer b)
-{
-	const char *s1 = *(const char **)a;
-	const char *s2 = *(const char **)b;
-
-	return strcmp (s1, s2);
-}
-
 static GPtrArray *
 _get_config_dir_files (const char *config_dir)
 {
@@ -882,7 +922,7 @@ _get_config_dir_files (const char *config_dir)
 	}
 	g_object_unref (dir);
 
-	g_ptr_array_sort (confs, sort_asciibetically);
+	g_ptr_array_sort (confs, nm_strcmp_p);
 	return confs;
 }
 
@@ -1645,7 +1685,190 @@ nm_config_set_values (NMConfig *self,
 	g_key_file_unref (keyfile_new);
 }
 
-/************************************************************************/
+/******************************************************************************
+ * State
+ ******************************************************************************/
+
+static const char *
+state_get_filename (const NMConfigCmdLineOptions *cli)
+{
+	/* For an empty filename, we assume the user wants to disable
+	 * state. NMConfig will not try to read it nor write it out. */
+	if (!cli->state_file)
+		return DEFAULT_STATE_FILE;
+	return cli->state_file[0] ? cli->state_file : NULL;
+}
+
+static State *
+state_new (void)
+{
+	State *state;
+
+	state = g_slice_new0 (State);
+	state->p.net_enabled = TRUE;
+	state->p.wifi_enabled = TRUE;
+	state->p.wwan_enabled = TRUE;
+
+	return state;
+}
+
+static void
+state_free (State *state)
+{
+	if (!state)
+		return;
+	g_slice_free (State, state);
+}
+
+static State *
+state_new_from_file (const char *filename)
+{
+	GKeyFile *keyfile;
+	gs_free_error GError *error = NULL;
+	State *state;
+
+	state = state_new ();
+
+	if (!filename)
+		return state;
+
+	keyfile = g_key_file_new ();
+	g_key_file_set_list_separator (keyfile, ',');
+	if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error)) {
+		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			_LOGD ("state: missing state file \"%s\": %s", filename, error->message);
+		else
+			_LOGW ("state: error reading state file \"%s\": %s", filename, error->message);
+		goto out;
+	}
+
+	_LOGD ("state: successfully read state file \"%s\"", filename);
+
+	state->p.net_enabled  = nm_config_keyfile_get_boolean (keyfile, "main", "NetworkingEnabled", state->p.net_enabled);
+	state->p.wifi_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WirelessEnabled", state->p.wifi_enabled);
+	state->p.wwan_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WWANEnabled", state->p.wwan_enabled);
+
+out:
+	g_key_file_unref (keyfile);
+	return state;
+}
+
+const NMConfigState *
+nm_config_state_get (NMConfig *self)
+{
+	NMConfigPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
+
+	priv = NM_CONFIG_GET_PRIVATE (self);
+
+	if (G_UNLIKELY (!priv->state)) {
+		/* read the state from file lazy on first access. The reason is that
+		 * we want to log a failure to read the file via nm-logging.
+		 *
+		 * So we cannot read the state during construction of NMConfig,
+		 * because at that time nm-logging is not yet configured.
+		 */
+		priv->state = state_new_from_file (state_get_filename (&priv->cli));
+	}
+
+	return &priv->state->p;
+}
+
+static void
+state_write (NMConfig *self)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
+	const char *filename;
+	GString *str;
+	GError *error = NULL;
+
+	filename = state_get_filename (&priv->cli);
+
+	if (!filename) {
+		priv->state->p.dirty = FALSE;
+		return;
+	}
+
+	str = g_string_sized_new (256);
+
+	/* Let's construct the keyfile data by hand. */
+
+	g_string_append (str, "[main]\n");
+	g_string_append_printf (str, "NetworkingEnabled=%s\n", priv->state->p.net_enabled ? "true" : "false");
+	g_string_append_printf (str, "WirelessEnabled=%s\n", priv->state->p.wifi_enabled ? "true" : "false");
+	g_string_append_printf (str, "WWANEnabled=%s\n", priv->state->p.wwan_enabled ? "true" : "false");
+
+	if (!g_file_set_contents (filename,
+	                          str->str, str->len,
+	                          &error)) {
+		_LOGD ("state: error writing state file \"%s\": %s", filename, error->message);
+		g_clear_error (&error);
+		/* we leave the state dirty. That potentally means, that we try to
+		 * write the file over and over again, although it isn't possible. */
+		priv->state->p.dirty = TRUE;
+	} else
+		priv->state->p.dirty = FALSE;
+
+	_LOGT ("state: success writing state file \"%s\"", filename);
+
+	g_string_free (str, TRUE);
+}
+
+void
+_nm_config_state_set (NMConfig *self,
+                      gboolean allow_persist,
+                      gboolean force_persist,
+                      ...)
+{
+	NMConfigPrivate *priv;
+	va_list ap;
+	NMConfigRunStatePropertyType property_type;
+
+	g_return_if_fail (NM_IS_CONFIG (self));
+
+	priv = NM_CONFIG_GET_PRIVATE (self);
+
+	va_start (ap, force_persist);
+
+	/* We expect that the NMConfigRunStatePropertyType is an integer type <= sizeof (int).
+	 * Smaller would be fine, since the variadic arguments get promoted to int.
+	 * Larger would be a problem, also, because we want that "0" is a valid sentinel. */
+	G_STATIC_ASSERT_EXPR (sizeof (NMConfigRunStatePropertyType) <= sizeof (int));
+
+	while ((property_type = va_arg (ap, int)) != NM_CONFIG_STATE_PROPERTY_NONE) {
+		bool *p_bool, v_bool;
+
+		switch (property_type) {
+		case NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED:
+			p_bool = &priv->state->p.net_enabled;
+			break;
+		case NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED:
+			p_bool = &priv->state->p.wifi_enabled;
+			break;
+		case NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED:
+			p_bool = &priv->state->p.wwan_enabled;
+			break;
+		default:
+			va_end (ap);
+			g_return_if_reached ();
+		}
+
+		v_bool = va_arg (ap, gboolean);
+		if (*p_bool == v_bool)
+			continue;
+		*p_bool = v_bool;
+		priv->state->p.dirty = TRUE;
+	}
+
+	va_end (ap);
+
+	if (   allow_persist
+	    && (force_persist || priv->state->p.dirty))
+		state_write (self);
+}
+
+/*****************************************************************************/
 
 void
 nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags)
@@ -1929,6 +2152,8 @@ finalize (GObject *gobject)
 {
 	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
 
+	state_free (priv->state);
+
 	g_free (priv->config_dir);
 	g_free (priv->system_config_dir);
 	g_free (priv->no_auto_default_file);
diff --git a/src/nm-config.h b/src/nm-config.h
index 9a153e48..a75e3935 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -45,6 +45,7 @@
 
 #define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN                ".intern."
 #define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION            "connection"
+#define NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE                "device"
 #define NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN     "global-dns-domain-"
 #define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist"
 
@@ -69,6 +70,8 @@
 #define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED              "managed"
 #define NM_CONFIG_KEYFILE_KEY_AUDIT                         "audit"
 
+#define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER         "ignore-carrier"
+
 #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS                     ".was."
 #define NM_CONFIG_KEYFILE_KEYPREFIX_SET                     ".set."
 
@@ -79,6 +82,24 @@
 
 typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
 
+typedef enum {
+	NM_CONFIG_STATE_PROPERTY_NONE,
+
+	/* 1 set-argument: (gboolean enabled) */
+	NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED,
+	NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED,
+	NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED,
+} NMConfigRunStatePropertyType;
+
+typedef struct {
+	bool net_enabled;
+	bool wifi_enabled;
+	bool wwan_enabled;
+
+	/* Whether the runstate is modified and not saved to disk. */
+	bool dirty;
+} NMConfigState;
+
 struct _NMConfig {
 	GObject parent;
 };
@@ -127,6 +148,15 @@ NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, char **atomic_sectio
 NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error);
 void nm_config_reload (NMConfig *config, NMConfigChangeFlags reload_flags);
 
+const NMConfigState *nm_config_state_get (NMConfig *config);
+
+void _nm_config_state_set (NMConfig *config,
+                           gboolean allow_persist,
+                           gboolean force_persist,
+                           ...);
+#define nm_config_state_set(config, allow_persist, force_persist, ...) \
+    _nm_config_state_set (config, allow_persist, force_persist, ##__VA_ARGS__, 0)
+
 gint nm_config_parse_boolean (const char *str, gint default_value);
 
 GKeyFile *nm_config_create_keyfile (void);
diff --git a/src/nm-connection-provider.c b/src/nm-connection-provider.c
deleted file mode 100644
index e04cab92..00000000
--- a/src/nm-connection-provider.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details:
- *
- * Copyright (C) 2012 Red Hat, Inc.
- */
-
-#include "nm-default.h"
-
-#include "nm-connection-provider.h"
-#include "nm-utils.h"
-
-G_DEFINE_INTERFACE (NMConnectionProvider, nm_connection_provider, G_TYPE_OBJECT)
-
-GSList *
-nm_connection_provider_get_best_connections (NMConnectionProvider *self,
-                                             guint max_requested,
-                                             const char *ctype1,
-                                             const char *ctype2,
-                                             NMConnectionFilterFunc func,
-                                             gpointer func_data)
-{
-	g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL);
-
-	if (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_best_connections)
-		return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_best_connections (self, max_requested, ctype1, ctype2, func, func_data);
-	return NULL;
-}
-
-const GSList *
-nm_connection_provider_get_connections (NMConnectionProvider *self)
-{
-	g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL);
-
-	if (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connections)
-		return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connections (self);
-	return NULL;
-}
-
-/**
- * nm_connection_provider_add_connection:
- * @self: the #NMConnectionProvider
- * @connection: the source connection to create a new #NMSettingsConnection from
- * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
- * not save to disk
- * @error: on return, a location to store any errors that may occur
- *
- * Creates a new #NMSettingsConnection for the given source @connection.  
- * The plugin owns the returned object and the caller must reference the object
- * to continue using it.
- *
- * Returns: the new #NMSettingsConnection or %NULL
- */
-NMConnection *
-nm_connection_provider_add_connection (NMConnectionProvider *self,
-                                       NMConnection *connection,
-                                       gboolean save_to_disk,
-                                       GError **error)
-{
-	g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL);
-
-	g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection);
-	return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection (self, connection, save_to_disk, error);
-}
-
-/**
- * nm_connection_provider_get_connection_by_uuid:
- * @self: the #NMConnectionProvider
- * @uuid: the UUID to search for
- *
- * Returns: the connection with the given @uuid, or %NULL
- */
-NMConnection *
-nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self,
-                                               const char *uuid)
-{
-	g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL);
-	g_return_val_if_fail (uuid != NULL, NULL);
-	g_return_val_if_fail (nm_utils_is_uuid (uuid), NULL);
-
-	g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid);
-	return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid (self, uuid);
-}
-
-/*****************************************************************************/
-
-static void
-nm_connection_provider_default_init (NMConnectionProviderInterface *g_iface)
-{
-	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
-	static gboolean initialized = FALSE;
-
-	if (initialized)
-		return;
-	initialized = TRUE;
-
-	/* Signals */
-	g_signal_new (NM_CP_SIGNAL_CONNECTION_ADDED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              0, NULL, NULL,
-	              g_cclosure_marshal_VOID__OBJECT,
-	              G_TYPE_NONE, 1, G_TYPE_OBJECT);
-
-	g_signal_new (NM_CP_SIGNAL_CONNECTION_UPDATED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              0, NULL, NULL,
-	              g_cclosure_marshal_VOID__OBJECT,
-	              G_TYPE_NONE, 1, G_TYPE_OBJECT);
-
-	g_signal_new (NM_CP_SIGNAL_CONNECTION_REMOVED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              0, NULL, NULL,
-	              g_cclosure_marshal_VOID__OBJECT,
-	              G_TYPE_NONE, 1, G_TYPE_OBJECT);
-}
diff --git a/src/nm-connection-provider.h b/src/nm-connection-provider.h
deleted file mode 100644
index b31af05f..00000000
--- a/src/nm-connection-provider.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details:
- *
- * Copyright (C) 2012 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_CONNECTION_PROVIDER_H__
-#define __NETWORKMANAGER_CONNECTION_PROVIDER_H__
-
-#include <nm-connection.h>
-
-#include "nm-default.h"
-
-#define NM_TYPE_CONNECTION_PROVIDER               (nm_connection_provider_get_type ())
-#define NM_CONNECTION_PROVIDER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTION_PROVIDER, NMConnectionProvider))
-#define NM_IS_CONNECTION_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTION_PROVIDER))
-#define NM_CONNECTION_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_CONNECTION_PROVIDER, NMConnectionProviderInterface))
-
-#define NM_CP_SIGNAL_CONNECTION_ADDED        "cp-connection-added"
-#define NM_CP_SIGNAL_CONNECTION_UPDATED      "cp-connection-updated"
-#define NM_CP_SIGNAL_CONNECTION_REMOVED      "cp-connection-removed"
-
-
-/**
- * NMConnectionFilterFunc:
- * @provider: The provider requesting the filtering
- * @connection: the connection to be filtered
- * @func_data: the caller-provided data pointer
- *
- * Returns: %TRUE to allow the connection, %FALSE to ignore it
- */
-typedef gboolean (*NMConnectionFilterFunc) (NMConnectionProvider *provider,
-                                            NMConnection *connection,
-                                            gpointer func_data);
-
-
-typedef struct {
-	GTypeInterface g_iface;
-
-	/* Methods */
-	GSList * (*get_best_connections) (NMConnectionProvider *self,
-	                                  guint max_requested,
-	                                  const char *ctype1,
-	                                  const char *ctype2,
-	                                  NMConnectionFilterFunc func,
-	                                  gpointer func_data);
-
-	const GSList * (*get_connections) (NMConnectionProvider *self);
-
-	NMConnection * (*add_connection) (NMConnectionProvider *self,
-	                                  NMConnection *connection,
-	                                  gboolean save_to_disk,
-	                                  GError **error);
-
-	NMConnection * (*get_connection_by_uuid) (NMConnectionProvider *self,
-	                                          const char *uuid);
-} NMConnectionProviderInterface;
-
-GType nm_connection_provider_get_type (void);
-
-/**
- * nm_connection_provider_get:
- *
- * Returns: the global #NMConnectionProvider
- */
-NMConnectionProvider *nm_connection_provider_get (void);
-
-/**
- * nm_connection_provider_get_best_connections:
- * @self: the #NMConnectionProvider
- * @max_requested: if non-zero, the maximum number of connections to return
- * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to
- *   filter connections against
- * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME)
- *   to filter connections against
- * @func: caller-supplied function for filtering connections
- * @func_data: caller-supplied data passed to @func
- *
- * Returns: a #GSList of #NMConnection objects in sorted order representing the
- *   "best" or highest-priority connections filtered by @ctype1 and/or @ctype2,
- *   and/or @func.  Caller is responsible for freeing the returned #GSList, but
- *   the contained values do not need to be unreffed.
- */
-GSList *nm_connection_provider_get_best_connections (NMConnectionProvider *self,
-                                                     guint max_requested,
-                                                     const char *ctype1,
-                                                     const char *ctype2,
-                                                     NMConnectionFilterFunc func,
-                                                     gpointer func_data);
-
-/**
- * nm_connection_provider_get_connections:
- * @self: the #NMConnectionProvider
- *
- * Returns: a #GSList of #NMConnection objects representing all known
- *   connections.  Returned list is owned by the connection provider and must
- *   not be freed.
- */
-const GSList *nm_connection_provider_get_connections (NMConnectionProvider *self);
-
-/**
- * nm_connection_provider_add_connection:
- * @self: the #NMConnectionProvider
- * @connection: the connection to be added
- * @save_to_disk: whether to store the connection on disk
- * @error: returns any error if adding fails
- *
- * returns: a newly added #NMConnection.
- */
-NMConnection *nm_connection_provider_add_connection (NMConnectionProvider *self,
-                                                     NMConnection *connection,
-                                                     gboolean save_to_disk,
-                                                     GError **error);
-
-NMConnection *nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self,
-                                                             const char *uuid);
-
-#endif /* __NETWORKMANAGER_CONNECTION_PROVIDER_H__ */
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 934727fa..7cf3e707 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -28,6 +28,7 @@
 
 #include "nm-connectivity.h"
 #include "nm-config.h"
+#include "nm-dispatcher.h"
 #include "NetworkManagerUtils.h"
 
 G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
@@ -96,6 +97,9 @@ update_state (NMConnectivity *self, NMConnectivityState state)
 		       nm_connectivity_state_to_string (state));
 		priv->state = state;
 		g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_STATE);
+
+		/* Notify dispatcher scripts of a connectivity state change */
+		nm_dispatcher_call_connectivity (DISPATCHER_ACTION_CONNECTIVITY_CHANGE, state);
 	}
 }
 
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index f037403c..10fcef97 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -21,8 +21,6 @@
 #ifndef __NETWORKMANAGER_CONNECTIVITY_H__
 #define __NETWORKMANAGER_CONNECTIVITY_H__
 
-
-#include "nm-default.h"
 #include "nm-dbus-interface.h"
 
 #define NM_TYPE_CONNECTIVITY            (nm_connectivity_get_type ())
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index e9365c0c..6dfbb4ce 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -26,6 +26,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <string.h>
+#include <poll.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <resolv.h>
@@ -54,7 +55,7 @@
 #endif
 
 G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int));
-int _nm_utils_testing = 0;
+static int _nm_utils_testing = 0;
 
 gboolean
 nm_utils_get_testing_initialized ()
@@ -199,6 +200,27 @@ nm_ethernet_address_is_valid (gconstpointer addr, gssize len)
 	return TRUE;
 }
 
+gconstpointer
+nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen)
+{
+	g_return_val_if_fail (src, NULL);
+	g_return_val_if_fail (dst, NULL);
+
+	switch (family) {
+	case AF_INET:
+		g_return_val_if_fail (plen <= 32, NULL);
+		*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address (*((guint32 *) src), plen);
+		break;
+	case AF_INET6:
+		g_return_val_if_fail (plen <= 128, NULL);
+		nm_utils_ip6_address_clear_host_address (dst, src, plen);
+		break;
+	default:
+		g_return_val_if_reached (NULL);
+	}
+	return dst;
+}
+
 /* nm_utils_ip4_address_clear_host_address:
  * @addr: source ip6 address
  * @plen: prefix length of network
@@ -445,8 +467,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	gs_strfreev gchar **tokens = NULL;
 	guint num_tokens;
 	gchar *p;
-	gchar *endp;
-	char state = '\0';
+	char state = ' ';
 	gint64 ppid = 0;
 
 	start_time = 0;
@@ -457,7 +478,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
 
 	if (!g_file_get_contents (filename, &contents, &length, NULL))
-		goto out;
+		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
@@ -465,10 +486,10 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	 */
 	p = strrchr (contents, ')');
 	if (p == NULL)
-		goto out;
+		goto fail;
 	p += 2; /* skip ') ' */
 	if (p - contents >= (int) length)
-		goto out;
+		goto fail;
 
 	state = p[0];
 
@@ -477,23 +498,26 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 	num_tokens = g_strv_length (tokens);
 
 	if (num_tokens < 20)
-		goto out;
+		goto fail;
 
-	if (out_ppid)
+	if (out_ppid) {
 		ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+		if (ppid == 0)
+			goto fail;
+	}
 
-	errno = 0;
-	start_time = strtoull (tokens[19], &endp, 10);
-	if (*endp != '\0' || errno != 0)
-		start_time = 0;
-
-out:
-	if (out_state)
-		*out_state = state;
-	if (out_ppid)
-		*out_ppid = ppid;
+	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;
 }
 
 /******************************************************************************************/
@@ -1248,8 +1272,10 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
 {
 	const GSList *iter;
 	NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH;
+	guint hwaddr_len = 0;
+	guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
 
-	g_return_val_if_fail (hwaddr != NULL, NM_MATCH_SPEC_NO_MATCH);
+	nm_assert (nm_utils_hwaddr_valid (hwaddr, -1));
 
 	for (iter = specs; iter; iter = g_slist_next (iter)) {
 		const char *spec_str = iter->data;
@@ -1270,7 +1296,15 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
 		else if (except)
 			continue;
 
-		if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) {
+		if (G_UNLIKELY (hwaddr_len == 0)) {
+			hwaddr_len = _nm_utils_hwaddr_length (hwaddr);
+			if (!hwaddr_len)
+				g_return_val_if_reached (NM_MATCH_SPEC_NO_MATCH);
+			if (!nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, hwaddr_len))
+				nm_assert_not_reached ();
+		}
+
+		if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr_bin, hwaddr_len)) {
 			if (except)
 				return NM_MATCH_SPEC_NEG_MATCH;
 			match = NM_MATCH_SPEC_MATCH;
@@ -1900,6 +1934,33 @@ nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id)
 	return ifname;
 }
 
+/* nm_utils_new_infiniband_name:
+ * @name: the output-buffer where the value will be written. Must be
+ *   not %NULL and point to a string buffer of at least IFNAMSIZ bytes.
+ * @parent_name: the parent interface name
+ * @p_key: the partition key.
+ *
+ * Returns: the infiniband name will be written to @name and @name
+ *   is returned.
+ */
+const char *
+nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key)
+{
+	g_return_val_if_fail (name, NULL);
+	g_return_val_if_fail (parent_name && parent_name[0], NULL);
+	g_return_val_if_fail (strlen (parent_name) < IFNAMSIZ, NULL);
+
+	/* technically, p_key of 0x0000 and 0x8000 is not allowed either. But we don't
+	 * want to assert against that in nm_utils_new_infiniband_name(). So be more
+	 * resilient here, and accept those. */
+	g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NULL);
+
+	/* If parent+suffix is too long, kernel would just truncate
+	 * the name. We do the same. See ipoib_vlan_add().  */
+	g_snprintf (name, IFNAMSIZ, "%s.%04x", parent_name, p_key);
+	return name;
+}
+
 /**
  * nm_utils_read_resolv_conf_nameservers():
  * @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf
@@ -2651,6 +2712,123 @@ nm_utils_machine_id_read (void)
 
 /*****************************************************************************/
 
+/* taken from systemd's fd_wait_for_event(). Note that the timeout
+ * is here in nano-seconds, not micro-seconds. */
+int
+nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
+{
+	struct pollfd pollfd = {
+		.fd = fd,
+		.events = event,
+	};
+	struct timespec ts, *pts;
+	int r;
+
+	if (timeout_ns < 0)
+		pts = NULL;
+	else {
+		ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND);
+		ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND);
+		pts = &ts;
+	}
+
+	r = ppoll (&pollfd, 1, pts, NULL);
+	if (r < 0)
+		return -errno;
+	if (r == 0)
+		return 0;
+	return pollfd.revents;
+}
+
+/* taken from systemd's loop_read() */
+ssize_t
+nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+	uint8_t *p = buf;
+	ssize_t n = 0;
+
+	g_return_val_if_fail (fd >= 0, -EINVAL);
+	g_return_val_if_fail (buf, -EINVAL);
+
+	/* If called with nbytes == 0, let's call read() at least
+	 * once, to validate the operation */
+
+	if (nbytes > (size_t) SSIZE_MAX)
+		return -EINVAL;
+
+	do {
+		ssize_t k;
+
+		k = read (fd, p, nbytes);
+		if (k < 0) {
+			if (errno == EINTR)
+				continue;
+
+			if (errno == EAGAIN && do_poll) {
+
+				/* We knowingly ignore any return value here,
+				 * and expect that any error/EOF is reported
+				 * via read() */
+
+				(void) nm_utils_fd_wait_for_event (fd, POLLIN, -1);
+				continue;
+			}
+
+			return n > 0 ? n : -errno;
+		}
+
+		if (k == 0)
+			return n;
+
+		g_assert ((size_t) k <= nbytes);
+
+		p += k;
+		nbytes -= k;
+		n += k;
+	} while (nbytes > 0);
+
+	return n;
+}
+
+/* taken from systemd's loop_read_exact() */
+int
+nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+	ssize_t n;
+
+	n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll);
+	if (n < 0)
+		return (int) n;
+	if ((size_t) n != nbytes)
+		return -EIO;
+
+	return 0;
+}
+
+/* taken from systemd's dev_urandom(). */
+int
+nm_utils_read_urandom (void *p, size_t nbytes)
+{
+	int fd = -1;
+	int r;
+
+again:
+	fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+	if (fd < 0) {
+		r = errno;
+		if (r == EINTR)
+			goto again;
+		return r == ENOENT ? -ENOSYS : -r;
+	}
+
+	r = nm_utils_fd_read_loop_exact (fd, p, nbytes, TRUE);
+	close (fd);
+
+	return r;
+}
+
+/*****************************************************************************/
+
 guint8 *
 nm_utils_secret_key_read (gsize *out_key_len, GError **error)
 {
@@ -2669,34 +2847,28 @@ nm_utils_secret_key_read (gsize *out_key_len, GError **error)
 			key_len = 0;
 		}
 	} else {
-		int urandom = open ("/dev/urandom", O_RDONLY);
+		int r;
 		mode_t key_mask;
 
-		if (urandom == -1) {
-			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			             "Can't open /dev/urandom: %s", strerror (errno));
-			key_len = 0;
-			goto out;
-		}
-
 		/* RFC7217 mandates the key SHOULD be at least 128 bits.
 		 * Let's use twice as much. */
 		key_len = 32;
 		secret_key = g_malloc (key_len);
 
+		r = nm_utils_read_urandom (secret_key, key_len);
+		if (r < 0) {
+			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+			             "Can't read /dev/urandom: %s", strerror (-r));
+			key_len = 0;
+			goto out;
+		}
+
 		key_mask = umask (0077);
-		if (read (urandom, secret_key, key_len) == key_len) {
-			if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) {
-				g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
-				key_len = 0;
-			}
-		} else {
-			g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
-			                     "Could not obtain a secret");
+		if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) {
+			g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: ");
 			key_len = 0;
 		}
 		umask (key_mask);
-		close (urandom);
 	}
 
 out:
@@ -2816,24 +2988,97 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
 	}
 	return FALSE;
 }
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_ipv6_addr_set_interface_identifier:
+ * @addr: output token encoded as %in6_addr
+ * @iid: %NMUtilsIPv6IfaceId interface identifier
+ *
+ * Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use
+ * with Linux platform). This only copies the lower 8 bytes, ignoring
+ * the /64 network prefix which is expected to be all-zero for a valid
+ * token.
+ */
 void
-nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
+nm_utils_ipv6_addr_set_interface_identifier (struct in6_addr *addr,
                                             const NMUtilsIPv6IfaceId iid)
 {
 	memcpy (addr->s6_addr + 8, &iid.id_u8, 8);
 }
 
+/**
+ * nm_utils_ipv6_interface_identifier_get_from_addr:
+ * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
+ * @addr: token encoded as %in6_addr
+ *
+ * Converts the %in6_addr encoded token (as used by Linux platform) to
+ * the interface identifier.
+ */
 void
-nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
+nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid,
                                                  const struct in6_addr *addr)
 {
 	memcpy (iid, addr->s6_addr + 8, 8);
 }
 
+/**
+ * nm_utils_ipv6_interface_identifier_get_from_token:
+ * @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
+ * @token: token encoded as string
+ *
+ * Converts the %in6_addr encoded token (as used in ip6 settings) to
+ * the interface identifier.
+ *
+ * Returns: %TRUE if the @token is a valid token, %FALSE otherwise
+ */
+gboolean
+nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid,
+                                                  const char *token)
+{
+	struct in6_addr i6_token;
+
+	g_return_val_if_fail (token, FALSE);
+
+	if (!inet_pton (AF_INET6, token, &i6_token))
+		return FALSE;
+
+	if (!_nm_utils_inet6_is_token (&i6_token))
+		return FALSE;
+
+	nm_utils_ipv6_interface_identifier_get_from_addr (iid, &i6_token);
+	return TRUE;
+}
+
+/**
+ * nm_utils_inet6_interface_identifier_to_token:
+ * @iid: %NMUtilsIPv6IfaceId interface identifier
+ * @buf: the destination buffer or %NULL
+ *
+ * Converts the interface identifier to a string token.
+ * If the destination buffer it set, set it is used to store the
+ * resulting token, otherwise an internal static buffer is used.
+ * The buffer needs to be %NM_UTILS_INET_ADDRSTRLEN characters long.
+ *
+ * Returns: a statically allocated array. Do not g_free().
+ */
+const char *
+nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, char *buf)
+{
+	struct in6_addr i6_token = { .s6_addr = { 0, } };
+
+	nm_utils_ipv6_addr_set_interface_identifier (&i6_token, iid);
+	return nm_utils_inet6_ntop (&i6_token, buf);
+}
+
+/*****************************************************************************/
+
 static gboolean
-_set_stable_privacy (struct in6_addr *addr,
+_set_stable_privacy (guint8 stable_type,
+                     struct in6_addr *addr,
                      const char *ifname,
-                     const char *uuid,
+                     const char *network_id,
                      guint dad_counter,
                      guint8 *secret_key,
                      gsize key_len,
@@ -2857,11 +3102,24 @@ _set_stable_privacy (struct in6_addr *addr,
 
 	key_len = MIN (key_len, G_MAXUINT32);
 
+	if (stable_type != NM_UTILS_STABLE_TYPE_UUID) {
+		/* Preferably, we would always like to include the stable-type,
+		 * but for backward compatibility reasons, we cannot for UUID.
+		 *
+		 * That is no real problem and it is still impossible to
+		 * force a collision here, because of how the remaining
+		 * fields are hashed. That is, as we also hash @key_len
+		 * and the terminating '\0' of @network_id, it is unambigiously
+		 * possible to revert the process and deduce the @stable_type.
+		 */
+		g_checksum_update (sum, &stable_type, sizeof (stable_type));
+	}
+
 	g_checksum_update (sum, addr->s6_addr, 8);
 	g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
-	if (!uuid)
-		uuid = "";
-	g_checksum_update (sum, (const guchar *) uuid, strlen (uuid) + 1);
+	if (!network_id)
+		network_id = "";
+	g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1);
 	tmp[0] = htonl (dad_counter);
 	tmp[1] = htonl (key_len);
 	g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp));
@@ -2887,15 +3145,20 @@ _set_stable_privacy (struct in6_addr *addr,
  * Returns: %TRUE on success, %FALSE if the address could not be generated.
  */
 gboolean
-nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
+nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type,
+                                       struct in6_addr *addr,
                                        const char *ifname,
-                                       const char *uuid,
+                                       const char *network_id,
                                        guint dad_counter,
                                        GError **error)
 {
 	gs_free guint8 *secret_key = NULL;
 	gsize key_len = 0;
 
+	nm_assert (NM_IN_SET (stable_type,
+	                      NM_UTILS_STABLE_TYPE_UUID,
+	                      NM_UTILS_STABLE_TYPE_STABLE_ID));
+
 	if (dad_counter >= RFC7217_IDGEN_RETRIES) {
 		g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		                     "Too many DAD collisions");
@@ -2906,10 +3169,151 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
 	if (!secret_key)
 		return FALSE;
 
-	return _set_stable_privacy (addr, ifname, uuid, dad_counter,
+	return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter,
 	                            secret_key, key_len, error);
 }
 
+/*****************************************************************************/
+
+static void
+_hw_addr_eth_complete (struct ether_addr *addr,
+                       const char *current_mac_address,
+                       const char *generate_mac_address_mask)
+{
+	struct ether_addr mask;
+	struct ether_addr oui;
+	struct ether_addr *ouis;
+	gsize ouis_len;
+	guint i;
+
+	/* the second LSB of the first octet means
+	 * "globally unique, OUI enforced, BIA (burned-in-address)"
+	 * vs. "locally-administered". By default, set it to
+	 * generate locally-administered addresses.
+	 *
+	 * Maybe be overwritten by a mask below. */
+	addr->ether_addr_octet[0] |= 2;
+
+	if (!generate_mac_address_mask || !*generate_mac_address_mask)
+		goto out;
+	if (!_nm_utils_generate_mac_address_mask_parse (generate_mac_address_mask,
+	                                                &mask,
+	                                                &ouis,
+	                                                &ouis_len,
+	                                                NULL))
+		goto out;
+
+	nm_assert ((ouis == NULL) ^ (ouis_len != 0));
+	if (ouis) {
+		/* g_random_int() is good enough here. It uses a static GRand instance
+		 * that is seeded from /dev/urandom. */
+		oui = ouis[g_random_int () % ouis_len];
+		g_free (ouis);
+	} else {
+		if (!nm_utils_hwaddr_aton (current_mac_address, &oui, ETH_ALEN))
+			goto out;
+	}
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		const guint8 a = addr->ether_addr_octet[i];
+		const guint8 o = oui.ether_addr_octet[i];
+		const guint8 m = mask.ether_addr_octet[i];
+
+		addr->ether_addr_octet[i] = (a & ~m) | (o & m);
+	}
+
+out:
+	/* The LSB of the first octet must always be cleared,
+	 * it means Unicast vs. Multicast */
+	addr->ether_addr_octet[0] &= ~1;
+}
+
+char *
+nm_utils_hw_addr_gen_random_eth (const char *current_mac_address,
+                                 const char *generate_mac_address_mask)
+{
+	struct ether_addr bin_addr;
+
+	if (nm_utils_read_urandom (&bin_addr, ETH_ALEN) < 0)
+		return NULL;
+	_hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask);
+	return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN);
+}
+
+static char *
+_hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
+                         const char *stable_id,
+                         const guint8 *secret_key,
+                         gsize key_len,
+                         const char *ifname,
+                         const char *current_mac_address,
+                         const char *generate_mac_address_mask)
+{
+	GChecksum *sum;
+	guint32 tmp;
+	guint8 digest[32];
+	gsize len = sizeof (digest);
+	struct ether_addr bin_addr;
+	guint8 stable_type_uint8;
+
+	nm_assert (stable_id);
+	nm_assert (NM_IN_SET (stable_type,
+	                      NM_UTILS_STABLE_TYPE_UUID,
+	                      NM_UTILS_STABLE_TYPE_STABLE_ID));
+	nm_assert (secret_key);
+
+	sum = g_checksum_new (G_CHECKSUM_SHA256);
+	if (!sum)
+		return NULL;
+
+	key_len = MIN (key_len, G_MAXUINT32);
+
+	stable_type_uint8 = stable_type;
+	g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8));
+
+	tmp = htonl ((guint32) key_len);
+	g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp));
+	g_checksum_update (sum, (const guchar *) secret_key, key_len);
+	g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1);
+	g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1);
+
+	g_checksum_get_digest (sum, digest, &len);
+	g_checksum_free (sum);
+
+	g_return_val_if_fail (len == 32, NULL);
+
+	memcpy (&bin_addr, digest, ETH_ALEN);
+	_hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask);
+	return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN);
+}
+
+char *
+nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
+                                 const char *stable_id,
+                                 const char *ifname,
+                                 const char *current_mac_address,
+                                 const char *generate_mac_address_mask)
+{
+	gs_free guint8 *secret_key = NULL;
+	gsize key_len = 0;
+
+	g_return_val_if_fail (stable_id, NULL);
+
+	secret_key = nm_utils_secret_key_read (&key_len, NULL);
+	if (!secret_key)
+		return NULL;
+
+	return _hw_addr_gen_stable_eth (stable_type,
+	                                stable_id,
+	                                secret_key,
+	                                key_len,
+	                                ifname,
+	                                current_mac_address,
+	                                generate_mac_address_mask);
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_setpgid:
  * @unused: unused
@@ -3120,12 +3524,11 @@ nm_utils_lifetime_get (guint32 timestamp,
 }
 
 const char *
-nm_utils_dnsmasq_status_to_string (int status, char *dest, guint size)
+nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size)
 {
-	static char buffer[128];
-	char *msg, *ret;
-	gs_free char *msg_free = NULL;
-	int len;
+	const char *msg;
+
+	nm_utils_to_string_buffer_init (&dest, &size);
 
 	if (status == 0)
 		msg = "Success";
@@ -3139,22 +3542,15 @@ nm_utils_dnsmasq_status_to_string (int status, char *dest, guint size)
 		msg = "Memory allocation failure";
 	else if (status == 5)
 		msg = "Other problem";
-	else if (status >= 11)
-		msg = msg_free = g_strdup_printf ("Lease script failed with error %d", status - 10);
+	else if (status >= 11) {
+		g_snprintf (dest, size, "Lease script failed with error %d", status - 10);
+		return dest;
+	}
 	else
 		msg = "Unknown problem";
 
-	if (dest) {
-		ret = dest;
-		len = size;
-	} else {
-		ret = buffer;
-		len = sizeof (buffer);
-	}
-
-	g_snprintf (ret, len, "%s (%d)", msg, status);
-
-	return ret;
+	g_snprintf (dest, size, "%s (%d)", msg, status);
+	return dest;
 }
 
 /**
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index a1213d2a..3ef960bb 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -25,7 +25,6 @@
 #include <stdio.h>
 #include <arpa/inet.h>
 
-#include "nm-default.h"
 #include "nm-connection.h"
 
 /*****************************************************************************/
@@ -93,6 +92,7 @@ GETTER (void) \
 
 gboolean nm_ethernet_address_is_valid (gconstpointer addr, gssize len);
 
+gconstpointer nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen);
 in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
 const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
 gboolean nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen);
@@ -275,7 +275,7 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
 		G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) > sizeof (char *)); \
 		_nm_utils_strbuf_init ((buf), sizeof (buf), (p_buf_ptr), (p_buf_len)); \
 	} G_STMT_END
-void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) __attribute__((__format__ (__printf__, 3, 4)));
+void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
 void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
 void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
 
@@ -283,6 +283,7 @@ const char *nm_utils_get_ip_config_method (NMConnection *connection,
                                            GType         ip_setting_type);
 
 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);
 
 GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents);
 GPtrArray *nm_utils_read_resolv_conf_dns_options (const char *rc_contents);
@@ -308,6 +309,12 @@ const char *nm_utils_ip4_property_path (const char *ifname, const char *property
 
 gboolean nm_utils_is_specific_hostname (const char *name);
 
+int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns);
+ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll);
+int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll);
+
+int nm_utils_read_urandom (void *p, size_t n);
+
 char *nm_utils_machine_id_read (void);
 gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid);
 
@@ -333,23 +340,43 @@ struct _NMUtilsIPv6IfaceId {
 
 #define NM_UTILS_IPV6_IFACE_ID_INIT { { .id = 0 } }
 
+void nm_utils_ipv6_addr_set_interface_identifier (struct in6_addr *addr,
+                                                 const NMUtilsIPv6IfaceId iid);
+
+void nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid,
+                                                      const struct in6_addr *addr);
+
+gboolean nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid,
+                                                           const char *token);
+
+const char *nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid,
+                                                         char *buf);
+
 gboolean nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
                                                  const guint8 *hwaddr,
                                                  guint len,
                                                  guint dev_id,
                                                  NMUtilsIPv6IfaceId *out_iid);
 
-void nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
-                                                 const NMUtilsIPv6IfaceId iid);
+typedef enum { /*< skip >*/
+	NM_UTILS_STABLE_TYPE_UUID = 0,
+	NM_UTILS_STABLE_TYPE_STABLE_ID = 1,
+} NMUtilsStableType;
 
-gboolean nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr,
+gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type,
+                                                struct in6_addr *addr,
                                                 const char *ifname,
-                                                const char *uuid,
+                                                const char *network_id,
                                                 guint dad_counter,
                                                 GError **error);
 
-void nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
-                                                      const struct in6_addr *addr);
+char *nm_utils_hw_addr_gen_random_eth (const char *current_mac_address,
+                                       const char *generate_mac_address_mask);
+char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
+                                       const char *stable_id,
+                                       const char *ifname,
+                                       const char *current_mac_address,
+                                       const char *generate_mac_address_mask);
 
 void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len);
 
@@ -396,7 +423,7 @@ gboolean nm_utils_lifetime_get (guint32 timestamp,
 
 gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr);
 
-const char *nm_utils_dnsmasq_status_to_string (int status, char *dest, guint size);
+const char *nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size);
 
 void nm_utils_get_reverse_dns_domains_ip4 (guint32 ip, guint8 plen, GPtrArray *domains);
 void nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GPtrArray *domains);
diff --git a/src/nm-dcb.h b/src/nm-dcb.h
index 86e6153d..8bc08470 100644
--- a/src/nm-dcb.h
+++ b/src/nm-dcb.h
@@ -21,7 +21,6 @@
 #ifndef __NETWORKMANAGER_DCB_H__
 #define __NETWORKMANAGER_DCB_H__
 
-#include "nm-default.h"
 #include "nm-setting-dcb.h"
 
 gboolean nm_dcb_enable (const char *iface, gboolean enable, GError **error);
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index 6f72ee84..2fd2c7db 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -200,7 +200,7 @@ _vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry
 		for (i = 0; i < routes->len; i++) {
 			NMPlatformIP4Route *r = &g_array_index (routes, NMPlatformIP4Route, i);
 
-			route.rx.source = r->source;
+			route.rx.rt_source = r->rt_source;
 			if (nm_platform_ip4_route_cmp (r, &route.r4) == 0)
 				return TRUE;
 		}
@@ -208,7 +208,7 @@ _vt_routes_has_entry (const VTableIP *vtable, GArray *routes, const Entry *entry
 		for (i = 0; i < routes->len; i++) {
 			NMPlatformIP6Route *r = &g_array_index (routes, NMPlatformIP6Route, i);
 
-			route.rx.source = r->source;
+			route.rx.rt_source = r->rt_source;
 			if (nm_platform_ip6_route_cmp (r, &route.r6) == 0)
 				return TRUE;
 		}
@@ -289,7 +289,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 	if (vtable->vt->is_ip4) {
 		success = nm_platform_ip4_route_add (priv->platform,
 		                                     entry->route.rx.ifindex,
-		                                     entry->route.rx.source,
+		                                     entry->route.rx.rt_source,
 		                                     0,
 		                                     0,
 		                                     entry->route.r4.gateway,
@@ -299,7 +299,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 	} else {
 		success = nm_platform_ip6_route_add (priv->platform,
 		                                     entry->route.rx.ifindex,
-		                                     entry->route.rx.source,
+		                                     entry->route.rx.rt_source,
 		                                     in6addr_any,
 		                                     0,
 		                                     entry->route.r6.gateway,
@@ -765,7 +765,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 				 * the device. */
 				memset (&rt, 0, sizeof (rt));
 				rt.rx.ifindex = ip_ifindex;
-				rt.rx.source = NM_IP_CONFIG_SOURCE_UNKNOWN;
+				rt.rx.rt_source = NM_IP_CONFIG_SOURCE_UNKNOWN;
 				rt.rx.metric = G_MAXUINT32;
 				default_route = &rt.rx;
 
@@ -787,7 +787,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 					if (vpn_config) {
 						never_default = nm_ip4_config_get_never_default (vpn_config);
 						rt.r4.ifindex = ip_ifindex;
-						rt.r4.source = NM_IP_CONFIG_SOURCE_VPN;
+						rt.r4.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 						rt.r4.gateway = nm_ip4_config_get_gateway (vpn_config);
 						rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn);
 						rt.r4.mss = nm_ip4_config_get_mss (vpn_config);
@@ -802,7 +802,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 
 						never_default = nm_ip6_config_get_never_default (vpn_config);
 						rt.r6.ifindex = ip_ifindex;
-						rt.r6.source = NM_IP_CONFIG_SOURCE_VPN;
+						rt.r6.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 						rt.r6.gateway = int_gw ? *int_gw : in6addr_any;
 						rt.r6.metric = nm_vpn_connection_get_ip6_route_metric (vpn);
 						rt.r6.mss = nm_ip6_config_get_mss (vpn_config);
diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h
index ca92814c..30f130a4 100644
--- a/src/nm-default-route-manager.h
+++ b/src/nm-default-route-manager.h
@@ -18,13 +18,10 @@
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
-
-#include "nm-default.h"
-#include "nm-connection.h"
-
 #ifndef __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__
 #define __NETWORKMANAGER_DEFAULT_ROUTE_MANAGER_H__
 
+#include "nm-connection.h"
 
 #define NM_TYPE_DEFAULT_ROUTE_MANAGER            (nm_default_route_manager_get_type ())
 #define NM_DEFAULT_ROUTE_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManager))
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index 4959ed5d..df9be2e7 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -28,11 +28,13 @@
 #include "nm-dispatcher-api.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
+#include "nm-connectivity.h"
 #include "nm-device.h"
 #include "nm-dhcp4-config.h"
 #include "nm-dhcp6-config.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
+#include "nm-manager.h"
 #include "nm-settings-connection.h"
 #include "nm-platform.h"
 #include "nm-core-internal.h"
@@ -422,6 +424,7 @@ static const char *action_table[] = {
 	[DISPATCHER_ACTION_VPN_DOWN]     = NMD_ACTION_VPN_DOWN,
 	[DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
 	[DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
+	[DISPATCHER_ACTION_CONNECTIVITY_CHANGE] = NMD_ACTION_CONNECTIVITY_CHANGE
 };
 
 static const char *
@@ -449,6 +452,7 @@ _dispatcher_call (DispatcherAction action,
                   NMSettingsConnection *settings_connection,
                   NMConnection *applied_connection,
                   NMDevice *device,
+                  NMConnectivityState connectivity_state,
                   const char *vpn_iface,
                   NMIP4Config *vpn_ip4_config,
                   NMIP6Config *vpn_ip6_config,
@@ -482,8 +486,9 @@ _dispatcher_call (DispatcherAction action,
 
 	_ensure_requests ();
 
-	/* All actions except 'hostname' require a device */
-	if (action == DISPATCHER_ACTION_HOSTNAME) {
+	/* All actions except 'hostname' and 'connectivity-change' require a device */
+	if (   action == DISPATCHER_ACTION_HOSTNAME
+	    || action == DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
 		_LOGD ("(%u) dispatching action '%s'%s",
 		       reqid, action_to_string (action),
 		       blocking
@@ -551,8 +556,9 @@ _dispatcher_call (DispatcherAction action,
 	g_variant_builder_init (&vpn_ip4_props, G_VARIANT_TYPE_VARDICT);
 	g_variant_builder_init (&vpn_ip6_props, G_VARIANT_TYPE_VARDICT);
 
-	/* hostname actions only send the hostname */
-	if (action != DISPATCHER_ACTION_HOSTNAME) {
+	/* hostname and connectivity-change actions don't send device data */
+	if (   action != DISPATCHER_ACTION_HOSTNAME
+	    && action != DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
 		fill_device_props (device,
 		                   &device_props,
 		                   &device_ip4_props,
@@ -578,7 +584,7 @@ _dispatcher_call (DispatcherAction action,
 		GVariantIter *results;
 
 		ret = _nm_dbus_proxy_call_sync (dispatcher_proxy, "Action",
-		                                g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}sa{sv}a{sv}b)",
+		                                g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}b)",
 		                                               action_to_string (action),
 		                                               connection_dict,
 		                                               &connection_props,
@@ -587,6 +593,7 @@ _dispatcher_call (DispatcherAction action,
 		                                               &device_ip6_props,
 		                                               device_dhcp4_props,
 		                                               device_dhcp6_props,
+		                                               nm_connectivity_state_to_string (connectivity_state),
 		                                               vpn_iface ? vpn_iface : "",
 		                                               &vpn_ip4_props,
 		                                               &vpn_ip6_props,
@@ -613,7 +620,7 @@ _dispatcher_call (DispatcherAction action,
 		info->callback = callback;
 		info->user_data = user_data;
 		g_dbus_proxy_call (dispatcher_proxy, "Action",
-		                   g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}sa{sv}a{sv}b)",
+		                   g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}b)",
 		                                  action_to_string (action),
 		                                  connection_dict,
 		                                  &connection_props,
@@ -622,6 +629,7 @@ _dispatcher_call (DispatcherAction action,
 		                                  &device_ip6_props,
 		                                  device_dhcp4_props,
 		                                  device_dhcp6_props,
+		                                  nm_connectivity_state_to_string (connectivity_state),
 		                                  vpn_iface ? vpn_iface : "",
 		                                  &vpn_ip4_props,
 		                                  &vpn_ip6_props,
@@ -671,8 +679,9 @@ nm_dispatcher_call (DispatcherAction action,
                     gpointer user_data,
                     guint *out_call_id)
 {
-	return _dispatcher_call (action, FALSE, settings_connection, applied_connection, device, NULL, NULL,
-	                         NULL, callback, user_data, out_call_id);
+	return _dispatcher_call (action, FALSE, settings_connection, applied_connection, device,
+	                         NM_CONNECTIVITY_UNKNOWN, NULL, NULL, NULL,
+	                         callback, user_data, out_call_id);
 }
 
 /**
@@ -693,8 +702,8 @@ nm_dispatcher_call_sync (DispatcherAction action,
                          NMConnection *applied_connection,
                          NMDevice *device)
 {
-	return _dispatcher_call (action, TRUE, settings_connection, applied_connection, device, NULL, NULL,
-	                         NULL, NULL, NULL, NULL);
+	return _dispatcher_call (action, TRUE, settings_connection, applied_connection, device,
+	                         NM_CONNECTIVITY_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 /**
@@ -728,8 +737,9 @@ nm_dispatcher_call_vpn (DispatcherAction action,
                         gpointer user_data,
                         guint *out_call_id)
 {
-	return _dispatcher_call (action, FALSE, settings_connection, applied_connection, parent_device, vpn_iface,
-	                         vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id);
+	return _dispatcher_call (action, FALSE, settings_connection, applied_connection,
+	                         parent_device, NM_CONNECTIVITY_UNKNOWN, vpn_iface, vpn_ip4_config,
+	                         vpn_ip6_config, callback, user_data, out_call_id);
 }
 
 /**
@@ -756,10 +766,28 @@ nm_dispatcher_call_vpn_sync (DispatcherAction action,
                              NMIP4Config *vpn_ip4_config,
                              NMIP6Config *vpn_ip6_config)
 {
-	return _dispatcher_call (action, TRUE, settings_connection, applied_connection, parent_device, vpn_iface,
+	return _dispatcher_call (action, TRUE, settings_connection, applied_connection,
+	                         parent_device, NM_CONNECTIVITY_UNKNOWN, vpn_iface,
 	                         vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL);
 }
 
+/**
+ * nm_dispatcher_call_connectivity():
+ * @action: the %DispatcherAction
+ * @connectivity_state: the #NMConnectivityState value
+ *
+ * This method does not block the caller.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
+nm_dispatcher_call_connectivity (DispatcherAction action,
+                                 NMConnectivityState connectivity_state)
+{
+	return _dispatcher_call (action, FALSE, NULL, NULL, NULL, connectivity_state,
+	                         NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
 void
 nm_dispatcher_call_cancel (guint call_id)
 {
diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h
index 42dcdc46..62818c45 100644
--- a/src/nm-dispatcher.h
+++ b/src/nm-dispatcher.h
@@ -24,7 +24,6 @@
 
 #include <stdio.h>
 
-#include "nm-default.h"
 #include "nm-connection.h"
 
 typedef enum {
@@ -38,7 +37,8 @@ typedef enum {
 	DISPATCHER_ACTION_VPN_PRE_DOWN,
 	DISPATCHER_ACTION_VPN_DOWN,
 	DISPATCHER_ACTION_DHCP4_CHANGE,
-	DISPATCHER_ACTION_DHCP6_CHANGE
+	DISPATCHER_ACTION_DHCP6_CHANGE,
+	DISPATCHER_ACTION_CONNECTIVITY_CHANGE
 } DispatcherAction;
 
 typedef void (*DispatcherFunc) (guint call_id, gpointer user_data);
@@ -75,6 +75,9 @@ gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action,
                                       NMIP4Config *vpn_ip4_config,
                                       NMIP6Config *vpn_ip6_config);
 
+gboolean nm_dispatcher_call_connectivity (DispatcherAction action,
+                                          NMConnectivityState state);
+
 void nm_dispatcher_call_cancel (guint call_id);
 
 void nm_dispatcher_init (void);
diff --git a/src/nm-enum-types.c b/src/nm-enum-types.c
index b2f91dca..670502cf 100644
--- a/src/nm-enum-types.c
+++ b/src/nm-enum-types.c
@@ -6,21 +6,11 @@
 #include "config.h"
 
 #include "nm-enum-types.h"
+#include "nm-default.h"
 
-#include "nm-device-bond.h" 
-#include "nm-device-bridge.h" 
-#include "nm-device-ethernet.h" 
-#include "nm-device-infiniband.h" 
-#include "nm-device-ip-tunnel.h" 
-#include "nm-device-macvlan.h" 
-#include "nm-device-tun.h" 
-#include "nm-device-veth.h" 
-#include "nm-device-vlan.h" 
-#include "nm-device-vxlan.h" 
-#include "nm-dhcp-dhclient.h" 
 #include "nm-dhcp-dhclient-utils.h" 
-#include "nm-dhcp-dhcpcd.h" 
-#include "nm-dhcp-systemd.h" 
+#include "nm-checkpoint-manager.h" 
+#include "nm-checkpoint.h" 
 #include "nm-device.h" 
 #include "nm-lldp-listener.h" 
 #include "nm-arping-manager.h" 
@@ -42,10 +32,8 @@
 #include "nm-platform-utils.h" 
 #include "nm-platform.h" 
 #include "nm-linux-platform.h" 
-#include "nm-fake-platform.h" 
 #include "wifi-utils-nl80211.h" 
 #include "wifi-utils.h" 
-#include "nm-fake-rdisc.h" 
 #include "nm-lndp-rdisc.h" 
 #include "nm-rdisc.h" 
 #include "nm-ppp-manager.h" 
@@ -74,7 +62,6 @@
 #include "nm-bus-manager.h" 
 #include "nm-config.h" 
 #include "nm-config-data.h" 
-#include "nm-connection-provider.h" 
 #include "nm-connectivity.h" 
 #include "nm-dcb.h" 
 #include "nm-route-manager.h" 
@@ -86,7 +73,6 @@
 #include "nm-firewall-manager.h" 
 #include "nm-ip4-config.h" 
 #include "nm-ip6-config.h" 
-#include "nm-logging.h" 
 #include "nm-auth-manager.h" 
 #include "nm-auth-subject.h" 
 #include "nm-auth-utils.h" 
@@ -97,31 +83,10 @@
 #include "nm-session-monitor.h" 
 #include "nm-sleep-monitor.h" 
 #include "nm-types.h" 
-#include "nm-core-utils.h" 
 #include "NetworkManagerUtils.h" 
 #include "wifi-utils-wext.h"
 
 GType
-nm_vlan_error_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { NM_VLAN_ERROR_CONNECTION_NOT_VLAN, "NM_VLAN_ERROR_CONNECTION_NOT_VLAN", "ConnectionNotVlan" },
-        { NM_VLAN_ERROR_CONNECTION_INVALID, "NM_VLAN_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
-        { NM_VLAN_ERROR_CONNECTION_INCOMPATIBLE, "NM_VLAN_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMVlanError"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-GType
 nm_unman_flag_op_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
@@ -475,6 +440,27 @@ nm_supplicant_error_get_type (void)
   return g_define_type_id__volatile;
 }
 GType
+nm_config_run_state_property_type_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { NM_CONFIG_STATE_PROPERTY_NONE, "NM_CONFIG_STATE_PROPERTY_NONE", "none" },
+        { NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, "NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED", "networking-enabled" },
+        { NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED, "NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED", "wifi-enabled" },
+        { NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED, "NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED", "wwan-enabled" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("NMConfigRunStatePropertyType"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+GType
 nm_config_get_value_flags_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
@@ -551,6 +537,7 @@ nm_dispatcher_action_get_type (void)
         { DISPATCHER_ACTION_VPN_DOWN, "DISPATCHER_ACTION_VPN_DOWN", "vpn-down" },
         { DISPATCHER_ACTION_DHCP4_CHANGE, "DISPATCHER_ACTION_DHCP4_CHANGE", "dhcp4-change" },
         { DISPATCHER_ACTION_DHCP6_CHANGE, "DISPATCHER_ACTION_DHCP6_CHANGE", "dhcp6-change" },
+        { DISPATCHER_ACTION_CONNECTIVITY_CHANGE, "DISPATCHER_ACTION_CONNECTIVITY_CHANGE", "connectivity-change" },
         { 0, NULL, NULL }
       };
       GType g_define_type_id =
@@ -610,8 +597,14 @@ nm_ip_config_source_get_type (void)
     {
       static const GEnumValue values[] = {
         { NM_IP_CONFIG_SOURCE_UNKNOWN, "NM_IP_CONFIG_SOURCE_UNKNOWN", "nm-ip-config-source-unknown" },
-        { _NM_IP_CONFIG_SOURCE_RTM_F_CLONED, "_NM_IP_CONFIG_SOURCE_RTM_F_CLONED", "-nm-ip-config-source-rtm-f-cloned" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC, "NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC", "nm-ip-config-source-rtprot-unspec" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT, "NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT", "nm-ip-config-source-rtprot-redirect" },
         { NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, "NM_IP_CONFIG_SOURCE_RTPROT_KERNEL", "nm-ip-config-source-rtprot-kernel" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_BOOT, "NM_IP_CONFIG_SOURCE_RTPROT_BOOT", "nm-ip-config-source-rtprot-boot" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_STATIC, "NM_IP_CONFIG_SOURCE_RTPROT_STATIC", "nm-ip-config-source-rtprot-static" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_RA, "NM_IP_CONFIG_SOURCE_RTPROT_RA", "nm-ip-config-source-rtprot-ra" },
+        { NM_IP_CONFIG_SOURCE_RTPROT_DHCP, "NM_IP_CONFIG_SOURCE_RTPROT_DHCP", "nm-ip-config-source-rtprot-dhcp" },
+        { _NM_IP_CONFIG_SOURCE_RTPROT_LAST, "_NM_IP_CONFIG_SOURCE_RTPROT_LAST", "-nm-ip-config-source-rtprot-last" },
         { NM_IP_CONFIG_SOURCE_KERNEL, "NM_IP_CONFIG_SOURCE_KERNEL", "nm-ip-config-source-kernel" },
         { NM_IP_CONFIG_SOURCE_SHARED, "NM_IP_CONFIG_SOURCE_SHARED", "nm-ip-config-source-shared" },
         { NM_IP_CONFIG_SOURCE_IP4LL, "NM_IP_CONFIG_SOURCE_IP4LL", "nm-ip-config-source-ip4ll" },
@@ -644,7 +637,7 @@ nm_link_type_get_type (void)
         { NM_LINK_TYPE_INFINIBAND, "NM_LINK_TYPE_INFINIBAND", "infiniband" },
         { NM_LINK_TYPE_OLPC_MESH, "NM_LINK_TYPE_OLPC_MESH", "olpc-mesh" },
         { NM_LINK_TYPE_WIFI, "NM_LINK_TYPE_WIFI", "wifi" },
-        { NM_LINK_TYPE_WWAN_ETHERNET, "NM_LINK_TYPE_WWAN_ETHERNET", "wwan-ethernet" },
+        { NM_LINK_TYPE_WWAN_NET, "NM_LINK_TYPE_WWAN_NET", "wwan-net" },
         { NM_LINK_TYPE_WIMAX, "NM_LINK_TYPE_WIMAX", "wimax" },
         { NM_LINK_TYPE_DUMMY, "NM_LINK_TYPE_DUMMY", "dummy" },
         { NM_LINK_TYPE_GRE, "NM_LINK_TYPE_GRE", "gre" },
@@ -730,49 +723,6 @@ nm_ip_config_merge_flags_get_type (void)
 
   return g_define_type_id__volatile;
 }
-GType
-nm_match_spec_match_type_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { NM_MATCH_SPEC_NO_MATCH, "NM_MATCH_SPEC_NO_MATCH", "no-match" },
-        { NM_MATCH_SPEC_MATCH, "NM_MATCH_SPEC_MATCH", "match" },
-        { NM_MATCH_SPEC_NEG_MATCH, "NM_MATCH_SPEC_NEG_MATCH", "neg-match" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMMatchSpecMatchType"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-GType
-nm_utils_test_flags_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GFlagsValue values[] = {
-        { NM_UTILS_TEST_NONE, "NM_UTILS_TEST_NONE", "nm-utils-test-none" },
-        { _NM_UTILS_TEST_INITIALIZED, "_NM_UTILS_TEST_INITIALIZED", "-nm-utils-test-initialized" },
-        { _NM_UTILS_TEST_GENERAL, "_NM_UTILS_TEST_GENERAL", "-nm-utils-test-general" },
-        { NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK, "NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK", "nm-utils-test-no-keyfile-owner-check" },
-        { _NM_UTILS_TEST_LAST, "_NM_UTILS_TEST_LAST", "-nm-utils-test-last" },
-        { NM_UTILS_TEST_ALL, "NM_UTILS_TEST_ALL", "nm-utils-test-all" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_flags_register_static (g_intern_static_string ("NMUtilsTestFlags"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
 
 
 
diff --git a/src/nm-enum-types.h b/src/nm-enum-types.h
index 36a9c270..09ffca60 100644
--- a/src/nm-enum-types.h
+++ b/src/nm-enum-types.h
@@ -10,8 +10,6 @@
 #include <glib-object.h>
 
 G_BEGIN_DECLS
-GType nm_vlan_error_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_VLAN_ERROR (nm_vlan_error_get_type ())
 GType nm_unman_flag_op_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_UNMAN_FLAG_OP (nm_unman_flag_op_get_type ())
 GType nm_dhcp_state_get_type (void) G_GNUC_CONST;
@@ -44,6 +42,8 @@ GType nm_supplicant_feature_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_SUPPLICANT_FEATURE (nm_supplicant_feature_get_type ())
 GType nm_supplicant_error_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_SUPPLICANT_ERROR (nm_supplicant_error_get_type ())
+GType nm_config_run_state_property_type_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_CONFIG_RUN_STATE_PROPERTY_TYPE (nm_config_run_state_property_type_get_type ())
 GType nm_config_get_value_flags_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_CONFIG_GET_VALUE_FLAGS (nm_config_get_value_flags_get_type ())
 GType nm_config_change_flags_get_type (void) G_GNUC_CONST;
@@ -62,10 +62,6 @@ GType nm_pobject_type_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_POBJECT_TYPE (nm_pobject_type_get_type ())
 GType nm_ip_config_merge_flags_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_IP_CONFIG_MERGE_FLAGS (nm_ip_config_merge_flags_get_type ())
-GType nm_match_spec_match_type_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_MATCH_SPEC_MATCH_TYPE (nm_match_spec_match_type_get_type ())
-GType nm_utils_test_flags_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_UTILS_TEST_FLAGS (nm_utils_test_flags_get_type ())
 G_END_DECLS
 
 #endif /* __NM_ENUM_TYPES_H__ */
diff --git a/src/nm-exported-object.c b/src/nm-exported-object.c
index 10f7d23f..c4dbab87 100644
--- a/src/nm-exported-object.c
+++ b/src/nm-exported-object.c
@@ -257,12 +257,16 @@ nm_exported_object_class_add_interface (NMExportedObjectClass *object_class,
 	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_slice_new (NMExportedObjectClassInfo);
-	classinfo->skeleton_types = NULL;
-	classinfo->methods = g_array_new (FALSE, FALSE, sizeof (NMExportedObjectDBusMethodImpl));
-	classinfo->properties = g_hash_table_new (g_str_hash, g_str_equal);
-	g_type_set_qdata (G_TYPE_FROM_CLASS (object_class),
-	                  nm_exported_object_class_info_quark (), classinfo);
+	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 (g_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));
@@ -342,8 +346,6 @@ nm_exported_object_class_add_interface (NMExportedObjectClass *object_class,
 		}
 	}
 
-	g_assert_cmpint (n_method_signals, ==, classinfo->methods->len);
-
 	g_type_class_unref (dbus_object_class);
 }
 
@@ -571,7 +573,9 @@ _create_export_path (NMExportedObjectClass *klass)
 			g_hash_table_insert (prefix_counters, g_strdup (class_export_path), counter);
 		}
 
+		NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral")
 		return g_strdup_printf (class_export_path, (*counter)++);
+		NM_PRAGMA_WARNING_REENABLE
 	}
 
 	return g_strdup (class_export_path);
diff --git a/src/nm-exported-object.h b/src/nm-exported-object.h
index c2c22804..7036e0d0 100644
--- a/src/nm-exported-object.h
+++ b/src/nm-exported-object.h
@@ -21,10 +21,6 @@
 #ifndef NM_EXPORTED_OBJECT_H
 #define NM_EXPORTED_OBJECT_H
 
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
 /*****************************************************************************/
 
 char *nm_exported_object_skeletonify_method_name (const char *dbus_method_name);
@@ -79,6 +75,4 @@ GDBusInterfaceSkeleton *nm_exported_object_get_interface_by_type (NMExportedObje
 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))
 
-G_END_DECLS
-
-#endif	/* NM_EXPORTED_OBJECT_H */
+#endif /* NM_EXPORTED_OBJECT_H */
diff --git a/src/nm-firewall-manager.h b/src/nm-firewall-manager.h
index a08ac8b8..92fac8e2 100644
--- a/src/nm-firewall-manager.h
+++ b/src/nm-firewall-manager.h
@@ -21,16 +21,11 @@
 #ifndef __NETWORKMANAGER_FIREWALL_MANAGER_H__
 #define __NETWORKMANAGER_FIREWALL_MANAGER_H__
 
-#include "nm-default.h"
-
 #define FIREWALL_DBUS_SERVICE         "org.fedoraproject.FirewallD1"
 #define FIREWALL_DBUS_PATH            "/org/fedoraproject/FirewallD1"
 #define FIREWALL_DBUS_INTERFACE       "org.fedoraproject.FirewallD1"
 #define FIREWALL_DBUS_INTERFACE_ZONE  "org.fedoraproject.FirewallD1.zone"
 
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_FIREWALL_MANAGER				(nm_firewall_manager_get_type ())
 #define NM_FIREWALL_MANAGER(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_FIREWALL_MANAGER, NMFirewallManager))
 #define NM_FIREWALL_MANAGER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_FIREWALL_MANAGER, NMFirewallManagerClass))
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 20f7729a..49672cf5 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -66,6 +66,7 @@ static struct {
 	int tempaddr;
 	char *ifname;
 	char *uuid;
+	char *stable_id;
 	char *dhcp4_address;
 	char *dhcp4_clientid;
 	char *dhcp4_hostname;
@@ -129,8 +130,9 @@ dhcp4_state_changed (NMDhcpClient *client,
 }
 
 static void
-rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
+rdisc_config_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, gpointer user_data)
 {
+	NMRDiscConfigMap changed = changed_int;
 	static NMIP6Config *rdisc_config = NULL;
 	NMIP6Config *existing;
 	static int system_support = -1;
@@ -165,11 +167,9 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 
 	if (changed & NM_RDISC_CONFIG_GATEWAYS) {
 		/* Use the first gateway as ordered in router discovery cache. */
-		if (rdisc->gateways->len) {
-			NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
-
-			nm_ip6_config_set_gateway (rdisc_config, &gateway->address);
-		} else
+		if (rdata->gateways_n)
+			nm_ip6_config_set_gateway (rdisc_config, &rdata->gateways[0].address);
+		else
 			nm_ip6_config_set_gateway (rdisc_config, NULL);
 	}
 
@@ -182,8 +182,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 		 * also counts static and temporary addresses when checking
 		 * max_addresses.
 		 **/
-		for (i = 0; i < rdisc->addresses->len; i++) {
-			NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+		for (i = 0; i < rdata->addresses_n; i++) {
+			const NMRDiscAddress *discovered_address = &rdata->addresses[i];
 			NMPlatformIP6Address address;
 
 			memset (&address, 0, sizeof (address));
@@ -194,7 +194,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 			address.preferred = discovered_address->preferred;
 			if (address.preferred > address.lifetime)
 				address.preferred = address.lifetime;
-			address.source = NM_IP_CONFIG_SOURCE_RDISC;
+			address.addr_source = NM_IP_CONFIG_SOURCE_RDISC;
 			address.n_ifa_flags = ifa_flags;
 
 			nm_ip6_config_add_address (rdisc_config, &address);
@@ -205,25 +205,17 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 		/* Rebuild route list from router discovery cache. */
 		nm_ip6_config_reset_routes (rdisc_config);
 
-		for (i = 0; i < rdisc->routes->len; i++) {
-			NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
-			NMPlatformIP6Route route;
-
-			/* Only accept non-default routes.  The router has no idea what the
-			 * local configuration or user preferences are, so sending routes
-			 * with a prefix length of 0 is quite rude and thus ignored.
-			 */
-			if (   discovered_route->plen > 0
-			    && discovered_route->plen <= 128) {
-				memset (&route, 0, sizeof (route));
-				route.network = discovered_route->network;
-				route.plen = discovered_route->plen;
-				route.gateway = discovered_route->gateway;
-				route.source = NM_IP_CONFIG_SOURCE_RDISC;
-				route.metric = global_opt.priority_v6;
-
-				nm_ip6_config_add_route (rdisc_config, &route);
-			}
+		for (i = 0; i < rdata->routes_n; i++) {
+			const NMRDiscRoute *discovered_route = &rdata->routes[i];
+			const NMPlatformIP6Route route = {
+				.network    = discovered_route->network,
+				.plen       = discovered_route->plen,
+				.gateway    = discovered_route->gateway,
+				.rt_source  = NM_IP_CONFIG_SOURCE_RDISC,
+				.metric     = global_opt.priority_v6,
+			};
+
+			nm_ip6_config_add_route (rdisc_config, &route);
 		}
 	}
 
@@ -232,12 +224,12 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 	}
 
 	if (changed & NM_RDISC_CONFIG_HOP_LIMIT)
-		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdisc->hop_limit);
+		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit);
 
 	if (changed & NM_RDISC_CONFIG_MTU) {
 		char val[16];
 
-		g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
+		g_snprintf (val, sizeof (val), "%d", rdata->mtu);
 		nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val);
 	}
 
@@ -278,8 +270,9 @@ do_early_setup (int *argc, char **argv[])
 	gint64 priority64_v6 = -1;
 	GOptionEntry options[] = {
 		/* Interface/IP config */
-		{ "ifname", 'i', 0, G_OPTION_ARG_STRING, &global_opt.ifname, N_("The interface to manage"), N_("eth0") },
-		{ "uuid", 'u', 0, G_OPTION_ARG_STRING, &global_opt.uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") },
+		{ "ifname", 'i', 0, G_OPTION_ARG_STRING, &global_opt.ifname, N_("The interface to manage"), "eth0" },
+		{ "uuid", 'u', 0, G_OPTION_ARG_STRING, &global_opt.uuid, N_("Connection UUID"),  "661e8cd0-b618-46b8-9dc9-31a52baaa16b" },
+		{ "stable-id", '\0', 0, G_OPTION_ARG_STRING, &global_opt.stable_id, N_("Connection Token for Stable IDs"),  "eth" },
 		{ "slaac", 's', 0, G_OPTION_ARG_NONE, &global_opt.slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
 		{ "slaac-required", '6', 0, G_OPTION_ARG_NONE, &global_opt.slaac_required, N_("Whether SLAAC must be successful"), NULL },
 		{ "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &global_opt.tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
@@ -469,9 +462,23 @@ main (int argc, char *argv[])
 	}
 
 	if (global_opt.slaac) {
+		NMUtilsStableType stable_type = NM_UTILS_STABLE_TYPE_UUID;
+		const char *stable_id = global_opt.uuid;
+
 		nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE);
 
-		rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode, NULL);
+		if (   global_opt.stable_id
+		    && (global_opt.stable_id[0] >= '0' && global_opt.stable_id[0] <= '9')
+		    && global_opt.stable_id[1] == ' ') {
+			/* strict parsing of --stable-id, which is the numeric stable-type
+			 * and the ID, joined with one space. For now, only support stable-types
+			 * from 0 to 9. */
+			stable_type = (global_opt.stable_id[0] - '0');
+			stable_id = &global_opt.stable_id[2];
+		}
+		rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname,
+		                           stable_type, stable_id,
+		                           global_opt.addr_gen_mode, NULL);
 		g_assert (rdisc);
 
 		if (iid)
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 0ae7d0d4..22d1d077 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -35,11 +35,7 @@
 
 #include "nmdbus-ip4-config.h"
 
-G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT)
-
-#define NM_IP4_CONFIG_GET_PRIVATE(o) ((o)->priv)
-
-typedef struct _NMIP4ConfigPrivate {
+typedef struct {
 	gboolean never_default;
 	guint32 gateway;
 	gboolean has_gateway;
@@ -61,6 +57,30 @@ typedef struct _NMIP4ConfigPrivate {
 	gint dns_priority;
 } NMIP4ConfigPrivate;
 
+struct _NMIP4Config {
+	NMExportedObject parent;
+	NMIP4ConfigPrivate _priv;
+};
+
+struct _NMIP4ConfigClass {
+	NMExportedObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT)
+
+#define NM_IP4_CONFIG_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMIP4Config *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_IP4_CONFIG (_self)); \
+		&_self->_priv; \
+	})
+
 /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
 G_STATIC_ASSERT (sizeof (uint) >= sizeof (guint32));
 G_STATIC_ASSERT (G_MAXUINT >= 0xFFFFFFFF);
@@ -209,8 +229,8 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b)
 		return p1 > p2 ? -1 : 1;
 
 	/* Sort the addresses based on their source. */
-	if (a1->source != a2->source)
-		return a1->source > a2->source ? -1 : 1;
+	if (a1->addr_source != a2->addr_source)
+		return a1->addr_source > a2->addr_source ? -1 : 1;
 
 	if ((a1->label[0] == '\0') != (a2->label[0] == '\0'))
 		return (a1->label[0] == '\0') ? -1 : 1;
@@ -337,7 +357,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 gboolean
 nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 	int i;
 	gs_unref_ptrarray GPtrArray *added_addresses = NULL;
 
@@ -370,7 +390,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 				nm_assert (addr->plen <= 32);
 
 				route.ifindex = ifindex;
-				route.source = NM_IP_CONFIG_SOURCE_KERNEL;
+				route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
 
 				/* The destination network depends on the peer-address. */
 				route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen);
@@ -400,14 +420,6 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 			const NMPlatformIP4Route *route;
 
 			route = nm_ip4_config_get_route (config, i);
-
-			/* Don't add the route if it's more specific than one of the subnets
-			 * the device already has an IP address on.
-			 */
-			if (   route->gateway == 0
-			    && nm_ip4_config_destination_is_direct (config, route->network, route->plen))
-				continue;
-
 			/* duplicates in @routes are no problem as route-manager handles them
 			 * gracefully (by ignoring them). */
 			g_array_append_vals (routes, route, 1);
@@ -473,7 +485,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 		nm_assert (address.plen <= 32);
 		address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
 		address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
-		address.source = NM_IP_CONFIG_SOURCE_USER;
+		address.addr_source = NM_IP_CONFIG_SOURCE_USER;
 
 		label = nm_ip_address_get_attribute (s_addr, "label");
 		if (label)
@@ -502,7 +514,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 			route.metric = default_route_metric;
 		else
 			route.metric = nm_ip_route_get_metric (s_route);
-		route.source = NM_IP_CONFIG_SOURCE_USER;
+		route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 
 		nm_ip4_config_add_route (config, &route);
 	}
@@ -612,7 +624,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 			continue;
 
 		/* Ignore routes provided by external sources */
-		if (route->source != NM_IP_CONFIG_SOURCE_USER)
+		if (route->rt_source != nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER))
 			continue;
 
 		s_route = nm_ip_route_new_binary (AF_INET,
@@ -654,7 +666,8 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 void
 nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags)
 {
-	NMIP4ConfigPrivate *dst_priv, *src_priv;
+	NMIP4ConfigPrivate *dst_priv;
+	const NMIP4ConfigPrivate *src_priv;
 	guint32 i;
 
 	g_return_if_fail (src != NULL);
@@ -748,7 +761,7 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFl
 static int
 _addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->addresses->len; i++) {
@@ -763,7 +776,7 @@ _addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr)
 static int
 _nameservers_get_index (const NMIP4Config *self, guint32 ns)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->nameservers->len; i++) {
@@ -778,7 +791,7 @@ _nameservers_get_index (const NMIP4Config *self, guint32 ns)
 static int
 _routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->routes->len; i++) {
@@ -794,7 +807,7 @@ _routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route)
 static int
 _domains_get_index (const NMIP4Config *self, const char *domain)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->domains->len; i++) {
@@ -809,7 +822,7 @@ _domains_get_index (const NMIP4Config *self, const char *domain)
 static int
 _searches_get_index (const NMIP4Config *self, const char *search)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->searches->len; i++) {
@@ -824,7 +837,7 @@ _searches_get_index (const NMIP4Config *self, const char *search)
 static int
 _dns_options_get_index (const NMIP4Config *self, const char *option)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->dns_options->len; i++) {
@@ -839,7 +852,7 @@ _dns_options_get_index (const NMIP4Config *self, const char *option)
 static int
 _nis_servers_get_index (const NMIP4Config *self, guint32 nis_server)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->nis->len; i++) {
@@ -854,7 +867,7 @@ _nis_servers_get_index (const NMIP4Config *self, guint32 nis_server)
 static int
 _wins_get_index (const NMIP4Config *self, guint32 wins_server)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->wins->len; i++) {
@@ -1041,7 +1054,8 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 #endif
 	gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
 	guint i, num;
-	NMIP4ConfigPrivate *dst_priv, *src_priv;
+	NMIP4ConfigPrivate *dst_priv;
+	const NMIP4ConfigPrivate *src_priv;
 	const NMPlatformIP4Address *dst_addr, *src_addr;
 	const NMPlatformIP4Route *dst_route, *src_route;
 
@@ -1398,7 +1412,7 @@ nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default)
 gboolean
 nm_ip4_config_get_never_default (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->never_default;
 }
@@ -1440,7 +1454,7 @@ nm_ip4_config_unset_gateway (NMIP4Config *config)
 gboolean
 nm_ip4_config_has_gateway (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->has_gateway;
 }
@@ -1448,7 +1462,7 @@ nm_ip4_config_has_gateway (const NMIP4Config *config)
 guint32
 nm_ip4_config_get_gateway (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->gateway;
 }
@@ -1456,7 +1470,7 @@ nm_ip4_config_get_gateway (const NMIP4Config *config)
 gint64
 nm_ip4_config_get_route_metric (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->route_metric;
 }
@@ -1507,14 +1521,14 @@ nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address *new)
 			*item = *new;
 
 			/* But restore highest priority source */
-			item->source = MAX (item_old.source, new->source);
+			item->addr_source = MAX (item_old.addr_source, new->addr_source);
 
 			/* for addresses that we read from the kernel, we keep the timestamps as defined
 			 * by the previous source (item_old). The reason is, that the other source configured the lifetimes
 			 * with "what should be" and the kernel values are "what turned out after configuring it".
 			 *
 			 * For other sources, the longer lifetime wins. */
-			if (   (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
+			if (   (new->addr_source == NM_IP_CONFIG_SOURCE_KERNEL && new->addr_source != item_old.addr_source)
 			    || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
 				item->timestamp = item_old.timestamp;
 				item->lifetime = item_old.lifetime;
@@ -1547,7 +1561,7 @@ nm_ip4_config_del_address (NMIP4Config *config, guint i)
 guint
 nm_ip4_config_get_num_addresses (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->addresses->len;
 }
@@ -1555,7 +1569,7 @@ nm_ip4_config_get_num_addresses (const NMIP4Config *config)
 const NMPlatformIP4Address *
 nm_ip4_config_get_address (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return &g_array_index (priv->addresses, NMPlatformIP4Address, i);
 }
@@ -1600,7 +1614,7 @@ nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
 
 	g_return_if_fail (new != NULL);
 	g_return_if_fail (new->plen > 0 && new->plen <= 32);
-	g_assert (priv->ifindex);
+	g_return_if_fail (priv->ifindex > 0);
 
 	for (i = 0; i < priv->routes->len; i++ ) {
 		NMPlatformIP4Route *item = &g_array_index (priv->routes, NMPlatformIP4Route, i);
@@ -1608,10 +1622,10 @@ nm_ip4_config_add_route (NMIP4Config *config, const NMPlatformIP4Route *new)
 		if (routes_are_duplicate (item, new, FALSE)) {
 			if (nm_platform_ip4_route_cmp (item, new) == 0)
 				return;
-			old_source = item->source;
+			old_source = item->rt_source;
 			memcpy (item, new, sizeof (*item));
 			/* Restore highest priority source */
-			item->source = MAX (old_source, new->source);
+			item->rt_source = MAX (old_source, new->rt_source);
 			item->ifindex = priv->ifindex;
 			goto NOTIFY;
 		}
@@ -1639,7 +1653,7 @@ nm_ip4_config_del_route (NMIP4Config *config, guint i)
 guint
 nm_ip4_config_get_num_routes (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->routes->len;
 }
@@ -1647,7 +1661,7 @@ nm_ip4_config_get_num_routes (const NMIP4Config *config)
 const NMPlatformIP4Route *
 nm_ip4_config_get_route (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return &g_array_index (priv->routes, NMPlatformIP4Route, i);
 }
@@ -1655,7 +1669,7 @@ nm_ip4_config_get_route (const NMIP4Config *config, guint i)
 const NMPlatformIP4Route *
 nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 	guint i;
 	NMPlatformIP4Route *best_route = NULL;
 
@@ -1725,7 +1739,7 @@ nm_ip4_config_del_nameserver (NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_nameservers (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->nameservers->len;
 }
@@ -1733,7 +1747,7 @@ nm_ip4_config_get_num_nameservers (const NMIP4Config *config)
 guint32
 nm_ip4_config_get_nameserver (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_array_index (priv->nameservers, guint32, i);
 }
@@ -1782,7 +1796,7 @@ nm_ip4_config_del_domain (NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_domains (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->domains->len;
 }
@@ -1790,7 +1804,7 @@ nm_ip4_config_get_num_domains (const NMIP4Config *config)
 const char *
 nm_ip4_config_get_domain (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->domains, i);
 }
@@ -1854,7 +1868,7 @@ nm_ip4_config_del_search (NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_searches (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->searches->len;
 }
@@ -1862,7 +1876,7 @@ nm_ip4_config_get_num_searches (const NMIP4Config *config)
 const char *
 nm_ip4_config_get_search (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->searches, i);
 }
@@ -1911,7 +1925,7 @@ nm_ip4_config_del_dns_option(NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_dns_options (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->dns_options->len;
 }
@@ -1919,7 +1933,7 @@ nm_ip4_config_get_num_dns_options (const NMIP4Config *config)
 const char *
 nm_ip4_config_get_dns_option (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->dns_options, i);
 }
@@ -1958,7 +1972,7 @@ nm_ip4_config_set_mss (NMIP4Config *config, guint32 mss)
 guint32
 nm_ip4_config_get_mss (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->mss;
 }
@@ -1999,7 +2013,7 @@ nm_ip4_config_del_nis_server (NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_nis_servers (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->nis->len;
 }
@@ -2007,7 +2021,7 @@ nm_ip4_config_get_num_nis_servers (const NMIP4Config *config)
 guint32
 nm_ip4_config_get_nis_server (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_array_index (priv->nis, guint32, i);
 }
@@ -2024,7 +2038,7 @@ nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain)
 const char *
 nm_ip4_config_get_nis_domain (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->nis_domain;
 }
@@ -2072,7 +2086,7 @@ nm_ip4_config_del_wins (NMIP4Config *config, guint i)
 guint32
 nm_ip4_config_get_num_wins (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->wins->len;
 }
@@ -2080,7 +2094,7 @@ nm_ip4_config_get_num_wins (const NMIP4Config *config)
 guint32
 nm_ip4_config_get_wins (const NMIP4Config *config, guint i)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return g_array_index (priv->wins, guint32, i);
 }
@@ -2102,7 +2116,7 @@ nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource source
 guint32
 nm_ip4_config_get_mtu (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->mtu;
 }
@@ -2110,7 +2124,7 @@ nm_ip4_config_get_mtu (const NMIP4Config *config)
 NMIPConfigSource
 nm_ip4_config_get_mtu_source (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->mtu_source;
 }
@@ -2128,7 +2142,7 @@ nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered)
 gboolean
 nm_ip4_config_get_metered (const NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	return priv->metered;
 }
@@ -2245,10 +2259,7 @@ nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b)
 static void
 nm_ip4_config_init (NMIP4Config *config)
 {
-	NMIP4ConfigPrivate *priv;
-
-	priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate);
-	config->priv = priv;
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
 	priv->addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address));
 	priv->routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
@@ -2282,7 +2293,7 @@ finalize (GObject *object)
 
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMIP4Config *config = NM_IP4_CONFIG (object);
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
@@ -2473,8 +2484,6 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
 	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
-	g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate));
-
 	exported_object_class->export_path = NM_DBUS_PATH "/IP4Config/%u";
 
 	object_class->get_property = get_property;
@@ -2482,72 +2491,72 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
 	object_class->finalize = finalize;
 
 	obj_properties[PROP_IFINDEX] =
-		g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "",
-		                  -1, G_MAXINT, -1,
-		                  G_PARAM_READWRITE |
-		                  G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS);
+	    g_param_spec_int (NM_IP4_CONFIG_IFINDEX, "", "",
+	                      -1, G_MAXINT, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESS_DATA] =
-		g_param_spec_variant (NM_IP4_CONFIG_ADDRESS_DATA, "", "",
-		                      G_VARIANT_TYPE ("aa{sv}"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_ADDRESS_DATA, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESSES] =
-		g_param_spec_variant (NM_IP4_CONFIG_ADDRESSES, "", "",
-		                      G_VARIANT_TYPE ("aau"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_ADDRESSES, "", "",
+	                          G_VARIANT_TYPE ("aau"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTE_DATA] =
-		g_param_spec_variant (NM_IP4_CONFIG_ROUTE_DATA, "", "",
-		                      G_VARIANT_TYPE ("aa{sv}"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_ROUTE_DATA, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTES] =
-		g_param_spec_variant (NM_IP4_CONFIG_ROUTES, "", "",
-		                      G_VARIANT_TYPE ("aau"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_ROUTES, "", "",
+	                          G_VARIANT_TYPE ("aau"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_GATEWAY] =
-		g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "",
-		                     NULL,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS);
+	    g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_NAMESERVERS] =
-		g_param_spec_variant (NM_IP4_CONFIG_NAMESERVERS, "", "",
-		                      G_VARIANT_TYPE ("au"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_NAMESERVERS, "", "",
+	                          G_VARIANT_TYPE ("au"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DOMAINS] =
-		g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "",
-		                    G_TYPE_STRV,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+	    g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_SEARCHES] =
-		g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "",
-		                    G_TYPE_STRV,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+	    g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DNS_OPTIONS] =
-		 g_param_spec_boxed (NM_IP4_CONFIG_DNS_OPTIONS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS);
+	     g_param_spec_boxed (NM_IP4_CONFIG_DNS_OPTIONS, "", "",
+	                         G_TYPE_STRV,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DNS_PRIORITY] =
-		 g_param_spec_int (NM_IP4_CONFIG_DNS_PRIORITY, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READABLE |
-		                   G_PARAM_STATIC_STRINGS);
+	     g_param_spec_int (NM_IP4_CONFIG_DNS_PRIORITY, "", "",
+	                       G_MININT32, G_MAXINT32, 0,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_WINS_SERVERS] =
-		g_param_spec_variant (NM_IP4_CONFIG_WINS_SERVERS, "", "",
-		                      G_VARIANT_TYPE ("au"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP4_CONFIG_WINS_SERVERS, "", "",
+	                          G_VARIANT_TYPE ("au"),
+	                          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-ip4-config.h b/src/nm-ip4-config.h
index e88fb141..f302630a 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -31,18 +31,7 @@
 #define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IP4_CONFIG))
 #define NM_IP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass))
 
-struct _NMIP4ConfigPrivate;
-
-struct _NMIP4Config {
-	NMExportedObject parent;
-
-	/* private */
-	struct _NMIP4ConfigPrivate *priv;
-};
-
-typedef struct {
-	NMExportedObjectClass parent;
-} NMIP4ConfigClass;
+typedef struct _NMIP4ConfigClass NMIP4ConfigClass;
 
 /* internal */
 #define NM_IP4_CONFIG_IFINDEX "ifindex"
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 86447357..ac9e6cd1 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -28,17 +28,14 @@
 
 #include "nm-utils.h"
 #include "nm-platform.h"
+#include "nm-platform-utils.h"
 #include "nm-route-manager.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 #include "nmdbus-ip6-config.h"
 
-G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
-
-#define NM_IP6_CONFIG_GET_PRIVATE(o) ((o)->priv)
-
-typedef struct _NMIP6ConfigPrivate {
+typedef struct {
 	gboolean never_default;
 	struct in6_addr gateway;
 	GArray *addresses;
@@ -53,6 +50,29 @@ typedef struct _NMIP6ConfigPrivate {
 	gint dns_priority;
 } NMIP6ConfigPrivate;
 
+struct _NMIP6Config {
+	NMExportedObject parent;
+	NMIP6ConfigPrivate _priv;
+};
+
+struct _NMIP6ConfigClass {
+	NMExportedObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
+
+#define NM_IP6_CONFIG_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMIP6Config *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_IP6_CONFIG (_self)); \
+		&_self->_priv; \
+	})
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config,
 	PROP_IFINDEX,
@@ -242,8 +262,8 @@ _addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
 	}
 
 	/* Sort the addresses based on their source. */
-	if (a1->source != a2->source)
-		return a1->source > a2->source ? -1 : 1;
+	if (a1->addr_source != a2->addr_source)
+		return a1->addr_source > a2->addr_source ? -1 : 1;
 
 	/* sort permanent addresses before non-permanent. */
 	perm1 = (a1->n_ifa_flags & IFA_F_PERMANENT);
@@ -374,7 +394,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 gboolean
 nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 	int i;
 	gboolean success;
 
@@ -392,14 +412,6 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_fu
 
 		for (i = 0; i < count; i++) {
 			route = nm_ip6_config_get_route (config, i);
-
-			/* Don't add the route if it's more specific than one of the subnets
-			 * the device already has an IP address on.
-			 */
-			if (   IN6_IS_ADDR_UNSPECIFIED (&route->gateway)
-			    && nm_ip6_config_destination_is_direct (config, &route->network, route->plen))
-				continue;
-
 			g_array_append_vals (routes, route, 1);
 		}
 
@@ -459,7 +471,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 		nm_assert (address.plen <= 128);
 		address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
 		address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
-		address.source = NM_IP_CONFIG_SOURCE_USER;
+		address.addr_source = NM_IP_CONFIG_SOURCE_USER;
 
 		nm_ip6_config_add_address (config, &address);
 	}
@@ -484,7 +496,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 			route.metric = default_route_metric;
 		else
 			route.metric = nm_ip_route_get_metric (s_route);
-		route.source = NM_IP_CONFIG_SOURCE_USER;
+		route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 
 		nm_ip6_config_add_route (config, &route);
 	}
@@ -602,7 +614,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 			continue;
 
 		/* Ignore routes provided by external sources */
-		if (route->source != NM_IP_CONFIG_SOURCE_USER)
+		if (route->rt_source != nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER))
 			continue;
 
 		s_route = nm_ip_route_new_binary (AF_INET6,
@@ -643,7 +655,8 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 void
 nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags)
 {
-	NMIP6ConfigPrivate *dst_priv, *src_priv;
+	NMIP6ConfigPrivate *dst_priv;
+	const NMIP6ConfigPrivate *src_priv;
 	guint32 i;
 
 	g_return_if_fail (src != NULL);
@@ -733,7 +746,7 @@ nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6
 static int
 _addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->addresses->len; i++) {
@@ -748,7 +761,7 @@ _addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr)
 static int
 _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->nameservers->len; i++) {
@@ -763,7 +776,7 @@ _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns)
 static int
 _routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->routes->len; i++) {
@@ -778,7 +791,7 @@ _routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route)
 static int
 _domains_get_index (const NMIP6Config *self, const char *domain)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->domains->len; i++) {
@@ -793,7 +806,7 @@ _domains_get_index (const NMIP6Config *self, const char *domain)
 static int
 _searches_get_index (const NMIP6Config *self, const char *search)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->searches->len; i++) {
@@ -808,7 +821,7 @@ _searches_get_index (const NMIP6Config *self, const char *search)
 static int
 _dns_options_get_index (const NMIP6Config *self, const char *option)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 	guint i;
 
 	for (i = 0; i < priv->dns_options->len; i++) {
@@ -976,7 +989,8 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 #endif
 	gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
 	guint i, num;
-	NMIP6ConfigPrivate *dst_priv, *src_priv;
+	NMIP6ConfigPrivate *dst_priv;
+	const NMIP6ConfigPrivate *src_priv;
 	const NMPlatformIP6Address *dst_addr, *src_addr;
 	const NMPlatformIP6Route *dst_route, *src_route;
 
@@ -1232,7 +1246,7 @@ nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default)
 gboolean
 nm_ip6_config_get_never_default (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->never_default;
 }
@@ -1257,7 +1271,7 @@ nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *gateway)
 const struct in6_addr *
 nm_ip6_config_get_gateway (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return IN6_IS_ADDR_UNSPECIFIED (&priv->gateway) ? NULL : &priv->gateway;
 }
@@ -1265,7 +1279,7 @@ nm_ip6_config_get_gateway (const NMIP6Config *config)
 gint64
 nm_ip6_config_get_route_metric (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->route_metric;
 }
@@ -1316,14 +1330,14 @@ nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
 			*item = *new;
 
 			/* But restore highest priority source */
-			item->source = MAX (item_old.source, new->source);
+			item->addr_source = MAX (item_old.addr_source, new->addr_source);
 
 			/* for addresses that we read from the kernel, we keep the timestamps as defined
 			 * by the previous source (item_old). The reason is, that the other source configured the lifetimes
 			 * with "what should be" and the kernel values are "what turned out after configuring it".
 			 *
 			 * For other sources, the longer lifetime wins. */
-			if (   (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
+			if (   (new->addr_source == NM_IP_CONFIG_SOURCE_KERNEL && new->addr_source != item_old.addr_source)
 			    || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
 				item->timestamp = item_old.timestamp;
 				item->lifetime = item_old.lifetime;
@@ -1356,7 +1370,7 @@ nm_ip6_config_del_address (NMIP6Config *config, guint i)
 guint
 nm_ip6_config_get_num_addresses (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->addresses->len;
 }
@@ -1364,7 +1378,7 @@ nm_ip6_config_get_num_addresses (const NMIP6Config *config)
 const NMPlatformIP6Address *
 nm_ip6_config_get_address (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return &g_array_index (priv->addresses, NMPlatformIP6Address, i);
 }
@@ -1379,7 +1393,7 @@ nm_ip6_config_address_exists (const NMIP6Config *config,
 const NMPlatformIP6Address *
 nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal)
 {
-	NMIP6ConfigPrivate *priv;
+	const NMIP6ConfigPrivate *priv;
 	guint i;
 
 	g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
@@ -1399,6 +1413,47 @@ nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolea
 	return NULL;
 }
 
+/**
+ * nm_ip6_config_has_dad_pending_addresses
+ * @self: configuration containing the addresses to check
+ * @candidates: configuration with the list of addresses we are
+ *   interested in
+ *
+ * Check whether there are addresses with DAD pending in @self, that
+ * are also contained in @candidates.
+ *
+ * Returns: %TRUE if at least one matching address was found, %FALSE
+ *   otherwise
+ */
+gboolean
+nm_ip6_config_has_any_dad_pending (const NMIP6Config *self,
+                                   const NMIP6Config *candidates)
+{
+	const NMPlatformIP6Address *addr, *addr_c;
+	guint i, j, num, num_c;
+
+	num = nm_ip6_config_get_num_addresses (self);
+
+	for (i = 0; i < num; i++) {
+		addr = nm_ip6_config_get_address (self, i);
+		if (   NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_TENTATIVE)
+		    && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_DADFAILED)
+		    && !NM_FLAGS_HAS (addr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
+
+			num_c = nm_ip6_config_get_num_addresses (candidates);
+
+			for (j = 0; j < num_c; j++) {
+				addr_c = nm_ip6_config_get_address (candidates, j);
+				if (   addresses_are_duplicate (addr, addr_c)
+				    && addr->plen == addr_c->plen)
+					return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
 /******************************************************************/
 
 void
@@ -1432,7 +1487,7 @@ nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
 
 	g_return_if_fail (new != NULL);
 	g_return_if_fail (new->plen > 0 && new->plen <= 128);
-	g_assert (priv->ifindex);
+	g_return_if_fail (priv->ifindex > 0);
 
 	for (i = 0; i < priv->routes->len; i++ ) {
 		NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
@@ -1440,10 +1495,10 @@ nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
 		if (routes_are_duplicate (item, new, FALSE)) {
 			if (nm_platform_ip6_route_cmp (item, new) == 0)
 				return;
-			old_source = item->source;
+			old_source = item->rt_source;
 			*item = *new;
 			/* Restore highest priority source */
-			item->source = MAX (old_source, new->source);
+			item->rt_source = MAX (old_source, new->rt_source);
 			item->ifindex = priv->ifindex;
 			goto NOTIFY;
 		}
@@ -1471,7 +1526,7 @@ nm_ip6_config_del_route (NMIP6Config *config, guint i)
 guint
 nm_ip6_config_get_num_routes (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->routes->len;
 }
@@ -1479,7 +1534,7 @@ nm_ip6_config_get_num_routes (const NMIP6Config *config)
 const NMPlatformIP6Route *
 nm_ip6_config_get_route (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return &g_array_index (priv->routes, NMPlatformIP6Route, i);
 }
@@ -1487,7 +1542,7 @@ nm_ip6_config_get_route (const NMIP6Config *config, guint i)
 const NMPlatformIP6Route *
 nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 	guint i;
 	NMPlatformIP6Route *best_route = NULL;
 
@@ -1518,7 +1573,7 @@ nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct
 const NMPlatformIP6Address *
 nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 	guint i;
 	NMPlatformIP6Address *subnet = NULL;
 	struct in6_addr subnet2, host2;
@@ -1585,7 +1640,7 @@ nm_ip6_config_del_nameserver (NMIP6Config *config, guint i)
 guint32
 nm_ip6_config_get_num_nameservers (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->nameservers->len;
 }
@@ -1593,7 +1648,7 @@ nm_ip6_config_get_num_nameservers (const NMIP6Config *config)
 const struct in6_addr *
 nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return &g_array_index (priv->nameservers, struct in6_addr, i);
 }
@@ -1642,7 +1697,7 @@ nm_ip6_config_del_domain (NMIP6Config *config, guint i)
 guint32
 nm_ip6_config_get_num_domains (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->domains->len;
 }
@@ -1650,7 +1705,7 @@ nm_ip6_config_get_num_domains (const NMIP6Config *config)
 const char *
 nm_ip6_config_get_domain (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->domains, i);
 }
@@ -1714,7 +1769,7 @@ nm_ip6_config_del_search (NMIP6Config *config, guint i)
 guint32
 nm_ip6_config_get_num_searches (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->searches->len;
 }
@@ -1722,7 +1777,7 @@ nm_ip6_config_get_num_searches (const NMIP6Config *config)
 const char *
 nm_ip6_config_get_search (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->searches, i);
 }
@@ -1771,7 +1826,7 @@ nm_ip6_config_del_dns_option (NMIP6Config *config, guint i)
 guint32
 nm_ip6_config_get_num_dns_options (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->dns_options->len;
 }
@@ -1779,7 +1834,7 @@ nm_ip6_config_get_num_dns_options (const NMIP6Config *config)
 const char *
 nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return g_ptr_array_index (priv->dns_options, i);
 }
@@ -1818,7 +1873,7 @@ nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss)
 guint32
 nm_ip6_config_get_mss (const NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	return priv->mss;
 }
@@ -1933,10 +1988,7 @@ nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
 static void
 nm_ip6_config_init (NMIP6Config *config)
 {
-	NMIP6ConfigPrivate *priv;
-
-	priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate);
-	config->priv = priv;
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 
 	priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address));
 	priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route));
@@ -1985,7 +2037,7 @@ nameservers_to_gvalue (GArray *array, GValue *value)
 
 static void
 get_property (GObject *object, guint prop_id,
-			  GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMIP6Config *config = NM_IP6_CONFIG (object);
 	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
@@ -2159,8 +2211,6 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
 	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
 
-	g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
-
 	exported_object_class->export_path = NM_DBUS_PATH "/IP6Config/%u";
 
 	/* virtual methods */
@@ -2170,66 +2220,66 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
 
 	/* properties */
 	obj_properties[PROP_IFINDEX] =
-		g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "",
-		                  -1, G_MAXINT, -1,
-		                  G_PARAM_READWRITE |
-		                  G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS);
+	    g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "",
+	                      -1, G_MAXINT, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESS_DATA] =
-		g_param_spec_variant (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
-		                      G_VARIANT_TYPE ("aa{sv}"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ADDRESSES] =
-		g_param_spec_variant (NM_IP6_CONFIG_ADDRESSES, "", "",
-		                      G_VARIANT_TYPE ("a(ayuay)"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP6_CONFIG_ADDRESSES, "", "",
+	                          G_VARIANT_TYPE ("a(ayuay)"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTE_DATA] =
-		g_param_spec_variant (NM_IP6_CONFIG_ROUTE_DATA, "", "",
-		                      G_VARIANT_TYPE ("aa{sv}"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP6_CONFIG_ROUTE_DATA, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTES] =
-		g_param_spec_variant (NM_IP6_CONFIG_ROUTES, "", "",
-		                      G_VARIANT_TYPE ("a(ayuayu)"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP6_CONFIG_ROUTES, "", "",
+	                          G_VARIANT_TYPE ("a(ayuayu)"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_GATEWAY] =
-		g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
-		                     NULL,
-		                     G_PARAM_READABLE |
-		                     G_PARAM_STATIC_STRINGS);
+	    g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_NAMESERVERS] =
-		g_param_spec_variant (NM_IP6_CONFIG_NAMESERVERS, "", "",
-		                      G_VARIANT_TYPE ("aay"),
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS);
+	    g_param_spec_variant (NM_IP6_CONFIG_NAMESERVERS, "", "",
+	                          G_VARIANT_TYPE ("aay"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DOMAINS] =
-		g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
-		                    G_TYPE_STRV,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+	    g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_SEARCHES] =
-		g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
-		                    G_TYPE_STRV,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+	    g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DNS_OPTIONS] =
-		g_param_spec_boxed (NM_IP6_CONFIG_DNS_OPTIONS, "", "",
-		                    G_TYPE_STRV,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS);
+	    g_param_spec_boxed (NM_IP6_CONFIG_DNS_OPTIONS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_DNS_PRIORITY] =
-		g_param_spec_int (NM_IP6_CONFIG_DNS_PRIORITY, "", "",
-		                  G_MININT32, G_MAXINT32, 0,
-		                  G_PARAM_READABLE |
-		                  G_PARAM_STATIC_STRINGS);
+	    g_param_spec_int (NM_IP6_CONFIG_DNS_PRIORITY, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READABLE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 010b1396..c3f8d9f8 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -33,18 +33,7 @@
 #define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IP6_CONFIG))
 #define NM_IP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass))
 
-struct _NMIP6ConfigPrivate;
-
-struct _NMIP6Config {
-	NMExportedObject parent;
-
-	/* private */
-	struct _NMIP6ConfigPrivate *priv;
-};
-
-typedef struct {
-	NMExportedObjectClass parent;
-} NMIP6ConfigClass;
+typedef struct _NMIP6ConfigClass NMIP6ConfigClass;
 
 /* internal */
 #define NM_IP6_CONFIG_IFINDEX "ifindex"
@@ -101,6 +90,8 @@ const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config
 const NMPlatformIP6Address *nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal);
 gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address);
 gboolean nm_ip6_config_addresses_sort (NMIP6Config *config, NMSettingIP6ConfigPrivacy use_temporary);
+gboolean nm_ip6_config_has_any_dad_pending (const NMIP6Config *self,
+                                            const NMIP6Config *candidates);
 
 /* Routes */
 void nm_ip6_config_reset_routes (NMIP6Config *config);
diff --git a/src/nm-logging.c b/src/nm-logging.c
index 935b34a8..3db8d20c 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -32,11 +32,6 @@
 #include <strings.h>
 #include <string.h>
 
-#if defined (NO_SYSTEMD_JOURNAL) && defined (SYSTEMD_JOURNAL)
-#undef SYSTEMD_JOURNAL
-#define SYSTEMD_JOURNAL 0
-#endif
-
 #if SYSTEMD_JOURNAL
 #define SD_JOURNAL_SUPPRESS_LOCATION
 #include <systemd/sd-journal.h>
@@ -45,38 +40,6 @@
 #include "nm-errors.h"
 #include "nm-core-utils.h"
 
-typedef enum {
-	LOG_FORMAT_FLAG_NONE                                = 0,
-	LOG_FORMAT_FLAG_TIMESTAMP_DEBUG                     = (1LL << 0),
-	LOG_FORMAT_FLAG_TIMESTAMP_INFO                      = (1LL << 1),
-	LOG_FORMAT_FLAG_TIMESTAMP_ERROR                     = (1LL << 2),
-	LOG_FORMAT_FLAG_LOCATION_DEBUG                      = (1LL << 3),
-	LOG_FORMAT_FLAG_LOCATION_INFO                       = (1LL << 4),
-	LOG_FORMAT_FLAG_LOCATION_ERROR                      = (1LL << 5),
-	LOG_FORMAT_FLAG_ALIGN_LOCATION                      = (1LL << 6),
-
-	_LOG_FORMAT_FLAG_TIMESTAMP                          = LOG_FORMAT_FLAG_TIMESTAMP_DEBUG |
-	                                                      LOG_FORMAT_FLAG_TIMESTAMP_INFO |
-	                                                      LOG_FORMAT_FLAG_TIMESTAMP_ERROR,
-	_LOG_FORMAT_FLAG_LOCATION                           = LOG_FORMAT_FLAG_LOCATION_DEBUG |
-	                                                      LOG_FORMAT_FLAG_LOCATION_INFO |
-	                                                      LOG_FORMAT_FLAG_LOCATION_ERROR,
-
-	_LOG_FORMAT_FLAG_LEVEL_DEBUG                        = LOG_FORMAT_FLAG_TIMESTAMP_DEBUG |
-	                                                      LOG_FORMAT_FLAG_LOCATION_DEBUG,
-	_LOG_FORMAT_FLAG_LEVEL_INFO                         = LOG_FORMAT_FLAG_TIMESTAMP_INFO |
-	                                                      LOG_FORMAT_FLAG_LOCATION_INFO,
-	_LOG_FORMAT_FLAG_LEVEL_ERROR                        = LOG_FORMAT_FLAG_TIMESTAMP_ERROR |
-	                                                      LOG_FORMAT_FLAG_LOCATION_ERROR,
-
-	_LOG_FORMAT_FLAG_SYSLOG                             = _LOG_FORMAT_FLAG_TIMESTAMP |
-	                                                      LOG_FORMAT_FLAG_LOCATION_DEBUG |
-	                                                      LOG_FORMAT_FLAG_LOCATION_ERROR |
-	                                                      LOG_FORMAT_FLAG_ALIGN_LOCATION,
-
-	_LOG_FORMAT_FLAG_DEFAULT                            = _LOG_FORMAT_FLAG_TIMESTAMP,
-} LogFormatFlags;
-
 void (*_nm_logging_clear_platform_logging_cache) (void);
 
 static void
@@ -93,16 +56,32 @@ typedef struct {
 typedef struct {
 	const char *name;
 	const char *level_str;
+
+	/* nm-logging uses syslog internally. Note that the three most-verbose syslog levels
+	 * are LOG_DEBUG, LOG_INFO and LOG_NOTICE. Journal already highlights LOG_NOTICE
+	 * as special.
+	 *
+	 * On the other hand, we have three levels LOGL_TRACE, LOGL_DEBUG and LOGL_INFO,
+	 * which are regular messages not to be highlighted. For that reason, we must map
+	 * LOGL_TRACE and LOGL_DEBUG both to syslog level LOG_DEBUG. */
 	int syslog_level;
+
 	GLogLevelFlags g_log_level;
-	LogFormatFlags log_format_level;
 } LogLevelDesc;
 
+NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL] = {
+	/* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL);
+	 *
+	 * Note: LOGD_VPN_PLUGIN is special and must be disabled for
+	 * DEBUG and TRACE levels. */
+	[LOGL_INFO] = LOGD_DEFAULT,
+	[LOGL_WARN] = LOGD_DEFAULT,
+	[LOGL_ERR]  = LOGD_DEFAULT,
+};
+
 static struct {
 	NMLogLevel log_level;
-	NMLogDomain logging[_LOGL_N_REAL];
-	gboolean logging_set_up;
-	LogFormatFlags log_format_flags;
+	bool uses_syslog:1;
 	enum {
 		LOG_BACKEND_GLIB,
 		LOG_BACKEND_SYSLOG,
@@ -111,22 +90,22 @@ static struct {
 	char *logging_domains_to_string;
 	const LogLevelDesc level_desc[_LOGL_N];
 
-#define _DOMAIN_DESC_LEN 37
+#define _DOMAIN_DESC_LEN 38
 	/* Would be nice to use C99 flexible array member here,
 	 * but that feature doesn't seem well supported. */
 	const LogDesc domain_desc[_DOMAIN_DESC_LEN];
 } global = {
+	/* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); */
 	.log_level = LOGL_INFO,
 	.log_backend = LOG_BACKEND_GLIB,
-	.log_format_flags = _LOG_FORMAT_FLAG_DEFAULT,
 	.level_desc = {
-		[LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
-		[LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_INFO,    G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
-		[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    _LOG_FORMAT_FLAG_LEVEL_INFO },
-		[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_INFO },
-		[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_ERROR },
-		[_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   0 },
-		[_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   0 },
+		[LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+		[LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+		[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    },
+		[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, },
+		[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, },
+		[_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   },
+		[_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   },
 	},
 	.domain_desc = {
 		{ LOGD_PLATFORM,  "PLATFORM" },
@@ -165,6 +144,7 @@ static struct {
 		{ LOGD_DISPATCH,  "DISPATCH" },
 		{ LOGD_AUDIT,     "AUDIT" },
 		{ LOGD_SYSTEMD,   "SYSTEMD" },
+		{ LOGD_VPN_PLUGIN,"VPN_PLUGIN" },
 		{ 0, NULL }
 		/* keep _DOMAIN_DESC_LEN in sync */
 	},
@@ -185,19 +165,6 @@ static char *_domains_to_string (gboolean include_level_override);
 
 /************************************************************************/
 
-static void
-_ensure_initialized (void)
-{
-	if (G_UNLIKELY (!global.logging_set_up)) {
-		int errsv = errno;
-
-		nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL);
-
-		/* must ensure that errno is not modified. */
-		errno = errsv;
-	}
-}
-
 static gboolean
 match_log_level (const char  *level,
                  NMLogLevel  *out_level,
@@ -224,7 +191,7 @@ nm_logging_setup (const char  *level,
                   GError     **error)
 {
 	GString *unrecognized = NULL;
-	NMLogDomain new_logging[G_N_ELEMENTS (global.logging)];
+	NMLogDomain new_logging[G_N_ELEMENTS (_nm_logging_enabled_state)];
 	NMLogLevel new_log_level = global.log_level;
 	char **tmp, **iter;
 	int i;
@@ -235,13 +202,8 @@ nm_logging_setup (const char  *level,
 	g_return_val_if_fail (!error || !*error, FALSE);
 
 	/* domains */
-	if (!domains || !*domains) {
-		domains = global.logging_set_up
-		          ? (domains_free = _domains_to_string (FALSE))
-		          : LOGD_DEFAULT_STRING;
-	}
-
-	global.logging_set_up = TRUE;
+	if (!domains || !*domains)
+		domains = (domains_free = _domains_to_string (FALSE));
 
 	for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
 		new_logging[i] = 0;
@@ -253,7 +215,7 @@ nm_logging_setup (const char  *level,
 		if (new_log_level == _LOGL_KEEP) {
 			new_log_level = global.log_level;
 			for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
-				new_logging[i] = global.logging[i];
+				new_logging[i] = _nm_logging_enabled_state[i];
 		}
 	}
 
@@ -264,6 +226,11 @@ nm_logging_setup (const char  *level,
 		NMLogDomain bits;
 		char *p;
 
+		/* LOGD_VPN_PLUGIN is protected, that is, when setting ALL or DEFAULT,
+		 * it does not enable the verbose levels DEBUG and TRACE, because that
+		 * may expose sensitive data. */
+		NMLogDomain protect = LOGD_NONE;
+
 		if (!strlen (*iter))
 			continue;
 
@@ -280,11 +247,13 @@ nm_logging_setup (const char  *level,
 		bits = 0;
 
 		/* Check for combined domains */
-		if (!g_ascii_strcasecmp (*iter, LOGD_ALL_STRING))
+		if (!g_ascii_strcasecmp (*iter, LOGD_ALL_STRING)) {
 			bits = LOGD_ALL;
-		else if (!g_ascii_strcasecmp (*iter, LOGD_DEFAULT_STRING))
+			protect = LOGD_VPN_PLUGIN;
+		} else if (!g_ascii_strcasecmp (*iter, LOGD_DEFAULT_STRING)) {
 			bits = LOGD_DEFAULT;
-		else if (!g_ascii_strcasecmp (*iter, LOGD_DHCP_STRING))
+			protect = LOGD_VPN_PLUGIN;
+		} else if (!g_ascii_strcasecmp (*iter, LOGD_DHCP_STRING))
 			bits = LOGD_DHCP;
 		else if (!g_ascii_strcasecmp (*iter, LOGD_IP_STRING))
 			bits = LOGD_IP;
@@ -321,13 +290,17 @@ nm_logging_setup (const char  *level,
 
 		if (domain_log_level == _LOGL_KEEP) {
 			for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
-				new_logging[i] = (new_logging[i] & ~bits) | (global.logging[i] & bits);
+				new_logging[i] = (new_logging[i] & ~bits) | (_nm_logging_enabled_state[i] & bits);
 		} else {
 			for (i = 0; i < G_N_ELEMENTS (new_logging); i++) {
 				if (i < domain_log_level)
 					new_logging[i] &= ~bits;
-				else
+				else {
 					new_logging[i] |= bits;
+					if (   protect
+					    && i < LOGL_INFO)
+						new_logging[i] &= ~protect;
+				}
 			}
 		}
 	}
@@ -339,7 +312,7 @@ nm_logging_setup (const char  *level,
 
 	global.log_level = new_log_level;
 	for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
-		global.logging[i] = new_logging[i];
+		_nm_logging_enabled_state[i] = new_logging[i];
 
 	if (   had_platform_debug
 	    && _nm_logging_clear_platform_logging_cache
@@ -384,8 +357,6 @@ nm_logging_all_levels_to_string (void)
 const char *
 nm_logging_domains_to_string (void)
 {
-	_ensure_initialized ();
-
 	if (G_UNLIKELY (!global.logging_domains_to_string))
 		global.logging_domains_to_string = _domains_to_string (TRUE);
 
@@ -406,7 +377,7 @@ _domains_to_string (gboolean include_level_override)
 	str = g_string_sized_new (75);
 	for (diter = &global.domain_desc[0]; diter->name; diter++) {
 		/* If it's set for any lower level, it will also be set for LOGL_ERR */
-		if (!(diter->num & global.logging[LOGL_ERR]))
+		if (!(diter->num & _nm_logging_enabled_state[LOGL_ERR]))
 			continue;
 
 		if (str->len)
@@ -418,15 +389,15 @@ _domains_to_string (gboolean include_level_override)
 
 		/* Check if it's logging at a lower level than the default. */
 		for (i = 0; i < global.log_level; i++) {
-			if (diter->num & global.logging[i]) {
+			if (diter->num & _nm_logging_enabled_state[i]) {
 				g_string_append_printf (str, ":%s", global.level_desc[i].name);
 				break;
 			}
 		}
 		/* Check if it's logging at a higher level than the default. */
-		if (!(diter->num & global.logging[global.log_level])) {
-			for (i = global.log_level + 1; i < G_N_ELEMENTS (global.logging); i++) {
-				if (diter->num & global.logging[i]) {
+		if (!(diter->num & _nm_logging_enabled_state[global.log_level])) {
+			for (i = global.log_level + 1; i < G_N_ELEMENTS (_nm_logging_enabled_state); i++) {
+				if (diter->num & _nm_logging_enabled_state[i]) {
 					g_string_append_printf (str, ":%s", global.level_desc[i].name);
 					break;
 				}
@@ -459,20 +430,29 @@ nm_logging_all_domains_to_string (void)
 	return str->str;
 }
 
-gboolean
-nm_logging_enabled (NMLogLevel level, NMLogDomain domain)
+/**
+ * nm_logging_get_level:
+ * @domain: find the lowest enabled logging level for the
+ *   given domain. If this is a set of multiple
+ *   domains, the most verbose level will be returned.
+ *
+ * Returns: the lowest (most verbose) logging level for the
+ *   give @domain, or %_LOGL_OFF if it is disabled.
+ **/
+NMLogLevel
+nm_logging_get_level (NMLogDomain domain)
 {
-	if ((guint) level >= G_N_ELEMENTS (global.logging))
-		g_return_val_if_reached (FALSE);
-
-	/* This function is guaranteed not to modify errno. */
-	_ensure_initialized ();
+	NMLogLevel sl = _LOGL_OFF;
 
-	return !!(global.logging[level] & domain);
+	G_STATIC_ASSERT (LOGL_TRACE == 0);
+	while (   sl > LOGL_TRACE
+	       && nm_logging_enabled (sl - 1, domain))
+		sl--;
+	return sl;
 }
 
 #if SYSTEMD_JOURNAL
-__attribute__((__format__ (__printf__, 4, 5)))
+_nm_printf (4, 5)
 static void
 _iovec_set_format (struct iovec *iov, gboolean *iov_free, int i, const char *format, ...)
 {
@@ -512,15 +492,12 @@ _nm_log_impl (const char *file,
 	char *msg;
 	char *fullmsg;
 	char s_buf_timestamp[64];
-	char s_buf_location[1024];
 	GTimeVal tv;
 
-	if ((guint) level >= G_N_ELEMENTS (global.logging))
+	if ((guint) level >= G_N_ELEMENTS (_nm_logging_enabled_state))
 		g_return_if_reached ();
 
-	_ensure_initialized ();
-
-	if (!(global.logging[level] & domain))
+	if (!(_nm_logging_enabled_state[level] & domain))
 		return;
 
 	/* Make sure that %m maps to the specified error */
@@ -534,51 +511,8 @@ _nm_log_impl (const char *file,
 	msg = g_strdup_vprintf (fmt, args);
 	va_end (args);
 
-	if (NM_FLAGS_ANY (global.log_format_flags, global.level_desc[level].log_format_level & _LOG_FORMAT_FLAG_TIMESTAMP)) {
-		g_get_current_time (&tv);
-		nm_sprintf_buf (s_buf_timestamp, " [%ld.%04ld]", tv.tv_sec, (tv.tv_usec + 50) / 100);
-	} else
-		s_buf_timestamp[0] = '\0';
-
-	s_buf_location[0] = '\0';
-	if (NM_FLAGS_ANY (global.log_format_flags, global.level_desc[level].log_format_level & _LOG_FORMAT_FLAG_LOCATION)) {
-#define MAX_LEN_FILE 37
-#define MAX_LEN_FUNC 26
-		gsize l = sizeof (s_buf_location);
-		char *p = s_buf_location, *p_buf;
-		gsize len;
-		char s_buf[MAX (MAX_LEN_FILE, MAX_LEN_FUNC) + 30];
-
-		if (file) {
-			if (NM_FLAGS_HAS (global.log_format_flags, LOG_FORMAT_FLAG_ALIGN_LOCATION)) {
-				/* left-align the "[file:line]" string, but truncate from left to MAX_LEN_FILE chars. */
-				len = strlen (file);
-				nm_sprintf_buf (s_buf, "[%s:%u]",
-				                len > MAX_LEN_FILE ? &file[len - MAX_LEN_FILE] : file,
-				                line);
-				len = strlen (s_buf);
-				if (len > MAX_LEN_FILE) {
-					p_buf = &s_buf[len - MAX_LEN_FILE];
-					p_buf[0] = '[';
-				} else
-					p_buf = s_buf;
-				nm_utils_strbuf_append (&p, &l, " %-"G_STRINGIFY (MAX_LEN_FILE)"s", p_buf);
-			} else
-				nm_utils_strbuf_append (&p, &l, " [%s:%u]", file, line);
-		}
-		if (func) {
-			if (NM_FLAGS_HAS (global.log_format_flags, LOG_FORMAT_FLAG_ALIGN_LOCATION)) {
-				/* left-align the "func():" string, but truncate from left to MAX_LEN_FUNC chars. */
-				len = strlen (func);
-				nm_sprintf_buf (s_buf, "%s():",
-				                len > MAX_LEN_FUNC ? &func[len - MAX_LEN_FUNC] : func);
-				len = strlen (s_buf);
-				nm_utils_strbuf_append (&p, &l, " %-"G_STRINGIFY (MAX_LEN_FUNC)"s",
-				                        len > MAX_LEN_FUNC ? &s_buf[len - MAX_LEN_FUNC] : s_buf);
-			} else
-				nm_utils_strbuf_append (&p, &l, " %s():", func);
-		}
-	}
+	g_get_current_time (&tv);
+	nm_sprintf_buf (s_buf_timestamp, " [%ld.%04ld]", tv.tv_sec, (tv.tv_usec + 50) / 100);
 
 	switch (global.log_backend) {
 #if SYSTEMD_JOURNAL
@@ -596,10 +530,9 @@ _nm_log_impl (const char *file,
 
 			_iovec_set_format (iov, iov_free, i_field++, "PRIORITY=%d", global.level_desc[level].syslog_level);
 			_iovec_set_format (iov, iov_free, i_field++, "MESSAGE="
-			                   "%-7s%s%s %s",
+			                   "%-7s%s %s",
 			                   global.level_desc[level].level_str,
 			                   s_buf_timestamp,
-			                   s_buf_location,
 			                   msg);
 			_iovec_set_literal_string (iov, iov_free, i_field++, "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN);
 			_iovec_set_format (iov, iov_free, i_field++, "SYSLOG_PID=%ld", (long) getpid ());
@@ -609,7 +542,7 @@ _nm_log_impl (const char *file,
 				const char *s_domain_1 = NULL;
 				GString *s_domain_all = NULL;
 				NMLogDomain dom_all = domain;
-				NMLogDomain dom = dom_all & global.logging[level];
+				NMLogDomain dom = dom_all & _nm_logging_enabled_state[level];
 
 				for (diter = &global.domain_desc[0]; diter->name; diter++) {
 					if (!NM_FLAGS_HAS (dom_all, diter->num))
@@ -646,7 +579,8 @@ _nm_log_impl (const char *file,
 					_iovec_set_format (iov, iov_free, i_field++, "NM_LOG_DOMAINS=%s", s_domain_1);
 			}
 			_iovec_set_format (iov, iov_free, i_field++, "NM_LOG_LEVEL=%s", global.level_desc[level].name);
-			_iovec_set_format (iov, iov_free, i_field++, "CODE_FUNC=%s", func ?: "");
+			if (func)
+				_iovec_set_format (iov, iov_free, i_field++, "CODE_FUNC=%s", func);
 			_iovec_set_format (iov, iov_free, i_field++, "CODE_FILE=%s", file ?: "");
 			_iovec_set_format (iov, iov_free, i_field++, "CODE_LINE=%u", line);
 			_iovec_set_format (iov, iov_free, i_field++, "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000));
@@ -667,10 +601,9 @@ _nm_log_impl (const char *file,
 		break;
 #endif
 	default:
-		fullmsg = g_strdup_printf ("%-7s%s%s %s",
+		fullmsg = g_strdup_printf ("%-7s%s %s",
 		                           global.level_desc[level].level_str,
 		                           s_buf_timestamp,
-		                           s_buf_location,
 		                           msg);
 
 		if (global.log_backend == LOG_BACKEND_SYSLOG)
@@ -744,25 +677,28 @@ nm_log_handler (const gchar *log_domain,
 	}
 }
 
+gboolean
+nm_logging_syslog_enabled (void)
+{
+	return global.uses_syslog;
+}
+
 void
 nm_logging_syslog_openlog (const char *logging_backend)
 {
-	LogFormatFlags log_format_flags;
-
 	if (global.log_backend != LOG_BACKEND_GLIB)
 		g_return_if_reached ();
 
 	if (!logging_backend)
 		logging_backend = ""NM_CONFIG_LOGGING_BACKEND_DEFAULT;
 
-	log_format_flags = _LOG_FORMAT_FLAG_DEFAULT;
-
 	if (strcmp (logging_backend, "debug") == 0) {
 		global.log_backend = LOG_BACKEND_SYSLOG;
 		openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
 #if SYSTEMD_JOURNAL
 	} else if (strcmp (logging_backend, "syslog") != 0) {
 		global.log_backend = LOG_BACKEND_JOURNAL;
+		global.uses_syslog = TRUE;
 
 		/* ensure we read a monotonic timestamp. Reading the timestamp the first
 		 * time causes a logging message. We don't want to do that during _nm_log_impl. */
@@ -770,11 +706,10 @@ nm_logging_syslog_openlog (const char *logging_backend)
 #endif
 	} else {
 		global.log_backend = LOG_BACKEND_SYSLOG;
+		global.uses_syslog = TRUE;
 		openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON);
 	}
 
-	global.log_format_flags = log_format_flags;
-
 	g_log_set_handler (G_LOG_DOMAIN,
 	                   G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
 	                   nm_log_handler,
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 27f89f5d..9e2b0108 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -65,12 +65,14 @@ typedef enum  { /*< skip >*/
 	LOGD_DISPATCH   = (1LL << 33),
 	LOGD_AUDIT      = (1LL << 34),
 	LOGD_SYSTEMD    = (1LL << 35),
+	LOGD_VPN_PLUGIN = (1LL << 36),
 
 	__LOGD_MAX,
-	LOGD_ALL       = ((__LOGD_MAX - 1LL) << 1) - 1LL,
+	LOGD_ALL       = (((__LOGD_MAX - 1LL) << 1) - 1LL),
 	LOGD_DEFAULT   = LOGD_ALL & ~(
 	                              LOGD_DBUS_PROPS |
 	                              LOGD_WIFI_SCAN |
+	                              LOGD_VPN_PLUGIN |
 	                              0),
 
 	/* aliases: */
@@ -101,12 +103,20 @@ typedef enum  { /*< skip >*/
 #define nm_log_dbg(domain, ...)     nm_log (LOGL_DEBUG, (domain), __VA_ARGS__)
 #define nm_log_trace(domain, ...)   nm_log (LOGL_TRACE, (domain), __VA_ARGS__)
 
+//#define _NM_LOG_FUNC G_STRFUNC
+#define _NM_LOG_FUNC NULL
+
 /* A wrapper for the _nm_log_impl() function that adds call site information.
  * Contrary to nm_log(), it unconditionally calls the function without
  * checking whether logging for the given level and domain is enabled. */
 #define _nm_log(level, domain, error, ...) \
     G_STMT_START { \
-        _nm_log_impl (__FILE__, __LINE__, G_STRFUNC, (level), (domain), (error), ""__VA_ARGS__); \
+        _nm_log_impl (__FILE__, __LINE__, \
+                      _NM_LOG_FUNC, \
+                      (level), \
+                      (domain), \
+                      (error), \
+                      ""__VA_ARGS__); \
     } G_STMT_END
 
 /* nm_log() only evaluates it's argument list after checking
@@ -154,11 +164,21 @@ void _nm_log_impl (const char *file,
                    NMLogDomain domain,
                    int error,
                    const char *fmt,
-                   ...) __attribute__((__format__ (__printf__, 7, 8)));
+                   ...) _nm_printf (7, 8);
 
 const char *nm_logging_level_to_string (void);
 const char *nm_logging_domains_to_string (void);
-gboolean nm_logging_enabled (NMLogLevel level, NMLogDomain domain);
+
+extern NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL];
+static inline gboolean
+nm_logging_enabled (NMLogLevel level, NMLogDomain domain)
+{
+	nm_assert (((guint) level) < G_N_ELEMENTS (_nm_logging_enabled_state));
+	return    (((guint) level) < G_N_ELEMENTS (_nm_logging_enabled_state))
+	       && !!(_nm_logging_enabled_state[level] & domain);
+}
+
+NMLogLevel nm_logging_get_level (NMLogDomain domain);
 
 const char *nm_logging_all_levels_to_string (void);
 const char *nm_logging_all_domains_to_string (void);
@@ -168,6 +188,7 @@ gboolean nm_logging_setup (const char  *level,
                            char       **bad_domains,
                            GError     **error);
 void     nm_logging_syslog_openlog (const char *logging_backend);
+gboolean nm_logging_syslog_enabled (void);
 
 /*****************************************************************************/
 
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 8277f6b0..5794bb9a 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -21,13 +21,14 @@
 
 #include "nm-default.h"
 
+#include "nm-manager.h"
+
 #include <stdlib.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "nm-manager.h"
 #include "nm-common-macros.h"
 #include "nm-bus-manager.h"
 #include "nm-vpn-manager.h"
@@ -46,13 +47,14 @@
 #include "nm-sleep-monitor.h"
 #include "nm-connectivity.h"
 #include "nm-policy.h"
-#include "nm-connection-provider.h"
 #include "nm-session-monitor.h"
 #include "nm-activation-request.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
 #include "nm-dbus-compat.h"
+#include "nm-checkpoint.h"
+#include "nm-checkpoint-manager.h"
 #include "NetworkManagerUtils.h"
 
 #include "nmdbus-manager.h"
@@ -91,15 +93,13 @@ typedef struct {
 	gboolean sw_enabled;
 	gboolean hw_enabled;
 	RfKillType rtype;
+	NMConfigRunStatePropertyType key;
 	const char *desc;
-	const char *key;
 	const char *prop;
 	const char *hw_prop;
 } RadioState;
 
 typedef struct {
-	char *state_file;
-
 	GSList *active_connections;
 	GSList *authorizing_connections;
 	guint ac_cleanup_id;
@@ -121,6 +121,8 @@ typedef struct {
 	} prop_filter;
 	NMRfkillManager *rfkill_mgr;
 
+	NMCheckpointManager *checkpoint_mgr;
+
 	NMSettings *settings;
 	char *hostname;
 
@@ -132,6 +134,8 @@ typedef struct {
 
 	NMSleepMonitor *sleep_monitor;
 
+	NMAuthManager *auth_mgr;
+
 	GSList *auth_chains;
 	GHashTable *sleep_devices;
 
@@ -145,7 +149,27 @@ typedef struct {
 	gboolean devices_inited;
 } NMManagerPrivate;
 
-#define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
+struct _NMManager {
+	NMExportedObject parent;
+	NMManagerPrivate _priv;
+};
+
+typedef struct {
+	NMExportedObjectClass parent;
+} NMManagerClass;
+
+#define NM_MANAGER_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMManager *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_MANAGER (_self)); \
+		&_self->_priv; \
+	})
 
 G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
 
@@ -168,7 +192,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 	PROP_VERSION,
 	PROP_STATE,
-	PROP_STATE_FILE,
 	PROP_STARTUP,
 	PROP_NETWORKING_ENABLED,
 	PROP_WIRELESS_ENABLED,
@@ -402,7 +425,7 @@ find_ac_for_connection (NMManager *manager, NMConnection *connection)
 }
 
 /* Filter out connections that are already active.
- * nm_settings_get_connections() returns sorted list. We need to preserve the
+ * nm_settings_get_connections_sorted() returns sorted list. We need to preserve the
  * order so that we didn't change auto-activation order (recent timestamps
  * are first).
  * Caller is responsible for freeing the returned list with g_slist_free().
@@ -411,7 +434,7 @@ GSList *
 nm_manager_get_activatable_connections (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *all_connections = nm_settings_get_connections (priv->settings);
+	GSList *all_connections = nm_settings_get_connections_sorted (priv->settings);
 	GSList *connections = NULL, *iter;
 	NMSettingsConnection *connection;
 
@@ -559,7 +582,7 @@ impl_manager_reload (NMManager *self,
 
 /************************************************************************/
 
-static NMDevice *
+NMDevice *
 nm_manager_get_device_by_path (NMManager *manager, const char *path)
 {
 	GSList *iter;
@@ -589,7 +612,7 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
 }
 
 static NMDevice *
-find_device_by_hw_addr (NMManager *manager, const char *hwaddr)
+find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr)
 {
 	GSList *iter;
 	const char *device_addr;
@@ -598,7 +621,7 @@ find_device_by_hw_addr (NMManager *manager, const char *hwaddr)
 
 	if (nm_utils_hwaddr_valid (hwaddr, -1)) {
 		for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-			device_addr = nm_device_get_hw_address (NM_DEVICE (iter->data));
+			device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data), FALSE);
 			if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1))
 				return NM_DEVICE (iter->data);
 		}
@@ -906,7 +929,7 @@ check_if_startup_complete (NMManager *self)
 		g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
 	}
 
-	if (nm_config_get_configure_and_quit (nm_config_get ()))
+	if (nm_config_get_configure_and_quit (priv->config))
 		g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
 }
 
@@ -939,28 +962,12 @@ remove_device (NMManager *self,
 	       nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE));
 
 	if (allow_unmanage && nm_device_get_managed (device, FALSE)) {
-		unmanage = TRUE;
 
-		if (!quitting) {
+		if (quitting)
+			unmanage = nm_device_unmanage_on_quit (device);
+		else {
 			/* the device is already gone. Unmanage it. */
-		} else {
-			/* Leave certain devices alone when quitting so their configuration
-			 * can be taken over when NM restarts.  This ensures connectivity while
-			 * NM is stopped.
-			 */
-			if (nm_device_uses_assumed_connection (device)) {
-				/* An assume connection must be left alone */
-				unmanage = FALSE;
-			} else if (!nm_device_get_act_request (device)) {
-				/* a device without any active connection is either UNAVAILABLE or DISCONNECTED
-				 * state. Since we don't know whether the device was upped by NetworkManager,
-				 * we must leave it up on exit. */
-				unmanage = FALSE;
-			} else if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (device))) {
-				/* The device has no layer 3 configuration. Leave it up. */
-				unmanage = FALSE;
-			} else if (nm_device_can_assume_active_connection (device))
-				unmanage = FALSE;
+			unmanage = TRUE;
 		}
 
 		if (unmanage) {
@@ -968,7 +975,7 @@ remove_device (NMManager *self,
 				nm_device_set_unmanaged_by_quitting (device);
 			else
 				nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
-		} else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
+		} else if (quitting && nm_config_get_configure_and_quit (priv->config)) {
 			nm_device_spawn_iface_helper (device);
 		}
 	}
@@ -1048,7 +1055,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 		return parent;
 
 	/* Maybe a hardware address */
-	parent = find_device_by_hw_addr (self, parent_name);
+	parent = find_device_by_permanent_hw_addr (self, parent_name);
 	if (parent)
 		return parent;
 
@@ -1224,7 +1231,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	}
 
 	/* Create backing resources if the device has any autoconnect connections */
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *candidate = iter->data;
 		NMSettingConnection *s_con;
@@ -1259,7 +1266,7 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 
 	g_return_if_fail (device);
 
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *candidate = iter->data;
 		gs_free_error GError *error = NULL;
@@ -1328,7 +1335,7 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 
 	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
 	for (iter = priv->devices; iter; iter = g_slist_next (iter))
-		nm_device_set_unmanaged_by_user_config (NM_DEVICE (iter->data), unmanaged_specs);
+		nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data), unmanaged_specs);
 }
 
 static void
@@ -1367,54 +1374,6 @@ system_hostname_changed_cb (NMSettings *settings,
 /* General NMManager stuff                                         */
 /*******************************************************************/
 
-/* Store value into key-file; supported types: boolean, int, string */
-static gboolean
-write_value_to_state_file (const char *filename,
-                           const char *group,
-                           const char *key,
-                           GType value_type,
-                           gpointer value,
-                           GError **error)
-{
-	GKeyFile *key_file;
-	char *data;
-	gsize len = 0;
-	gboolean ret = FALSE;
-
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (group != NULL, FALSE);
-	g_return_val_if_fail (key != NULL, FALSE);
-	g_return_val_if_fail (value_type == G_TYPE_BOOLEAN ||
-	                      value_type == G_TYPE_INT ||
-	                      value_type == G_TYPE_STRING,
-	                      FALSE);
-
-	key_file = g_key_file_new ();
-
-	g_key_file_set_list_separator (key_file, ',');
-	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
-	switch (value_type) {
-	case G_TYPE_BOOLEAN:
-		g_key_file_set_boolean (key_file, group, key, *((gboolean *) value));
-		break;
-	case G_TYPE_INT:
-		g_key_file_set_integer (key_file, group, key, *((gint *) value));
-		break;
-	case G_TYPE_STRING:
-		g_key_file_set_string (key_file, group, key, *((const gchar **) value));
-		break;
-	}
-
-	data = g_key_file_to_data (key_file, &len, NULL);
-	if (data) {
-		ret = g_file_set_contents (filename, data, len, error);
-		g_free (data);
-	}
-	g_key_file_free (key_file);
-
-	return ret;
-}
-
 static gboolean
 radio_enabled_for_rstate (RadioState *rstate, gboolean check_changeable)
 {
@@ -2043,7 +2002,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	type_desc = nm_device_get_type_desc (device);
 	g_assert (type_desc);
 
-	nm_device_set_unmanaged_by_user_config (device, nm_settings_get_unmanaged_specs (priv->settings));
+	nm_device_set_unmanaged_by_user_settings (device, nm_settings_get_unmanaged_specs (priv->settings));
 
 	nm_device_set_unmanaged_flags (device,
 	                               NM_UNMANAGED_SLEEPING,
@@ -2098,11 +2057,12 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             GObject *component,
                             gpointer user_data)
 {
+	NMManager *self = user_data;
 	GSList *iter;
 
-	g_return_val_if_fail (NM_IS_MANAGER (user_data), FALSE);
+	g_return_val_if_fail (self, FALSE);
 
-	for (iter = NM_MANAGER_GET_PRIVATE (user_data)->devices; iter; iter = iter->next) {
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
 		if (nm_device_notify_component_added ((NMDevice *) iter->data, component))
 			return TRUE;
 	}
@@ -2178,6 +2138,9 @@ platform_link_added (NMManager *self,
 			if (!ignore) {
 				_LOGW (LOGD_HW, "%s: factory failed to create device: %s",
 				       plink->name, error->message);
+			} else {
+				_LOGD (LOGD_HW, "%s: factory failed to create device: %s",
+				       plink->name, error->message);
 			}
 			return;
 		}
@@ -2185,7 +2148,7 @@ platform_link_added (NMManager *self,
 
 	if (device == NULL) {
 		switch (plink->type) {
-		case NM_LINK_TYPE_WWAN_ETHERNET:
+		case NM_LINK_TYPE_WWAN_NET:
 		case NM_LINK_TYPE_BNEP:
 		case NM_LINK_TYPE_OLPC_MESH:
 		case NM_LINK_TYPE_TEAM:
@@ -2740,7 +2703,7 @@ find_slaves (NMManager *manager,
 	 * even if a slave was already active, it might be deactivated during
 	 * master reactivation.
 	 */
-	all_connections = nm_settings_get_connections (priv->settings);
+	all_connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = all_connections; iter; iter = iter->next) {
 		NMSettingsConnection *master_connection = NULL;
 		NMDevice *master_device = NULL;
@@ -3474,7 +3437,7 @@ _activation_auth_done (NMActiveConnection *active,
 			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,
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL,
 			                            subject, NULL);
 			g_object_unref (active);
 			return;
@@ -3486,7 +3449,7 @@ _activation_auth_done (NMActiveConnection *active,
 	}
 
 	g_assert (error);
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 	                            subject, error->message);
 	_internal_activation_failed (self, active, error->message);
 
@@ -3574,7 +3537,7 @@ impl_manager_activate_connection (NMManager *self,
 
 error:
 	if (connection) {
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 		                            subject, error->message);
 	}
 	g_clear_object (&active);
@@ -3623,6 +3586,7 @@ activation_add_done (NMSettings *settings,
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 			                            nm_active_connection_get_settings_connection (active),
 			                            TRUE,
+			                            NULL,
 			                            nm_active_connection_get_subject (active),
 			                            NULL);
 			return;
@@ -3637,6 +3601,7 @@ activation_add_done (NMSettings *settings,
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 	                            NULL,
 	                            FALSE,
+	                            NULL,
 	                            nm_active_connection_get_subject (active),
 	                            error->message);
 	g_clear_error (&local);
@@ -3681,6 +3646,7 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 		                            NULL,
 		                            FALSE,
+		                            NULL,
 		                            nm_active_connection_get_subject (active),
 		                            error->message);
 		g_dbus_method_invocation_take_error (context, error);
@@ -3732,7 +3698,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	if (!subject)
 		goto error;
 
-	all_connections = nm_settings_get_connections (priv->settings);
+	all_connections = nm_settings_get_connections_sorted (priv->settings);
 	if (vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
 		if (!nm_connection_get_setting_vpn (connection)) {
@@ -3782,7 +3748,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	return;
 
 error:
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, subject, error->message);
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, NULL, subject, error->message);
 	g_clear_object (&connection);
 	g_slist_free (all_connections);
 	g_clear_object (&subject);
@@ -3878,6 +3844,7 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE,
 		                            nm_active_connection_get_settings_connection (active),
 		                            !error,
+		                            NULL,
 		                            nm_auth_chain_get_subject (chain),
 		                            error ? error->message : NULL);
 	}
@@ -3951,7 +3918,7 @@ impl_manager_deactivate_connection (NMManager *self,
 done:
 	if (error) {
 		if (connection) {
-			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE,
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL,
 			                            subject, error->message);
 		}
 		g_dbus_method_invocation_take_error (context, error);
@@ -4291,21 +4258,9 @@ static void
 _internal_enable (NMManager *self, gboolean enable)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
 
-	/* Update "NetworkingEnabled" key in state file */
-	if (priv->state_file) {
-		if (!write_value_to_state_file (priv->state_file,
-		                                "main", "NetworkingEnabled",
-		                                G_TYPE_BOOLEAN, (gpointer) &enable,
-		                                &error)) {
-			/* Not a hard error */
-			_LOGW (LOGD_SUSPEND, "writing to state file %s failed: %s",
-			       priv->state_file,
-			       error->message);
-			g_clear_error (&error);
-		}
-	}
+	nm_config_state_set (priv->config, TRUE, FALSE,
+	                     NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, enable);
 
 	_LOGI (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
 	       enable ? "enable" : "disable",
@@ -4457,7 +4412,10 @@ get_permissions_done_cb (NMAuthChain *chain,
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_RELOAD);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS);
 
 		g_dbus_method_invocation_return_value (context,
 		                                       g_variant_new ("(a{ss})", &results));
@@ -4495,7 +4453,10 @@ impl_manager_get_permissions (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_RELOAD, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, FALSE);
 }
 
 static void
@@ -4513,30 +4474,24 @@ impl_manager_set_logging (NMManager *self,
                           const char *level,
                           const char *domains)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
-	gulong caller_uid = G_MAXULONG;
 
-	if (!nm_bus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Failed to get request UID.");
-		goto done;
-	}
-
-	if (0 != caller_uid) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Permission denied");
-		goto done;
-	}
+	/* 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_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_MANAGER_ERROR,
+	                                NM_MANAGER_ERROR_PERMISSION_DENIED))
+		return;
 
 	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 ());
 	}
 
-done:
 	if (error)
 		g_dbus_method_invocation_take_error (context, error);
 	else
@@ -4693,7 +4648,7 @@ nm_manager_start (NMManager *self, GError **error)
 	 * connection-added signals thus devices have to be created manually.
 	 */
 	_LOGD (LOGD_CORE, "creating virtual devices...");
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = iter->next)
 		connection_changed (self, NM_CONNECTION (iter->data));
 	g_slist_free (connections);
@@ -4956,6 +4911,10 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 		/* ... 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 */
@@ -5090,6 +5049,15 @@ prop_filter (GDBusConnection *connection,
 		} 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;
 
@@ -5169,8 +5137,176 @@ _set_prop_filter (NMManager *self, GDBusConnection *connection)
 
 /******************************************************************************/
 
+static NMCheckpointManager *
+_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);
+	return priv->checkpoint_mgr;
+}
+
+static void
+checkpoint_auth_done_cb (NMAuthChain *chain,
+                         GError *auth_error,
+                         GDBusMethodInvocation *context,
+                         gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	char *op, *checkpoint_path = NULL, **devices;
+	NMCheckpoint *checkpoint;
+	NMAuthCallResult result;
+	guint32 timeout, flags;
+	GVariant *variant = NULL;
+	GError *error = NULL;
+	const char *arg = NULL;
+
+	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_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) {
+		error = g_error_new (NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     "checkpoint check request failed: %s",
+		                     auth_error->message);
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Not authorized to checkpoint/rollback");
+	} else {
+		if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_CREATE)) {
+			timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "timeout"));
+			flags = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "flags"));
+			devices = nm_auth_chain_get_data (chain, "devices");
+
+			checkpoint = nm_checkpoint_manager_create (_checkpoint_mgr_get (self, TRUE),
+			                                           (const char *const *) devices,
+			                                           timeout,
+			                                           (NMCheckpointCreateFlags) flags,
+			                                           &error);
+			if (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)) {
+			nm_checkpoint_manager_destroy (_checkpoint_mgr_get (self, TRUE),
+			                               checkpoint_path, &error);
+		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
+			nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
+			                                checkpoint_path, &variant, &error);
+		} else
+			g_return_if_reached ();
+	}
+
+	nm_audit_log_checkpoint_op (op, arg ?: "", !error, nm_auth_chain_get_subject (chain),
+	                            error ? error->message : NULL);
+
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	else
+		g_dbus_method_invocation_return_value (context, variant);
+
+
+	nm_auth_chain_unref (chain);
+}
+
 static void
-authority_changed_cb (NMAuthManager *auth_manager, gpointer user_data)
+impl_manager_checkpoint_create (NMManager *self,
+                                GDBusMethodInvocation *context,
+                                const char *const *devices,
+                                guint32 rollback_timeout,
+                                guint32 flags)
+{
+	NMManagerPrivate *priv;
+	NMAuthChain *chain;
+	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 (context, checkpoint_auth_done_cb, self);
+	if (!chain) {
+		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;
+	}
+
+	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", 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 (NMManager *self,
+                                 GDBusMethodInvocation *context,
+                                 const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
+	NMAuthChain *chain;
+
+	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) {
+		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;
+	}
+
+	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);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
+}
+
+static void
+impl_manager_checkpoint_rollback (NMManager *self,
+                                  GDBusMethodInvocation *context,
+                                  const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
+	NMAuthChain *chain;
+
+	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) {
+		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;
+	}
+
+	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
+auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data)
 {
 	/* Let clients know they should re-check their authorization */
 	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
@@ -5245,7 +5381,6 @@ manager_radio_user_toggled (NMManager *self,
                             gboolean enabled)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
 	gboolean old_enabled, new_enabled;
 
 	/* Don't touch devices if asleep/networking disabled */
@@ -5259,17 +5394,8 @@ manager_radio_user_toggled (NMManager *self,
 	}
 
 	/* Update enabled key in state file */
-	if (priv->state_file) {
-		if (!write_value_to_state_file (priv->state_file,
-		                                "main", rstate->key,
-		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
-		                                &error)) {
-			_LOGW (LOGD_CORE, "writing to state file %s failed: %s",
-			       priv->state_file,
-			       error->message);
-			g_clear_error (&error);
-		}
-	}
+	nm_config_state_set (priv->config, TRUE, FALSE,
+	                     rstate->key, enabled);
 
 	/* When the user toggles the radio, their request should override any
 	 * daemon (like ModemManager) enabled state that can be changed.  For WWAN
@@ -5331,34 +5457,22 @@ nm_manager_get (void)
 	return singleton_instance;
 }
 
-NMConnectionProvider *
-nm_connection_provider_get (void)
+NMSettings *
+nm_settings_get (void)
 {
-	NMConnectionProvider *p;
-
 	g_return_val_if_fail (singleton_instance, NULL);
 
-	p = NM_CONNECTION_PROVIDER (NM_MANAGER_GET_PRIVATE (singleton_instance)->settings);
-	g_return_val_if_fail (p, NULL);
-	return p;
+	return NM_MANAGER_GET_PRIVATE (singleton_instance)->settings;
 }
 
 NMManager *
-nm_manager_setup (const char *state_file,
-                  gboolean initial_net_enabled,
-                  gboolean initial_wifi_enabled,
-                  gboolean initial_wwan_enabled)
+nm_manager_setup (void)
 {
 	NMManager *self;
 
 	g_return_val_if_fail (!singleton_instance, singleton_instance);
 
-	self = g_object_new (NM_TYPE_MANAGER,
-	                     NM_MANAGER_NETWORKING_ENABLED, initial_net_enabled,
-	                     NM_MANAGER_WIRELESS_ENABLED, initial_wifi_enabled,
-	                     NM_MANAGER_WWAN_ENABLED, initial_wwan_enabled,
-	                     NM_MANAGER_STATE_FILE, state_file,
-	                     NULL);
+	self = g_object_new (NM_TYPE_MANAGER, NULL);
 	nm_assert (NM_IS_MANAGER (self));
 	singleton_instance = self;
 
@@ -5376,6 +5490,7 @@ constructed (GObject *object)
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConfigData *config_data;
+	const NMConfigState *state;
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
 
@@ -5421,9 +5536,16 @@ constructed (GObject *object)
 	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
 	                  G_CALLBACK (connectivity_changed), self);
 
+	state = nm_config_state_get (priv->config);
+
+	priv->net_enabled = state->net_enabled;
+
+	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled;
+	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled;
+
 	priv->rfkill_mgr = nm_rfkill_manager_new ();
 	g_signal_connect (priv->rfkill_mgr,
-	                  "rfkill-changed",
+	                  NM_RFKILL_MANAGER_SIGNAL_RFKILL_CHANGED,
 	                  G_CALLBACK (rfkill_manager_rfkill_changed_cb),
 	                  self);
 
@@ -5447,14 +5569,14 @@ nm_manager_init (NMManager *self)
 	memset (priv->radio_states, 0, sizeof (priv->radio_states));
 
 	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE;
-	priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled";
+	priv->radio_states[RFKILL_TYPE_WLAN].key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi";
 	priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
 
 	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
-	priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled";
+	priv->radio_states[RFKILL_TYPE_WWAN].key = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN";
@@ -5479,12 +5601,12 @@ nm_manager_init (NMManager *self)
 	                  G_CALLBACK (sleeping_cb), self);
 
 	/* Listen for authorization changes */
-	g_signal_connect (nm_auth_manager_get (),
+	priv->auth_mgr = g_object_ref (nm_auth_manager_get ());
+	g_signal_connect (priv->auth_mgr,
 	                  NM_AUTH_MANAGER_SIGNAL_CHANGED,
-	                  G_CALLBACK (authority_changed_cb),
+	                  G_CALLBACK (auth_mgr_changed),
 	                  self);
 
-
 	/* Monitor the firmware directory */
 	if (strlen (KERNEL_FIRMWARE_DIR)) {
 		file = g_file_new_for_path (KERNEL_FIRMWARE_DIR "/");
@@ -5617,33 +5739,15 @@ set_property (GObject *object, guint prop_id,
 	GError *error = NULL;
 
 	switch (prop_id) {
-	case PROP_STATE_FILE:
-		/* construct-only */
-		priv->state_file = g_value_dup_string (value);
-		break;
-	case PROP_NETWORKING_ENABLED:
-		/* construct-only */
-		priv->net_enabled = g_value_get_boolean (value);
-		break;
 	case PROP_WIRELESS_ENABLED:
-		if (!priv->rfkill_mgr) {
-			/* called during object construction. */
-			priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = g_value_get_boolean (value);
-		} else {
-			manager_radio_user_toggled (NM_MANAGER (object),
-			                            &priv->radio_states[RFKILL_TYPE_WLAN],
-			                            g_value_get_boolean (value));
-		}
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WLAN],
+		                            g_value_get_boolean (value));
 		break;
 	case PROP_WWAN_ENABLED:
-		if (!priv->rfkill_mgr) {
-			/* called during object construction. */
-			priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = g_value_get_boolean (value);
-		} else {
-			manager_radio_user_toggled (NM_MANAGER (object),
-			                            &priv->radio_states[RFKILL_TYPE_WWAN],
-			                            g_value_get_boolean (value));
-		}
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WWAN],
+		                            g_value_get_boolean (value));
 		break;
 	case PROP_WIMAX_ENABLED:
 		/* WIMAX is depreacted. This does nothing. */
@@ -5681,9 +5785,17 @@ dispose (GObject *object)
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
 
-	g_signal_handlers_disconnect_by_func (nm_auth_manager_get (),
-	                                      G_CALLBACK (authority_changed_cb),
-	                                      manager);
+	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) {
+		g_signal_handlers_disconnect_by_func (priv->auth_mgr,
+		                                      G_CALLBACK (auth_mgr_changed),
+		                                      manager);
+		g_clear_object (&priv->auth_mgr);
+	}
 
 	g_assert (priv->devices == NULL);
 
@@ -5721,7 +5833,6 @@ dispose (GObject *object)
 		g_clear_object (&priv->settings);
 	}
 
-	g_clear_pointer (&priv->state_file, g_free);
 	g_clear_object (&priv->vpn_manager);
 
 	/* Unregister property filter */
@@ -5766,8 +5877,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
 	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class);
 
-	g_type_class_add_private (manager_class, sizeof (NMManagerPrivate));
-
 	exported_object_class->export_path = NM_DBUS_PATH;
 
 	/* virtual methods */
@@ -5783,13 +5892,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                         G_PARAM_READABLE |
 	                         G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_STATE_FILE] =
-	    g_param_spec_string (NM_MANAGER_STATE_FILE, "", "",
-	                         NULL,
-	                         G_PARAM_WRITABLE |
-	                         G_PARAM_CONSTRUCT_ONLY |
-	                         G_PARAM_STATIC_STRINGS);
-
 	obj_properties[PROP_STATE] =
 	    g_param_spec_uint (NM_MANAGER_STATE, "", "",
 	                       0, NM_STATE_DISCONNECTED, 0,
@@ -5805,14 +5907,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	obj_properties[PROP_NETWORKING_ENABLED] =
 	    g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED, "", "",
 	                          TRUE,
-	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_READABLE |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WIRELESS_ENABLED] =
 	    g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED, "", "",
 	                          TRUE,
 	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] =
@@ -5825,7 +5926,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_param_spec_boolean (NM_MANAGER_WWAN_ENABLED, "", "",
 	                          TRUE,
 	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WWAN_HARDWARE_ENABLED] =
@@ -5945,8 +6045,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_DEVICE_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, device_added),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	/* Emitted for both realized devices and placeholder devices */
@@ -5962,8 +6061,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_DEVICE_REMOVED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, device_removed),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	/* Emitted for both realized devices and placeholder devices */
@@ -5978,8 +6076,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_STATE_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, state_changed),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_UINT);
 
 	signals[CHECK_PERMISSIONS] =
@@ -6026,6 +6123,9 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                                        "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 5401b002..d7aabff1 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -55,7 +55,6 @@
 /* Not exported */
 #define NM_MANAGER_HOSTNAME "hostname"
 #define NM_MANAGER_SLEEPING "sleeping"
-#define NM_MANAGER_STATE_FILE "state-file"
 
 /* signals */
 #define NM_MANAGER_CHECK_PERMISSIONS         "check-permissions"
@@ -72,26 +71,10 @@
 #define NM_MANAGER_INTERNAL_DEVICE_REMOVED   "internal-device-removed"
 
 
-struct _NMManager {
-	NMExportedObject parent;
-};
-
-typedef struct {
-	NMExportedObjectClass parent;
-
-	/* Signals */
-	void (*device_added) (NMManager *manager, NMDevice *device);
-	void (*device_removed) (NMManager *manager, NMDevice *device);
-	void (*state_changed) (NMManager *manager, guint state);
-} NMManagerClass;
-
 GType nm_manager_get_type (void);
 
 /* nm_manager_setup() should only be used by main.c */
-NMManager *   nm_manager_setup                         (const char *state_file,
-                                                        gboolean initial_net_enabled,
-                                                        gboolean initial_wifi_enabled,
-                                                        gboolean initial_wwan_enabled);
+NMManager *   nm_manager_setup                         (void);
 
 NMManager *   nm_manager_get                           (void);
 
@@ -108,6 +91,8 @@ const GSList *      nm_manager_get_devices             (NMManager *manager);
 
 NMDevice *          nm_manager_get_device_by_ifindex   (NMManager *manager,
                                                         int ifindex);
+NMDevice *          nm_manager_get_device_by_path      (NMManager *manager,
+                                                        const char *path);
 
 char *              nm_manager_get_connection_iface (NMManager *self,
                                                      NMConnection *connection,
diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h
index 1e1e8fda..fb102574 100644
--- a/src/nm-multi-index.h
+++ b/src/nm-multi-index.h
@@ -21,11 +21,6 @@
 #ifndef __NM_MULTI_INDEX__
 #define __NM_MULTI_INDEX__
 
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
-
 typedef struct {
 	char _dummy;
 } NMMultiIndexId;
@@ -106,7 +101,5 @@ void nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter,
 gboolean nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter,
                                       void **out_value);
 
-G_END_DECLS
-
 #endif /* __NM_MULTI_INDEX__ */
 
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 9613ad82..5fa46259 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -180,38 +180,35 @@ _set_hostname (NMPolicy *self,
 	if (new_hostname)
 		g_clear_object (&priv->lookup_addr);
 
-	/* Don't change the hostname or update DNS this is the first time we're
-	 * trying to change the hostname, and it's not actually changing.
-	 */
 	if (   priv->orig_hostname
 	    && (priv->hostname_changed == FALSE)
-	    && g_strcmp0 (priv->orig_hostname, new_hostname) == 0)
-		return;
-
-	/* Don't change the hostname or update DNS if the hostname isn't actually
-	 * going to change.
-	 */
-	if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0)
-		return;
-
-	g_free (priv->cur_hostname);
-	priv->cur_hostname = g_strdup (new_hostname);
-	priv->hostname_changed = TRUE;
+	    && g_strcmp0 (priv->orig_hostname, new_hostname) == 0) {
+		/* Don't change the hostname or update DNS this is the first time we're
+		 * trying to change the hostname, and it's not actually changing.
+		 */
+	} else if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0) {
+		/* Don't change the hostname or update DNS if the hostname isn't actually
+		 * going to change.
+		 */
+	} else {
+		g_free (priv->cur_hostname);
+		priv->cur_hostname = g_strdup (new_hostname);
+		priv->hostname_changed = TRUE;
 
-	/* Notify the DNS manager of the hostname change so that the domain part, if
-	 * present, can be added to the search list.
-	 */
-	nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
+		/* Notify the DNS manager of the hostname change so that the domain part, if
+		 * present, can be added to the search list.
+		 */
+		nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
+	}
 
 	 /* Finally, set kernel hostname */
-
-	if (!priv->cur_hostname)
+	if (!new_hostname)
 		name = FALLBACK_HOSTNAME4;
-	else if (!priv->cur_hostname[0]) {
+	else if (!new_hostname[0]) {
 		g_warn_if_reached ();
 		name = FALLBACK_HOSTNAME4;
 	} else
-		name = priv->cur_hostname;
+		name = new_hostname;
 
 	old_hostname[HOST_NAME_MAX] = '\0';
 	errno = 0;
@@ -862,7 +859,7 @@ reset_autoconnect_all (NMPolicy *self, NMDevice *device)
 	} else
 		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections");
 
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		if (!device || nm_device_check_connection_compatible (device, iter->data)) {
 			nm_settings_connection_reset_autoconnect_retries (iter->data);
@@ -880,7 +877,7 @@ reset_autoconnect_for_failed_secrets (NMPolicy *self)
 
 	_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections with failed secrets");
 
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data);
 
@@ -908,7 +905,7 @@ block_autoconnect_for_device (NMPolicy *self, NMDevice *device)
 	if (!nm_device_is_software (device))
 		return;
 
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		if (nm_device_check_connection_compatible (device, iter->data)) {
 			nm_settings_connection_set_autoconnect_blocked_reason (NM_SETTINGS_CONNECTION (iter->data),
@@ -991,7 +988,7 @@ reset_connections_retries (gpointer user_data)
 
 	min_stamp = 0;
 	now = nm_utils_get_monotonic_timestamp_s ();
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data);
 
@@ -1023,8 +1020,10 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *master_device, *master_uuid_settings = NULL, *master_uuid_applied = NULL;
-	GSList *connections, *iter;
+	gs_free_slist GSList *connections = NULL;
+	GSList *iter;
 	NMActRequest *req;
+	gboolean internal_activation = FALSE;
 
 	master_device = nm_device_get_iface (device);
 	g_assert (master_device);
@@ -1032,6 +1031,7 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 	req = nm_device_get_act_request (device);
 	if (req) {
 		NMConnection *con;
+		NMAuthSubject *subject;
 
 		con = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (req));
 		if (con)
@@ -1042,9 +1042,14 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 			if (!g_strcmp0 (master_uuid_settings, master_uuid_applied))
 				master_uuid_settings = NULL;
 		}
+
+		subject = nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req));
+		internal_activation = subject && nm_auth_subject_is_internal (subject);
 	}
 
-	connections = nm_settings_get_connections (priv->settings);
+	if (!internal_activation)
+		connections = nm_settings_get_connections_sorted (priv->settings);
+
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *slave;
 		NMSettingConnection *s_slave_con;
@@ -1065,8 +1070,6 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 			nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave));
 	}
 
-	g_slist_free (connections);
-
 	schedule_activate_all (self);
 }
 
@@ -1312,6 +1315,7 @@ device_ip4_config_changed (NMDevice *device,
 		}
 		update_ip4_dns (self, priv->dns_manager);
 		update_ip4_routing (self, TRUE);
+		update_system_hostname (self, priv->default_device4, priv->default_device6);
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
@@ -1346,6 +1350,7 @@ device_ip6_config_changed (NMDevice *device,
 		}
 		update_ip6_dns (self, priv->dns_manager);
 		update_ip6_routing (self, TRUE);
+		update_system_hostname (self, priv->default_device4, priv->default_device6);
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
diff --git a/src/nm-policy.h b/src/nm-policy.h
index 476ddf1d..ce900ba6 100644
--- a/src/nm-policy.h
+++ b/src/nm-policy.h
@@ -22,8 +22,6 @@
 #ifndef __NETWORKMANAGER_POLICY_H__
 #define __NETWORKMANAGER_POLICY_H__
 
-#include "nm-default.h"
-
 #define NM_TYPE_POLICY            (nm_policy_get_type ())
 #define NM_POLICY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_POLICY, NMPolicy))
 #define NM_POLICY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_POLICY, NMPolicyClass))
diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c
index 16742656..453a3b9b 100644
--- a/src/nm-rfkill-manager.c
+++ b/src/nm-rfkill-manager.c
@@ -395,7 +395,7 @@ dispose (GObject *object)
 		priv->killswitches = NULL;
 	}
 
-	G_OBJECT_CLASS (nm_rfkill_manager_parent_class)->dispose (object);	
+	G_OBJECT_CLASS (nm_rfkill_manager_parent_class)->dispose (object);
 }
 
 static void
@@ -410,7 +410,7 @@ nm_rfkill_manager_class_init (NMRfkillManagerClass *klass)
 
 	/* Signals */
 	signals[RFKILL_CHANGED] =
-		g_signal_new ("rfkill-changed",
+		g_signal_new (NM_RFKILL_MANAGER_SIGNAL_RFKILL_CHANGED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMRfkillManagerClass, rfkill_changed),
diff --git a/src/nm-rfkill-manager.h b/src/nm-rfkill-manager.h
index 0084364c..3aca8f50 100644
--- a/src/nm-rfkill-manager.h
+++ b/src/nm-rfkill-manager.h
@@ -19,12 +19,11 @@
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
-
-#include "nm-default.h"
-
 #ifndef __NETWORKMANAGER_RFKILL_MANAGER_H__
 #define __NETWORKMANAGER_RFKILL_MANAGER_H__
 
+#define NM_RFKILL_MANAGER_SIGNAL_RFKILL_CHANGED "rfkill-changed"
+
 typedef enum { /*< skip >*/
 	RFKILL_UNBLOCKED = 0,
 	RFKILL_SOFT_BLOCKED = 1,
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index 05e28c74..2d4c7d95 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -873,7 +873,7 @@ next:
 			    || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) {
 
 				if (!vtable->vt->route_add (priv->platform, ifindex, cur_ipx_route, *p_effective_metric)) {
-					if (cur_ipx_route->rx.source < NM_IP_CONFIG_SOURCE_USER) {
+					if (cur_ipx_route->rx.rt_source < NM_IP_CONFIG_SOURCE_USER) {
 						_LOGD (vtable->vt->addr_family,
 						       "ignore error adding IPv%c route to kernel: %s",
 						       vtable->vt->is_ip4 ? '4' : '6',
@@ -1024,7 +1024,7 @@ _ip4_device_routes_ip4_route_changed (NMPlatform *platform,
 	if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
 		return;
 
-	if (   route->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL
+	if (   route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL
 	    || route->metric != 0) {
 		/* we don't have an automatically created device route at hand. Bail out early. */
 		return;
diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h
index 1f4b8174..8e2746d9 100644
--- a/src/nm-route-manager.h
+++ b/src/nm-route-manager.h
@@ -18,9 +18,6 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-
-#include "nm-default.h"
-
 #ifndef __NETWORKMANAGER_ROUTE_MANAGER_H__
 #define __NETWORKMANAGER_ROUTE_MANAGER_H__
 
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c
index 46709427..279a8886 100644
--- a/src/nm-session-monitor.c
+++ b/src/nm-session-monitor.c
@@ -279,45 +279,6 @@ ck_finalize (NMSessionMonitor *monitor)
 NM_DEFINE_SINGLETON_GETTER (NMSessionMonitor, nm_session_monitor_get, NM_TYPE_SESSION_MONITOR);
 
 /**
- * nm_session_monitor_connect:
- * @self: the session monitor
- * @callback: The callback.
- * @user_data: User data for the callback.
- *
- * Connect a callback to the session monitor.
- *
- * Returns: Handler ID to be used with nm_session_monitor_disconnect().
- */
-gulong
-nm_session_monitor_connect (NMSessionMonitor *self,
-                            NMSessionCallback callback,
-                            gpointer user_data)
-{
-	g_return_val_if_fail (NM_IS_SESSION_MONITOR (self), 0);
-
-	return g_signal_connect (self,
-                             NM_SESSION_MONITOR_CHANGED,
-                             G_CALLBACK (callback),
-                             user_data);
-}
-
-/**
- * nm_session_monitor_disconnect:
- * @self: the session monitor
- * @handler_id: Handler ID returned by nm_session_monitor-connect().
- *
- * Disconnect callback from the session handler.
- */
-void
-nm_session_monitor_disconnect (NMSessionMonitor *self,
-                               gulong handler_id)
-{
-	g_return_if_fail (NM_IS_SESSION_MONITOR (self));
-
-	g_signal_handler_disconnect (self, handler_id);
-}
-
-/**
  * nm_session_monitor_uid_to_user:
  * @uid: UID.
  * @out_user: Return location for user name.
diff --git a/src/nm-session-monitor.h b/src/nm-session-monitor.h
index af50b71b..3751d7db 100644
--- a/src/nm-session-monitor.h
+++ b/src/nm-session-monitor.h
@@ -21,11 +21,6 @@
 #ifndef __NETWORKMANAGER_SESSION_MONITOR_H__
 #define __NETWORKMANAGER_SESSION_MONITOR_H__
 
-
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
 #define NM_TYPE_SESSION_MONITOR         (nm_session_monitor_get_type ())
 #define NM_SESSION_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_SESSION_MONITOR, NMSessionMonitor))
 #define NM_SESSION_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), NM_TYPE_SESSION_MONITOR, NMSessionMonitorClass))
@@ -43,19 +38,11 @@ GType             nm_session_monitor_get_type       (void) G_GNUC_CONST;
 
 NMSessionMonitor *nm_session_monitor_get (void);
 
-gulong            nm_session_monitor_connect        (NMSessionMonitor *self,
-                                                     NMSessionCallback callback,
-                                                     gpointer user_data);
-void              nm_session_monitor_disconnect     (NMSessionMonitor *self,
-                                                     gulong handler_id);
-
 gboolean          nm_session_monitor_uid_to_user    (uid_t uid, const char **out_user);
 gboolean          nm_session_monitor_user_to_uid    (const char *user, uid_t *out_uid);
 gboolean          nm_session_monitor_session_exists (NMSessionMonitor *self,
                                                      uid_t uid,
                                                      gboolean active);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_SESSION_MONITOR_H__ */
 
diff --git a/src/nm-sleep-monitor.h b/src/nm-sleep-monitor.h
index 140f964f..d54aebbc 100644
--- a/src/nm-sleep-monitor.h
+++ b/src/nm-sleep-monitor.h
@@ -20,8 +20,6 @@
 #ifndef __NETWORKMANAGER_SLEEP_MONITOR_H__
 #define __NETWORKMANAGER_SLEEP_MONITOR_H__
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_SLEEP_MONITOR         (nm_sleep_monitor_get_type ())
 #define NM_SLEEP_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitor))
 #define NM_SLEEP_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass))
@@ -42,7 +40,5 @@ NMSleepMonitorInhibitorHandle *nm_sleep_monitor_inhibit_take    (NMSleepMonitor
 void                           nm_sleep_monitor_inhibit_release (NMSleepMonitor *self,
                                                                  NMSleepMonitorInhibitorHandle *handle);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_SLEEP_MONITOR_H__ */
 
diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h
new file mode 100644
index 00000000..f5118398
--- /dev/null
+++ b/src/nm-test-utils-core.h
@@ -0,0 +1,296 @@
+/* -*- 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.
+ *
+ * Copyright 2014 - 2016 Red Hat, Inc.
+ */
+
+#ifndef __NM_TEST_UTILS_CORE_H__
+#define __NM_TEST_UTILS_CORE_H__
+
+#include "NetworkManagerUtils.h"
+#include "nm-keyfile-internal.h"
+
+#define _NMTST_INSIDE_CORE 1
+
+#include "nm-utils/nm-test-utils.h"
+
+/*****************************************************************************/
+
+inline static void
+nmtst_init_with_logging (int *argc, char ***argv, const char *log_level, const char *log_domains)
+{
+	__nmtst_init (argc, argv, FALSE, log_level, log_domains, NULL);
+}
+inline static void
+nmtst_init_assert_logging (int *argc, char ***argv, const char *log_level, const char *log_domains)
+{
+	gboolean set_logging;
+
+	__nmtst_init (argc, argv, TRUE, NULL, NULL, &set_logging);
+
+	if (!set_logging) {
+		gboolean success;
+
+		success = nm_logging_setup (log_level, log_domains, NULL, NULL);
+		g_assert (success);
+	}
+}
+
+/*****************************************************************************/
+
+#ifdef __NETWORKMANAGER_PLATFORM_H__
+
+inline static NMPlatformIP4Address *
+nmtst_platform_ip4_address (const char *address, const char *peer_address, guint plen)
+{
+	static NMPlatformIP4Address addr;
+
+	g_assert (plen <= 32);
+
+	memset (&addr, 0, sizeof (addr));
+	addr.address = nmtst_inet4_from_string (address);
+	if (peer_address)
+		addr.peer_address = nmtst_inet4_from_string (peer_address);
+	else
+		addr.peer_address = addr.address;
+	addr.plen = plen;
+
+	return &addr;
+}
+
+inline static NMPlatformIP4Address *
+nmtst_platform_ip4_address_full (const char *address, const char *peer_address, guint plen,
+                                 int ifindex, NMIPConfigSource source, guint32 timestamp,
+                                 guint32 lifetime, guint32 preferred, guint32 flags,
+                                 const char *label)
+{
+	NMPlatformIP4Address *addr = nmtst_platform_ip4_address (address, peer_address, plen);
+
+	G_STATIC_ASSERT (IFNAMSIZ == sizeof (addr->label));
+	g_assert (!label || strlen (label) < IFNAMSIZ);
+
+	addr->ifindex = ifindex;
+	addr->addr_source = source;
+	addr->timestamp = timestamp;
+	addr->lifetime = lifetime;
+	addr->preferred = preferred;
+	addr->n_ifa_flags = flags;
+	if (label)
+		g_strlcpy (addr->label, label, sizeof (addr->label));
+
+	return addr;
+}
+
+inline static NMPlatformIP6Address *
+nmtst_platform_ip6_address (const char *address, const char *peer_address, guint plen)
+{
+	static NMPlatformIP6Address addr;
+
+	g_assert (plen <= 128);
+
+	memset (&addr, 0, sizeof (addr));
+	addr.address = *nmtst_inet6_from_string (address);
+	addr.peer_address = *nmtst_inet6_from_string (peer_address);
+	addr.plen = plen;
+
+	return &addr;
+}
+
+inline static NMPlatformIP6Address *
+nmtst_platform_ip6_address_full (const char *address, const char *peer_address, guint plen,
+                                 int ifindex, NMIPConfigSource source, guint32 timestamp,
+                                 guint32 lifetime, guint32 preferred, guint32 flags)
+{
+	NMPlatformIP6Address *addr = nmtst_platform_ip6_address (address, peer_address, plen);
+
+	addr->ifindex = ifindex;
+	addr->addr_source = source;
+	addr->timestamp = timestamp;
+	addr->lifetime = lifetime;
+	addr->preferred = preferred;
+	addr->n_ifa_flags = flags;
+
+	return addr;
+}
+
+inline static NMPlatformIP4Route *
+nmtst_platform_ip4_route (const char *network, guint plen, const char *gateway)
+{
+	static NMPlatformIP4Route route;
+
+	g_assert (plen <= 32);
+
+	memset (&route, 0, sizeof (route));
+	route.network = nmtst_inet4_from_string (network);
+	route.plen = plen;
+	route.gateway = nmtst_inet4_from_string (gateway);
+
+	return &route;
+}
+
+inline static NMPlatformIP4Route *
+nmtst_platform_ip4_route_full (const char *network, guint plen, const char *gateway,
+                               int ifindex, NMIPConfigSource source,
+                               guint metric, guint mss,
+                               guint8 scope,
+                               const char *pref_src)
+{
+	NMPlatformIP4Route *route = nmtst_platform_ip4_route (network, plen, gateway);
+
+	route->ifindex = ifindex;
+	route->rt_source = source;
+	route->metric = metric;
+	route->mss = mss;
+	route->scope_inv = nm_platform_route_scope_inv (scope);
+	route->pref_src = nmtst_inet4_from_string (pref_src);
+
+	return route;
+}
+
+inline static NMPlatformIP6Route *
+nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway)
+{
+	static NMPlatformIP6Route route;
+
+	nm_assert (plen <= 128);
+
+	memset (&route, 0, sizeof (route));
+	route.network = *nmtst_inet6_from_string (network);
+	route.plen = plen;
+	route.gateway = *nmtst_inet6_from_string (gateway);
+
+	return &route;
+}
+
+inline static NMPlatformIP6Route *
+nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gateway,
+                               int ifindex, NMIPConfigSource source,
+                               guint metric, guint mss)
+{
+	NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway);
+
+	route->ifindex = ifindex;
+	route->rt_source = source;
+	route->metric = metric;
+	route->mss = mss;
+
+	return route;
+}
+
+inline static int
+_nmtst_platform_ip4_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	return nm_platform_ip4_route_cmp ((const NMPlatformIP4Route *) a, (const NMPlatformIP4Route *) b);
+}
+
+inline static void
+nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gsize len, gboolean ignore_order)
+{
+	gsize i;
+	gs_free const NMPlatformIP4Route *c_a = NULL, *c_b = NULL;
+
+	g_assert (a);
+	g_assert (b);
+
+	if (ignore_order) {
+		a = c_a = g_memdup (a, sizeof (NMPlatformIP4Route) * len);
+		b = c_b = g_memdup (b, sizeof (NMPlatformIP4Route) * len);
+		g_qsort_with_data (c_a, len, sizeof (NMPlatformIP4Route), _nmtst_platform_ip4_routes_equal_sort, NULL);
+		g_qsort_with_data (c_b, len, sizeof (NMPlatformIP4Route), _nmtst_platform_ip4_routes_equal_sort, NULL);
+	}
+
+	for (i = 0; i < len; i++) {
+		if (nm_platform_ip4_route_cmp (&a[i], &b[i]) != 0) {
+			char buf[sizeof (_nm_utils_to_string_buffer)];
+
+			g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (long unsigned) i,
+			         nm_platform_ip4_route_to_string (&a[i], NULL, 0),
+			         nm_platform_ip4_route_to_string (&b[i], buf, sizeof (buf)));
+			g_assert_not_reached ();
+		}
+	}
+}
+
+inline static int
+_nmtst_platform_ip6_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	return nm_platform_ip6_route_cmp ((const NMPlatformIP6Route *) a, (const NMPlatformIP6Route *) b);
+}
+
+inline static void
+nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gsize len, gboolean ignore_order)
+{
+	gsize i;
+	gs_free const NMPlatformIP6Route *c_a = NULL, *c_b = NULL;
+
+	g_assert (a);
+	g_assert (b);
+
+	if (ignore_order) {
+		a = c_a = g_memdup (a, sizeof (NMPlatformIP6Route) * len);
+		b = c_b = g_memdup (b, sizeof (NMPlatformIP6Route) * len);
+		g_qsort_with_data (c_a, len, sizeof (NMPlatformIP6Route), _nmtst_platform_ip6_routes_equal_sort, NULL);
+		g_qsort_with_data (c_b, len, sizeof (NMPlatformIP6Route), _nmtst_platform_ip6_routes_equal_sort, NULL);
+	}
+
+	for (i = 0; i < len; i++) {
+		if (nm_platform_ip6_route_cmp (&a[i], &b[i]) != 0) {
+			char buf[sizeof (_nm_utils_to_string_buffer)];
+
+			g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (long unsigned) i,
+			         nm_platform_ip6_route_to_string (&a[i], NULL, 0),
+			         nm_platform_ip6_route_to_string (&b[i], buf, sizeof (buf)));
+			g_assert_not_reached ();
+		}
+	}
+}
+
+#endif
+
+
+#ifdef __NETWORKMANAGER_IP4_CONFIG_H__
+
+inline static NMIP4Config *
+nmtst_ip4_config_clone (NMIP4Config *config)
+{
+	NMIP4Config *copy = nm_ip4_config_new (-1);
+
+	g_assert (copy);
+	g_assert (config);
+	nm_ip4_config_replace (copy, config, NULL);
+	return copy;
+}
+
+#endif
+
+
+#ifdef __NETWORKMANAGER_IP6_CONFIG_H__
+
+inline static NMIP6Config *
+nmtst_ip6_config_clone (NMIP6Config *config)
+{
+	NMIP6Config *copy = nm_ip6_config_new (-1);
+
+	g_assert (copy);
+	g_assert (config);
+	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 997723e6..1fbf043e 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -54,13 +54,20 @@ typedef struct _NMLldpListener       NMLldpListener;
 
 typedef enum {
 	/* In priority order; higher number == higher priority */
-	NM_IP_CONFIG_SOURCE_UNKNOWN,
 
-	/* platform internal flag used to mark routes with RTM_F_CLONED. */
-	_NM_IP_CONFIG_SOURCE_RTM_F_CLONED,
+	NM_IP_CONFIG_SOURCE_UNKNOWN                 = 0,
 
-	/* routes from platform with protocol RTPROT_KERNEL. */
-	NM_IP_CONFIG_SOURCE_RTPROT_KERNEL,
+	/* for routes, the source is mapped to the uint8 field rtm_protocol.
+	 * Reserve the range [1,0x100] for native RTPROT values. */
+
+	NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC           = 1 + 0,
+	NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT         = 1 + 1,
+	NM_IP_CONFIG_SOURCE_RTPROT_KERNEL           = 1 + 2,
+	NM_IP_CONFIG_SOURCE_RTPROT_BOOT             = 1 + 3,
+	NM_IP_CONFIG_SOURCE_RTPROT_STATIC           = 1 + 4,
+	NM_IP_CONFIG_SOURCE_RTPROT_RA               = 1 + 9,
+	NM_IP_CONFIG_SOURCE_RTPROT_DHCP             = 1 + 16,
+	_NM_IP_CONFIG_SOURCE_RTPROT_LAST            = 1 + 0xFF,
 
 	NM_IP_CONFIG_SOURCE_KERNEL,
 	NM_IP_CONFIG_SOURCE_SHARED,
@@ -73,6 +80,12 @@ typedef enum {
 	NM_IP_CONFIG_SOURCE_USER,
 } NMIPConfigSource;
 
+inline static gboolean
+NM_IS_IP_CONFIG_SOURCE_RTPROT (NMIPConfigSource source)
+{
+	return source > NM_IP_CONFIG_SOURCE_UNKNOWN && source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST;
+}
+
 /* platform */
 typedef struct _NMPlatform           NMPlatform;
 typedef struct _NMPlatformIP4Address NMPlatformIP4Address;
@@ -102,7 +115,7 @@ typedef enum {
 	NM_LINK_TYPE_INFINIBAND,
 	NM_LINK_TYPE_OLPC_MESH,
 	NM_LINK_TYPE_WIFI,
-	NM_LINK_TYPE_WWAN_ETHERNET,   /* WWAN pseudo-ethernet */
+	NM_LINK_TYPE_WWAN_NET,   /* WWAN kernel netdevice */
 	NM_LINK_TYPE_WIMAX,
 
 	/* Software types */
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in
index a528da66..bf094348 100644
--- a/src/platform/Makefile.in
+++ b/src/platform/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/platform
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -225,6 +226,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -281,13 +283,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -299,6 +302,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -359,6 +364,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -394,6 +401,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 7b545076..8a4f69b6 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -31,9 +31,10 @@
 #include "nm-utils.h"
 
 #include "nm-core-utils.h"
+#include "nm-platform-utils.h"
 #include "nmp-object.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /*********************************************************************************************/
 
@@ -505,7 +506,7 @@ link_set_noarp (NMPlatform *platform, int ifindex)
 	return TRUE;
 }
 
-static gboolean
+static NMPlatformError
 link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len)
 {
 	NMFakePlatformLink *device = link_get (platform, ifindex);
@@ -514,7 +515,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t
 	    || len == 0
 	    || len > NM_UTILS_HWADDR_LEN_MAX
 	    || !addr)
-		g_return_val_if_reached (FALSE);
+		g_return_val_if_reached (NM_PLATFORM_ERROR_BUG);
 
 	if (   device->link.addr.len != len
 	    || (   len > 0
@@ -524,7 +525,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t
 		link_changed (platform, link_get (platform, ifindex), TRUE);
 	}
 
-	return TRUE;
+	return NM_PLATFORM_ERROR_SUCCESS;
 }
 
 static gboolean
@@ -709,12 +710,13 @@ static gboolean
 infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link)
 {
 	NMFakePlatformLink *device, *parent_device;
-	gs_free char *name = NULL;
+	char name[IFNAMSIZ];
 
 	parent_device = link_get (platform, parent);
 	g_return_val_if_fail (parent_device != NULL, FALSE);
 
-	name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key);
+	nm_utils_new_infiniband_name (name, parent_device->link.name, p_key);
+
 	if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0, out_link))
 		return FALSE;
 
@@ -726,7 +728,6 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
 	device->lnk->lnk_infiniband.p_key = p_key;
 	device->lnk->lnk_infiniband.mode = "datagram";
 	device->link.parent = parent;
-
 	return TRUE;
 }
 
@@ -739,7 +740,7 @@ infiniband_partition_delete (NMPlatform *platform, int parent, int p_key)
 	parent_device = link_get (platform, parent);
 	g_return_val_if_fail (parent_device != NULL, FALSE);
 
-	name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key);
+	nm_utils_new_infiniband_name (name, parent_device->link.name, p_key);
 	return link_delete (platform, nm_platform_link_get_ifindex (platform, name));
 }
 
@@ -911,7 +912,7 @@ ip4_address_add (NMPlatform *platform,
 	int i;
 
 	memset (&address, 0, sizeof (address));
-	address.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	address.ifindex = ifindex;
 	address.address = addr;
 	address.peer_address = peer_addr;
@@ -962,7 +963,7 @@ ip6_address_add (NMPlatform *platform,
 	int i;
 
 	memset (&address, 0, sizeof (address));
-	address.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	address.ifindex = ifindex;
 	address.address = addr;
 	address.peer_address = (IN6_IS_ADDR_UNSPECIFIED (&peer_addr) || IN6_ARE_ADDR_EQUAL (&addr, &peer_addr)) ? in6addr_any : peer_addr;
@@ -1207,9 +1208,8 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 	scope = gateway == 0 ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
 
 	memset (&route, 0, sizeof (route));
-	route.source = NM_IP_CONFIG_SOURCE_KERNEL;
 	route.ifindex = ifindex;
-	route.source = source;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source);
 	route.network = nm_utils_ip4_address_clear_host_address (network, plen);
 	route.plen = plen;
 	route.gateway = gateway;
@@ -1273,9 +1273,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 	metric = nm_utils_ip6_route_metric_normalize (metric);
 
 	memset (&route, 0, sizeof (route));
-	route.source = NM_IP_CONFIG_SOURCE_KERNEL;
 	route.ifindex = ifindex;
-	route.source = source;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source);
 	nm_utils_ip6_address_clear_host_address (&route.network, &network, plen);
 	route.plen = plen;
 	route.gateway = gateway;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index f6f7be80..98c4e461 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -176,6 +176,11 @@
  * Forward declarations and enums
  ******************************************************************/
 
+typedef enum {
+	INFINIBAND_ACTION_CREATE_CHILD,
+	INFINIBAND_ACTION_DELETE_CHILD,
+} InfinibandAction;
+
 enum {
 	DELAYED_ACTION_IDX_REFRESH_ALL_LINKS,
 	DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES,
@@ -303,23 +308,6 @@ _support_user_ipv6ll_detect (struct nlattr **tb)
  * Various utilities
  ******************************************************************/
 
-static void
-clear_host_address (int family, const void *network, guint8 plen, void *dst)
-{
-	g_return_if_fail (network);
-
-	switch (family) {
-	case AF_INET:
-		*((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen);
-		break;
-	case AF_INET6:
-		nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen);
-		break;
-	default:
-		g_assert_not_reached ();
-	}
-}
-
 static int
 _vlan_qos_mapping_cmp_from (gconstpointer a, gconstpointer b, gpointer user_data)
 {
@@ -372,7 +360,7 @@ static const LinkDesc linktypes[] = {
 	{ NM_LINK_TYPE_INFINIBAND,    "infiniband",  NULL,          NULL },
 	{ NM_LINK_TYPE_OLPC_MESH,     "olpc-mesh",   NULL,          NULL },
 	{ NM_LINK_TYPE_WIFI,          "wifi",        NULL,          "wlan" },
-	{ NM_LINK_TYPE_WWAN_ETHERNET, "wwan",        NULL,          "wwan" },
+	{ NM_LINK_TYPE_WWAN_NET,      "wwan",        NULL,          "wwan" },
 	{ NM_LINK_TYPE_WIMAX,         "wimax",       "wimax",       "wimax" },
 
 	{ NM_LINK_TYPE_DUMMY,         "dummy",       "dummy",       NULL },
@@ -588,12 +576,17 @@ _lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_fro
 #define DEVTYPE_PREFIX "DEVTYPE="
 
 static char *
-_linktype_read_devtype (const char *sysfs_path)
+_linktype_read_devtype (const char *ifname)
 {
-	gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path);
+	char uevent[NM_STRLEN ("/sys/class/net/123456789012345/uevent\0") + 100 /*safety*/];
 	char *contents = NULL;
 	char *cont, *end;
 
+	nm_sprintf_buf (uevent,
+	                "/sys/class/net/%s/uevent",
+	                NM_ASSERT_VALID_PATH_COMPONENT (ifname));
+	nm_assert (strlen (uevent) < sizeof (uevent) - 1);
+
 	if (!g_file_get_contents (uevent, &contents, NULL, NULL))
 		return NULL;
 	for (cont = contents; cont; cont = end) {
@@ -690,9 +683,8 @@ _linktype_get_type (NMPlatform *platform,
 		return NM_LINK_TYPE_IP6TNL;
 
 	if (ifname) {
+		char anycast_mask[NM_STRLEN ("/sys/class/net/123456789012345/anycast_mask\0") + 100 /*safety*/];
 		gs_free char *driver = NULL;
-		gs_free char *sysfs_path = NULL;
-		gs_free char *anycast_mask = NULL;
 		gs_free char *devtype = NULL;
 
 		/* Fallback OVS detection for kernel <= 3.16 */
@@ -709,12 +701,15 @@ _linktype_get_type (NMPlatform *platform,
 			}
 		}
 
-		sysfs_path = g_strdup_printf ("/sys/class/net/%s", ifname);
-		anycast_mask = g_strdup_printf ("%s/anycast_mask", sysfs_path);
+		nm_sprintf_buf (anycast_mask,
+		                "/sys/class/net/%s/anycast_mask",
+		                NM_ASSERT_VALID_PATH_COMPONENT (ifname));
+		nm_assert (strlen (anycast_mask) < sizeof (anycast_mask) - 1);
+
 		if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS))
 			return NM_LINK_TYPE_OLPC_MESH;
 
-		devtype = _linktype_read_devtype (sysfs_path);
+		devtype = _linktype_read_devtype (ifname);
 		for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) {
 			if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) {
 				if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) {
@@ -729,10 +724,19 @@ _linktype_get_type (NMPlatform *platform,
 		}
 
 		/* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */
-		if (wifi_utils_is_wifi (ifname, sysfs_path))
+		if (wifi_utils_is_wifi (ifname))
 			return NM_LINK_TYPE_WIFI;
 
 		if (arptype == ARPHRD_ETHER) {
+			/* Misc non-upstream WWAN drivers.  rmnet is Qualcomm's proprietary
+			 * modem interface, ccmni is MediaTek's.  FIXME: these drivers should
+			 * really set devtype=WWAN.
+			 */
+			if (g_str_has_prefix (ifname, "rmnet") ||
+			    g_str_has_prefix (ifname, "rev_rmnet") ||
+			    g_str_has_prefix (ifname, "ccmni"))
+				return NM_LINK_TYPE_WWAN_NET;
+
 			/* Standard wired ethernet interfaces don't report an rtnl_link_type, so
 			 * only allow fallback to Ethernet if no type is given.  This should
 			 * prevent future virtual network drivers from being treated as Ethernet
@@ -803,9 +807,10 @@ _nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len)
 static gboolean
 _parse_af_inet6 (NMPlatform *platform,
                  struct nlattr *attr,
-                 NMUtilsIPv6IfaceId *out_iid,
-                 guint8 *out_iid_is_valid,
-                 guint8 *out_addr_gen_mode_inv)
+                 NMUtilsIPv6IfaceId *out_token,
+                 gboolean *out_token_valid,
+                 guint8 *out_addr_gen_mode_inv,
+                 gboolean *out_addr_gen_mode_valid)
 {
 	static struct nla_policy policy[IFLA_INET6_MAX+1] = {
 		[IFLA_INET6_FLAGS]              = { .type = NLA_U32 },
@@ -819,7 +824,8 @@ _parse_af_inet6 (NMPlatform *platform,
 	struct nlattr *tb[IFLA_INET6_MAX+1];
 	int err;
 	struct in6_addr i6_token;
-	gboolean iid_is_valid = FALSE;
+	gboolean token_valid = FALSE;
+	gboolean addr_gen_mode_valid = FALSE;
 	guint8 i6_addr_gen_mode_inv = 0;
 	gboolean success = FALSE;
 
@@ -836,8 +842,7 @@ _parse_af_inet6 (NMPlatform *platform,
 
 	if (_check_addr_or_errout (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr))) {
 		nla_memcpy (&i6_token, tb[IFLA_INET6_TOKEN], sizeof (struct in6_addr));
-		if (!IN6_IS_ADDR_UNSPECIFIED (&i6_token))
-			iid_is_valid = TRUE;
+		token_valid = TRUE;
 	}
 
 	/* Hack to detect support addrgenmode of the kernel. We only parse
@@ -852,21 +857,18 @@ _parse_af_inet6 (NMPlatform *platform,
 			 * to signal "unset". */
 			goto errout;
 		}
+		addr_gen_mode_valid = TRUE;
 	}
 
 	success = TRUE;
-	if (iid_is_valid) {
-		out_iid->id_u8[7] = i6_token.s6_addr[15];
-		out_iid->id_u8[6] = i6_token.s6_addr[14];
-		out_iid->id_u8[5] = i6_token.s6_addr[13];
-		out_iid->id_u8[4] = i6_token.s6_addr[12];
-		out_iid->id_u8[3] = i6_token.s6_addr[11];
-		out_iid->id_u8[2] = i6_token.s6_addr[10];
-		out_iid->id_u8[1] = i6_token.s6_addr[9];
-		out_iid->id_u8[0] = i6_token.s6_addr[8];
-		*out_iid_is_valid = TRUE;
-	}
-	*out_addr_gen_mode_inv = i6_addr_gen_mode_inv;
+	if (token_valid) {
+		*out_token_valid = token_valid;
+		nm_utils_ipv6_interface_identifier_get_from_addr (out_token, &i6_token);
+	}
+	if (addr_gen_mode_valid) {
+		*out_addr_gen_mode_valid = addr_gen_mode_valid;
+		*out_addr_gen_mode_inv = i6_addr_gen_mode_inv;
+	}
 errout:
 	return success;
 }
@@ -1441,6 +1443,8 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 	NMPObject *lnk_data = NULL;
 	gboolean address_complete_from_cache = TRUE;
 	gboolean lnk_data_complete_from_cache = TRUE;
+	gboolean af_inet6_token_valid = FALSE;
+	gboolean af_inet6_addr_gen_mode_valid = FALSE;
 
 	if (!nlmsg_valid_hdr (nlh, sizeof (*ifi)))
 		return NULL;
@@ -1472,6 +1476,15 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 		nl_info_data = li[IFLA_INFO_DATA];
 	}
 
+	if (tb[IFLA_STATS64]) {
+		struct rtnl_link_stats64 *stats = nla_data (tb[IFLA_STATS64]);
+
+		obj->link.rx_packets = stats->rx_packets;
+		obj->link.rx_bytes = stats->rx_bytes;
+		obj->link.tx_packets = stats->tx_packets;
+		obj->link.tx_bytes = stats->tx_bytes;
+	}
+
 	obj->link.n_ifi_flags = ifi->ifi_flags;
 	obj->link.connected = NM_FLAGS_HAS (obj->link.n_ifi_flags, IFF_LOWER_UP);
 	obj->link.arptype = ifi->ifi_type;
@@ -1517,9 +1530,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 			case AF_INET6:
 				_parse_af_inet6 (platform,
 				                 af_attr,
-				                 &obj->link.inet6_token.iid,
-				                 &obj->link.inet6_token.is_valid,
-				                 &obj->link.inet6_addr_gen_mode_inv);
+				                 &obj->link.inet6_token,
+				                 &af_inet6_token_valid,
+				                 &obj->link.inet6_addr_gen_mode_inv,
+				                 &af_inet6_addr_gen_mode_valid);
 				break;
 			}
 		}
@@ -1561,7 +1575,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 
 	if (   completed_from_cache
 	    && (   lnk_data_complete_from_cache
-	        || address_complete_from_cache)) {
+	        || address_complete_from_cache
+	        || !af_inet6_token_valid
+	        || !af_inet6_addr_gen_mode_valid
+	        || !tb[IFLA_STATS64])) {
 		_lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached);
 		if (link_cached) {
 			if (   lnk_data_complete_from_cache
@@ -1580,6 +1597,16 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 			}
 			if (address_complete_from_cache)
 				obj->link.addr = link_cached->link.addr;
+			if (!af_inet6_token_valid)
+				obj->link.inet6_token = link_cached->link.inet6_token;
+			if (!af_inet6_addr_gen_mode_valid)
+				obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv;
+			if (!tb[IFLA_STATS64]) {
+				obj->link.rx_packets = link_cached->link.rx_packets;
+				obj->link.rx_bytes = link_cached->link.rx_bytes;
+				obj->link.tx_packets = link_cached->link.tx_packets;
+				obj->link.tx_bytes = link_cached->link.tx_bytes;
+			}
 		}
 	}
 
@@ -1666,7 +1693,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 		}
 	}
 
-	obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
 
 	obj->ip_address.n_ifa_flags = tb[IFA_FLAGS]
 	                              ? nla_get_u32 (tb[IFA_FLAGS])
@@ -1896,9 +1923,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 		 *
 		 * This happens, because this route is not nmp_object_is_alive().
 		 * */
-		obj->ip_route.source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED;
-	} else
-		obj->ip_route.source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
+		obj->ip_route.rt_cloned = TRUE;
+	}
+
+	obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
 
 	obj_result = obj;
 	obj = NULL;
@@ -1951,7 +1979,8 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m
 
 static gboolean
 _nl_msg_new_link_set_afspec (struct nl_msg *msg,
-                             int addr_gen_mode)
+                             int addr_gen_mode,
+                             NMUtilsIPv6IfaceId *iid)
 {
 	struct nlattr *af_spec;
 	struct nlattr *af_attr;
@@ -1961,11 +1990,19 @@ _nl_msg_new_link_set_afspec (struct nl_msg *msg,
 	if (!(af_spec = nla_nest_start (msg, IFLA_AF_SPEC)))
 		goto nla_put_failure;
 
-	if (addr_gen_mode >= 0) {
+	if (addr_gen_mode >= 0 || iid) {
 		if (!(af_attr = nla_nest_start (msg, AF_INET6)))
 			goto nla_put_failure;
 
-		NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode);
+		if (addr_gen_mode >= 0)
+			NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode);
+
+		if (iid) {
+			struct in6_addr i6_token = { .s6_addr = { 0, } };
+
+			nm_utils_ipv6_addr_set_interface_identifier (&i6_token, *iid);
+			NLA_PUT (msg, IFLA_INET6_TOKEN, sizeof (struct in6_addr), &i6_token);
+		}
 
 		nla_nest_end (msg, af_attr);
 	}
@@ -2258,7 +2295,7 @@ _nl_msg_new_route (int nlmsg_type,
 		.rtm_family = family,
 		.rtm_tos = 0,
 		.rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */
-		.rtm_protocol = nmp_utils_ip_config_source_to_rtprot (source),
+		.rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source),
 		.rtm_scope = scope,
 		.rtm_type = RTN_UNICAST,
 		.rtm_flags = 0,
@@ -2282,7 +2319,7 @@ _nl_msg_new_route (int nlmsg_type,
 
 	addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
 
-	clear_host_address (family, network, plen, &network_clean);
+	nm_utils_ipx_address_clear_host_address (family, &network_clean, network, plen);
 	NLA_PUT (msg, RTA_DST, addr_len, &network_clean);
 
 	NLA_PUT_U32 (msg, RTA_PRIORITY, metric);
@@ -2485,6 +2522,7 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
 	gsize len;
 	char *actual;
 	gs_free char *actual_free = NULL;
+	int errsv;
 
 	g_return_val_if_fail (path != NULL, FALSE);
 	g_return_val_if_fail (value != NULL, FALSE);
@@ -2495,18 +2533,22 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
 	/* Don't write to suspicious locations */
 	g_assert (!strstr (path, "/../"));
 
-	if (!nm_platform_netns_push (platform, &netns))
+	if (!nm_platform_netns_push (platform, &netns)) {
+		errno = ENETDOWN;
 		return FALSE;
+	}
 
 	fd = open (path, O_WRONLY | O_TRUNC);
 	if (fd == -1) {
-		if (errno == ENOENT) {
+		errsv = errno;
+		if (errsv == ENOENT) {
 			_LOGD ("sysctl: failed to open '%s': (%d) %s",
-			       path, errno, strerror (errno));
+			       path, errsv, strerror (errsv));
 		} else {
 			_LOGE ("sysctl: failed to open '%s': (%d) %s",
-			       path, errno, strerror (errno));
+			       path, errsv, strerror (errsv));
 		}
+		errno = errsv;
 		return FALSE;
 	}
 
@@ -2527,26 +2569,43 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
 	actual[len] = '\0';
 
 	/* Try to write the entire value three times if a partial write occurs */
+	errsv = 0;
 	for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
 		nwrote = write (fd, actual, len);
 		if (nwrote == -1) {
-			if (errno == EINTR) {
+			errsv = errno;
+			if (errsv == EINTR) {
 				_LOGD ("sysctl: interrupted, will try again");
 				continue;
 			}
 			break;
 		}
 	}
-	if (nwrote == -1 && errno != EEXIST) {
+	if (nwrote == -1 && errsv != EEXIST) {
 		_LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s",
-		       path, value, errno, strerror (errno));
+		       path, value, errsv, strerror (errsv));
 	} else if (nwrote < len) {
 		_LOGE ("sysctl: failed to set '%s' to '%s' after three attempts",
 		       path, value);
 	}
 
-	close (fd);
-	return (nwrote == len);
+	if (nwrote != len) {
+		if (close (fd) != 0) {
+			if (errsv != 0)
+				errno = errsv;
+		} else if (errsv != 0)
+			errno = errsv;
+		else
+			errno = EIO;
+		return FALSE;
+	}
+	if (close (fd) != 0) {
+		/* errno is already properly set. */
+		return FALSE;
+	}
+
+	/* success. errno is undefined (no need to set). */
+	return TRUE;
 }
 
 static GSList *sysctl_clear_cache_list;
@@ -3689,6 +3748,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	case RTM_NEWLINK:
 	case RTM_NEWADDR:
 	case RTM_NEWROUTE:
+	case RTM_GETLINK:
 		cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform);
 
 		cache_post (platform, msghdr, cache_op, obj, obj_cache);
@@ -3724,6 +3784,16 @@ cache_lookup_link (NMPlatform *platform, int ifindex)
 	return obj_cache;
 }
 
+const NMPlatformObject *const*
+nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len)
+{
+	g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL);
+	g_return_val_if_fail (cache_id, NULL);
+
+	return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
+	                               cache_id, out_len);
+}
+
 static GArray *
 link_get_all (NMPlatform *platform)
 {
@@ -4036,6 +4106,9 @@ retry:
 	} else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) {
 		log_detail = ", firmware not found";
 		result = NM_PLATFORM_ERROR_NO_FIRMWARE;
+	} else if (NM_IN_SET (-((int) seq_result), ENODEV)) {
+		log_level = LOGL_DEBUG;
+		result = NM_PLATFORM_ERROR_NOT_FOUND;
 	} else {
 		log_level = LOGL_ERR;
 		result = NM_PLATFORM_ERROR_UNSPECIFIED;
@@ -4272,7 +4345,7 @@ link_get_udev_device (NMPlatform *platform, int ifindex)
 	return obj_cache ? (GObject *) obj_cache->_link.udev.device : NULL;
 }
 
-static gboolean
+static NMPlatformError
 link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled)
 {
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
@@ -4280,7 +4353,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
 
 	if (!_support_user_ipv6ll_get ()) {
 		_LOGD ("link: change %d: user-ipv6ll: not supported", ifindex);
-		return FALSE;
+		return NM_PLATFORM_ERROR_OPNOTSUPP;
 	}
 
 	_LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s",
@@ -4294,8 +4367,22 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
 	                          0,
 	                          0);
 	if (   !nlmsg
-	    || !_nl_msg_new_link_set_afspec (nlmsg,
-	                                     mode))
+	    || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL))
+		g_return_val_if_reached (NM_PLATFORM_ERROR_BUG);
+
+	return do_change_link (platform, ifindex, nlmsg);
+}
+
+static gboolean
+link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid)
+{
+	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+
+	_LOGD ("link: change %d: token: set IPv6 address generation token to %s",
+	       ifindex, nm_utils_inet6_interface_identifier_to_token (iid, NULL));
+
+	nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0);
+	if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid))
 		g_return_val_if_reached (FALSE);
 
 	return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
@@ -4338,14 +4425,14 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
 	return nmp_utils_ethtool_supports_vlans (obj->link.name);
 }
 
-static gboolean
+static NMPlatformError
 link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length)
 {
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 	gs_free char *mac = NULL;
 
 	if (!address || !length)
-		g_return_val_if_reached (FALSE);
+		g_return_val_if_reached (NM_PLATFORM_ERROR_BUG);
 
 	_LOGD ("link: change %d: address: %s (%lu bytes)", ifindex,
 	       (mac = nm_utils_hwaddr_ntoa (address, length)),
@@ -4358,13 +4445,13 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size
 	                          0,
 	                          0);
 	if (!nlmsg)
-		return FALSE;
+		g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED);
 
 	NLA_PUT (nlmsg, IFLA_ADDRESS, length, address);
 
-	return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
+	return do_change_link (platform, ifindex, nlmsg);
 nla_put_failure:
-	g_return_val_if_reached (FALSE);
+	g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED);
 }
 
 static gboolean
@@ -5095,57 +5182,67 @@ link_release (NMPlatform *platform, int master, int slave)
 /******************************************************************/
 
 static gboolean
-_infiniband_partition_action (NMPlatform *platform, int parent, int p_key, const char *action, char **ifname)
+_infiniband_partition_action (NMPlatform *platform,
+                              InfinibandAction action,
+                              int parent,
+                              int p_key,
+                              const NMPlatformLink **out_link)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	const NMPObject *obj_parent;
-	gs_free char *path = NULL;
-	gs_free char *id = NULL;
+	const NMPObject *obj;
+	char path[NM_STRLEN ("/sys/class/net/%s/%s") + IFNAMSIZ + 100];
+	char id[20];
+	char name[IFNAMSIZ];
+	gboolean success;
+
+	nm_assert (NM_IN_SET (action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD));
+	nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000);
 
 	obj_parent = nmp_cache_lookup_link (priv->cache, parent);
-	if (!obj_parent || !obj_parent->link.name[0])
-		g_return_val_if_reached (FALSE);
+	if (!obj_parent || !obj_parent->link.name[0]) {
+		errno = ENOENT;
+		return FALSE;
+	}
+
+	nm_sprintf_buf (path,
+	                "/sys/class/net/%s/%s",
+	                NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name),
+	                (action == INFINIBAND_ACTION_CREATE_CHILD
+	                     ? "create_child"
+	                     : "delete_child"));
+	nm_sprintf_buf (id, "0x%04x", p_key);
+	success = nm_platform_sysctl_set (platform, path, id);
+	if (!success) {
+		if (   action == INFINIBAND_ACTION_DELETE_CHILD
+		    && errno == ENODEV)
+			return TRUE;
+		return FALSE;
+	}
 
-	*ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
+	nm_utils_new_infiniband_name (name, obj_parent->link.name, p_key);
+	do_request_link (platform, 0, name);
 
-	path = g_strdup_printf ("/sys/class/net/%s/%s",
-	                        NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name),
-	                        action);
-	id = g_strdup_printf ("0x%04x", p_key);
+	if (action == INFINIBAND_ACTION_DELETE_CHILD)
+		return TRUE;
 
-	return nm_platform_sysctl_set (platform, path, id);
+	obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE,
+	                                  NM_LINK_TYPE_INFINIBAND, NULL, NULL);
+	if (out_link)
+		*out_link = obj ? &obj->link : NULL;
+	return !!obj;
 }
 
-
 static gboolean
 infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link)
 {
-	const NMPObject *obj;
-	gs_free char *ifname = NULL;
-
-	if (!_infiniband_partition_action (platform, parent, p_key, "create_child", &ifname))
-		return FALSE;
-
-	do_request_link (platform, 0, ifname);
-
-	obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
-	                                  0, ifname, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL);
-	if (out_link)
-		*out_link = obj ? &obj->link : NULL;
-	return !!obj;
+	return _infiniband_partition_action (platform, INFINIBAND_ACTION_CREATE_CHILD, parent, p_key, out_link);
 }
 
 static gboolean
 infiniband_partition_delete (NMPlatform *platform, int parent, int p_key)
 {
-	gs_free char *ifname = NULL;
-
-	if (!_infiniband_partition_action (platform, parent, p_key, "delete_child", &ifname)) {
-		if (errno != ENODEV)
-			return FALSE;
-	}
-
-	return TRUE;
+	return _infiniband_partition_action (platform, INFINIBAND_ACTION_DELETE_CHILD, parent, p_key, NULL);
 }
 
 /******************************************************************/
@@ -5587,7 +5684,7 @@ ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NM
 		nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass);
 
 		if (   with_rtprot_kernel
-		    || routes[i]->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
+		    || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
 			g_array_append_vals (array, routes[i], 1);
 	}
 	return array;
@@ -6421,6 +6518,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_get_udev_device = link_get_udev_device;
 
 	platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled;
+	platform_class->link_set_token = link_set_token;
 
 	platform_class->link_set_address = link_set_address;
 	platform_class->link_get_permanent_address = link_get_permanent_address;
diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h
index 4ae2fd14..3b2a440e 100644
--- a/src/platform/nm-linux-platform.h
+++ b/src/platform/nm-linux-platform.h
@@ -52,4 +52,10 @@ NMPlatform *nm_linux_platform_new (gboolean netns_support);
 
 void nm_linux_platform_setup (void);
 
+struct _NMPCacheId;
+
+const NMPlatformObject *const *nm_linux_platform_lookup (NMPlatform *platform,
+                                                         const struct _NMPCacheId *cache_id,
+                                                         guint *out_len);
+
 #endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index b4542adf..068801ee 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -144,8 +144,7 @@ nmp_utils_ethtool_get_permanent_address (const char *ifname,
 		struct ethtool_perm_addr e;
 		guint8 _extra_data[NM_UTILS_HWADDR_LEN_MAX + 1];
 	} edata;
-	static const guint8 zeros[NM_UTILS_HWADDR_LEN_MAX] = { 0 };
-	static guint8 ones[NM_UTILS_HWADDR_LEN_MAX] = { 0 };
+	guint i;
 
 	if (!ifname)
 		return FALSE;
@@ -157,18 +156,23 @@ nmp_utils_ethtool_get_permanent_address (const char *ifname,
 	if (!ethtool_get (ifname, &edata.e))
 		return FALSE;
 
-	g_assert (edata.e.size <= NM_UTILS_HWADDR_LEN_MAX);
-
-	/* Some drivers might return a permanent address of all zeros.
-	 * Reject that (rh#1264024) */
-	if (memcmp (edata.e.data, zeros, edata.e.size) == 0)
+	if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX)
+		return FALSE;
+	if (edata.e.size < 1)
 		return FALSE;
 
-	/* Some drivers return a permanent address of all ones. Reject that too */
-	if (G_UNLIKELY (ones[0] != 0xFF))
-		memset (ones, 0xFF, sizeof (ones));
-	if (memcmp (edata.e.data, ones, edata.e.size) == 0)
+	if (NM_IN_SET (edata.e.data[0], 0, 0xFF)) {
+		/* Some drivers might return a permanent address of all zeros.
+		 * Reject that (rh#1264024)
+		 *
+		 * Some drivers return a permanent address of all ones. Reject that too */
+		for (i = 1; i < edata.e.size; i++) {
+			if (edata.e.data[0] != edata.e.data[i])
+				goto not_all_0or1;
+		}
 		return FALSE;
+	}
+not_all_0or1:
 
 	memcpy (buf, edata.e.data, edata.e.size);
 	*length = edata.e.size;
@@ -433,14 +437,35 @@ nmp_utils_device_exists (const char *name)
 	return g_file_test (sysdir, G_FILE_TEST_EXISTS);
 }
 
-guint
-nmp_utils_ip_config_source_to_rtprot (NMIPConfigSource source)
+NMIPConfigSource
+nmp_utils_ip_config_source_from_rtprot (guint8 rtprot)
 {
-	switch (source) {
-	case NM_IP_CONFIG_SOURCE_UNKNOWN:
+	return ((int) rtprot) + 1;
+}
+
+NMIPConfigSource
+nmp_utils_ip_config_source_round_trip_rtprot (NMIPConfigSource source)
+{
+	/* when adding a route to kernel for a give @source, the resulting route
+	 * will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*.
+	 * This function returns that. */
+	return nmp_utils_ip_config_source_from_rtprot (nmp_utils_ip_config_source_coerce_to_rtprot (source));
+}
+
+guint8
+nmp_utils_ip_config_source_coerce_to_rtprot (NMIPConfigSource source)
+{
+	/* when adding a route to kernel, we coerce the @source field
+	 * to rtm_protocol. This is not lossless as we map different
+	 * source values to the same RTPROT uint8 value. */
+	if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN)
 		return RTPROT_UNSPEC;
+
+	if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST)
+		return source - 1;
+
+	switch (source) {
 	case NM_IP_CONFIG_SOURCE_KERNEL:
-	case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
 		return RTPROT_KERNEL;
 	case NM_IP_CONFIG_SOURCE_DHCP:
 		return RTPROT_DHCP;
@@ -453,18 +478,32 @@ nmp_utils_ip_config_source_to_rtprot (NMIPConfigSource source)
 }
 
 NMIPConfigSource
-nmp_utils_ip_config_source_from_rtprot (guint rtprot)
+nmp_utils_ip_config_source_coerce_from_rtprot (NMIPConfigSource source)
 {
-	switch (rtprot) {
-	case RTPROT_UNSPEC:
+	/* When we receive a route from kernel and put it into the platform cache,
+	 * we preserve the protocol field by converting it to a NMIPConfigSource
+	 * via nmp_utils_ip_config_source_from_rtprot().
+	 *
+	 * However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot().
+	 * Instead, to go back to the original value, you need another step:
+	 *   nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)).
+	 *
+	 * This might partly restore the original source value, but of course that
+	 * is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot()
+	 * is not injective.
+	 * */
+	switch (source) {
+	case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC:
 		return NM_IP_CONFIG_SOURCE_UNKNOWN;
-	case RTPROT_KERNEL:
-		return NM_IP_CONFIG_SOURCE_RTPROT_KERNEL;
-	case RTPROT_REDIRECT:
+
+	case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
+	case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT:
 		return NM_IP_CONFIG_SOURCE_KERNEL;
-	case RTPROT_RA:
+
+	case NM_IP_CONFIG_SOURCE_RTPROT_RA:
 		return NM_IP_CONFIG_SOURCE_RDISC;
-	case RTPROT_DHCP:
+
+	case NM_IP_CONFIG_SOURCE_RTPROT_DHCP:
 		return NM_IP_CONFIG_SOURCE_DHCP;
 
 	default:
@@ -472,3 +511,50 @@ nmp_utils_ip_config_source_from_rtprot (guint rtprot)
 	}
 }
 
+const char *
+nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize len)
+{
+	const char *s = NULL;
+	nm_utils_to_string_buffer_init (&buf, &len); \
+
+	if (!len)
+		return buf;
+
+	switch (source) {
+	case NM_IP_CONFIG_SOURCE_UNKNOWN:         s = "unknown"; break;
+
+	case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC:   s = "rt-unspec"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: s = "rt-redirect"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:   s = "rt-kernel"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_BOOT:     s = "rt-boot"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_STATIC:   s = "rt-static"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_DHCP:     s = "rt-dhcp"; break;
+	case NM_IP_CONFIG_SOURCE_RTPROT_RA:       s = "rt-ra"; break;
+
+	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_PPP:             s = "ppp"; break;
+	case NM_IP_CONFIG_SOURCE_WWAN:            s = "wwan"; break;
+	case NM_IP_CONFIG_SOURCE_VPN:             s = "vpn"; break;
+	case NM_IP_CONFIG_SOURCE_DHCP:            s = "dhcp"; break;
+	case NM_IP_CONFIG_SOURCE_RDISC:           s = "rdisc"; break;
+	case NM_IP_CONFIG_SOURCE_USER:            s = "user"; break;
+	default:
+		break;
+	}
+
+	if (source >= 1 && source <= 0x100) {
+		if (s)
+			g_snprintf (buf, len, "%s", s);
+		else
+			g_snprintf (buf, len, "rt-%d", ((int) source) - 1);
+	} else {
+		if (s)
+			g_strlcpy (buf, s, len);
+		else
+			g_snprintf (buf, len, "(%d)", source);
+	}
+	return buf;
+}
+
diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h
index f2594747..456c0865 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -54,7 +54,10 @@ const char *nmp_utils_udev_get_driver (GUdevDevice *device);
 
 gboolean nmp_utils_device_exists (const char *name);
 
-guint            nmp_utils_ip_config_source_to_rtprot   (NMIPConfigSource source);
-NMIPConfigSource nmp_utils_ip_config_source_from_rtprot (guint rtprot);
+NMIPConfigSource nmp_utils_ip_config_source_from_rtprot (guint8 rtprot) _nm_const;
+guint8           nmp_utils_ip_config_source_coerce_to_rtprot   (NMIPConfigSource source) _nm_const;
+NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot (NMIPConfigSource source) _nm_const;
+NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot  (NMIPConfigSource source) _nm_const;
+const char *     nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize len);
 
 #endif /* __NM_PLATFORM_UTILS_H__ */
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 26ac766d..9dace699 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -205,6 +205,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError,
 	NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_WRONG_TYPE,  "wrong-type"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE,   "not-slave"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP,   "not-supported"),
 	NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT),
 );
 
@@ -387,6 +388,20 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint ba
 
 /******************************************************************/
 
+static int
+_link_get_all_presort (gconstpointer  p_a,
+                       gconstpointer  p_b)
+{
+	const NMPlatformLink *a = p_a;
+	const NMPlatformLink *b = p_b;
+
+	if (a->ifindex < b->ifindex)
+		return -1;
+	if (a->ifindex > b->ifindex)
+		return 1;
+	return 0;
+}
+
 /**
  * nm_platform_link_get_all:
  * self: platform instance
@@ -409,15 +424,17 @@ nm_platform_link_get_all (NMPlatform *self)
 	if (!links || links->len == 0)
 		return links;
 
+	/* first sort the links by their ifindex. Below we will sort further by moving
+	 * children/slaves to the end. */
+	g_array_sort (links, _link_get_all_presort);
+
 	unseen = g_hash_table_new (g_direct_hash, g_direct_equal);
 	for (i = 0; i < links->len; i++) {
 		item = &g_array_index (links, NMPlatformLink, i);
 
-		_LOGt ("link-get: %3d: %s", i, nm_platform_link_to_string (item, NULL, 0));
-
-		nm_assert (item->ifindex > 0 && !g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex)));
-
-		g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL);
+		nm_assert (item->ifindex > 0);
+		if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL))
+			nm_assert_not_reached ();
 	}
 
 #ifndef G_DISABLE_ASSERT
@@ -468,8 +485,6 @@ nm_platform_link_get_all (NMPlatform *self)
 			if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)))
 				continue;
 
-			_LOGt ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item, NULL, 0));
-
 			g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
 			g_array_index (result, NMPlatformLink, j++) = *item;
 			item->ifindex = 0;
@@ -481,8 +496,6 @@ nm_platform_link_get_all (NMPlatform *self)
 			 * This can happen for veth pairs where each peer is parent of the other end. */
 			item = &g_array_index (links, NMPlatformLink, first_idx);
 
-			_LOGt ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item, NULL, 0));
-
 			g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
 			g_array_index (result, NMPlatformLink, j++) = *item;
 			item->ifindex = 0;
@@ -888,33 +901,25 @@ nm_platform_link_uses_arp (NMPlatform *self, int ifindex)
 }
 
 /**
- * nm_platform_link_get_ipv6_token:
+ * nm_platform_link_set_ipv6_token:
  * @self: platform instance
  * @ifindex: Interface index
  * @iid: Tokenized interface identifier
  *
- * Returns IPv6 tokenized interface identifier. If the platform or OS doesn't
- * support IPv6 tokenized interface identifiers, or the token is not set
- * this call will fail and return %FALSE.
+ * Sets then IPv6 tokenized interface identifier.
  *
  * Returns: %TRUE a tokenized identifier was available
  */
 gboolean
-nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid)
+nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid)
 {
-	const NMPlatformLink *pllink;
-
 	_CHECK_SELF (self, klass, FALSE);
 
 	g_return_val_if_fail (ifindex >= 0, FALSE);
-	g_return_val_if_fail (iid, FALSE);
+	g_return_val_if_fail (iid.id, FALSE);
 
-
-	pllink = nm_platform_link_get (self, ifindex);
-	if (pllink && pllink->inet6_token.is_valid) {
-		*iid = pllink->inet6_token.iid;
-		return TRUE;
-	}
+	if (klass->link_set_token)
+		return klass->link_set_token (self, ifindex, iid);
 	return FALSE;
 }
 
@@ -977,18 +982,16 @@ nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex)
  * platform or OS doesn't support changing the IPv6LL address mode, this call
  * will fail and return %FALSE.
  *
- * Returns: %TRUE if the operation was successful, %FALSE if it failed.
+ * Returns: %NM_PLATFORM_ERROR_SUCCESS if the operation was successful or an error code otherwise.
  */
-gboolean
+NMPlatformError
 nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled)
 {
-	_CHECK_SELF (self, klass, FALSE);
+	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
 
-	g_return_val_if_fail (ifindex >= 0, FALSE);
+	g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG);
 
-	if (klass->link_set_user_ipv6ll_enabled)
-		return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled);
-	return FALSE;
+	return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled);
 }
 
 /**
@@ -999,16 +1002,18 @@ nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolea
  *
  * Set interface MAC address.
  */
-gboolean
+NMPlatformError
 nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length)
 {
-	_CHECK_SELF (self, klass, FALSE);
+	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
 
-	g_return_val_if_fail (ifindex > 0, FALSE);
-	g_return_val_if_fail (address, FALSE);
-	g_return_val_if_fail (length > 0, FALSE);
+	g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG);
+	g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG);
+	g_return_val_if_fail (length > 0, NM_PLATFORM_ERROR_BUG);
 
-	_LOGD ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (self, ifindex), ifindex);
+	_LOGD ("link: setting %s (%d) hardware address",
+	       nm_strquote_a (20, nm_platform_link_get_name (self, ifindex)),
+	       ifindex);
 	return klass->link_set_address (self, ifindex, address, length);
 }
 
@@ -1895,21 +1900,27 @@ _infiniband_add_add_or_delete (NMPlatform *self,
                                gboolean add,
                                const NMPlatformLink **out_link)
 {
-	gs_free char *parent_name = NULL;
-	gs_free char *name = NULL;
+	char name[IFNAMSIZ];
+	const NMPlatformLink *parent_link;
 	NMPlatformError plerr;
 
 	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
 
 	g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG);
-	g_return_val_if_fail (p_key >= 0, NM_PLATFORM_ERROR_BUG);
+	g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NM_PLATFORM_ERROR_BUG);
+
+	/* the special keys 0x0000 and 0x8000 are not allowed. */
+	if (NM_IN_SET (p_key, 0, 0x8000))
+		return NM_PLATFORM_ERROR_UNSPECIFIED;
 
-	parent_name = g_strdup (nm_platform_link_get_name (self, parent));
-	if (   !parent_name
-	    || nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND)
+	parent_link = nm_platform_link_get (self, parent);
+	if (!parent_link)
+		return NM_PLATFORM_ERROR_NOT_FOUND;
+
+	if (parent_link->type != NM_LINK_TYPE_INFINIBAND)
 		return NM_PLATFORM_ERROR_WRONG_TYPE;
 
-	name = g_strdup_printf ("%s.%04x", parent_name, p_key);
+	nm_utils_new_infiniband_name (name, parent_link->name, p_key);
 
 	if (add) {
 		plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link);
@@ -1917,7 +1928,7 @@ _infiniband_add_add_or_delete (NMPlatform *self,
 			return plerr;
 
 		_LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d",
-		       name, parent_name, parent, p_key);
+		       name, parent_link->name, parent, p_key);
 		if (!klass->infiniband_partition_add (self, parent, p_key, out_link))
 			return NM_PLATFORM_ERROR_UNSPECIFIED;
 	} else {
@@ -2894,7 +2905,7 @@ nm_platform_ip4_route_add (NMPlatform *self,
 		NMPlatformIP4Route route = { 0 };
 
 		route.ifindex = ifindex;
-		route.source = source;
+		route.rt_source = source;
 		route.network = network;
 		route.plen = plen;
 		route.gateway = gateway;
@@ -2921,7 +2932,7 @@ nm_platform_ip6_route_add (NMPlatform *self,
 		NMPlatformIP6Route route = { 0 };
 
 		route.ifindex = ifindex;
-		route.source = source;
+		route.rt_source = source;
 		route.network = network;
 		route.plen = plen;
 		route.gateway = gateway;
@@ -3012,38 +3023,6 @@ nm_platform_vlan_qos_mapping_to_string (const char *name,
 }
 
 static const char *
-source_to_string (NMIPConfigSource source)
-{
-	switch (source) {
-	case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL:
-		return "rtprot-kernel";
-	case _NM_IP_CONFIG_SOURCE_RTM_F_CLONED:
-		return "rtm-f-cloned";
-	case NM_IP_CONFIG_SOURCE_KERNEL:
-		return "kernel";
-	case NM_IP_CONFIG_SOURCE_SHARED:
-		return "shared";
-	case NM_IP_CONFIG_SOURCE_IP4LL:
-		return "ipv4ll";
-	case NM_IP_CONFIG_SOURCE_PPP:
-		return "ppp";
-	case NM_IP_CONFIG_SOURCE_WWAN:
-		return "wwan";
-	case NM_IP_CONFIG_SOURCE_VPN:
-		return "vpn";
-	case NM_IP_CONFIG_SOURCE_DHCP:
-		return "dhcp";
-	case NM_IP_CONFIG_SOURCE_RDISC:
-		return "rdisc";
-	case NM_IP_CONFIG_SOURCE_USER:
-		return "user";
-	default:
-		break;
-	}
-	return "unknown";
-}
-
-static const char *
 _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size)
 {
 	if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT)
@@ -3081,7 +3060,7 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
 	GString *str_flags;
 	char str_addrmode[30];
 	gs_free char *str_addr = NULL;
-	gs_free char *str_inet6_token = NULL;
+	char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN];
 	const char *str_link_type;
 
 	if (!nm_utils_to_string_buffer_init_null (link, &buf, &len))
@@ -3118,8 +3097,6 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
 
 	if (link->addr.len)
 		str_addr = nm_utils_hwaddr_ntoa (link->addr.data, MIN (link->addr.len, sizeof (link->addr.data)));
-	if (link->inet6_token.is_valid)
-		str_inet6_token = nm_utils_hwaddr_ntoa (&link->inet6_token.iid, sizeof (link->inet6_token.iid));
 
 	str_link_type = nm_link_type_to_string (link->type);
 
@@ -3138,6 +3115,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
 	            "%s%s" /* addr */
 	            "%s%s" /* inet6_token */
 	            "%s%s" /* driver */
+	            " rx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT
+	            " tx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT
 	            ,
 	            link->ifindex,
 	            link->name,
@@ -3153,10 +3132,12 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
 	            link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv), str_addrmode, sizeof (str_addrmode)) : "",
 	            str_addr ? " addr " : "",
 	            str_addr ? str_addr : "",
-	            str_inet6_token ? " inet6token " : "",
-	            str_inet6_token ? str_inet6_token : "",
+	            link->inet6_token.id ? " inet6token " : "",
+	            link->inet6_token.id ? nm_utils_inet6_interface_identifier_to_token (link->inet6_token, str_inet6_token) : "",
 	            link->driver ? " driver " : "",
-	            link->driver ? link->driver : "");
+	            link->driver ? link->driver : "",
+	            link->rx_packets, link->rx_bytes,
+	            link->tx_packets, link->tx_bytes);
 	g_string_free (str_flags, TRUE);
 	return buf;
 }
@@ -3471,7 +3452,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu
 	char s_peer[INET_ADDRSTRLEN];
 	char str_dev[TO_STRING_DEV_BUF_SIZE];
 	char str_label[32];
-	char str_lft[30], str_pref[30], str_time[50];
+	char str_lft[30], str_pref[30], str_time[50], s_source[50];
 	char *str_peer = NULL;
 	const char *str_lft_p, *str_pref_p, *str_time_p;
 	gint32 now = nm_utils_get_monotonic_timestamp_s ();
@@ -3510,7 +3491,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu
 	            str_dev,
 	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
 	            str_label,
-	            source_to_string (address->source));
+	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
 	g_free (str_peer);
 	return buf;
 }
@@ -3582,7 +3563,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 	char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
 	char s_address[INET6_ADDRSTRLEN];
 	char s_peer[INET6_ADDRSTRLEN];
-	char str_lft[30], str_pref[30], str_time[50];
+	char str_lft[30], str_pref[30], str_time[50], s_source[50];
 	char str_dev[TO_STRING_DEV_BUF_SIZE];
 	char *str_peer = NULL;
 	const char *str_lft_p, *str_pref_p, *str_time_p;
@@ -3616,7 +3597,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 	            str_peer ? str_peer : "",
 	            str_dev,
 	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
-	            source_to_string (address->source));
+	            nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
 	g_free (str_peer);
 	return buf;
 }
@@ -3639,7 +3620,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN];
 	char s_pref_src[INET_ADDRSTRLEN];
 	char str_dev[TO_STRING_DEV_BUF_SIZE];
-	char str_scope[30];
+	char str_scope[30], s_source[50];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
@@ -3656,6 +3637,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	            " metric %"G_GUINT32_FORMAT
 	            " mss %"G_GUINT32_FORMAT
 	            " src %s" /* source */
+	            "%s" /* cloned */
 	            "%s%s" /* scope */
 	            "%s%s" /* pref-src */
 	            "",
@@ -3665,7 +3647,8 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	            str_dev,
 	            route->metric,
 	            route->mss,
-	            source_to_string (route->source),
+	            nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)),
+	            route->rt_cloned ? " cloned" : "",
 	            route->scope_inv ? " scope " : "",
 	            route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "",
 	            route->pref_src ? " pref-src " : "",
@@ -3689,7 +3672,7 @@ const char *
 nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len)
 {
 	char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN];
-	char str_dev[TO_STRING_DEV_BUF_SIZE];
+	char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
@@ -3706,6 +3689,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	            " metric %"G_GUINT32_FORMAT
 	            " mss %"G_GUINT32_FORMAT
 	            " src %s" /* source */
+	            "%s" /* cloned */
 	            "",
 	            s_network,
 	            route->plen,
@@ -3713,7 +3697,8 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	            str_dev,
 	            route->metric,
 	            route->mss,
-	            source_to_string (route->source));
+	            nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)),
+	            route->rt_cloned ? " cloned" : "");
 	return buf;
 }
 
@@ -3808,13 +3793,15 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
 	_CMP_FIELD (a, b, arptype);
 	_CMP_FIELD (a, b, addr.len);
 	_CMP_FIELD (a, b, inet6_addr_gen_mode_inv);
-	_CMP_FIELD (a, b, inet6_token.is_valid);
 	_CMP_FIELD_STR_INTERNED (a, b, kind);
 	_CMP_FIELD_STR_INTERNED (a, b, driver);
 	if (a->addr.len)
 		_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len);
-	if (a->inet6_token.is_valid)
-		_CMP_FIELD_MEMCMP (a, b, inet6_token.iid);
+	_CMP_FIELD_MEMCMP (a, b, inet6_token);
+	_CMP_FIELD (a, b, rx_packets);
+	_CMP_FIELD (a, b, rx_bytes);
+	_CMP_FIELD (a, b, tx_packets);
+	_CMP_FIELD (a, b, tx_bytes);
 	return 0;
 }
 
@@ -3935,10 +3922,10 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A
 {
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD (a, b, source);
 	_CMP_FIELD (a, b, address);
 	_CMP_FIELD (a, b, plen);
 	_CMP_FIELD (a, b, peer_address);
+	_CMP_FIELD (a, b, addr_source);
 	_CMP_FIELD (a, b, timestamp);
 	_CMP_FIELD (a, b, lifetime);
 	_CMP_FIELD (a, b, preferred);
@@ -3954,14 +3941,12 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A
 
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD (a, b, source);
 	_CMP_FIELD_MEMCMP (a, b, address);
-
+	_CMP_FIELD (a, b, plen);
 	p_a = nm_platform_ip6_address_get_peer (a);
 	p_b = nm_platform_ip6_address_get_peer (b);
 	_CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a));
-
-	_CMP_FIELD (a, b, plen);
+	_CMP_FIELD (a, b, addr_source);
 	_CMP_FIELD (a, b, timestamp);
 	_CMP_FIELD (a, b, lifetime);
 	_CMP_FIELD (a, b, preferred);
@@ -3974,14 +3959,15 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
 {
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD (a, b, source);
 	_CMP_FIELD (a, b, network);
 	_CMP_FIELD (a, b, plen);
-	_CMP_FIELD (a, b, gateway);
 	_CMP_FIELD (a, b, metric);
+	_CMP_FIELD (a, b, gateway);
+	_CMP_FIELD (a, b, rt_source);
 	_CMP_FIELD (a, b, mss);
 	_CMP_FIELD (a, b, scope_inv);
 	_CMP_FIELD (a, b, pref_src);
+	_CMP_FIELD (a, b, rt_cloned);
 	return 0;
 }
 
@@ -3990,12 +3976,13 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
 {
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD (a, b, source);
 	_CMP_FIELD_MEMCMP (a, b, network);
 	_CMP_FIELD (a, b, plen);
-	_CMP_FIELD_MEMCMP (a, b, gateway);
 	_CMP_FIELD (a, b, metric);
+	_CMP_FIELD_MEMCMP (a, b, gateway);
+	_CMP_FIELD (a, b, rt_source);
 	_CMP_FIELD (a, b, mss);
+	_CMP_FIELD (a, b, rt_cloned);
 	return 0;
 }
 
@@ -4128,7 +4115,7 @@ _vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *rout
 {
 	return nm_platform_ip4_route_add (self,
 	                                  ifindex > 0 ? ifindex : route->rx.ifindex,
-	                                  route->rx.source,
+	                                  route->rx.rt_source,
 	                                  route->r4.network,
 	                                  route->rx.plen,
 	                                  route->r4.gateway,
@@ -4142,7 +4129,7 @@ _vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *rout
 {
 	return nm_platform_ip6_route_add (self,
 	                                  ifindex > 0 ? ifindex : route->rx.ifindex,
-	                                  route->rx.source,
+	                                  route->rx.rt_source,
 	                                  route->r6.network,
 	                                  route->rx.plen,
 	                                  route->r6.gateway,
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 19724f1c..fc6965e9 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -49,8 +49,6 @@
 
 /******************************************************************/
 
-typedef struct _NMPlatform NMPlatform;
-
 /* workaround for older libnl version, that does not define these flags. */
 #ifndef IFA_F_MANAGETEMPADDR
 #define IFA_F_MANAGETEMPADDR 0x100
@@ -90,6 +88,7 @@ typedef enum { /*< skip >*/
 	NM_PLATFORM_ERROR_WRONG_TYPE,
 	NM_PLATFORM_ERROR_NOT_SLAVE,
 	NM_PLATFORM_ERROR_NO_FIRMWARE,
+	NM_PLATFORM_ERROR_OPNOTSUPP,
 } NMPlatformError;
 
 
@@ -98,6 +97,11 @@ typedef struct {
 		guint8 addr_ptr[1];
 		in_addr_t addr4;
 		struct in6_addr addr6;
+
+		/* NMIPAddr is really a union for IP addresses.
+		 * However, as ethernet addresses fit in here nicely, ruse
+		 * it also for an ethernet MAC address. */
+		guint8 addr_eth[6 /*ETH_ALEN*/];
 	};
 } NMIPAddr;
 
@@ -144,17 +148,20 @@ struct _NMPlatformLink {
 		guint8 len;
 	} addr;
 
-	/* rtnl_link_inet6_get_token() */
-	struct {
-		NMUtilsIPv6IfaceId iid;
-		guint8 is_valid;
-	} inet6_token;
+	/* rtnl_link_inet6_get_token(), IFLA_INET6_TOKEN */
+	NMUtilsIPv6IfaceId inet6_token;
 
 	/* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse
 	 * to have a default of 0 -- meaning: unspecified. That way, a struct
 	 * initialized with memset(0) has and unset value.*/
 	guint8 inet6_addr_gen_mode_inv;
 
+	/* Statistics */
+	guint64 rx_packets;
+	guint64 rx_bytes;
+	guint64 tx_packets;
+	guint64 tx_bytes;
+
 	/* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters,
 	 * where we coerce the link as disconnect if it has no slaves. */
 	bool connected:1;
@@ -200,7 +207,7 @@ typedef struct {
 
 #define __NMPlatformIPAddress_COMMON \
 	__NMPlatformObject_COMMON; \
-	NMIPConfigSource source; \
+	NMIPConfigSource addr_source; \
 	\
 	/* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*().
 	 *
@@ -303,8 +310,21 @@ typedef union {
 
 #define __NMPlatformIPRoute_COMMON \
 	__NMPlatformObject_COMMON; \
-	NMIPConfigSource source; \
+	\
+	/* The NMIPConfigSource. For routes that we receive from cache this corresponds
+	 * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values).
+	 * When adding a route, the source will be coerced to the protocol using
+	 * nmp_utils_ip_config_source_coerce_to_rtprot(). */ \
+	NMIPConfigSource rt_source; \
+	\
 	guint8 plen; \
+	\
+	/* the route has rtm_flags set to RTM_F_CLONED. Such a route
+	 * is hidden by platform and does not exist from the point-of-view
+	 * of platform users. This flag is internal to track those hidden
+	 * routes. Such a route is not alive, according to nmp_object_is_alive(). */ \
+	bool rt_cloned:1; \
+	\
 	guint32 metric; \
 	guint32 mss; \
 	;
@@ -506,13 +526,14 @@ typedef struct {
 	const char *(*link_get_udi) (NMPlatform *self, int ifindex);
 	GObject *(*link_get_udev_device) (NMPlatform *self, int ifindex);
 
-	gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled);
+	NMPlatformError (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled);
+	gboolean (*link_set_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId iid);
 
 	gboolean (*link_get_permanent_address) (NMPlatform *,
 	                                        int ifindex,
 	                                        guint8 *buf,
 	                                        size_t *length);
-	gboolean (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length);
+	NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length);
 	gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu);
 
 	char *   (*link_get_physical_port_id) (NMPlatform *, int ifindex);
@@ -717,7 +738,6 @@ gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex);
 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_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid);
 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);
 int nm_platform_link_get_master (NMPlatform *self, int slave);
@@ -740,10 +760,11 @@ const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex);
 
 GObject *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex);
 
-gboolean nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled);
+NMPlatformError nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled);
+gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid);
 
 gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length);
-gboolean nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length);
+NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length);
 gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu);
 
 char    *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index eb7e1ca4..30a52c25 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -47,6 +47,10 @@
         } \
     } G_STMT_END
 
+/* logging to trace object lifetime and references.
+ * Disabled by default. */
+#define _LOGr(...) G_STMT_START { if (FALSE) { _LOGt (__VA_ARGS__); } } G_STMT_END
+
 /*********************************************************************************************/
 
 struct _NMPCache {
@@ -211,7 +215,7 @@ nmp_object_ref (NMPObject *obj)
 	g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL);
 	obj->_ref_count++;
 
-	_LOGt (obj, "ref: %d", obj->_ref_count);
+	_LOGr (obj, "ref: %d", obj->_ref_count);
 
 	return obj;
 }
@@ -222,7 +226,7 @@ nmp_object_unref (NMPObject *obj)
 	if (obj) {
 		g_return_if_fail (obj->_ref_count > 0);
 		g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT);
-		_LOGt (obj, "%s: %d",
+		_LOGr (obj, "%s: %d",
 		       obj->_ref_count <= 1 ? "destroy" : "unref",
 		       obj->_ref_count - 1);
 		if (--obj->_ref_count <= 0) {
@@ -262,7 +266,7 @@ _nmp_object_new_from_class (const NMPClass *klass)
 	obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object));
 	obj->_class = klass;
 	obj->_ref_count = 1;
-	_LOGt (obj, "new");
+	_LOGr (obj, "new");
 	return obj;
 }
 
@@ -915,12 +919,12 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj)
 	 *
 	 * If nmp_object_from_nl() would just return NULL, we couldn't look
 	 * into the cache to see if it contains a route that now disappears
-	 * (because it is cloned).
+	 * (because it changed to be cloned).
 	 *
 	 * Instead we create a dead object, and nmp_cache_update_netlink()
 	 * will remove the old version of the update.
 	 **/
-	return obj->object.ifindex > 0 && (obj->ip_route.source != _NM_IP_CONFIG_SOURCE_RTM_F_CLONED);
+	return obj->object.ifindex > 0 && !obj->ip_route.rt_cloned;
 }
 
 gboolean
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index c5241037..d295f7a0 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -94,9 +94,9 @@ typedef enum { /*< skip >*/
 	NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1,
 } NMPCacheIdType;
 
-typedef struct _NMPObject NMPObject;
+typedef struct _NMPCacheId NMPCacheId;
 
-typedef struct {
+struct _NMPCacheId {
 	union {
 		NMMultiIndexId base;
 		guint8 _id_type; /* NMPCacheIdType as guint8 */
@@ -136,7 +136,7 @@ typedef struct {
 			struct in6_addr _misaligned_network;
 		} routes_by_destination_ip6;
 	};
-} NMPCacheId;
+};
 
 extern NMPCacheId _nmp_cache_id_static;
 #define NMP_CACHE_ID_STATIC  (&_nmp_cache_id_static)
diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am
index 5af4ce91..0633dd28 100644
--- a/src/platform/tests/Makefile.am
+++ b/src/platform/tests/Makefile.am
@@ -28,7 +28,7 @@ TEST_SOURCES = \
 AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
 AM_LDFLAGS = $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS)
 PLATFORM_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 @GNOME_CODE_COVERAGE_RULES@
 
@@ -109,12 +109,12 @@ test_cleanup_linux_LDADD = $(PLATFORM_LDADD)
 test_nmp_object_SOURCES = \
 	test-nmp-object.c
 test_nmp_object_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 test_general_SOURCES = \
 	test-general.c
 test_general_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 
 @VALGRIND_RULES@
diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in
index 49f2fa09..4922a3dc 100644
--- a/src/platform/tests/Makefile.in
+++ b/src/platform/tests/Makefile.in
@@ -102,8 +102,9 @@ TESTS = test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \
 	test-route-fake$(EXEEXT) test-route-linux$(EXEEXT)
 subdir = src/platform/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -170,7 +171,8 @@ test_cleanup_linux_OBJECTS = $(am_test_cleanup_linux_OBJECTS)
 test_cleanup_linux_DEPENDENCIES = $(PLATFORM_LDADD)
 am_test_general_OBJECTS = test-general.$(OBJEXT)
 test_general_OBJECTS = $(am_test_general_OBJECTS)
-test_general_DEPENDENCIES = $(top_builddir)/src/libNetworkManager.la
+test_general_DEPENDENCIES =  \
+	$(top_builddir)/src/libNetworkManagerTest.la
 am__objects_10 = test_link_fake-nm-platform.$(OBJEXT) \
 	test_link_fake-nm-fake-platform.$(OBJEXT) \
 	test_link_fake-nm-linux-platform.$(OBJEXT)
@@ -192,7 +194,7 @@ test_link_linux_DEPENDENCIES = $(PLATFORM_LDADD)
 am_test_nmp_object_OBJECTS = test-nmp-object.$(OBJEXT)
 test_nmp_object_OBJECTS = $(am_test_nmp_object_OBJECTS)
 test_nmp_object_DEPENDENCIES =  \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 am__objects_14 = test_route_fake-nm-platform.$(OBJEXT) \
 	test_route_fake-nm-fake-platform.$(OBJEXT) \
 	test_route_fake-nm-linux-platform.$(OBJEXT)
@@ -522,6 +524,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -578,13 +581,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -596,6 +600,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -656,6 +662,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -691,6 +699,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -772,7 +781,7 @@ TEST_SOURCES = \
 AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
 AM_LDFLAGS = $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(CODE_COVERAGE_LDFLAGS)
 PLATFORM_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 EXTRA_DIST = test-common.h
 monitor_SOURCES = monitor.c $(PLATFORM_SOURCES)
@@ -837,13 +846,13 @@ test_nmp_object_SOURCES = \
 	test-nmp-object.c
 
 test_nmp_object_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 test_general_SOURCES = \
 	test-general.c
 
 test_general_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 all: all-am
 
diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c
index 3af34be4..937eea11 100644
--- a/src/platform/tests/monitor.c
+++ b/src/platform/tests/monitor.c
@@ -25,7 +25,7 @@
 
 #include "nm-linux-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 NMTST_DEFINE ();
 
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 0e3cf10b..87fccee2 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -8,7 +8,7 @@
 #include "nm-fake-platform.h"
 #include "nm-linux-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 #define DEVICE_NAME "nm-test-device"
 
@@ -84,7 +84,7 @@ void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, N
 
 /*****************************************************************************/
 
-int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2)));
+int nmtstp_run_command (const char *format, ...) _nm_printf (1, 2);
 #define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0)
 
 /*****************************************************************************/
diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c
index d8d92561..f67f0089 100644
--- a/src/platform/tests/test-general.c
+++ b/src/platform/tests/test-general.c
@@ -25,7 +25,7 @@
 #include "nm-platform-utils.h"
 #include "nm-linux-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 
 /******************************************************************/
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 9d548aab..99873620 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -30,7 +30,7 @@
 #include "nm-platform-utils.h"
 
 #include "test-common.h"
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 #define LO_INDEX 1
 #define LO_NAME "lo"
@@ -60,16 +60,12 @@ test_bogus(void)
 	g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -80,7 +76,6 @@ test_bogus(void)
 	g_assert (!addrlen);
 	g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU));
 
 	g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -226,7 +221,7 @@ test_slave (int master, int type, SignalData *master_changed)
 	g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master);
 
 	accept_signals (link_changed, 1, 3);
-	accept_signals (master_changed, 0, 1);
+	accept_signals (master_changed, 0, 2);
 
 	/* enslaveing brings put the slave */
 	if (NM_IN_SET (link_type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM))
@@ -515,13 +510,13 @@ test_bridge_addr (void)
 		g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
 		g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64);
 
-		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE));
+		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE) == NM_PLATFORM_ERROR_SUCCESS);
 		g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
 		plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex);
 		g_assert (plink);
 		g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE);
 
-		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE));
+		g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE) == NM_PLATFORM_ERROR_SUCCESS);
 		g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex));
 		plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex);
 		g_assert (plink);
@@ -572,7 +567,7 @@ test_internal (void)
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL));
 	g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex));
 	g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
-	accept_signal (link_changed);
+	accept_signals (link_changed, 1, 2);
 	g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex));
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex));
 	g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
@@ -592,7 +587,7 @@ test_internal (void)
 	g_assert (nm_platform_link_supports_vlans (NM_PLATFORM_GET, ifindex));
 
 	/* Set MAC address */
-	g_assert (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac)));
+	g_assert (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac)) == NM_PLATFORM_ERROR_SUCCESS);
 	address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addrlen);
 	g_assert (addrlen == sizeof(mac));
 	g_assert (!memcmp (address, mac, addrlen));
@@ -778,11 +773,32 @@ test_software_detect (gconstpointer user_data)
 	}
 	case NM_LINK_TYPE_MACVLAN: {
 		NMPlatformLnkMacvlan lnk_macvlan = { };
+		const NMPlatformLink *dummy;
+		char buf[256];
+		int i;
 
 		lnk_macvlan.mode = MACVLAN_MODE_BRIDGE;
 		lnk_macvlan.no_promisc = FALSE;
 		lnk_macvlan.tap = FALSE;
 
+		/* Since in old kernel versions sysfs files for macvtaps are not
+		 * namespaced, the creation can fail if a macvtap in another namespace
+		 * has the same index. Try to detect this situation and skip already
+		 * used indexes.
+		 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17af2bce88d31e65ed73d638bb752d2e13c66ced
+		 */
+		for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) {
+			snprintf (buf, sizeof (buf), "/sys/class/macvtap/tap%d", i);
+			if (!g_file_test (buf, G_FILE_TEST_IS_SYMLINK))
+				break;
+
+			_LOGD ("skipping ifindex %d as already used by a macvtap", i);
+
+			dummy = nmtstp_link_dummy_add (NM_PLATFORM_GET, FALSE, "dummy-tmp");
+			g_assert_cmpint (dummy->ifindex, ==, i);
+			nmtstp_link_del (NM_PLATFORM_GET, FALSE, dummy->ifindex, NULL);
+		}
+
 		if (!nmtstp_link_macvlan_add (NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan))
 			g_error ("Failed adding MACVLAN interface");
 		break;
@@ -1861,7 +1877,7 @@ _test_netns_setup (gpointer fixture, gconstpointer test_data)
 {
 	/* the singleton platform instance has netns support disabled.
 	 * Destroy the instance before the test and re-create it afterwards. */
-	g_object_unref (nm_platform_get ());
+	g_object_unref (NM_PLATFORM_GET);
 }
 
 static void
@@ -1919,6 +1935,14 @@ _test_netns_check_skip (void)
 
 /******************************************************************/
 
+#define _sysctl_assert_eq(plat, path, value) \
+	G_STMT_START { \
+		gs_free char *_val = NULL; \
+		\
+		_val = nm_platform_sysctl_get (plat, path); \
+		g_assert_cmpstr (_val, ==, value); \
+	} G_STMT_END
+
 static void
 test_netns_general (gpointer fixture, gconstpointer test_data)
 {
@@ -1954,13 +1978,19 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
 			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i));
 	}
 
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex));
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, NULL);
+	_sysctl_assert_eq (platform_1,"/sys/devices/virtual/net/dummy1_/ifindex",
+	                   nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
+	_sysctl_assert_eq (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex",
+	                   nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex));
+	_sysctl_assert_eq (platform_1, "/sys/devices/virtual/net/dummy2b/ifindex",
+	                   NULL);
 
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, NULL);
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex));
+	_sysctl_assert_eq (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex",
+	                   nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
+	_sysctl_assert_eq (platform_2, "/sys/devices/virtual/net/dummy2a/ifindex",
+	                   NULL);
+	_sysctl_assert_eq (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex",
+	                   nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex));
 
 	for (i = 0; i < 10; i++) {
 		NMPlatform *pl;
@@ -1982,10 +2012,11 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
 				path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6";
 		}
 		g_assert (nm_platform_sysctl_set (pl, path, nm_sprintf_buf (sbuf, "%d", j)));
-		g_assert_cmpstr (nm_platform_sysctl_get (pl, path), ==, nm_sprintf_buf (sbuf, "%d", j));
+		_sysctl_assert_eq (pl, path, nm_sprintf_buf (sbuf, "%d", j));
 	}
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"), ==, NULL);
-	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"), ==, NULL);
+
+	_sysctl_assert_eq (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6", NULL);
+	_sysctl_assert_eq (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6", NULL);
 
 	/* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and
 	 * skip asserts that are known to fail. */
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index d77170b3..9d4f69de 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -22,7 +22,7 @@
 
 #include "nmp-object.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 struct {
 	GList *udev_devices;
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index a50392a6..360404e9 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -23,9 +23,9 @@
 #include <linux/rtnetlink.h>
 
 #include "nm-core-utils.h"
-#include "test-common.h"
+#include "nm-platform-utils.h"
 
-#include "nm-test-utils.h"
+#include "test-common.h"
 
 #define DEVICE_NAME "nm-test-device"
 
@@ -178,7 +178,7 @@ test_ip4_route (void)
 	/* Test route listing */
 	routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
 	memset (rts, 0, sizeof (rts));
-	rts[0].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[0].network = gateway;
 	rts[0].plen = 32;
 	rts[0].ifindex = ifindex;
@@ -186,7 +186,7 @@ test_ip4_route (void)
 	rts[0].metric = metric;
 	rts[0].mss = mss;
 	rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK);
-	rts[1].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[1].network = network;
 	rts[1].plen = plen;
 	rts[1].ifindex = ifindex;
@@ -194,7 +194,7 @@ test_ip4_route (void)
 	rts[1].metric = metric;
 	rts[1].mss = mss;
 	rts[1].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE);
-	rts[2].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[2].network = 0;
 	rts[2].plen = 0;
 	rts[2].ifindex = ifindex;
@@ -265,21 +265,21 @@ test_ip6_route (void)
 	/* Test route listing */
 	routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
 	memset (rts, 0, sizeof (rts));
-	rts[0].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[0].network = gateway;
 	rts[0].plen = 128;
 	rts[0].ifindex = ifindex;
 	rts[0].gateway = in6addr_any;
 	rts[0].metric = nm_utils_ip6_route_metric_normalize (metric);
 	rts[0].mss = mss;
-	rts[1].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[1].network = network;
 	rts[1].plen = plen;
 	rts[1].ifindex = ifindex;
 	rts[1].gateway = gateway;
 	rts[1].metric = nm_utils_ip6_route_metric_normalize (metric);
 	rts[1].mss = mss;
-	rts[2].source = NM_IP_CONFIG_SOURCE_USER;
+	rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	rts[2].network = in6addr_any;
 	rts[2].plen = 0;
 	rts[2].ifindex = ifindex;
diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h
index 21a4c57b..ebe76f1b 100644
--- a/src/platform/wifi/wifi-utils-private.h
+++ b/src/platform/wifi/wifi-utils-private.h
@@ -21,8 +21,6 @@
 #ifndef __WIFI_UTILS_PRIVATE_H__
 #define __WIFI_UTILS_PRIVATE_H__
 
-
-#include "nm-default.h"
 #include "nm-dbus-interface.h"
 #include "wifi-utils.h"
 
diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c
index 4f04041a..b7fe86bb 100644
--- a/src/platform/wifi/wifi-utils.c
+++ b/src/platform/wifi/wifi-utils.c
@@ -21,16 +21,18 @@
 
 #include "nm-default.h"
 
+#include "wifi-utils.h"
+
 #include <sys/stat.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "wifi-utils.h"
 #include "wifi-utils-private.h"
 #include "wifi-utils-nl80211.h"
 #if HAVE_WEXT
 #include "wifi-utils-wext.h"
 #endif
+#include "nm-core-utils.h"
 
 gpointer
 wifi_data_new (const char *iface, int ifindex, gsize len)
@@ -178,19 +180,20 @@ wifi_utils_deinit (WifiData *data)
 }
 
 gboolean
-wifi_utils_is_wifi (const char *iface, const char *sysfs_path)
+wifi_utils_is_wifi (const char *iface)
 {
-	char phy80211_path[255];
+	char phy80211_path[NM_STRLEN ("/sys/class/net/123456789012345/phy80211\0") + 100 /*safety*/];
 	struct stat s;
 
 	g_return_val_if_fail (iface != NULL, FALSE);
 
-	if (sysfs_path) {
-		/* Check for nl80211 sysfs paths */
-		g_snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", sysfs_path);
-		if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR)))
-			return TRUE;
-	}
+	nm_sprintf_buf (phy80211_path,
+	                "/sys/class/net/%s/phy80211",
+	                NM_ASSERT_VALID_PATH_COMPONENT (iface));
+	nm_assert (strlen (phy80211_path) < sizeof (phy80211_path) - 1);
+
+	if ((stat (phy80211_path, &s) == 0 && (s.st_mode & S_IFDIR)))
+		return TRUE;
 
 #if HAVE_WEXT
 	if (wifi_wext_is_wifi (iface))
diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h
index 9555bbee..8e2b93f1 100644
--- a/src/platform/wifi/wifi-utils.h
+++ b/src/platform/wifi/wifi-utils.h
@@ -24,12 +24,11 @@
 
 #include <net/ethernet.h>
 
-#include "nm-default.h"
 #include "nm-dbus-interface.h"
 
 typedef struct WifiData WifiData;
 
-gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path);
+gboolean wifi_utils_is_wifi (const char *iface);
 
 WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan);
 
diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am
index 7d3e503b..3026dab4 100644
--- a/src/ppp-manager/Makefile.am
+++ b/src/ppp-manager/Makefile.am
@@ -22,6 +22,7 @@ nm_pppd_plugin_la_LDFLAGS = -module -avoid-version
 
 nm_pppd_plugin_la_LIBADD = \
 	$(GLIB_LIBS) \
-	-ldl
+	$(DL_LIBS) \
+	$(NULL)
 
 endif
diff --git a/src/ppp-manager/Makefile.in b/src/ppp-manager/Makefile.in
index c80ae6bc..b8215aab 100644
--- a/src/ppp-manager/Makefile.in
+++ b/src/ppp-manager/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/ppp-manager
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -140,7 +141,8 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(pppd_plugindir)"
 LTLIBRARIES = $(pppd_plugin_LTLIBRARIES)
 am__DEPENDENCIES_1 =
-@WITH_PPP_TRUE@nm_pppd_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+@WITH_PPP_TRUE@nm_pppd_plugin_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+@WITH_PPP_TRUE@	$(am__DEPENDENCIES_1)
 am__nm_pppd_plugin_la_SOURCES_DIST = nm-pppd-plugin.c nm-pppd-plugin.h \
 	nm-ppp-status.h
 @WITH_PPP_TRUE@am_nm_pppd_plugin_la_OBJECTS = nm-pppd-plugin.lo
@@ -251,6 +253,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -307,13 +310,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -325,6 +329,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -385,6 +391,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -420,6 +428,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -501,7 +510,8 @@ with_valgrind = @with_valgrind@
 @WITH_PPP_TRUE@nm_pppd_plugin_la_LDFLAGS = -module -avoid-version
 @WITH_PPP_TRUE@nm_pppd_plugin_la_LIBADD = \
 @WITH_PPP_TRUE@	$(GLIB_LIBS) \
-@WITH_PPP_TRUE@	-ldl
+@WITH_PPP_TRUE@	$(DL_LIBS) \
+@WITH_PPP_TRUE@	$(NULL)
 
 all: all-am
 
diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c
index 89a7addf..a51f7cfa 100644
--- a/src/ppp-manager/nm-ppp-manager.c
+++ b/src/ppp-manager/nm-ppp-manager.c
@@ -482,7 +482,7 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
 		address.plen = u32;
 
 	if (address.address && address.plen && address.plen <= 32) {
-		address.source = NM_IP_CONFIG_SOURCE_PPP;
+		address.addr_source = NM_IP_CONFIG_SOURCE_PPP;
 		nm_ip4_config_add_address (config, &address);
 	} else {
 		_LOGE ("invalid IPv4 address received!");
@@ -540,7 +540,7 @@ iid_value_to_ll6_addr (GVariant *dict,
 	out_addr->s6_addr16[0] = htons (0xfe80);
 	memcpy (out_addr->s6_addr + 8, &iid, sizeof (iid));
 	if (out_iid)
-		nm_utils_ipv6_interface_identfier_get_from_addr (out_iid, out_addr);
+		nm_utils_ipv6_interface_identifier_get_from_addr (out_iid, out_addr);
 	return TRUE;
 }
 
@@ -1028,7 +1028,7 @@ nm_ppp_manager_start (NMPPPManager *manager,
 	NMPPPManagerPrivate *priv;
 	NMConnection *connection;
 	NMSettingPpp *s_ppp;
-	gboolean s_ppp_created = FALSE;
+	gs_unref_object NMSettingPpp *s_ppp_free = NULL;
 	NMSettingPppoe *pppoe_setting;
 	NMSettingAdsl *adsl_setting;
 	NMCmdLine *ppp_cmd;
@@ -1056,24 +1056,21 @@ nm_ppp_manager_start (NMPPPManager *manager,
 		nm_utils_modprobe (NULL, FALSE, "ppp_generic", NULL);
 
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
+	g_return_val_if_fail (connection, FALSE);
 
 	s_ppp = nm_connection_get_setting_ppp (connection);
 	if (!s_ppp) {
 		/* If the PPP settings are all default we may not have a PPP setting yet,
 		 * so just make a default one here.
 		 */
-		s_ppp = NM_SETTING_PPP (nm_setting_ppp_new ());
-		s_ppp_created = TRUE;
+		s_ppp = s_ppp_free = NM_SETTING_PPP (nm_setting_ppp_new ());
 	}
-	
+
 	pppoe_setting = nm_connection_get_setting_pppoe (connection);
 	if (pppoe_setting) {
 		/* We can't modify the applied connection's setting, make a copy */
-		if (!s_ppp_created) {
-			s_ppp = NM_SETTING_PPP (nm_setting_duplicate ((NMSetting *) s_ppp));
-			s_ppp_created = TRUE;
-		}
+		if (!s_ppp_free)
+			s_ppp = s_ppp_free = NM_SETTING_PPP (nm_setting_duplicate ((NMSetting *) s_ppp));
 		pppoe_fill_defaults (s_ppp);
 	}
 
@@ -1106,9 +1103,6 @@ nm_ppp_manager_start (NMPPPManager *manager,
 	priv->act_req = g_object_ref (req);
 
 out:
-	if (s_ppp_created)
-		g_object_unref (s_ppp);
-
 	if (ppp_cmd)
 		nm_cmd_line_destroy (ppp_cmd);
 
diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c
index 4c16f1f0..9c47c339 100644
--- a/src/ppp-manager/nm-pppd-plugin.c
+++ b/src/ppp-manager/nm-pppd-plugin.c
@@ -19,7 +19,7 @@
  * Copyright (C) 2008 Red Hat, Inc.
  */
 
-#include "config.h"
+#include <config.h>
 #define ___CONFIG_H__
 
 #include <string.h>
diff --git a/src/rdisc/Makefile.in b/src/rdisc/Makefile.in
index ebf72fb7..68fed001 100644
--- a/src/rdisc/Makefile.in
+++ b/src/rdisc/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/rdisc
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -225,6 +226,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -281,13 +283,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -299,6 +302,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -359,6 +364,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -394,6 +401,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/rdisc/nm-fake-rdisc.c b/src/rdisc/nm-fake-rdisc.c
index 6a1585b3..46f2b08c 100644
--- a/src/rdisc/nm-fake-rdisc.c
+++ b/src/rdisc/nm-fake-rdisc.c
@@ -233,15 +233,20 @@ receive_ra (gpointer user_data)
 	NMFakeRDisc *self = user_data;
 	NMFakeRDiscPrivate *priv = NM_FAKE_RDISC_GET_PRIVATE (self);
 	NMRDisc *rdisc = NM_RDISC (self);
+	NMRDiscDataInternal *rdata = rdisc->rdata;
 	FakeRa *ra = priv->ras->data;
 	NMRDiscConfigMap changed = 0;
 	guint32 now = nm_utils_get_monotonic_timestamp_s ();
 	guint i;
+	NMRDiscDHCPLevel dhcp_level;
 
 	priv->receive_ra_id = 0;
 
-	if (rdisc->dhcp_level != ra->dhcp_level) {
-		rdisc->dhcp_level = ra->dhcp_level;
+	/* preserve the "most managed" level  on updates. */
+	dhcp_level = MAX (rdata->public.dhcp_level, ra->dhcp_level);
+
+	if (rdata->public.dhcp_level != dhcp_level) {
+		rdata->public.dhcp_level = dhcp_level;
 		changed |= NM_RDISC_CONFIG_DHCP_LEVEL;
 	}
 
@@ -263,6 +268,8 @@ receive_ra (gpointer user_data)
 			.preference = item->preference,
 		};
 
+		g_assert (route.plen > 0 && route.plen <= 128);
+
 		if (nm_rdisc_add_route (rdisc, &route))
 			changed |= NM_RDISC_CONFIG_ROUTES;
 
@@ -294,13 +301,13 @@ receive_ra (gpointer user_data)
 			changed |= NM_RDISC_CONFIG_DNS_DOMAINS;
 	}
 
-	if (rdisc->mtu != ra->mtu) {
-		rdisc->mtu = ra->mtu;
+	if (rdata->public.mtu != ra->mtu) {
+		rdata->public.mtu = ra->mtu;
 		changed |= NM_RDISC_CONFIG_MTU;
 	}
 
-	if (rdisc->hop_limit != ra->hop_limit) {
-		rdisc->hop_limit = ra->hop_limit;
+	if (rdata->public.hop_limit != ra->hop_limit) {
+		rdata->public.hop_limit = ra->hop_limit;
 		changed |= NM_RDISC_CONFIG_HOP_LIMIT;
 	}
 
@@ -344,15 +351,10 @@ nm_fake_rdisc_emit_new_ras (NMFakeRDisc *self)
 NMRDisc *
 nm_fake_rdisc_new (int ifindex, const char *ifname)
 {
-	NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL);
-
-	rdisc->ifindex = ifindex;
-	rdisc->ifname = g_strdup (ifname);
-	rdisc->max_addresses = NM_RDISC_MAX_ADDRESSES_DEFAULT;
-	rdisc->rtr_solicitations = NM_RDISC_RTR_SOLICITATIONS_DEFAULT;
-	rdisc->rtr_solicitation_interval = NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT;
-
-	return rdisc;
+	return g_object_new (NM_TYPE_FAKE_RDISC,
+	                     NM_RDISC_IFINDEX, ifindex,
+	                     NM_RDISC_IFNAME, ifname,
+	                     NULL);
 }
 
 static void
@@ -385,10 +387,10 @@ nm_fake_rdisc_class_init (NMFakeRDiscClass *klass)
 	rdisc_class->start = start;
 	rdisc_class->send_rs = send_rs;
 
-	signals[RS_SENT] = g_signal_new (
-			NM_FAKE_RDISC_RS_SENT,
-			G_OBJECT_CLASS_TYPE (klass),
-			G_SIGNAL_RUN_FIRST,
-			0,  NULL, NULL, NULL,
-			G_TYPE_NONE, 0);
+	signals[RS_SENT] =
+	    g_signal_new (NM_FAKE_RDISC_RS_SENT,
+	                  G_OBJECT_CLASS_TYPE (klass),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0,  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 }
diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c
index 0c5971f4..12c2e30d 100644
--- a/src/rdisc/nm-lndp-rdisc.c
+++ b/src/rdisc/nm-lndp-rdisc.c
@@ -20,39 +20,65 @@
 
 #include "nm-default.h"
 
+#include "nm-lndp-rdisc.h"
+
 #include <string.h>
 #include <arpa/inet.h>
 /* stdarg.h included because of a bug in ndp.h */
 #include <stdarg.h>
 #include <ndp.h>
 
-#include "nm-lndp-rdisc.h"
 #include "nm-rdisc-private.h"
-
 #include "NetworkManagerUtils.h"
 #include "nm-platform.h"
 #include "nmp-netns.h"
 
 #define _NMLOG_PREFIX_NAME                "rdisc-lndp"
 
+/*****************************************************************************/
+
 typedef struct {
 	struct ndp *ndp;
 
 	GIOChannel *event_channel;
 	guint event_id;
 	guint ra_timeout_id;  /* first RA timeout */
-} NMLNDPRDiscPrivate;
+} NMLndpRDiscPrivate;
+
+/*****************************************************************************/
 
-#define NM_LNDP_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LNDP_RDISC, NMLNDPRDiscPrivate))
+struct _NMLndpRDisc {
+	NMRDisc parent;
+	NMLndpRDiscPrivate _priv;
+};
 
-G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC)
+struct _NMLndpRDiscClass {
+	NMRDiscClass parent;
+};
 
-/******************************************************************/
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMLndpRDisc, nm_lndp_rdisc, NM_TYPE_RDISC)
+
+#define NM_LNDP_RDISC_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMLndpRDisc *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_LNDP_RDISC (_self)); \
+		&_self->_priv; \
+	})
+
+/*****************************************************************************/
 
 static gboolean
 send_rs (NMRDisc *rdisc, GError **error)
 {
-	NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+	NMLndpRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE ((NMLndpRDisc *) rdisc);
 	struct ndp_msg *msg;
 	int errsv;
 
@@ -63,7 +89,7 @@ send_rs (NMRDisc *rdisc, GError **error)
 		                     "cannot create router solicitation");
 		return FALSE;
 	}
-	ndp_msg_ifindex_set (msg, rdisc->ifindex);
+	ndp_msg_ifindex_set (msg, nm_rdisc_get_ifindex (rdisc));
 
 	errsv = ndp_msg_send (priv->ndp, msg);
 	ndp_msg_destroy (msg);
@@ -89,6 +115,7 @@ static int
 receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 {
 	NMRDisc *rdisc = (NMRDisc *) user_data;
+	NMRDiscDataInternal *rdata = rdisc->rdata;
 	NMRDiscConfigMap changed = 0;
 	struct ndp_msgra *msgra = ndp_msgra (msg);
 	struct in6_addr gateway_addr;
@@ -125,8 +152,14 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 		else
 			dhcp_level = NM_RDISC_DHCP_LEVEL_NONE;
 
-		if (dhcp_level != rdisc->dhcp_level) {
-			rdisc->dhcp_level = dhcp_level;
+		/* when receiving multiple RA (possibly from different routers),
+		 * let's keep the "most managed" level. */
+		G_STATIC_ASSERT_EXPR (NM_RDISC_DHCP_LEVEL_MANAGED > NM_RDISC_DHCP_LEVEL_OTHERCONF);
+		G_STATIC_ASSERT_EXPR (NM_RDISC_DHCP_LEVEL_OTHERCONF > NM_RDISC_DHCP_LEVEL_NONE);
+		dhcp_level = MAX (dhcp_level, rdata->public.dhcp_level);
+
+		if (dhcp_level != rdata->public.dhcp_level) {
+			rdata->public.dhcp_level = dhcp_level;
 			changed |= NM_RDISC_CONFIG_DHCP_LEVEL;
 		}
 	}
@@ -255,8 +288,8 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 	}
 
 	hop_limit = ndp_msgra_curhoplimit (msgra);
-	if (rdisc->hop_limit != hop_limit) {
-		rdisc->hop_limit = hop_limit;
+	if (rdata->public.hop_limit != hop_limit) {
+		rdata->public.hop_limit = hop_limit;
 		changed |= NM_RDISC_CONFIG_HOP_LIMIT;
 	}
 
@@ -264,8 +297,10 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 	ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_MTU) {
 		guint32 mtu = ndp_msg_opt_mtu(msg, offset);
 		if (mtu >= 1280) {
-			rdisc->mtu = mtu;
-			changed |= NM_RDISC_CONFIG_MTU;
+			if (rdata->public.mtu != mtu) {
+				rdata->public.mtu = mtu;
+				changed |= NM_RDISC_CONFIG_MTU;
+			}
 		} else {
 			/* All sorts of bad things would happen if we accepted this.
 			 * Kernel would set it, but would flush out all IPv6 addresses away
@@ -283,7 +318,7 @@ static gboolean
 event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
-	NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+	NMLndpRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE ((NMLndpRDisc *) rdisc);
 
 	_LOGD ("processing libndp events");
 
@@ -297,7 +332,7 @@ event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
 static void
 start (NMRDisc *rdisc)
 {
-	NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+	NMLndpRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE ((NMLndpRDisc *) rdisc);
 	int fd = ndp_get_eventfd (priv->ndp);
 
 	priv->event_channel = g_io_channel_unix_new (fd);
@@ -306,28 +341,39 @@ start (NMRDisc *rdisc)
 	/* Flush any pending messages to avoid using obsolete information */
 	event_ready (priv->event_channel, 0, rdisc);
 
-	ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, rdisc->ifindex, rdisc);
+	ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, nm_rdisc_get_ifindex (rdisc), rdisc);
 }
 
-/******************************************************************/
+/*****************************************************************************/
 
-static inline gint32
-ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, gint32 defval)
+static inline int
+ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, int min, int max, int defval)
+{
+	return (int) nm_platform_sysctl_get_int_checked (platform,
+	                                                 nm_utils_ip6_property_path (ifname, property),
+	                                                 10,
+	                                                 min,
+	                                                 max,
+	                                                 defval);
+}
+
+static void
+nm_lndp_rdisc_init (NMLndpRDisc *lndp_rdisc)
 {
-	return nm_platform_sysctl_get_int32 (platform, nm_utils_ip6_property_path (ifname, property), defval);
 }
 
 NMRDisc *
 nm_lndp_rdisc_new (NMPlatform *platform,
                    int ifindex,
                    const char *ifname,
-                   const char *uuid,
+                   NMUtilsStableType stable_type,
+                   const char *network_id,
                    NMSettingIP6ConfigAddrGenMode addr_gen_mode,
                    GError **error)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDisc *rdisc;
-	NMLNDPRDiscPrivate *priv;
+	NMLndpRDiscPrivate *priv;
 	int errsv;
 
 	g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL);
@@ -338,21 +384,23 @@ nm_lndp_rdisc_new (NMPlatform *platform,
 
 	rdisc = g_object_new (NM_TYPE_LNDP_RDISC,
 	                      NM_RDISC_PLATFORM, platform,
+	                      NM_RDISC_STABLE_TYPE, (int) stable_type,
+	                      NM_RDISC_IFINDEX, ifindex,
+	                      NM_RDISC_IFNAME, ifname,
+	                      NM_RDISC_NETWORK_ID, network_id,
+	                      NM_RDISC_ADDR_GEN_MODE, (int) addr_gen_mode,
+	                      NM_RDISC_MAX_ADDRESSES, ipv6_sysctl_get (platform, ifname,
+	                                                               "max_addresses",
+	                                                               0, G_MAXINT32, NM_RDISC_MAX_ADDRESSES_DEFAULT),
+	                      NM_RDISC_ROUTER_SOLICITATIONS, ipv6_sysctl_get (platform, ifname,
+	                                                                      "router_solicitations",
+	                                                                      1, G_MAXINT32, NM_RDISC_ROUTER_SOLICITATIONS_DEFAULT),
+	                      NM_RDISC_ROUTER_SOLICITATION_INTERVAL, ipv6_sysctl_get (platform, ifname,
+	                                                                              "router_solicitation_interval",
+	                                                                              1, G_MAXINT32, NM_RDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT),
 	                      NULL);
 
-	rdisc->ifindex = ifindex;
-	rdisc->ifname = g_strdup (ifname);
-	rdisc->uuid = g_strdup (uuid);
-	rdisc->addr_gen_mode = addr_gen_mode;
-
-	rdisc->max_addresses = ipv6_sysctl_get (platform, ifname, "max_addresses",
-	                                        NM_RDISC_MAX_ADDRESSES_DEFAULT);
-	rdisc->rtr_solicitations = ipv6_sysctl_get (platform, ifname, "router_solicitations",
-	                                            NM_RDISC_RTR_SOLICITATIONS_DEFAULT);
-	rdisc->rtr_solicitation_interval = ipv6_sysctl_get (platform, ifname, "router_solicitation_interval",
-	                                                    NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT);
-
-	priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+	priv = NM_LNDP_RDISC_GET_PRIVATE ((NMLndpRDisc *) rdisc);
 
 	errsv = ndp_open (&priv->ndp);
 
@@ -368,21 +416,16 @@ nm_lndp_rdisc_new (NMPlatform *platform,
 }
 
 static void
-nm_lndp_rdisc_init (NMLNDPRDisc *lndp_rdisc)
-{
-}
-
-static void
 dispose (GObject *object)
 {
-	NMLNDPRDisc *rdisc = NM_LNDP_RDISC (object);
-	NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+	NMRDisc *rdisc = (NMRDisc *) object;
+	NMLndpRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE ((NMLndpRDisc *) rdisc);
 
 	nm_clear_g_source (&priv->event_id);
 	g_clear_pointer (&priv->event_channel, g_io_channel_unref);
 
 	if (priv->ndp) {
-		ndp_msgrcv_handler_unregister (priv->ndp, receive_ra, NDP_MSG_RA, NM_RDISC (rdisc)->ifindex, rdisc);
+		ndp_msgrcv_handler_unregister (priv->ndp, receive_ra, NDP_MSG_RA, nm_rdisc_get_ifindex (rdisc), rdisc);
 		ndp_close (priv->ndp);
 		priv->ndp = NULL;
 	}
@@ -391,13 +434,11 @@ dispose (GObject *object)
 }
 
 static void
-nm_lndp_rdisc_class_init (NMLNDPRDiscClass *klass)
+nm_lndp_rdisc_class_init (NMLndpRDiscClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMRDiscClass *rdisc_class = NM_RDISC_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (NMLNDPRDiscPrivate));
-
 	object_class->dispose = dispose;
 	rdisc_class->start = start;
 	rdisc_class->send_rs = send_rs;
diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h
index 4c7c4743..e2b47c04 100644
--- a/src/rdisc/nm-lndp-rdisc.h
+++ b/src/rdisc/nm-lndp-rdisc.h
@@ -22,32 +22,25 @@
 #define __NETWORKMANAGER_LNDP_RDISC_H__
 
 #include "nm-rdisc.h"
+#include "nm-core-utils.h"
 
 #define NM_TYPE_LNDP_RDISC            (nm_lndp_rdisc_get_type ())
-#define NM_LNDP_RDISC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LNDP_RDISC, NMLNDPRDisc))
-#define NM_LNDP_RDISC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LNDP_RDISC, NMLNDPRDiscClass))
+#define NM_LNDP_RDISC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LNDP_RDISC, NMLndpRDisc))
+#define NM_LNDP_RDISC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_LNDP_RDISC, NMLndpRDiscClass))
 #define NM_IS_LNDP_RDISC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_LNDP_RDISC))
 #define NM_IS_LNDP_RDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_LNDP_RDISC))
-#define NM_LNDP_RDISC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LNDP_RDISC, NMLNDPRDiscClass))
+#define NM_LNDP_RDISC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_LNDP_RDISC, NMLndpRDiscClass))
 
-/******************************************************************/
-
-typedef struct {
-	NMRDisc parent;
-} NMLNDPRDisc;
-
-typedef struct {
-	NMRDiscClass parent;
-} NMLNDPRDiscClass;
-
-/******************************************************************/
+typedef struct _NMLndpRDisc NMLndpRDisc;
+typedef struct _NMLndpRDiscClass NMLndpRDiscClass;
 
 GType nm_lndp_rdisc_get_type (void);
 
 NMRDisc *nm_lndp_rdisc_new (NMPlatform *platform,
                             int ifindex,
                             const char *ifname,
-                            const char *uuid,
+                            NMUtilsStableType stable_type,
+                            const char *network_id,
                             NMSettingIP6ConfigAddrGenMode addr_gen_mode,
                             GError **error);
 
diff --git a/src/rdisc/nm-rdisc-private.h b/src/rdisc/nm-rdisc-private.h
index c0ec739a..abc5a399 100644
--- a/src/rdisc/nm-rdisc-private.h
+++ b/src/rdisc/nm-rdisc-private.h
@@ -25,6 +25,17 @@
 
 /* Functions only used by rdisc implementations */
 
+struct _NMRDiscDataInternal {
+	NMRDiscData public;
+	GArray *gateways;
+	GArray *addresses;
+	GArray *routes;
+	GArray *dns_servers;
+	GArray *dns_domains;
+};
+
+typedef struct _NMRDiscDataInternal NMRDiscDataInternal;
+
 void nm_rdisc_ra_received (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed);
 
 gboolean nm_rdisc_add_gateway              (NMRDisc *rdisc, const NMRDiscGateway *new);
@@ -44,19 +55,20 @@ gboolean nm_rdisc_add_dns_domain           (NMRDisc *rdisc, const NMRDiscDNSDoma
         const NMLogDomain __domain = (domain); \
         \
         if (nm_logging_enabled (__level, __domain)) { \
+            NMRDisc *const __self = (self); \
             char __prefix[64]; \
-            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
-            const NMRDisc *const __self = (self); \
             \
-            if (__self) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p,%s%s%s]", \
-                            _NMLOG_PREFIX_NAME, __self, \
-                            NM_PRINT_FMT_QUOTE_STRING (__self->ifname)); \
-                __p_prefix = __prefix; \
-            } \
             _nm_log (__level, __domain, 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+                     (__self \
+                        ? ({ \
+                            const char *__ifname = nm_rdisc_get_ifname (__self); \
+                            nm_sprintf_buf (__prefix, "%s[%p,%s%s%s]", \
+                                            _NMLOG_PREFIX_NAME, __self, \
+                                            NM_PRINT_FMT_QUOTE_STRING (__ifname)); \
+                            }) \
+                        : _NMLOG_PREFIX_NAME) \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
 
diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c
index 12e3962b..cf993bc3 100644
--- a/src/rdisc/nm-rdisc.c
+++ b/src/rdisc/nm-rdisc.c
@@ -20,36 +20,61 @@
 
 #include "nm-default.h"
 
+#include "nm-rdisc.h"
+
 #include <stdlib.h>
 #include <arpa/inet.h>
 #include <string.h>
 
-#include "nm-rdisc.h"
-#include "nm-rdisc-private.h"
+#include "nm-setting-ip6-config.h"
 
+#include "nm-rdisc-private.h"
 #include "nm-utils.h"
 #include "nm-platform.h"
 #include "nmp-netns.h"
 
-#include <nm-setting-ip6-config.h>
-
 #define _NMLOG_PREFIX_NAME                "rdisc"
 
-typedef struct {
-	int solicitations_left;
+/*****************************************************************************/
+
+struct _NMRDiscPrivate {
+	/* this *must* be the first field. */
+	NMRDiscDataInternal rdata;
+
+	gint32 solicitations_left;
 	guint send_rs_id;
-	gint64 last_rs;
+	gint32 last_rs;
 	guint ra_timeout_id;  /* first RA timeout */
 	guint timeout_id;   /* prefix/dns/etc lifetime timeout */
 	char *last_send_rs_error;
-} NMRDiscPrivate;
-
-#define NM_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_RDISC, NMRDiscPrivate))
+	NMUtilsIPv6IfaceId iid;
+
+	/* immutable values: */
+	int ifindex;
+	char *ifname;
+	char *network_id;
+	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+	NMUtilsStableType stable_type;
+	gint32 max_addresses;
+	gint32 router_solicitations;
+	gint32 router_solicitation_interval;
+
+	NMPlatform *platform;
+	NMPNetns *netns;
+};
 
-G_DEFINE_TYPE (NMRDisc, nm_rdisc, G_TYPE_OBJECT)
+typedef struct _NMRDiscPrivate NMRDiscPrivate;
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_PLATFORM,
+	PROP_IFINDEX,
+	PROP_IFNAME,
+	PROP_STABLE_TYPE,
+	PROP_NETWORK_ID,
+	PROP_ADDR_GEN_MODE,
+	PROP_MAX_ADDRESSES,
+	PROP_ROUTER_SOLICITATIONS,
+	PROP_ROUTER_SOLICITATION_INTERVAL,
 );
 
 enum {
@@ -60,49 +85,120 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-/******************************************************************/
+G_DEFINE_TYPE (NMRDisc, nm_rdisc, G_TYPE_OBJECT)
+
+#define NM_RDISC_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMRDisc *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_RDISC (_self)); \
+		_self->_priv; \
+	})
+
+/*****************************************************************************/
+
+static void _config_changed_log (NMRDisc *rdisc, NMRDiscConfigMap changed);
+
+/*****************************************************************************/
 
 NMPNetns *
 nm_rdisc_netns_get (NMRDisc *self)
 {
 	g_return_val_if_fail (NM_IS_RDISC (self), NULL);
 
-	return self->_netns;
+	return NM_RDISC_GET_PRIVATE (self)->netns;
 }
 
 gboolean
 nm_rdisc_netns_push (NMRDisc *self, NMPNetns **netns)
 {
+	NMRDiscPrivate *priv;
+
 	g_return_val_if_fail (NM_IS_RDISC (self), FALSE);
 
-	if (   self->_netns
-	    && !nmp_netns_push (self->_netns)) {
+	priv = NM_RDISC_GET_PRIVATE (self);
+	if (   priv->netns
+	    && !nmp_netns_push (priv->netns)) {
 		NM_SET_OUT (netns, NULL);
 		return FALSE;
 	}
 
-	NM_SET_OUT (netns, self->_netns);
+	NM_SET_OUT (netns, priv->netns);
 	return TRUE;
 }
 
-/******************************************************************/
+/*****************************************************************************/
+
+int
+nm_rdisc_get_ifindex (NMRDisc *self)
+{
+	g_return_val_if_fail (NM_IS_RDISC (self), 0);
+
+	return NM_RDISC_GET_PRIVATE (self)->ifindex;
+}
+
+const char *
+nm_rdisc_get_ifname (NMRDisc *self)
+{
+	g_return_val_if_fail (NM_IS_RDISC (self), NULL);
+
+	return NM_RDISC_GET_PRIVATE (self)->ifname;
+}
+
+/*****************************************************************************/
+
+static const NMRDiscData *
+_data_complete (NMRDiscDataInternal *data)
+{
+#define _SET(data, field) \
+	G_STMT_START { \
+		if ((data->public.field##_n = data->field->len) > 0) \
+			data->public.field = (gpointer) data->field->data; \
+		else \
+			data->public.field = NULL; \
+	} G_STMT_END
+	_SET (data, gateways);
+	_SET (data, addresses);
+	_SET (data, routes);
+	_SET (data, dns_servers);
+	_SET (data, dns_domains);
+#undef _SET
+	return &data->public;
+}
+
+static void
+_emit_config_change (NMRDisc *self, NMRDiscConfigMap changed)
+{
+	_config_changed_log (self, changed);
+	g_signal_emit (self, signals[CONFIG_CHANGED], 0,
+	               _data_complete (&NM_RDISC_GET_PRIVATE (self)->rdata),
+	               (guint) changed);
+}
+
+/*****************************************************************************/
 
 gboolean
 nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
 {
+	NMRDiscDataInternal *rdata = &NM_RDISC_GET_PRIVATE(rdisc)->rdata;
 	int i, insert_idx = -1;
 
-	for (i = 0; i < rdisc->gateways->len; i++) {
-		NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i);
+	for (i = 0; i < rdata->gateways->len; i++) {
+		NMRDiscGateway *item = &g_array_index (rdata->gateways, NMRDiscGateway, i);
 
 		if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
 			if (new->lifetime == 0) {
-				g_array_remove_index (rdisc->gateways, i--);
+				g_array_remove_index (rdata->gateways, i--);
 				return TRUE;
 			}
 
 			if (item->preference != new->preference) {
-				g_array_remove_index (rdisc->gateways, i--);
+				g_array_remove_index (rdata->gateways, i--);
 				continue;
 			}
 
@@ -116,7 +212,7 @@ nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
 	}
 
 	if (new->lifetime)
-		g_array_insert_val (rdisc->gateways, MAX (insert_idx, 0), *new);
+		g_array_insert_val (rdata->gateways, MAX (insert_idx, 0), *new);
 	return !!new->lifetime;
 }
 
@@ -137,12 +233,17 @@ nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
 static gboolean
 complete_address (NMRDisc *rdisc, NMRDiscAddress *addr)
 {
+	NMRDiscPrivate *priv;
 	GError *error = NULL;
 
-	if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
-		if (!nm_utils_ipv6_addr_set_stable_privacy (&addr->address,
-		                                            rdisc->ifname,
-		                                            rdisc->uuid,
+	g_return_val_if_fail (NM_IS_RDISC (rdisc), FALSE);
+
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		if (!nm_utils_ipv6_addr_set_stable_privacy (priv->stable_type,
+		                                            &addr->address,
+		                                            priv->ifname,
+		                                            priv->network_id,
 		                                            addr->dad_counter++,
 		                                            &error)) {
 			_LOGW ("complete-address: failed to generate an stable-privacy address: %s",
@@ -154,14 +255,14 @@ complete_address (NMRDisc *rdisc, NMRDiscAddress *addr)
 		return TRUE;
 	}
 
-	if (!rdisc->iid.id) {
+	if (!priv->iid.id) {
 		_LOGW ("complete-address: can't generate an EUI-64 address: no interface identifier");
 		return FALSE;
 	}
 
 	if (addr->address.s6_addr32[2] == 0x0 && addr->address.s6_addr32[3] == 0x0) {
 		_LOGD ("complete-address: adding an EUI-64 address");
-		nm_utils_ipv6_addr_set_interface_identfier (&addr->address, rdisc->iid);
+		nm_utils_ipv6_addr_set_interface_identifier (&addr->address, priv->iid);
 		return TRUE;
 	}
 
@@ -172,19 +273,24 @@ complete_address (NMRDisc *rdisc, NMRDiscAddress *addr)
 gboolean
 nm_rdisc_complete_and_add_address (NMRDisc *rdisc, NMRDiscAddress *new)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
 	int i;
 
 	if (!complete_address (rdisc, new))
 		return FALSE;
 
-	for (i = 0; i < rdisc->addresses->len; i++) {
-		NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
+
+	for (i = 0; i < rdata->addresses->len; i++) {
+		NMRDiscAddress *item = &g_array_index (rdata->addresses, NMRDiscAddress, i);
 
 		if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
 			gboolean changed;
 
 			if (new->lifetime == 0) {
-				g_array_remove_index (rdisc->addresses, i--);
+				g_array_remove_index (rdata->addresses, i--);
 				return TRUE;
 			}
 
@@ -199,33 +305,46 @@ nm_rdisc_complete_and_add_address (NMRDisc *rdisc, NMRDiscAddress *new)
 	 * what the kernel does, because it considers *all* addresses (including
 	 * static and other temporary addresses).
 	 **/
-	if (rdisc->max_addresses && rdisc->addresses->len >= rdisc->max_addresses)
+	if (priv->max_addresses && rdata->addresses->len >= priv->max_addresses)
 		return FALSE;
 
 	if (new->lifetime)
-		g_array_insert_val (rdisc->addresses, i, *new);
+		g_array_insert_val (rdata->addresses, i, *new);
 	return !!new->lifetime;
 }
 
 gboolean
 nm_rdisc_add_route (NMRDisc *rdisc, const NMRDiscRoute *new)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
 	int i, insert_idx = -1;
 
-	if (new->plen == 0 || new->plen > 128)
-		return FALSE;
+	if (new->plen == 0 || new->plen > 128) {
+		/* Only expect non-default routes.  The router has no idea what the
+		 * local configuration or user preferences are, so sending routes
+		 * with a prefix length of 0 must be ignored by NMRDisc.
+		 *
+		 * Also, upper layers also don't expect that NMRDisc exposes routes
+		 * with a plen or zero or larger then 128.
+		 */
+		g_return_val_if_reached (FALSE);
+	}
+
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
 
-	for (i = 0; i < rdisc->routes->len; i++) {
-		NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+	for (i = 0; i < rdata->routes->len; i++) {
+		NMRDiscRoute *item = &g_array_index (rdata->routes, NMRDiscRoute, i);
 
 		if (IN6_ARE_ADDR_EQUAL (&item->network, &new->network) && item->plen == new->plen) {
 			if (new->lifetime == 0) {
-				g_array_remove_index (rdisc->routes, i--);
+				g_array_remove_index (rdata->routes, i--);
 				return TRUE;
 			}
 
 			if (item->preference != new->preference) {
-				g_array_remove_index (rdisc->routes, i--);
+				g_array_remove_index (rdata->routes, i--);
 				continue;
 			}
 
@@ -239,21 +358,26 @@ nm_rdisc_add_route (NMRDisc *rdisc, const NMRDiscRoute *new)
 	}
 
 	if (new->lifetime)
-		g_array_insert_val (rdisc->routes, CLAMP (insert_idx, 0, G_MAXINT), *new);
+		g_array_insert_val (rdata->routes, CLAMP (insert_idx, 0, G_MAXINT), *new);
 	return !!new->lifetime;
 }
 
 gboolean
 nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
 	int i;
 
-	for (i = 0; i < rdisc->dns_servers->len; i++) {
-		NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i);
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
+
+	for (i = 0; i < rdata->dns_servers->len; i++) {
+		NMRDiscDNSServer *item = &g_array_index (rdata->dns_servers, NMRDiscDNSServer, i);
 
 		if (IN6_ARE_ADDR_EQUAL (&item->address, &new->address)) {
 			if (new->lifetime == 0) {
-				g_array_remove_index (rdisc->dns_servers, i);
+				g_array_remove_index (rdata->dns_servers, i);
 				return TRUE;
 			}
 			if (item->timestamp != new->timestamp || item->lifetime != new->lifetime) {
@@ -265,7 +389,7 @@ nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new)
 	}
 
 	if (new->lifetime)
-		g_array_insert_val (rdisc->dns_servers, i, *new);
+		g_array_insert_val (rdata->dns_servers, i, *new);
 	return !!new->lifetime;
 }
 
@@ -273,17 +397,22 @@ nm_rdisc_add_dns_server (NMRDisc *rdisc, const NMRDiscDNSServer *new)
 gboolean
 nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
 	NMRDiscDNSDomain *item;
 	int i;
 
-	for (i = 0; i < rdisc->dns_domains->len; i++) {
-		item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
+
+	for (i = 0; i < rdata->dns_domains->len; i++) {
+		item = &g_array_index (rdata->dns_domains, NMRDiscDNSDomain, i);
 
 		if (!g_strcmp0 (item->domain, new->domain)) {
 			gboolean changed;
 
 			if (new->lifetime == 0) {
-				g_array_remove_index (rdisc->dns_domains, i);
+				g_array_remove_index (rdata->dns_domains, i);
 				return TRUE;
 			}
 
@@ -298,8 +427,8 @@ nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
 	}
 
 	if (new->lifetime) {
-		g_array_insert_val (rdisc->dns_domains, i, *new);
-		item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
+		g_array_insert_val (rdata->dns_domains, i, *new);
+		item = &g_array_index (rdata->dns_domains, NMRDiscDNSDomain, i);
 		item->domain = g_strdup (new->domain);
 	}
 	return !!new->lifetime;
@@ -329,18 +458,24 @@ nm_rdisc_add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
 gboolean
 nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
+
 	g_return_val_if_fail (NM_IS_RDISC (rdisc), FALSE);
 
-	if (rdisc->iid.id != iid.id) {
-		rdisc->iid = iid;
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
 
-		if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)
+	if (priv->iid.id != iid.id) {
+		priv->iid = iid;
+
+		if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY)
 			return FALSE;
 
-		if (rdisc->addresses->len) {
+		if (rdata->addresses->len) {
 			_LOGD ("IPv6 interface identifier changed, flushing addresses");
-			g_array_remove_range (rdisc->addresses, 0, rdisc->addresses->len);
-			g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES);
+			g_array_remove_range (rdata->addresses, 0, rdata->addresses->len);
+			_emit_config_change (rdisc, NM_RDISC_CONFIG_ADDRESSES);
 		}
 		return TRUE;
 	}
@@ -349,13 +484,15 @@ nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
 }
 
 static gboolean
-send_rs (NMRDisc *rdisc)
+send_rs_timeout (NMRDisc *rdisc)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc);
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
 	GError *error = NULL;
 
+	priv->send_rs_id = 0;
+
 	if (!nm_rdisc_netns_push (rdisc, &netns))
 		return G_SOURCE_REMOVE;
 
@@ -379,13 +516,12 @@ send_rs (NMRDisc *rdisc)
 	priv->last_rs = nm_utils_get_monotonic_timestamp_s ();
 	if (priv->solicitations_left > 0) {
 		_LOGD ("scheduling router solicitation retry in %d seconds.",
-		       rdisc->rtr_solicitation_interval);
-		priv->send_rs_id = g_timeout_add_seconds (rdisc->rtr_solicitation_interval,
-		                                          (GSourceFunc) send_rs, rdisc);
+		       (int) priv->router_solicitation_interval);
+		priv->send_rs_id = g_timeout_add_seconds (priv->router_solicitation_interval,
+		                                          (GSourceFunc) send_rs_timeout, rdisc);
 	} else {
 		_LOGD ("did not receive a router advertisement after %d solicitations.",
-		       rdisc->rtr_solicitations);
-		priv->send_rs_id = 0;
+		       (int) priv->router_solicitations);
 	}
 
 	return G_SOURCE_REMOVE;
@@ -395,17 +531,20 @@ static void
 solicit (NMRDisc *rdisc)
 {
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
-	guint32 now = nm_utils_get_monotonic_timestamp_s ();
-	gint64 next;
+	gint64 next, now;
+
+	if (priv->send_rs_id)
+		return;
 
-	if (!priv->send_rs_id) {
-		priv->solicitations_left = rdisc->rtr_solicitations;
+	now = nm_utils_get_monotonic_timestamp_s ();
 
-		next = CLAMP (priv->last_rs + rdisc->rtr_solicitation_interval - now, 0, G_MAXINT32);
-		_LOGD ("scheduling explicit router solicitation request in %" G_GINT64_FORMAT " seconds.",
-		       next);
-		priv->send_rs_id = g_timeout_add_seconds ((guint32) next, (GSourceFunc) send_rs, rdisc);
-	}
+	priv->solicitations_left = priv->router_solicitations;
+
+	next = (((gint64) priv->last_rs) + priv->router_solicitation_interval) - now;
+	next = CLAMP (next, 0, G_MAXINT32);
+	_LOGD ("scheduling explicit router solicitation request in %" G_GINT64_FORMAT " seconds.",
+	       next);
+	priv->send_rs_id = g_timeout_add_seconds ((guint32) next, (GSourceFunc) send_rs_timeout, rdisc);
 }
 
 static gboolean
@@ -414,7 +553,7 @@ rdisc_ra_timeout_cb (gpointer user_data)
 	NMRDisc *rdisc = NM_RDISC (user_data);
 
 	NM_RDISC_GET_PRIVATE (rdisc)->ra_timeout_id = 0;
-	g_signal_emit_by_name (rdisc, NM_RDISC_RA_TIMEOUT);
+	g_signal_emit (rdisc, signals[RA_TIMEOUT], 0);
 	return G_SOURCE_REMOVE;
 }
 
@@ -424,19 +563,20 @@ nm_rdisc_start (NMRDisc *rdisc)
 	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
 	NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc);
-	guint ra_wait_secs;
+	gint64 ra_wait_secs;
 
 	g_assert (klass->start);
 
-	_LOGD ("starting router discovery: %d", rdisc->ifindex);
+	_LOGD ("starting router discovery: %d", priv->ifindex);
 
 	if (!nm_rdisc_netns_push (rdisc, &netns))
 		return;
 
 	nm_clear_g_source (&priv->ra_timeout_id);
-	ra_wait_secs = CLAMP (rdisc->rtr_solicitations * rdisc->rtr_solicitation_interval, 30, 120);
+	ra_wait_secs = (((gint64) priv->router_solicitations) * priv->router_solicitation_interval) + 1;
+	ra_wait_secs = CLAMP (ra_wait_secs, 30, 120);
 	priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, rdisc_ra_timeout_cb, rdisc);
-	_LOGD ("scheduling RA timeout in %d seconds", ra_wait_secs);
+	_LOGD ("scheduling RA timeout in %d seconds", (int) ra_wait_secs);
 
 	if (klass->start)
 		klass->start (rdisc);
@@ -447,23 +587,26 @@ nm_rdisc_start (NMRDisc *rdisc)
 void
 nm_rdisc_dad_failed (NMRDisc *rdisc, struct in6_addr *address)
 {
+	NMRDiscDataInternal *rdata;
 	int i;
 	gboolean changed = FALSE;
 
-	for (i = 0; i < rdisc->addresses->len; i++) {
-		NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
+
+	for (i = 0; i < rdata->addresses->len; i++) {
+		NMRDiscAddress *item = &g_array_index (rdata->addresses, NMRDiscAddress, i);
 
 		if (!IN6_ARE_ADDR_EQUAL (&item->address, address))
 			continue;
 
 		_LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, NULL));
 		if (!complete_address (rdisc, item))
-			g_array_remove_index (rdisc->addresses, i--);
+			g_array_remove_index (rdata->addresses, i--);
 		changed = TRUE;
 	}
 
 	if (changed)
-		g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES);
+		_emit_config_change (rdisc, NM_RDISC_CONFIG_ADDRESSES);
 }
 
 #define CONFIG_MAP_MAX_STR 7
@@ -504,64 +647,73 @@ dhcp_level_to_string (NMRDiscDHCPLevel dhcp_level)
 #define expiry(item) (item->timestamp + item->lifetime)
 
 static void
-config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed)
+_config_changed_log (NMRDisc *rdisc, NMRDiscConfigMap changed)
 {
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
 	int i;
 	char changedstr[CONFIG_MAP_MAX_STR];
 	char addrstr[INET6_ADDRSTRLEN];
 
-	if (_LOGD_ENABLED ()) {
-		config_map_to_string (changed, changedstr);
-		_LOGD ("router discovery configuration changed [%s]:", changedstr);
-		_LOGD ("  dhcp-level %s", dhcp_level_to_string (rdisc->dhcp_level));
-		for (i = 0; i < rdisc->gateways->len; i++) {
-			NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, i);
+	if (!_LOGD_ENABLED ())
+		return;
 
-			inet_ntop (AF_INET6, &gateway->address, addrstr, sizeof (addrstr));
-			_LOGD ("  gateway %s pref %d exp %u", addrstr, gateway->preference, expiry (gateway));
-		}
-		for (i = 0; i < rdisc->addresses->len; i++) {
-			NMRDiscAddress *address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+	priv = NM_RDISC_GET_PRIVATE (rdisc);
+	rdata = &priv->rdata;
 
-			inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
-			_LOGD ("  address %s exp %u", addrstr, expiry (address));
-		}
-		for (i = 0; i < rdisc->routes->len; i++) {
-			NMRDiscRoute *route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+	config_map_to_string (changed, changedstr);
+	_LOGD ("router discovery configuration changed [%s]:", changedstr);
+	_LOGD ("  dhcp-level %s", dhcp_level_to_string (priv->rdata.public.dhcp_level));
+	for (i = 0; i < rdata->gateways->len; i++) {
+		NMRDiscGateway *gateway = &g_array_index (rdata->gateways, NMRDiscGateway, i);
 
-			inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr));
-			_LOGD ("  route %s/%d via %s pref %d exp %u", addrstr, route->plen,
-				   nm_utils_inet6_ntop (&route->gateway, NULL), route->preference,
-				   expiry (route));
-		}
-		for (i = 0; i < rdisc->dns_servers->len; i++) {
-			NMRDiscDNSServer *dns_server = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i);
+		inet_ntop (AF_INET6, &gateway->address, addrstr, sizeof (addrstr));
+		_LOGD ("  gateway %s pref %d exp %u", addrstr, gateway->preference, expiry (gateway));
+	}
+	for (i = 0; i < rdata->addresses->len; i++) {
+		NMRDiscAddress *address = &g_array_index (rdata->addresses, NMRDiscAddress, i);
 
-			inet_ntop (AF_INET6, &dns_server->address, addrstr, sizeof (addrstr));
-			_LOGD ("  dns_server %s exp %u", addrstr, expiry (dns_server));
-		}
-		for (i = 0; i < rdisc->dns_domains->len; i++) {
-			NMRDiscDNSDomain *dns_domain = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
+		inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr));
+		_LOGD ("  address %s exp %u", addrstr, expiry (address));
+	}
+	for (i = 0; i < rdata->routes->len; i++) {
+		NMRDiscRoute *route = &g_array_index (rdata->routes, NMRDiscRoute, i);
 
-			_LOGD ("  dns_domain %s exp %u", dns_domain->domain, expiry (dns_domain));
-		}
+		inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr));
+		_LOGD ("  route %s/%d via %s pref %d exp %u", addrstr, (int) route->plen,
+		       nm_utils_inet6_ntop (&route->gateway, NULL), route->preference,
+		       expiry (route));
+	}
+	for (i = 0; i < rdata->dns_servers->len; i++) {
+		NMRDiscDNSServer *dns_server = &g_array_index (rdata->dns_servers, NMRDiscDNSServer, i);
+
+		inet_ntop (AF_INET6, &dns_server->address, addrstr, sizeof (addrstr));
+		_LOGD ("  dns_server %s exp %u", addrstr, expiry (dns_server));
+	}
+	for (i = 0; i < rdata->dns_domains->len; i++) {
+		NMRDiscDNSDomain *dns_domain = &g_array_index (rdata->dns_domains, NMRDiscDNSDomain, i);
+
+		_LOGD ("  dns_domain %s exp %u", dns_domain->domain, expiry (dns_domain));
 	}
 }
 
 static void
 clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
 {
-	int i;
+	NMRDiscDataInternal *rdata;
+	guint i;
+
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
 
-	for (i = 0; i < rdisc->gateways->len; i++) {
-		NMRDiscGateway *item = &g_array_index (rdisc->gateways, NMRDiscGateway, i);
+	for (i = 0; i < rdata->gateways->len; i++) {
+		NMRDiscGateway *item = &g_array_index (rdata->gateways, NMRDiscGateway, i);
 		guint64 expiry = (guint64) item->timestamp + item->lifetime;
 
 		if (item->lifetime == G_MAXUINT32)
 			continue;
 
 		if (now >= expiry) {
-			g_array_remove_index (rdisc->gateways, i--);
+			g_array_remove_index (rdata->gateways, i--);
 			*changed |= NM_RDISC_CONFIG_GATEWAYS;
 		} else if (*nextevent > expiry)
 			*nextevent = expiry;
@@ -571,17 +723,20 @@ clean_gateways (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32
 static void
 clean_addresses (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
 {
-	int i;
+	NMRDiscDataInternal *rdata;
+	guint i;
+
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
 
-	for (i = 0; i < rdisc->addresses->len; i++) {
-		NMRDiscAddress *item = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+	for (i = 0; i < rdata->addresses->len; i++) {
+		NMRDiscAddress *item = &g_array_index (rdata->addresses, NMRDiscAddress, i);
 		guint64 expiry = (guint64) item->timestamp + item->lifetime;
 
 		if (item->lifetime == G_MAXUINT32)
 			continue;
 
 		if (now >= expiry) {
-			g_array_remove_index (rdisc->addresses, i--);
+			g_array_remove_index (rdata->addresses, i--);
 			*changed |= NM_RDISC_CONFIG_ADDRESSES;
 		} else if (*nextevent > expiry)
 			*nextevent = expiry;
@@ -591,17 +746,20 @@ clean_addresses (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32
 static void
 clean_routes (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
 {
-	int i;
+	NMRDiscDataInternal *rdata;
+	guint i;
+
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
 
-	for (i = 0; i < rdisc->routes->len; i++) {
-		NMRDiscRoute *item = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+	for (i = 0; i < rdata->routes->len; i++) {
+		NMRDiscRoute *item = &g_array_index (rdata->routes, NMRDiscRoute, i);
 		guint64 expiry = (guint64) item->timestamp + item->lifetime;
 
 		if (item->lifetime == G_MAXUINT32)
 			continue;
 
 		if (now >= expiry) {
-			g_array_remove_index (rdisc->routes, i--);
+			g_array_remove_index (rdata->routes, i--);
 			*changed |= NM_RDISC_CONFIG_ROUTES;
 		} else if (*nextevent > expiry)
 			*nextevent = expiry;
@@ -611,10 +769,13 @@ clean_routes (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *n
 static void
 clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
 {
-	int i;
+	NMRDiscDataInternal *rdata;
+	guint i;
 
-	for (i = 0; i < rdisc->dns_servers->len; i++) {
-		NMRDiscDNSServer *item = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i);
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
+
+	for (i = 0; i < rdata->dns_servers->len; i++) {
+		NMRDiscDNSServer *item = &g_array_index (rdata->dns_servers, NMRDiscDNSServer, i);
 		guint64 expiry = (guint64) item->timestamp + item->lifetime;
 		guint64 refresh = (guint64) item->timestamp + item->lifetime / 2;
 
@@ -622,7 +783,7 @@ clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint
 			continue;
 
 		if (now >= expiry) {
-			g_array_remove_index (rdisc->dns_servers, i--);
+			g_array_remove_index (rdata->dns_servers, i--);
 			*changed |= NM_RDISC_CONFIG_DNS_SERVERS;
 		} else if (now >= refresh)
 			solicit (rdisc);
@@ -634,10 +795,13 @@ clean_dns_servers (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint
 static void
 clean_dns_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint32 *nextevent)
 {
-	int i;
+	NMRDiscDataInternal *rdata;
+	guint i;
+
+	rdata = &NM_RDISC_GET_PRIVATE (rdisc)->rdata;
 
-	for (i = 0; i < rdisc->dns_domains->len; i++) {
-		NMRDiscDNSDomain *item = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i);
+	for (i = 0; i < rdata->dns_domains->len; i++) {
+		NMRDiscDNSDomain *item = &g_array_index (rdata->dns_domains, NMRDiscDNSDomain, i);
 		guint64 expiry = (guint64) item->timestamp + item->lifetime;
 		guint64 refresh = (guint64) item->timestamp + item->lifetime / 2;
 
@@ -645,7 +809,7 @@ clean_dns_domains (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap *changed, guint
 			continue;
 
 		if (now >= expiry) {
-			g_array_remove_index (rdisc->dns_domains, i--);
+			g_array_remove_index (rdata->dns_domains, i--);
 			*changed |= NM_RDISC_CONFIG_DNS_DOMAINS;
 		} else if (now >= refresh)
 			solicit (rdisc);
@@ -673,7 +837,7 @@ check_timestamps (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed)
 	clean_dns_domains (rdisc, now, &changed, &nextevent);
 
 	if (changed)
-		g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, changed);
+		_emit_config_change (rdisc, changed);
 
 	if (nextevent != never) {
 		g_return_if_fail (nextevent > now);
@@ -686,8 +850,10 @@ check_timestamps (NMRDisc *rdisc, guint32 now, NMRDiscConfigMap changed)
 static gboolean
 timeout_cb (gpointer user_data)
 {
-	NM_RDISC_GET_PRIVATE (user_data)->timeout_id = 0;
-	check_timestamps (NM_RDISC (user_data), nm_utils_get_monotonic_timestamp_s (), 0);
+	NMRDisc *self = user_data;
+
+	NM_RDISC_GET_PRIVATE (self)->timeout_id = 0;
+	check_timestamps (self, nm_utils_get_monotonic_timestamp_s (), 0);
 	return G_SOURCE_REMOVE;
 }
 
@@ -715,21 +881,56 @@ set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
 	NMRDisc *self = NM_RDISC (object);
+	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (self);
 
 	switch (prop_id) {
 	case PROP_PLATFORM:
 		/* construct-only */
-		self->_platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
-		if (!self->_platform)
+		priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
+		if (!priv->platform)
 			g_return_if_reached ();
 
-		g_object_ref (self->_platform);
+		g_object_ref (priv->platform);
 
-		self->_netns = nm_platform_netns_get (self->_platform);
-		if (self->_netns)
-			g_object_ref (self->_netns);
+		priv->netns = nm_platform_netns_get (priv->platform);
+		if (priv->netns)
+			g_object_ref (priv->netns);
 
-		g_return_if_fail (!self->_netns || self->_netns == nmp_netns_get_current ());
+		g_return_if_fail (!priv->netns || priv->netns == nmp_netns_get_current ());
+		break;
+	case PROP_IFINDEX:
+		/* construct-only */
+		priv->ifindex = g_value_get_int (value);
+		g_return_if_fail (priv->ifindex > 0);
+		break;
+	case PROP_IFNAME:
+		/* construct-only */
+		priv->ifname = g_value_dup_string (value);
+		g_return_if_fail (priv->ifname && priv->ifname[0]);
+		break;
+	case PROP_STABLE_TYPE:
+		/* construct-only */
+		priv->stable_type = g_value_get_int (value);
+		break;
+	case PROP_NETWORK_ID:
+		/* construct-only */
+		priv->network_id = g_value_dup_string (value);
+		break;
+	case PROP_ADDR_GEN_MODE:
+		/* construct-only */
+		priv->addr_gen_mode = g_value_get_int (value);
+		break;
+	case PROP_MAX_ADDRESSES:
+		/* construct-only */
+		priv->max_addresses = g_value_get_int (value);
+		break;
+	case PROP_ROUTER_SOLICITATIONS:
+		/* construct-only */
+		priv->router_solicitations = g_value_get_int (value);
+		break;
+	case PROP_ROUTER_SOLICITATION_INTERVAL:
+		/* construct-only */
+		priv->router_solicitation_interval = g_value_get_int (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -740,17 +941,23 @@ set_property (GObject *object, guint prop_id,
 static void
 nm_rdisc_init (NMRDisc *rdisc)
 {
-	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
+	NMRDiscPrivate *priv;
+	NMRDiscDataInternal *rdata;
+
+	priv = G_TYPE_INSTANCE_GET_PRIVATE (rdisc, NM_TYPE_RDISC, NMRDiscPrivate);
+	rdisc->_priv = priv;
 
-	rdisc->gateways = g_array_new (FALSE, FALSE, sizeof (NMRDiscGateway));
-	rdisc->addresses = g_array_new (FALSE, FALSE, sizeof (NMRDiscAddress));
-	rdisc->routes = g_array_new (FALSE, FALSE, sizeof (NMRDiscRoute));
-	rdisc->dns_servers = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSServer));
-	rdisc->dns_domains = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSDomain));
-	g_array_set_clear_func (rdisc->dns_domains, dns_domain_free);
-	rdisc->hop_limit = 64;
+	rdata = &priv->rdata;
 
-	/* Start at very low number so that last_rs - rtr_solicitation_interval
+	rdata->gateways = g_array_new (FALSE, FALSE, sizeof (NMRDiscGateway));
+	rdata->addresses = g_array_new (FALSE, FALSE, sizeof (NMRDiscAddress));
+	rdata->routes = g_array_new (FALSE, FALSE, sizeof (NMRDiscRoute));
+	rdata->dns_servers = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSServer));
+	rdata->dns_domains = g_array_new (FALSE, FALSE, sizeof (NMRDiscDNSDomain));
+	g_array_set_clear_func (rdata->dns_domains, dns_domain_free);
+	priv->rdata.public.hop_limit = 64;
+
+	/* Start at very low number so that last_rs - router_solicitation_interval
 	 * is much lower than nm_utils_get_monotonic_timestamp_s() at startup.
 	 */
 	priv->last_rs = G_MININT32;
@@ -775,17 +982,20 @@ static void
 finalize (GObject *object)
 {
 	NMRDisc *rdisc = NM_RDISC (object);
+	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
+	NMRDiscDataInternal *rdata = &priv->rdata;
+
+	g_free (priv->ifname);
+	g_free (priv->network_id);
 
-	g_free (rdisc->ifname);
-	g_free (rdisc->uuid);
-	g_array_unref (rdisc->gateways);
-	g_array_unref (rdisc->addresses);
-	g_array_unref (rdisc->routes);
-	g_array_unref (rdisc->dns_servers);
-	g_array_unref (rdisc->dns_domains);
+	g_array_unref (rdata->gateways);
+	g_array_unref (rdata->addresses);
+	g_array_unref (rdata->routes);
+	g_array_unref (rdata->dns_servers);
+	g_array_unref (rdata->dns_domains);
 
-	g_clear_object (&rdisc->_netns);
-	g_clear_object (&rdisc->_platform);
+	g_clear_object (&priv->netns);
+	g_clear_object (&priv->platform);
 
 	G_OBJECT_CLASS (nm_rdisc_parent_class)->finalize (object);
 }
@@ -800,7 +1010,6 @@ nm_rdisc_class_init (NMRDiscClass *klass)
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
-	klass->config_changed = config_changed;
 
 	obj_properties[PROP_PLATFORM] =
 	    g_param_spec_object (NM_RDISC_PLATFORM, "", "",
@@ -808,20 +1017,68 @@ nm_rdisc_class_init (NMRDiscClass *klass)
 	                         G_PARAM_WRITABLE |
 	                         G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IFINDEX] =
+	    g_param_spec_int (NM_RDISC_IFINDEX, "", "",
+	                      0, G_MAXINT, 0,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_IFNAME] =
+	    g_param_spec_string (NM_RDISC_IFNAME, "", "",
+	                         NULL,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_STABLE_TYPE] =
+	    g_param_spec_int (NM_RDISC_STABLE_TYPE, "", "",
+	                      NM_UTILS_STABLE_TYPE_UUID, NM_UTILS_STABLE_TYPE_STABLE_ID, NM_UTILS_STABLE_TYPE_UUID,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_NETWORK_ID] =
+	    g_param_spec_string (NM_RDISC_NETWORK_ID, "", "",
+	                         NULL,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ADDR_GEN_MODE] =
+	    g_param_spec_int (NM_RDISC_ADDR_GEN_MODE, "", "",
+	                      NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_MAX_ADDRESSES] =
+	    g_param_spec_int (NM_RDISC_MAX_ADDRESSES, "", "",
+	                      0, G_MAXINT32, NM_RDISC_MAX_ADDRESSES_DEFAULT,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ROUTER_SOLICITATIONS] =
+	    g_param_spec_int (NM_RDISC_ROUTER_SOLICITATIONS, "", "",
+	                      1, G_MAXINT32, NM_RDISC_ROUTER_SOLICITATIONS_DEFAULT,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ROUTER_SOLICITATION_INTERVAL] =
+	    g_param_spec_int (NM_RDISC_ROUTER_SOLICITATION_INTERVAL, "", "",
+	                      1, G_MAXINT32, NM_RDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[CONFIG_CHANGED] =
 	    g_signal_new (NM_RDISC_CONFIG_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (klass),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMRDiscClass, config_changed),
+	                  0,
 	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 1, G_TYPE_INT);
+	                  G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
 	signals[RA_TIMEOUT] =
 	    g_signal_new (NM_RDISC_RA_TIMEOUT,
 	                  G_OBJECT_CLASS_TYPE (klass),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMRDiscClass, ra_timeout),
+	                  0,
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
 }
diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h
index 5b97bec5..9e5e9349 100644
--- a/src/rdisc/nm-rdisc.h
+++ b/src/rdisc/nm-rdisc.h
@@ -35,6 +35,15 @@
 #define NM_RDISC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_RDISC, NMRDiscClass))
 
 #define NM_RDISC_PLATFORM       "platform"
+#define NM_RDISC_IFINDEX        "ifindex"
+#define NM_RDISC_IFNAME         "ifname"
+#define NM_RDISC_NETWORK_ID     "network-id"
+#define NM_RDISC_ADDR_GEN_MODE  "addr-gen-mode"
+#define NM_RDISC_STABLE_TYPE    "stable-type"
+#define NM_RDISC_MAX_ADDRESSES  "max-addresses"
+#define NM_RDISC_ROUTER_SOLICITATIONS "router-solicitations"
+#define NM_RDISC_ROUTER_SOLICITATION_INTERVAL "router-solicitation-interval"
+
 #define NM_RDISC_CONFIG_CHANGED "config-changed"
 #define NM_RDISC_RA_TIMEOUT     "ra-timeout"
 
@@ -69,7 +78,7 @@ typedef struct {
 
 typedef struct {
 	struct in6_addr network;
-	int plen;
+	guint8 plen;
 	struct in6_addr gateway;
 	guint32 timestamp;
 	guint32 lifetime;
@@ -100,39 +109,42 @@ typedef enum {
 } NMRDiscConfigMap;
 
 #define NM_RDISC_MAX_ADDRESSES_DEFAULT 16
-#define NM_RDISC_RTR_SOLICITATIONS_DEFAULT 3
-#define NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT 4
+#define NM_RDISC_ROUTER_SOLICITATIONS_DEFAULT 3
+#define NM_RDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT 4
+
+struct _NMRDiscPrivate;
+struct _NMRDiscDataInternal;
+
+typedef struct {
+	NMRDiscDHCPLevel dhcp_level;
+	guint32 mtu;
+	int hop_limit;
+
+	guint gateways_n;
+	guint addresses_n;
+	guint routes_n;
+	guint dns_servers_n;
+	guint dns_domains_n;
+
+	const NMRDiscGateway *gateways;
+	const NMRDiscAddress *addresses;
+	const NMRDiscRoute *routes;
+	const NMRDiscDNSServer *dns_servers;
+	const NMRDiscDNSDomain *dns_domains;
+} NMRDiscData;
 
 /**
  * NMRDisc:
- * @ifindex: Interface index
  *
  * Interface-specific structure that handles incoming router advertisements,
  * caches advertised items and removes them when they are obsolete.
  */
 typedef struct {
 	GObject parent;
-
-	NMPlatform *_platform;
-	NMPNetns *_netns;
-
-	int ifindex;
-	char *ifname;
-	char *uuid;
-	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
-	NMUtilsIPv6IfaceId iid;
-	gint32 max_addresses;
-	gint32 rtr_solicitations;
-	gint32 rtr_solicitation_interval;
-
-	NMRDiscDHCPLevel dhcp_level;
-	GArray *gateways;
-	GArray *addresses;
-	GArray *routes;
-	GArray *dns_servers;
-	GArray *dns_domains;
-	int hop_limit;
-	guint32 mtu;
+	union {
+		struct _NMRDiscPrivate *_priv;
+		struct _NMRDiscDataInternal *rdata;
+	};
 } NMRDisc;
 
 typedef struct {
@@ -140,13 +152,13 @@ typedef struct {
 
 	void (*start) (NMRDisc *rdisc);
 	gboolean (*send_rs) (NMRDisc *rdisc, GError **error);
-	void (*config_changed) (NMRDisc *rdisc, NMRDiscConfigMap changed);
-	void (*ra_process) (NMRDisc *rdisc);
-	void (*ra_timeout) (NMRDisc *rdisc);
 } NMRDiscClass;
 
 GType nm_rdisc_get_type (void);
 
+int nm_rdisc_get_ifindex (NMRDisc *self);
+const char *nm_rdisc_get_ifname (NMRDisc *self);
+
 gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid);
 void nm_rdisc_start (NMRDisc *rdisc);
 void nm_rdisc_dad_failed (NMRDisc *rdisc, struct in6_addr *address);
diff --git a/src/rdisc/tests/Makefile.am b/src/rdisc/tests/Makefile.am
index 65191734..b054179e 100644
--- a/src/rdisc/tests/Makefile.am
+++ b/src/rdisc/tests/Makefile.am
@@ -29,7 +29,7 @@ test_rdisc_linux_LDADD = \
 test_rdisc_fake_SOURCES = \
 	test-rdisc-fake.c
 test_rdisc_fake_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 @VALGRIND_RULES@
 TESTS = test-rdisc-fake
diff --git a/src/rdisc/tests/Makefile.in b/src/rdisc/tests/Makefile.in
index 895d7063..f160600e 100644
--- a/src/rdisc/tests/Makefile.in
+++ b/src/rdisc/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-rdisc-linux$(EXEEXT) test-rdisc-fake$(EXEEXT)
 TESTS = test-rdisc-fake$(EXEEXT)
 subdir = src/rdisc/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -116,7 +117,7 @@ PROGRAMS = $(noinst_PROGRAMS)
 am_test_rdisc_fake_OBJECTS = test-rdisc-fake.$(OBJEXT)
 test_rdisc_fake_OBJECTS = $(am_test_rdisc_fake_OBJECTS)
 test_rdisc_fake_DEPENDENCIES =  \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
@@ -426,6 +427,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -482,13 +484,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -500,6 +503,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -560,6 +565,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -595,6 +602,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -685,7 +693,7 @@ test_rdisc_fake_SOURCES = \
 	test-rdisc-fake.c
 
 test_rdisc_fake_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 all: all-am
 
diff --git a/src/rdisc/tests/test-rdisc-fake.c b/src/rdisc/tests/test-rdisc-fake.c
index 1c514b90..02ffb510 100644
--- a/src/rdisc/tests/test-rdisc-fake.c
+++ b/src/rdisc/tests/test-rdisc-fake.c
@@ -28,7 +28,7 @@
 
 #include "nm-fake-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static NMFakeRDisc *
 rdisc_new (void)
@@ -46,11 +46,17 @@ rdisc_new (void)
 }
 
 static void
-match_gateway (GArray *array, guint idx, const char *addr, guint32 ts, guint32 lt, NMRDiscPreference pref)
+match_gateway (const NMRDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt, NMRDiscPreference pref)
 {
-	NMRDiscGateway *gw = &g_array_index (array, NMRDiscGateway, idx);
+	const NMRDiscGateway *gw;
 	char buf[INET6_ADDRSTRLEN];
 
+	g_assert (rdata);
+	g_assert_cmpint (idx, <, rdata->gateways_n);
+	g_assert (rdata->gateways);
+
+	gw = &rdata->gateways[idx];
+
 	g_assert_cmpstr (inet_ntop (AF_INET6, &gw->address, buf, sizeof (buf)), ==, addr);
 	g_assert_cmpint (gw->timestamp, ==, ts);
 	g_assert_cmpint (gw->lifetime, ==, lt);
@@ -58,11 +64,17 @@ match_gateway (GArray *array, guint idx, const char *addr, guint32 ts, guint32 l
 }
 
 static void
-match_address (GArray *array, guint idx, const char *addr, guint32 ts, guint32 lt, guint32 preferred)
+match_address (const NMRDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt, guint32 preferred)
 {
-	NMRDiscAddress *a = &g_array_index (array, NMRDiscAddress, idx);
+	const NMRDiscAddress *a;
 	char buf[INET6_ADDRSTRLEN];
 
+	g_assert (rdata);
+	g_assert_cmpint (idx, <, rdata->addresses_n);
+	g_assert (rdata->addresses);
+
+	a = &rdata->addresses[idx];
+
 	g_assert_cmpstr (inet_ntop (AF_INET6, &a->address, buf, sizeof (buf)), ==, addr);
 	g_assert_cmpint (a->timestamp, ==, ts);
 	g_assert_cmpint (a->lifetime, ==, lt);
@@ -70,13 +82,20 @@ match_address (GArray *array, guint idx, const char *addr, guint32 ts, guint32 l
 }
 
 static void
-match_route (GArray *array, guint idx, const char *nw, int plen, const char *gw, guint32 ts, guint32 lt, NMRDiscPreference pref)
+match_route (const NMRDiscData *rdata, guint idx, const char *nw, int plen, const char *gw, guint32 ts, guint32 lt, NMRDiscPreference pref)
 {
-	NMRDiscRoute *route = &g_array_index (array, NMRDiscRoute, idx);
+	const NMRDiscRoute *route;
 	char buf[INET6_ADDRSTRLEN];
 
+	g_assert (rdata);
+	g_assert_cmpint (idx, <, rdata->routes_n);
+	g_assert (rdata->routes);
+	g_assert (plen > 0 && plen <= 128);
+
+	route = &rdata->routes[idx];
+
 	g_assert_cmpstr (inet_ntop (AF_INET6, &route->network, buf, sizeof (buf)), ==, nw);
-	g_assert_cmpint (route->plen, ==, plen);
+	g_assert_cmpint ((int) route->plen, ==, plen);
 	g_assert_cmpstr (inet_ntop (AF_INET6, &route->gateway, buf, sizeof (buf)), ==, gw);
 	g_assert_cmpint (route->timestamp, ==, ts);
 	g_assert_cmpint (route->lifetime, ==, lt);
@@ -84,20 +103,32 @@ match_route (GArray *array, guint idx, const char *nw, int plen, const char *gw,
 }
 
 static void
-match_dns_server (GArray *array, guint idx, const char *addr, guint32 ts, guint32 lt)
+match_dns_server (const NMRDiscData *rdata, guint idx, const char *addr, guint32 ts, guint32 lt)
 {
-	NMRDiscDNSServer *dns = &g_array_index (array, NMRDiscDNSServer, idx);
+	const NMRDiscDNSServer *dns;
 	char buf[INET6_ADDRSTRLEN];
 
+	g_assert (rdata);
+	g_assert_cmpint (idx, <, rdata->dns_servers_n);
+	g_assert (rdata->dns_servers);
+
+	dns = &rdata->dns_servers[idx];
+
 	g_assert_cmpstr (inet_ntop (AF_INET6, &dns->address, buf, sizeof (buf)), ==, addr);
 	g_assert_cmpint (dns->timestamp, ==, ts);
 	g_assert_cmpint (dns->lifetime, ==, lt);
 }
 
 static void
-match_dns_domain (GArray *array, guint idx, const char *domain, guint32 ts, guint32 lt)
+match_dns_domain (const NMRDiscData *rdata, guint idx, const char *domain, guint32 ts, guint32 lt)
 {
-	NMRDiscDNSDomain *dns = &g_array_index (array, NMRDiscDNSDomain, idx);
+	const NMRDiscDNSDomain *dns;
+
+	g_assert (rdata);
+	g_assert_cmpint (idx, <, rdata->dns_domains_n);
+	g_assert (rdata->dns_domains);
+
+	dns = &rdata->dns_domains[idx];
 
 	g_assert_cmpstr (dns->domain, ==, domain);
 	g_assert_cmpint (dns->timestamp, ==, ts);
@@ -114,8 +145,10 @@ typedef struct {
 } TestData;
 
 static void
-test_simple_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *data)
+test_simple_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, TestData *data)
 {
+	NMRDiscConfigMap changed = changed_int;
+
 	g_assert_cmpint (changed, ==, NM_RDISC_CONFIG_DHCP_LEVEL |
 	                              NM_RDISC_CONFIG_GATEWAYS |
 	                              NM_RDISC_CONFIG_ADDRESSES |
@@ -124,12 +157,12 @@ test_simple_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *data)
 	                              NM_RDISC_CONFIG_DNS_DOMAINS |
 	                              NM_RDISC_CONFIG_HOP_LIMIT |
 	                              NM_RDISC_CONFIG_MTU);
-	g_assert_cmpint (rdisc->dhcp_level, ==, NM_RDISC_DHCP_LEVEL_OTHERCONF);
-	match_gateway (rdisc->gateways, 0, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
-	match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
-	match_route (rdisc->routes, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10);
-	match_dns_server (rdisc->dns_servers, 0, "2001:db8:c:c::1", data->timestamp1, 10);
-	match_dns_domain (rdisc->dns_domains, 0, "foobar.com", data->timestamp1, 10);
+	g_assert_cmpint (rdata->dhcp_level, ==, NM_RDISC_DHCP_LEVEL_OTHERCONF);
+	match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
+	match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
+	match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10);
+	match_dns_server (rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10);
+	match_dns_domain (rdata, 0, "foobar.com", data->timestamp1, 10);
 
 	g_assert (nm_fake_rdisc_done (NM_FAKE_RDISC (rdisc)));
 	data->counter++;
@@ -172,40 +205,42 @@ test_everything_rs_sent (NMRDisc *rdisc, TestData *data)
 }
 
 static void
-test_everything_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *data)
+test_everything_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, TestData *data)
 {
+	NMRDiscConfigMap changed = changed_int;
+
 	if (data->counter == 0) {
 		g_assert_cmpint (data->rs_counter, ==, 1);
 		g_assert_cmpint (changed, ==, NM_RDISC_CONFIG_DHCP_LEVEL |
-			                          NM_RDISC_CONFIG_GATEWAYS |
-			                          NM_RDISC_CONFIG_ADDRESSES |
-			                          NM_RDISC_CONFIG_ROUTES |
-			                          NM_RDISC_CONFIG_DNS_SERVERS |
-			                          NM_RDISC_CONFIG_DNS_DOMAINS |
-			                          NM_RDISC_CONFIG_HOP_LIMIT |
-			                          NM_RDISC_CONFIG_MTU);
-		match_gateway (rdisc->gateways, 0, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
-		match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
-		match_route (rdisc->routes, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10);
-		match_dns_server (rdisc->dns_servers, 0, "2001:db8:c:c::1", data->timestamp1, 10);
-		match_dns_domain (rdisc->dns_domains, 0, "foobar.com", data->timestamp1, 10);
+		                              NM_RDISC_CONFIG_GATEWAYS |
+		                              NM_RDISC_CONFIG_ADDRESSES |
+		                              NM_RDISC_CONFIG_ROUTES |
+		                              NM_RDISC_CONFIG_DNS_SERVERS |
+		                              NM_RDISC_CONFIG_DNS_DOMAINS |
+		                              NM_RDISC_CONFIG_HOP_LIMIT |
+		                              NM_RDISC_CONFIG_MTU);
+		match_gateway (rdata, 0, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
+		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
+		match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 10);
+		match_dns_server (rdata, 0, "2001:db8:c:c::1", data->timestamp1, 10);
+		match_dns_domain (rdata, 0, "foobar.com", data->timestamp1, 10);
 	} else if (data->counter == 1) {
 		g_assert_cmpint (changed, ==, NM_RDISC_CONFIG_GATEWAYS |
-			                          NM_RDISC_CONFIG_ADDRESSES |
-			                          NM_RDISC_CONFIG_ROUTES |
-			                          NM_RDISC_CONFIG_DNS_SERVERS |
-			                          NM_RDISC_CONFIG_DNS_DOMAINS);
-
-		g_assert_cmpint (rdisc->gateways->len, ==, 1);
-		match_gateway (rdisc->gateways, 0, "fe80::2", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
-		g_assert_cmpint (rdisc->addresses->len, ==, 1);
-		match_address (rdisc->addresses, 0, "2001:db8:a:b::1", data->timestamp1, 10, 10);
-		g_assert_cmpint (rdisc->routes->len, ==, 1);
-		match_route (rdisc->routes, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1, 10, 10);
-		g_assert_cmpint (rdisc->dns_servers->len, ==, 1);
-		match_dns_server (rdisc->dns_servers, 0, "2001:db8:c:c::2", data->timestamp1, 10);
-		g_assert_cmpint (rdisc->dns_domains->len, ==, 1);
-		match_dns_domain (rdisc->dns_domains, 0, "foobar2.com", data->timestamp1, 10);
+		                              NM_RDISC_CONFIG_ADDRESSES |
+		                              NM_RDISC_CONFIG_ROUTES |
+		                              NM_RDISC_CONFIG_DNS_SERVERS |
+		                              NM_RDISC_CONFIG_DNS_DOMAINS);
+
+		g_assert_cmpint (rdata->gateways_n, ==, 1);
+		match_gateway (rdata, 0, "fe80::2", data->timestamp1, 10, NM_RDISC_PREFERENCE_MEDIUM);
+		g_assert_cmpint (rdata->addresses_n, ==, 1);
+		match_address (rdata, 0, "2001:db8:a:b::1", data->timestamp1, 10, 10);
+		g_assert_cmpint (rdata->routes_n, ==, 1);
+		match_route (rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1, 10, 10);
+		g_assert_cmpint (rdata->dns_servers_n, ==, 1);
+		match_dns_server (rdata, 0, "2001:db8:c:c::2", data->timestamp1, 10);
+		g_assert_cmpint (rdata->dns_domains_n, ==, 1);
+		match_dns_domain (rdata, 0, "foobar2.com", data->timestamp1, 10);
 
 		g_assert (nm_fake_rdisc_done (NM_FAKE_RDISC (rdisc)));
 		g_main_loop_quit (data->loop);
@@ -263,35 +298,37 @@ test_everything (void)
 }
 
 static void
-test_preference_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *data)
+test_preference_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, TestData *data)
 {
+	NMRDiscConfigMap changed = changed_int;
+
 	if (data->counter == 1) {
 		g_assert_cmpint (changed, ==, NM_RDISC_CONFIG_GATEWAYS |
-			                          NM_RDISC_CONFIG_ADDRESSES |
-			                          NM_RDISC_CONFIG_ROUTES);
-		g_assert_cmpint (rdisc->gateways->len, ==, 2);
-		match_gateway (rdisc->gateways, 0, "fe80::2", data->timestamp1 + 1, 10, NM_RDISC_PREFERENCE_MEDIUM);
-		match_gateway (rdisc->gateways, 1, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_LOW);
-		g_assert_cmpint (rdisc->addresses->len, ==, 2);
-		match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
-		match_address (rdisc->addresses, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
-		g_assert_cmpint (rdisc->routes->len, ==, 2);
-		match_route (rdisc->routes, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
-		match_route (rdisc->routes, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5);
+		                              NM_RDISC_CONFIG_ADDRESSES |
+		                              NM_RDISC_CONFIG_ROUTES);
+		g_assert_cmpint (rdata->gateways_n, ==, 2);
+		match_gateway (rdata, 0, "fe80::2", data->timestamp1 + 1, 10, NM_RDISC_PREFERENCE_MEDIUM);
+		match_gateway (rdata, 1, "fe80::1", data->timestamp1, 10, NM_RDISC_PREFERENCE_LOW);
+		g_assert_cmpint (rdata->addresses_n, ==, 2);
+		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1, 10, 10);
+		match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
+		g_assert_cmpint (rdata->routes_n, ==, 2);
+		match_route (rdata, 0, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
+		match_route (rdata, 1, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1, 10, 5);
 	} else if (data->counter == 2) {
 		g_assert_cmpint (changed, ==, NM_RDISC_CONFIG_GATEWAYS |
-			                          NM_RDISC_CONFIG_ADDRESSES |
-			                          NM_RDISC_CONFIG_ROUTES);
-
-		g_assert_cmpint (rdisc->gateways->len, ==, 2);
-		match_gateway (rdisc->gateways, 0, "fe80::1", data->timestamp1 + 2, 10, NM_RDISC_PREFERENCE_HIGH);
-		match_gateway (rdisc->gateways, 1, "fe80::2", data->timestamp1 + 1, 10, NM_RDISC_PREFERENCE_MEDIUM);
-		g_assert_cmpint (rdisc->addresses->len, ==, 2);
-		match_address (rdisc->addresses, 0, "2001:db8:a:a::1", data->timestamp1 + 2, 10, 10);
-		match_address (rdisc->addresses, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
-		g_assert_cmpint (rdisc->routes->len, ==, 2);
-		match_route (rdisc->routes, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1 + 2, 10, 15);
-		match_route (rdisc->routes, 1, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
+		                              NM_RDISC_CONFIG_ADDRESSES |
+		                              NM_RDISC_CONFIG_ROUTES);
+
+		g_assert_cmpint (rdata->gateways_n, ==, 2);
+		match_gateway (rdata, 0, "fe80::1", data->timestamp1 + 2, 10, NM_RDISC_PREFERENCE_HIGH);
+		match_gateway (rdata, 1, "fe80::2", data->timestamp1 + 1, 10, NM_RDISC_PREFERENCE_MEDIUM);
+		g_assert_cmpint (rdata->addresses_n, ==, 2);
+		match_address (rdata, 0, "2001:db8:a:a::1", data->timestamp1 + 2, 10, 10);
+		match_address (rdata, 1, "2001:db8:a:b::1", data->timestamp1 + 1, 10, 10);
+		g_assert_cmpint (rdata->routes_n, ==, 2);
+		match_route (rdata, 0, "2001:db8:a:a::", 64, "fe80::1", data->timestamp1 + 2, 10, 15);
+		match_route (rdata, 1, "2001:db8:a:b::", 64, "fe80::2", data->timestamp1 + 1, 10, 10);
 
 		g_assert (nm_fake_rdisc_done (NM_FAKE_RDISC (rdisc)));
 		g_main_loop_quit (data->loop);
@@ -342,7 +379,7 @@ test_preference (void)
 }
 
 static void
-test_dns_solicit_loop_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, TestData *data)
+test_dns_solicit_loop_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, TestData *data)
 {
 	data->counter++;
 }
diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c
index e22eb1ad..299abf3e 100644
--- a/src/rdisc/tests/test-rdisc-linux.c
+++ b/src/rdisc/tests/test-rdisc-linux.c
@@ -28,7 +28,7 @@
 
 #include "nm-linux-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 NMTST_DEFINE ();
 
@@ -64,6 +64,7 @@ main (int argc, char **argv)
 	rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET,
 	                           ifindex,
 	                           ifname,
+	                           NM_UTILS_STABLE_TYPE_UUID,
 	                           "8ce666e8-d34d-4fb1-b858-f15a7al28086",
 	                           NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
 	                           &error);
diff --git a/src/settings/nm-inotify-helper.h b/src/settings/nm-inotify-helper.h
index b4e2c612..2ac9376c 100644
--- a/src/settings/nm-inotify-helper.h
+++ b/src/settings/nm-inotify-helper.h
@@ -23,8 +23,6 @@
 
 #include <sys/inotify.h>
 
-#include "nm-default.h"
-
 /* NOTE: this code should be killed once we depend on a new enough glib to
  * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815
  */
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 333aa461..916623a7 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -22,7 +22,6 @@
 #define __NETWORKMANAGER_SECRET_AGENT_H__
 
 #include <nm-connection.h>
-#include "nm-default.h"
 
 #define NM_TYPE_SECRET_AGENT            (nm_secret_agent_get_type ())
 #define NM_SECRET_AGENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent))
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 5b3a3999..f22807d3 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -93,7 +93,7 @@ typedef struct {
 
 	NMAgentManager *agent_mgr;
 	NMSessionMonitor *session_monitor;
-	guint session_changed_id;
+	gulong session_changed_id;
 
 	NMSettingsConnectionFlags flags;
 	gboolean ready;
@@ -1529,6 +1529,7 @@ typedef struct {
 	NMAuthSubject *subject;
 	NMConnection *new_settings;
 	gboolean save_to_disk;
+	char *audit_args;
 } UpdateInfo;
 
 typedef struct {
@@ -1600,12 +1601,13 @@ update_complete (NMSettingsConnection *self,
 	else
 		g_dbus_method_invocation_return_value (info->context, NULL);
 
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, !error,
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, !error, info->audit_args,
 	                            info->subject, error ? error->message : NULL);
 
 	g_clear_object (&info->subject);
 	g_clear_object (&info->agent_mgr);
 	g_clear_object (&info->new_settings);
+	g_free (info->audit_args);
 	memset (info, 0, sizeof (*info));
 	g_free (info);
 }
@@ -1637,6 +1639,49 @@ con_update_cb (NMSettingsConnection *self,
 	update_complete (self, info, error);
 }
 
+static char *
+con_list_changed_props (NMConnection *old, NMConnection *new)
+{
+	gs_unref_hashtable GHashTable *diff = NULL;
+	GHashTable *setting_diff;
+	char *setting_name, *prop_name;
+	GHashTableIter iter, iter2;
+	gboolean same;
+	GString *str;
+
+	same = nm_connection_diff (old, new,
+	                           NM_SETTING_COMPARE_FLAG_EXACT |
+	                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
+	                           &diff);
+
+	if (same || !diff)
+		return NULL;
+
+	str = g_string_sized_new (32);
+	g_hash_table_iter_init (&iter, diff);
+
+	while (g_hash_table_iter_next (&iter,
+	                               (gpointer *) &setting_name,
+	                               (gpointer *) &setting_diff)) {
+		if (!setting_diff)
+			continue;
+
+		g_hash_table_iter_init (&iter2, setting_diff);
+
+		while (g_hash_table_iter_next (&iter2, (gpointer *) &prop_name, NULL)) {
+			g_string_append (str, setting_name);
+			g_string_append_c (str, '.');
+			g_string_append (str, prop_name);
+			g_string_append_c (str, ',');
+		}
+	}
+
+	if (str->len)
+		str->str[str->len - 1] = '\0';
+
+	return g_string_free (str, FALSE);
+}
+
 static void
 update_auth_cb (NMSettingsConnection *self,
                 GDBusMethodInvocation *context,
@@ -1666,6 +1711,9 @@ update_auth_cb (NMSettingsConnection *self,
 		update_agent_secrets_cache (self, info->new_settings);
 	}
 
+	if (nm_audit_manager_audit_enabled (nm_audit_manager_get ()))
+		info->audit_args = con_list_changed_props (NM_CONNECTION (self), info->new_settings);
+
 	if (info->save_to_disk) {
 		nm_settings_connection_replace_and_commit (self,
 		                                           info->new_settings,
@@ -1769,7 +1817,7 @@ settings_connection_update_helper (NMSettingsConnection *self,
 	return;
 
 error:
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, FALSE, subject,
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, FALSE, NULL, subject,
 	                            error->message);
 
 	g_clear_object (&tmp);
@@ -1818,7 +1866,7 @@ con_delete_cb (NMSettingsConnection *self,
 		g_dbus_method_invocation_return_value (info->context, NULL);
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self,
-	                            !error, info->subject, error ? error->message : NULL);
+	                            !error, NULL, info->subject, error ? error->message : NULL);
 	g_free (info);
 }
 
@@ -1832,7 +1880,7 @@ delete_auth_cb (NMSettingsConnection *self,
 	CallbackInfo *info;
 
 	if (error) {
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject,
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject,
 		                            error->message);
 		g_dbus_method_invocation_return_gerror (context, error);
 		return;
@@ -1881,7 +1929,7 @@ impl_settings_connection_delete (NMSettingsConnection *self,
 
 	return;
 out_err:
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, subject, error->message);
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, FALSE, NULL, subject, error->message);
 	g_dbus_method_invocation_take_error (context, error);
 }
 
@@ -1974,7 +2022,7 @@ clear_secrets_cb (NMSettingsConnection *self,
 		g_dbus_method_invocation_return_value (info->context, NULL);
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
-	                            !error, info->subject, error ? error->message : NULL);
+	                            !error, NULL, info->subject, error ? error->message : NULL);
 	g_free (info);
 }
 
@@ -1991,7 +2039,7 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
-		                            FALSE, subject, error->message);
+		                            FALSE, NULL, subject, error->message);
 	} else {
 		/* Clear secrets in connection and caches */
 		nm_connection_clear_secrets (NM_CONNECTION (self));
@@ -2031,7 +2079,7 @@ impl_settings_connection_clear_secrets (NMSettingsConnection *self,
 		g_object_unref (subject);
 	} else {
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
-		                            FALSE, NULL, error->message);
+		                            FALSE, NULL, NULL, error->message);
 		g_dbus_method_invocation_take_error (context, error);
 	}
 }
@@ -2567,7 +2615,9 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	priv->ready = TRUE;
 
 	priv->session_monitor = g_object_ref (nm_session_monitor_get ());
-	priv->session_changed_id = nm_session_monitor_connect (priv->session_monitor, session_changed_cb, self);
+	priv->session_changed_id = g_signal_connect (priv->session_monitor,
+	                                             NM_SESSION_MONITOR_CHANGED,
+	                                             G_CALLBACK (session_changed_cb), self);
 
 	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
 
@@ -2627,11 +2677,9 @@ dispose (GObject *object)
 
 	set_visible (self, FALSE);
 
-	if (priv->session_monitor) {
-		nm_session_monitor_disconnect (priv->session_monitor, priv->session_changed_id);
-		priv->session_changed_id = 0;
-		g_clear_object (&priv->session_monitor);
-	}
+	nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id);
+	g_clear_object (&priv->session_monitor);
+
 	g_clear_object (&priv->agent_mgr);
 
 	g_clear_pointer (&priv->filename, g_free);
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index c45505d9..761203ea 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -26,9 +26,6 @@
 
 #include "nm-exported-object.h"
 #include <nm-connection.h>
-#include "nm-default.h"
-
-G_BEGIN_DECLS
 
 #define NM_TYPE_SETTINGS_CONNECTION            (nm_settings_connection_get_type ())
 #define NM_SETTINGS_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection))
@@ -230,6 +227,4 @@ const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
 const char *nm_settings_connection_get_id   (NMSettingsConnection *connection);
 const char *nm_settings_connection_get_uuid (NMSettingsConnection *connection);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index ab88e7c2..8abd72ea 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -22,11 +22,7 @@
 #ifndef __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 #define __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 
-
 #include <nm-connection.h>
-#include "nm-default.h"
-
-G_BEGIN_DECLS
 
 /* Plugin's factory function that returns a GObject that implements
  * NMSettingsPlugin.
@@ -156,6 +152,4 @@ NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *confi
                                                          gboolean save_to_disk,
                                                          GError **error);
 
-G_END_DECLS
-
-#endif	/* NM_SETTINGS_PLUGIN_H */
+#endif /* __NETWORKMANAGER_SETTINGS_PLUGIN_H__ */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 73c678a6..1f691c0b 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -25,6 +25,8 @@
 
 #include "nm-default.h"
 
+#include "nm-settings.h"
+
 #include <unistd.h>
 #include <sys/stat.h>
 #include <errno.h>
@@ -60,7 +62,6 @@
 #include "nm-core-internal.h"
 
 #include "nm-device-ethernet.h"
-#include "nm-settings.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
 #include "nm-bus-manager.h"
@@ -69,7 +70,6 @@
 #include "nm-session-monitor.h"
 #include "plugins/keyfile/plugin.h"
 #include "nm-agent-manager.h"
-#include "nm-connection-provider.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
 #include "NetworkManagerUtils.h"
@@ -133,11 +133,11 @@ static void claim_connection (NMSettings *self,
 static void unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data);
 static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data);
 
-static void connection_provider_iface_init (NMConnectionProviderInterface *cp_iface);
-
-G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION_PROVIDER, connection_provider_iface_init))
+static void connection_ready_changed (NMSettingsConnection *conn,
+                                      GParamSpec *pspec,
+                                      gpointer user_data);
 
+G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT);
 
 typedef struct {
 	NMAgentManager *agent_mgr;
@@ -149,9 +149,9 @@ typedef struct {
 	GSList *plugins;
 	gboolean connections_loaded;
 	GHashTable *connections;
+	NMSettingsConnection **connections_cached_list;
 	GSList *unmanaged_specs;
 	GSList *unrecognized_specs;
-	GSList *get_connections_cache;
 
 	gboolean started;
 	gboolean startup_complete;
@@ -204,6 +204,11 @@ check_startup_complete (NMSettings *self)
 			return;
 	}
 
+	/* the connection_ready_changed signal handler is no longer needed. */
+	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;
 	_notify (self, PROP_STARTUP_COMPLETE);
 }
@@ -258,7 +263,7 @@ load_connections (NMSettings *self)
 	}
 
 	priv->connections_loaded = TRUE;
-	g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
+	_notify (self, PROP_CONNECTIONS);
 
 	unmanaged_specs_changed (NULL, self);
 	unrecognized_specs_changed (NULL, self);
@@ -275,7 +280,7 @@ nm_settings_for_each_connection (NMSettings *self,
 
 	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);
@@ -401,13 +406,60 @@ connection_sort (gconstpointer pa, gconstpointer pb)
 	return 1;
 }
 
+/**
+ * nm_settings_get_connections:
+ * @self: the #NMSettings
+ * @out_len: (out): (allow-none): returns the number of returned
+ *   connections.
+ *
+ * Returns: (transfer-none): a list of NMSettingsConnections. The list is
+ * unsorted and NULL terminated. The result is never %NULL, in case of no
+ * connections, it returns an empty list.
+ * The returned list is cached internally, only valid until the next
+ * NMSettings operation.
+ */
+NMSettingsConnection *const*
+nm_settings_get_connections (NMSettings *self, guint *out_len)
+{
+	GHashTableIter iter;
+	NMSettingsPrivate *priv;
+	guint l, i;
+	NMSettingsConnection **v;
+	NMSettingsConnection *con;
+
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	if (priv->connections_cached_list) {
+		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
+		return priv->connections_cached_list;
+	}
+
+	l = g_hash_table_size (priv->connections);
+
+	v = g_new (NMSettingsConnection *, l + 1);
+
+	i = 0;
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con))
+		v[i++] = con;
+	v[i] = NULL;
+
+	nm_assert (i == l);
+
+	NM_SET_OUT (out_len, l);
+	priv->connections_cached_list = v;
+	return v;
+}
+
 /* Returns a list of NMSettingsConnections.
  * The list is sorted in the order suitable for auto-connecting, i.e.
  * first go connections with autoconnect=yes and most recent timestamp.
  * Caller must free the list with g_slist_free().
  */
 GSList *
-nm_settings_get_connections (NMSettings *self)
+nm_settings_get_connections_sorted (NMSettings *self)
 {
 	GHashTableIter iter;
 	gpointer data = NULL;
@@ -886,7 +938,6 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer
 	               0,
 	               connection,
 	               by_user);
-	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
 }
 
 static void
@@ -920,17 +971,18 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self);
-	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_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 */
 	g_hash_table_remove (priv->connections, (gpointer) cpath);
+	g_clear_pointer (&priv->connections_cached_list, g_free);
 
 	/* Notify D-Bus */
 	g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
 
 	/* Re-emit for listeners like NMPolicy */
-	g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
 	_notify (self, PROP_CONNECTIONS);
 	if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection)))
 		nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection));
@@ -1069,6 +1121,7 @@ claim_connection (NMSettings *self, NMSettingsConnection *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", "++ ");
 
@@ -1078,7 +1131,6 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	if (priv->connections_loaded) {
 		/* Internal added signal */
 		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
-		g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection);
 		_notify (self, PROP_CONNECTIONS);
 
 		/* Exported D-Bus signal */
@@ -1154,16 +1206,6 @@ nm_settings_add_connection (NMSettings *self,
 	return NULL;
 }
 
-static NMConnection *
-_nm_connection_provider_add_connection (NMConnectionProvider *provider,
-                                        NMConnection *connection,
-                                        gboolean save_to_disk,
-                                        GError **error)
-{
-	g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider));
-	return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error));
-}
-
 static gboolean
 secrets_filter_cb (NMSetting *setting,
                    const char *secret,
@@ -1411,12 +1453,12 @@ impl_settings_add_connection_add_cb (NMSettings *self,
 {
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, subject, error->message);
+		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_connection_get_path (NM_CONNECTION (connection))));
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE,
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL,
 		                            subject, NULL);
 	}
 }
@@ -1470,31 +1512,6 @@ impl_settings_add_connection_unsaved (NMSettings *self,
 	impl_settings_add_connection_helper (self, context, settings, FALSE);
 }
 
-static gboolean
-ensure_root (NMBusManager          *dbus_mgr,
-             GDBusMethodInvocation *context)
-{
-	gulong caller_uid;
-	GError *error = NULL;
-
-	if (!nm_bus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Unable to determine request UID.");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-	if (caller_uid != 0) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
-		                             "Permission denied");
-		g_dbus_method_invocation_take_error (context, error);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
 static void
 impl_settings_load_connections (NMSettings *self,
                                 GDBusMethodInvocation *context,
@@ -1505,7 +1522,15 @@ impl_settings_load_connections (NMSettings *self,
 	GSList *iter;
 	int i;
 
-	if (!ensure_root (nm_bus_manager_get (), context))
+	/* 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_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_SETTINGS_ERROR,
+	                                NM_SETTINGS_ERROR_PERMISSION_DENIED))
 		return;
 
 	failures = g_ptr_array_new ();
@@ -1541,7 +1566,15 @@ impl_settings_reload_connections (NMSettings *self,
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GSList *iter;
 
-	if (!ensure_root (nm_bus_manager_get (), context))
+	/* 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_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)) {
@@ -1837,11 +1870,11 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	const char *setting_hwaddr;
-	const char *device_hwaddr;
+	const char *perm_hw_addr;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
 
-	device_hwaddr = nm_device_get_hw_address (device);
+	perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
 
 	/* Find a wired connection locked to the given MAC address, if any */
 	g_hash_table_iter_init (&iter, priv->connections);
@@ -1875,8 +1908,8 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		setting_hwaddr = nm_setting_wired_get_mac_address (s_wired);
 		if (setting_hwaddr) {
 			/* A connection mac-locked to this device */
-			if (   device_hwaddr
-			    && nm_utils_hwaddr_matches (setting_hwaddr, -1, device_hwaddr, -1))
+			if (   perm_hw_addr
+			    && nm_utils_hwaddr_matches (setting_hwaddr, -1, perm_hw_addr, -1))
 				return TRUE;
 		} else {
 			/* A connection that applies to any wired device */
@@ -2071,22 +2104,41 @@ nm_settings_sort_connections (gconstpointer a, gconstpointer b)
 	return 0;
 }
 
-static GSList *
-get_best_connections (NMConnectionProvider *provider,
-                      guint max_requested,
-                      const char *ctype1,
-                      const char *ctype2,
-                      NMConnectionFilterFunc func,
-                      gpointer func_data)
+/**
+ * nm_settings_get_best_connections:
+ * @self: the #NMSetting
+ * @max_requested: if non-zero, the maximum number of connections to return
+ * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to
+ *   filter connections against
+ * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME)
+ *   to filter connections against
+ * @func: caller-supplied function for filtering connections
+ * @func_data: caller-supplied data passed to @func
+ *
+ * Returns: a #GSList of #NMConnection objects in sorted order representing the
+ *   "best" or highest-priority connections filtered by @ctype1 and/or @ctype2,
+ *   and/or @func.  Caller is responsible for freeing the returned #GSList, but
+ *   the contained values do not need to be unreffed.
+ */
+GSList *
+nm_settings_get_best_connections (NMSettings *self,
+                                  guint max_requested,
+                                  const char *ctype1,
+                                  const char *ctype2,
+                                  NMConnectionFilterFunc func,
+                                  gpointer func_data)
 {
-	NMSettings *self = NM_SETTINGS (provider);
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	NMSettingsPrivate *priv;
 	GSList *sorted = NULL;
 	GHashTableIter iter;
 	NMSettingsConnection *connection;
 	guint added = 0;
 	guint64 oldest = 0;
 
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
 	g_hash_table_iter_init (&iter, priv->connections);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
 		guint64 cur_ts = 0;
@@ -2095,7 +2147,7 @@ get_best_connections (NMConnectionProvider *provider,
 			continue;
 		if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
 			continue;
-		if (func && !func (provider, NM_CONNECTION (connection), func_data))
+		if (func && !func (self, NM_CONNECTION (connection), func_data))
 			continue;
 
 		/* Don't bother with a connection that's older than the oldest one in the list */
@@ -2121,27 +2173,6 @@ get_best_connections (NMConnectionProvider *provider,
 	return g_slist_reverse (sorted);
 }
 
-static const GSList *
-get_connections (NMConnectionProvider *provider)
-{
-	GSList *list = NULL;
-	NMSettings *self = NM_SETTINGS (provider);
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	list = _nm_utils_hash_values_to_slist (priv->connections);
-
-	/* Cache the list every call so we can keep it 'const' for callers */
-	g_slist_free (priv->get_connections_cache);
-	priv->get_connections_cache = list;
-	return list;
-}
-
-static NMConnection *
-cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
-{
-	return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
-}
-
 /***************************************************************/
 
 gboolean
@@ -2190,14 +2221,30 @@ setup_hostname_file_monitors (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	GFileMonitor *monitor;
+	const char *path = HOSTNAME_FILE;
+	char *link_path = NULL;
+	struct stat file_stat;
 	GFile *file;
 
 	priv->hostname.value = nm_settings_get_hostname (self);
 
+	/* resolve the path to the hostname file if it is a symbolic link */
+	if (   lstat(path, &file_stat) == 0
+	    && S_ISLNK (file_stat.st_mode)
+	    && (link_path = nm_utils_read_link_absolute (path, NULL))) {
+		path = link_path;
+		if (   lstat(link_path, &file_stat) == 0
+		    && S_ISLNK (file_stat.st_mode)) {
+			_LOGW ("only one level of symbolic link indirection is allowed when monitoring "
+			       HOSTNAME_FILE);
+		}
+	}
+
 	/* monitor changes to hostname file */
-	file = g_file_new_for_path (HOSTNAME_FILE);
+	file = g_file_new_for_path (path);
 	monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
 	g_object_unref (file);
+	g_free(link_path);
 	if (monitor) {
 		priv->hostname.monitor_id = g_signal_connect (monitor, "changed",
 		                                              G_CALLBACK (hostname_file_changed_cb),
@@ -2287,15 +2334,6 @@ nm_settings_start (NMSettings *self, GError **error)
 }
 
 static void
-connection_provider_iface_init (NMConnectionProviderInterface *cp_iface)
-{
-    cp_iface->get_best_connections = get_best_connections;
-    cp_iface->get_connections = get_connections;
-    cp_iface->add_connection = _nm_connection_provider_add_connection;
-    cp_iface->get_connection_by_uuid = cp_get_connection_by_uuid;
-}
-
-static void
 nm_settings_init (NMSettings *self)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -2359,7 +2397,7 @@ finalize (GObject *object)
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 
 	g_hash_table_destroy (priv->connections);
-	g_slist_free (priv->get_connections_cache);
+	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);
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 5d41e5bc..a0be42ee 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -49,6 +49,18 @@
 #define NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED "connection-visibility-changed"
 #define NM_SETTINGS_SIGNAL_AGENT_REGISTERED              "agent-registered"
 
+/**
+ * NMConnectionFilterFunc:
+ * @settings: The #NMSettings requesting the filtering
+ * @connection: the connection to be filtered
+ * @func_data: the caller-provided data pointer
+ *
+ * Returns: %TRUE to allow the connection, %FALSE to ignore it
+ */
+typedef gboolean (*NMConnectionFilterFunc) (NMSettings *settings,
+                                            NMConnection *connection,
+                                            gpointer func_data);
+
 struct _NMSettings {
 	NMExportedObject parent_instance;
 };
@@ -61,6 +73,9 @@ typedef void (*NMSettingsSetHostnameCb) (const char *name, gboolean result, gpoi
 
 GType nm_settings_get_type (void);
 
+NMSettings *nm_settings_get (void);
+#define NM_SETTINGS_GET (nm_settings_get ())
+
 NMSettings *nm_settings_new (void);
 gboolean nm_settings_start (NMSettings *self, GError **error);
 
@@ -86,10 +101,16 @@ void nm_settings_add_connection_dbus (NMSettings *self,
                                       NMSettingsAddCallback callback,
                                       gpointer user_data);
 
-/* Returns a list of NMSettingsConnections.  Caller must free the list with
- * g_slist_free().
- */
-GSList *nm_settings_get_connections (NMSettings *settings);
+NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings, guint *out_len);
+
+GSList *nm_settings_get_connections_sorted (NMSettings *settings);
+
+GSList *nm_settings_get_best_connections (NMSettings *self,
+                                          guint max_requested,
+                                          const char *ctype1,
+                                          const char *ctype2,
+                                          NMConnectionFilterFunc func,
+                                          gpointer func_data);
 
 NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
                                                   NMConnection *connection,
diff --git a/src/settings/plugins/Makefile.in b/src/settings/plugins/Makefile.in
index e770ccde..ba388a5f 100644
--- a/src/settings/plugins/Makefile.in
+++ b/src/settings/plugins/Makefile.in
@@ -93,8 +93,9 @@ host_triplet = @host@
 @CONFIG_PLUGIN_IFNET_TRUE@am__append_4 = ifnet
 subdir = src/settings/plugins
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -229,6 +230,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -285,13 +287,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -303,6 +306,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -363,6 +368,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -398,6 +405,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ibft/Makefile.in b/src/settings/plugins/ibft/Makefile.in
index fe124670..9da764de 100644
--- a/src/settings/plugins/ibft/Makefile.in
+++ b/src/settings/plugins/ibft/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/ibft
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -296,6 +297,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -352,13 +354,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -370,6 +373,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -430,6 +435,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -465,6 +472,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ibft/nm-ibft-connection.h b/src/settings/plugins/ibft/nm-ibft-connection.h
index 4ccebc3c..a4ee17ac 100644
--- a/src/settings/plugins/ibft/nm-ibft-connection.h
+++ b/src/settings/plugins/ibft/nm-ibft-connection.h
@@ -21,8 +21,6 @@
 #ifndef NM_IBFT_CONNECTION_H
 #define NM_IBFT_CONNECTION_H
 
-G_BEGIN_DECLS
-
 #include <nm-settings-connection.h>
 
 #define NM_TYPE_IBFT_CONNECTION            (nm_ibft_connection_get_type ())
@@ -45,6 +43,4 @@ GType nm_ibft_connection_get_type (void);
 NMIbftConnection *nm_ibft_connection_new (const GPtrArray *block,
                                           GError **error);
 
-G_END_DECLS
-
 #endif /* NM_IBFT_CONNECTION_H */
diff --git a/src/settings/plugins/ibft/plugin.h b/src/settings/plugins/ibft/plugin.h
index 3fc58c7f..4867e07d 100644
--- a/src/settings/plugins/ibft/plugin.h
+++ b/src/settings/plugins/ibft/plugin.h
@@ -21,8 +21,6 @@
 #ifndef _PLUGIN_H_
 #define _PLUGIN_H_
 
-#include "nm-default.h"
-
 #define SETTINGS_TYPE_PLUGIN_IBFT            (settings_plugin_ibft_get_type ())
 #define SETTINGS_PLUGIN_IBFT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IBFT, SettingsPluginIbft))
 #define SETTINGS_PLUGIN_IBFT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IBFT, SettingsPluginIbftClass))
diff --git a/src/settings/plugins/ibft/reader.h b/src/settings/plugins/ibft/reader.h
index 42969ae0..97ef0c71 100644
--- a/src/settings/plugins/ibft/reader.h
+++ b/src/settings/plugins/ibft/reader.h
@@ -23,8 +23,6 @@
 
 #include <nm-connection.h>
 
-#include "nm-default.h"
-
 gboolean read_ibft_blocks (const char *iscsiadm_path,
                            GSList **out_blocks,
                            GError **error);
diff --git a/src/settings/plugins/ibft/tests/Makefile.am b/src/settings/plugins/ibft/tests/Makefile.am
index 939947ca..e5cff73f 100644
--- a/src/settings/plugins/ibft/tests/Makefile.am
+++ b/src/settings/plugins/ibft/tests/Makefile.am
@@ -34,6 +34,9 @@ test_ibft_LDADD = \
 @VALGRIND_RULES@
 TESTS = test-ibft
 
+check-local:
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ibft.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 endif
 
 EXTRA_DIST = \
diff --git a/src/settings/plugins/ibft/tests/Makefile.in b/src/settings/plugins/ibft/tests/Makefile.in
index c3fef788..f0c681ab 100644
--- a/src/settings/plugins/ibft/tests/Makefile.in
+++ b/src/settings/plugins/ibft/tests/Makefile.in
@@ -92,8 +92,9 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@TESTS = test-ibft$(EXEEXT)
 subdir = src/settings/plugins/ibft/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -424,6 +425,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -480,13 +482,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -498,6 +501,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -558,6 +563,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -593,6 +600,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -1034,8 +1042,9 @@ distdir: $(DISTFILES)
 	    || exit 1; \
 	  fi; \
 	done
+@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
 check: check-am
 all-am: Makefile $(PROGRAMS)
 installdirs:
@@ -1145,19 +1154,20 @@ uninstall-am:
 
 .MAKE: check-am install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
-	clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
-	ctags ctags-am distclean distclean-compile distclean-generic \
-	distclean-libtool distclean-tags distdir dvi dvi-am html \
-	html-am info info-am install install-am install-data \
-	install-data-am install-dvi install-dvi-am install-exec \
-	install-exec-am install-html install-html-am install-info \
-	install-info-am install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	recheck tags tags-am uninstall uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am \
+	check-local clean clean-generic clean-libtool \
+	clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+	uninstall-am
 
 .PRECIOUS: Makefile
 
@@ -1166,6 +1176,9 @@ uninstall-am:
 
 @ENABLE_TESTS_TRUE@@VALGRIND_RULES@
 
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ibft.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c
index 9c98d891..cab468de 100644
--- a/src/settings/plugins/ibft/tests/test-ibft.c
+++ b/src/settings/plugins/ibft/tests/test-ibft.c
@@ -34,7 +34,7 @@
 
 #include "reader.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static GPtrArray *
 read_block (const char *iscsiadm_path, const char *expected_mac)
diff --git a/src/settings/plugins/ifcfg-rh/Makefile.in b/src/settings/plugins/ifcfg-rh/Makefile.in
index a87ee94c..3cd841c3 100644
--- a/src/settings/plugins/ifcfg-rh/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/Makefile.in
@@ -91,8 +91,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/ifcfg-rh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -306,6 +307,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -362,13 +364,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -380,6 +383,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -440,6 +445,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -475,6 +482,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ifcfg-rh/common.h b/src/settings/plugins/ifcfg-rh/common.h
index 5d6ebe6d..d850ac4e 100644
--- a/src/settings/plugins/ifcfg-rh/common.h
+++ b/src/settings/plugins/ifcfg-rh/common.h
@@ -21,8 +21,6 @@
 #ifndef __COMMON_H__
 #define __COMMON_H__
 
-#include "nm-default.h"
-
 #define IFCFG_TAG "ifcfg-"
 #define KEYS_TAG "keys-"
 #define ROUTE_TAG "route-"
@@ -38,7 +36,7 @@
 #define AUGNEW_TAG ".augnew"
 #define AUGTMP_TAG ".augtmp"
 
-#define IFCFG_DIR SYSCONFDIR"/sysconfig/network-scripts"
+#define IFCFG_DIR SYSCONFDIR "/sysconfig/network-scripts"
 
 #define IFCFG_PLUGIN_NAME "ifcfg-rh"
 #define IFCFG_PLUGIN_INFO "(c) 2007 - 2015 Red Hat, Inc.  To report bugs please use the NetworkManager mailing list."
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
index b9bbf6d5..82f20598 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
@@ -20,6 +20,8 @@
 
 #include "nm-default.h"
 
+#include "nm-ifcfg-connection.h"
+
 #include <string.h>
 
 #include <glib/gstdio.h>
@@ -37,7 +39,6 @@
 
 #include "common.h"
 #include "nm-config.h"
-#include "nm-ifcfg-connection.h"
 #include "reader.h"
 #include "writer.h"
 #include "nm-inotify-helper.h"
@@ -469,7 +470,7 @@ nm_ifcfg_connection_init (NMIfcfgConnection *connection)
 
 static void
 set_property (GObject *object, guint prop_id,
-		    const GValue *value, GParamSpec *pspec)
+            const GValue *value, GParamSpec *pspec)
 {
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
 
@@ -488,7 +489,7 @@ set_property (GObject *object, guint prop_id,
 
 static void
 get_property (GObject *object, guint prop_id,
-		    GValue *value, GParamSpec *pspec)
+            GValue *value, GParamSpec *pspec)
 {
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (object);
 
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
index 44e02987..57db059b 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h
@@ -21,10 +21,8 @@
 #ifndef __NETWORKMANAGER_IFCFG_CONNECTION_H__
 #define __NETWORKMANAGER_IFCFG_CONNECTION_H__
 
-G_BEGIN_DECLS
-
-#include <nm-dbus-interface.h>
-#include <nm-settings-connection.h>
+#include "nm-dbus-interface.h"
+#include "nm-settings-connection.h"
 
 #define NM_TYPE_IFCFG_CONNECTION            (nm_ifcfg_connection_get_type ())
 #define NM_IFCFG_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection))
@@ -58,6 +56,4 @@ gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self,
                                      GHashTable *new_settings,
                                      GError **error);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_IFCFG_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index 63184eb5..c2956d22 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -23,6 +23,8 @@
 
 #include "nm-default.h"
 
+#include "plugin.h"
+
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
@@ -31,20 +33,19 @@
 
 #include <gmodule.h>
 
+#include "nm-dbus-compat.h"
 #include "nm-setting-connection.h"
 
-#include "common.h"
-#include "plugin.h"
 #include "nm-settings-plugin.h"
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
 
 #include "nm-ifcfg-connection.h"
 #include "shvar.h"
+#include "common.h"
 #include "reader.h"
 #include "writer.h"
 #include "utils.h"
-#include "nm-dbus-compat.h"
 #include "nm-exported-object.h"
 
 #include "nmdbus-ifcfg-rh.h"
@@ -61,8 +62,6 @@
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
     } G_STMT_END
 
-#define ERR_GET_MSG(err) (((err) && (err)->message) ? (err)->message : "(unknown)")
-
 
 static NMIfcfgConnection *update_connection (SettingsPluginIfcfg *plugin,
                                              NMConnection *source,
@@ -364,17 +363,9 @@ update_connection (SettingsPluginIfcfg *self,
 		                  self);
 
 		if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) {
-			const char *spec;
-			const char *device_id;
-
-			spec = nm_ifcfg_connection_get_unmanaged_spec (connection_new);
-			device_id = strchr (spec, ':');
-			if (device_id)
-				device_id++;
-			else
-				device_id = spec;
-			_LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" / device '%s' due to NM_CONTROLLED=no.",
-			       NM_IFCFG_CONNECTION_LOG_ARG (connection_new), device_id);
+			_LOGI ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" due to NM_CONTROLLED=no. Unmanaged: %s.",
+			       NM_IFCFG_CONNECTION_LOG_ARG (connection_new),
+			       nm_ifcfg_connection_get_unmanaged_spec (connection_new));
 		} else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
 			_LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" of unrecognized type.", NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
 
diff --git a/src/settings/plugins/ifcfg-rh/plugin.h b/src/settings/plugins/ifcfg-rh/plugin.h
index eba734cf..74c4b00a 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.h
+++ b/src/settings/plugins/ifcfg-rh/plugin.h
@@ -24,8 +24,6 @@
 #ifndef _PLUGIN_H_
 #define _PLUGIN_H_
 
-#include "nm-exported-object.h"
-
 #define SETTINGS_TYPE_PLUGIN_IFCFG            (settings_plugin_ifcfg_get_type ())
 #define SETTINGS_PLUGIN_IFCFG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfg))
 #define SETTINGS_PLUGIN_IFCFG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass))
@@ -46,5 +44,5 @@ struct _SettingsPluginIfcfgClass {
 
 GType settings_plugin_ifcfg_get_type (void);
 
-#endif	/* _PLUGIN_H_ */
+#endif /* _PLUGIN_H_ */
 
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index fe762db1..af5d2910 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -20,6 +20,8 @@
 
 #include "nm-default.h"
 
+#include "reader.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -57,35 +59,31 @@
 #include "shvar.h"
 #include "utils.h"
 
-#include "reader.h"
+/*****************************************************************************/
 
-#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, "    " msg)
+#define _NMLOG_DOMAIN      LOGD_SETTINGS
+#define _NMLOG_PREFIX_NAME "ifcfg-rh"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), (_NMLOG_DOMAIN), \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
 
-static gboolean
-get_int (const char *str, int *value)
-{
-	char *e;
-	long int tmp;
+#define PARSE_WARNING(...) _LOGW ("%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), "    " _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
-	errno = 0;
-	tmp = strtol (str, &e, 0);
-	if (errno || *e != '\0' || tmp > G_MAXINT || tmp < G_MININT)
-		return FALSE;
-	*value = (int) tmp;
-	return TRUE;
-}
+/*****************************************************************************/
 
 static gboolean
 get_uint (const char *str, guint32 *value)
 {
-	char *e;
-	long unsigned int tmp;
+	gint64 tmp;
 
-	errno = 0;
-	tmp = strtoul (str, &e, 0);
-	if (errno || *e != '\0')
+	tmp = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT32, -1);
+	if (tmp == -1)
 		return FALSE;
-	*value = (guint32) tmp;
+	*value = tmp;
 	return TRUE;
 }
 
@@ -130,6 +128,7 @@ make_connection_setting (const char *file,
 	NMSettingConnectionLldp lldp;
 	const char *ifcfg_name = NULL;
 	char *new_id, *uuid = NULL, *zone = NULL, *value;
+	gs_free char *stable_id = NULL;
 
 	ifcfg_name = utils_get_ifcfg_name (file, TRUE);
 	if (!ifcfg_name)
@@ -148,9 +147,12 @@ make_connection_setting (const char *file,
 		uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
 	}
 
+	stable_id = svGetValue (ifcfg, "STABLE_ID", FALSE);
+
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_TYPE, type,
 	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_STABLE_ID, stable_id,
 	              NULL);
 	g_free (uuid);
 
@@ -242,15 +244,12 @@ make_connection_setting (const char *file,
 
 	value = svGetValue (ifcfg, "GATEWAY_PING_TIMEOUT", FALSE);
 	if (value) {
-		long int tmp;
-		guint32 timeout;
+		gint64 tmp;
 
-		errno = 0;
-		tmp = strtol (value, NULL, 10);
-		if (errno == 0 && tmp >= 0 && tmp < G_MAXINT32) {
-			timeout = (guint32) tmp;
-			g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, timeout, NULL);
-		} else
+		tmp = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32 - 1, -1);
+		if (tmp >= 0)
+			g_object_set (s_con, NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, (guint) tmp, NULL);
+		else
 			PARSE_WARNING ("invalid GATEWAY_PING_TIMEOUT time");
 		g_free (value);
 	}
@@ -541,7 +540,8 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError *
 	char **lines = NULL, **iter;
 	GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
 	GMatchInfo *match_info;
-	gint64 prefix_int, metric_int;
+	int prefix_int;
+	gint64 metric_int;
 	gboolean success = FALSE;
 
 	const char *pattern_empty = "^\\s*(\\#.*)?$";
@@ -609,9 +609,8 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError *
 		g_match_info_free (match_info);
 		prefix_int = 32;
 		if (prefix) {
-			errno = 0;
-			prefix_int = strtol (prefix, NULL, 10);
-			if (errno || prefix_int <= 0 || prefix_int > 32) {
+			prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 32, -1);
+			if (prefix_int == -1) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IP4 route destination prefix '%s'", prefix);
 				g_free (prefix);
@@ -641,9 +640,8 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError *
 		metric_int = -1;
 		if (g_match_info_matches (match_info)) {
 			metric = g_match_info_fetch (match_info, 1);
-			errno = 0;
-			metric_int = strtol (metric, NULL, 10);
-			if (errno || metric_int < 0) {
+			metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1);
+			if (metric_int == -1) {
 				g_match_info_free (match_info);
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IP4 route metric '%s'", metric);
@@ -766,7 +764,8 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 	GRegex *regex_to1, *regex_to2, *regex_via, *regex_metric;
 	GMatchInfo *match_info;
 	char *dest = NULL, *prefix = NULL, *next_hop = NULL, *metric = NULL;
-	gint64 prefix_int, metric_int;
+	int prefix_int;
+	gint64 metric_int;
 	gboolean success = FALSE;
 
 	const char *pattern_empty = "^\\s*(\\#.*)?$";
@@ -828,9 +827,8 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 		g_match_info_free (match_info);
 		prefix_int = 128;
 		if (prefix) {
-			errno = 0;
-			prefix_int = strtol (prefix, NULL, 10);
-			if (errno || prefix_int <= 0 || prefix_int > 128) {
+			prefix_int = _nm_utils_ascii_str_to_int64 (prefix, 10, 1, 128, -1);
+			if (prefix_int == -1) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IP6 route destination prefix '%s'", prefix);
 				g_free (dest);
@@ -864,9 +862,8 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 		metric_int = -1;
 		if (g_match_info_matches (match_info)) {
 			metric = g_match_info_fetch (match_info, 1);
-			errno = 0;
-			metric_int = strtol (metric, NULL, 10);
-			if (errno || metric_int < 0 || metric_int > G_MAXUINT32) {
+			metric_int = _nm_utils_ascii_str_to_int64 (metric, 10, 0, G_MAXUINT32, -1);
+			if (metric_int == -1) {
 				g_match_info_free (match_info);
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IP6 route metric '%s'", metric);
@@ -919,6 +916,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	shvarFile *route_ifcfg;
 	gboolean never_default = FALSE;
 	gint64 timeout;
+	gint priority;
 
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
 
@@ -1141,6 +1139,13 @@ make_ip4_setting (shvarFile *ifcfg,
 	g_free (dns_options);
 	dns_options = NULL;
 
+	/* DNS priority */
+	priority = svGetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0);
+	g_object_set (s_ip4,
+	              NM_SETTING_IP_CONFIG_DNS_PRIORITY,
+	              priority,
+	              NULL);
+
 	/* Static routes  - route-<name> file */
 	route_path = utils_get_route_path (ifcfg->fileName);
 
@@ -1323,6 +1328,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	char *ipv6addr, *ipv6addr_secondaries;
 	char **list = NULL, **iter;
 	guint32 i;
+	gint priority;
 	shvarFile *network_ifcfg;
 	gboolean never_default = FALSE;
 	gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
@@ -1525,6 +1531,13 @@ make_ip6_setting (shvarFile *ifcfg,
 		              NULL);
 	}
 
+	/* IPv6 tokenized interface identifier */
+	tmp = svGetValue (ifcfg, "IPV6_TOKEN", FALSE);
+	if (tmp) {
+		g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, tmp, NULL);
+		g_free (tmp);
+	}
+
 	/* DNS servers
 	 * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
 	 */
@@ -1572,6 +1585,13 @@ make_ip6_setting (shvarFile *ifcfg,
 	g_free (value);
 	g_free (dns_options);
 
+	/* DNS priority */
+	priority = svGetValueInt64 (ifcfg, "IPV6_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0);
+	g_object_set (s_ip6,
+	              NM_SETTING_IP_CONFIG_DNS_PRIORITY,
+	              priority,
+	              NULL);
+
 	return NM_SETTING (s_ip6);
 
 error:
@@ -1681,10 +1701,9 @@ read_dcb_app (shvarFile *ifcfg,
 	tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app);
 	val = svGetValue (ifcfg, tmp, FALSE);
 	if (val) {
-		success = get_int (val, &priority);
-		if (success)
-			success = (priority >= 0 && priority <= 7);
-		if (!success) {
+		priority = _nm_utils_ascii_str_to_int64 (val, 0, 0, 7, -1);
+		if (priority < 0) {
+			success = FALSE;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid %s value '%s' (expected 0 - 7)",
 			             tmp, val);
@@ -1824,7 +1843,6 @@ read_dcb_percent_array (shvarFile *ifcfg,
 	char *val;
 	gboolean success = FALSE;
 	char **split = NULL, **iter;
-	int tmp;
 	guint i, sum = 0;
 
 	val = svGetValue (ifcfg, prop, FALSE);
@@ -1847,7 +1865,10 @@ read_dcb_percent_array (shvarFile *ifcfg,
 	}
 
 	for (iter = split, i = 0; iter && *iter; iter++, i++) {
-		if (!get_int (*iter, &tmp) || tmp < 0 || tmp > 100) {
+		int tmp;
+
+		tmp = _nm_utils_ascii_str_to_int64 (*iter, 0, 0, 100, -1);
+		if (tmp < 0) {
 			PARSE_WARNING ("invalid %s percentage value '%s'", prop, *iter);
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid percent element");
@@ -2169,7 +2190,7 @@ make_wep_setting (shvarFile *ifcfg,
 	char *value;
 	shvarFile *keys_ifcfg = NULL;
 	int default_key_idx = 0;
-	gboolean has_default_key = FALSE, success;
+	gboolean has_default_key = FALSE;
 	NMSettingSecretFlags key_flags;
 
 	s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
@@ -2177,18 +2198,17 @@ make_wep_setting (shvarFile *ifcfg,
 
 	value = svGetValue (ifcfg, "DEFAULTKEY", FALSE);
 	if (value) {
-		success = get_int (value, &default_key_idx);
-		if (success && (default_key_idx >= 1) && (default_key_idx <= 4)) {
-			has_default_key = TRUE;
-			default_key_idx--;  /* convert to [0...3] */
-			g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, default_key_idx, NULL);
-		} else {
+		default_key_idx = _nm_utils_ascii_str_to_int64 (value, 0, 1, 4, 0);
+		if (default_key_idx == 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid default WEP key '%s'", value);
-	 		g_free (value);
+			g_free (value);
 			goto error;
 		}
- 		g_free (value);
+		has_default_key = TRUE;
+		default_key_idx--;  /* convert to [0...3] */
+		g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, (guint) default_key_idx, NULL);
+		g_free (value);
 	}
 
 	/* Read WEP key flags */
@@ -3348,6 +3368,10 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
+	value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE);
+	g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL);
+	g_free (value);
+
 	value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
 	if (value) {
 		char **strv;
@@ -3489,17 +3513,16 @@ make_wireless_setting (shvarFile *ifcfg,
 
 	value = svGetValue (ifcfg, "MTU", FALSE);
 	if (value) {
-		long int mtu;
+		int mtu;
 
-		errno = 0;
-		mtu = strtol (value, NULL, 10);
-		if (errno || mtu < 0 || mtu > 50000) {
+		mtu = _nm_utils_ascii_str_to_int64 (value, 10, 0, 50000, -1);
+		if (mtu == -1) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid wireless MTU '%s'", value);
 			g_free (value);
 			goto error;
 		}
-		g_object_set (s_wireless, NM_SETTING_WIRELESS_MTU, (guint32) mtu, NULL);
+		g_object_set (s_wireless, NM_SETTING_WIRELESS_MTU, (guint) mtu, NULL);
 		g_free (value);
 	}
 
@@ -3761,20 +3784,19 @@ make_wired_setting (shvarFile *ifcfg,
 {
 	NMSettingWired *s_wired;
 	char *value = NULL;
-	int mtu;
 	char *nettype;
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
 
 	value = svGetValue (ifcfg, "MTU", FALSE);
 	if (value) {
-		if (get_int (value, &mtu)) {
-			if (mtu >= 0 && mtu < 65536)
-				g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu, NULL);
-		} else {
-			/* Shouldn't be fatal... */
+		int mtu;
+
+		mtu = _nm_utils_ascii_str_to_int64 (value, 0, 0, 65535, -1);
+		if (mtu >= 0)
+			g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL);
+		else
 			PARSE_WARNING ("invalid MTU '%s'", value);
-		}
 		g_free (value);
 	}
 
@@ -3864,6 +3886,10 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
+	value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE);
+	g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL);
+	g_free (value);
+
 	value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE);
 	if (value) {
 		char **strv;
@@ -3945,9 +3971,9 @@ parse_infiniband_p_key (shvarFile *ifcfg,
                         char **out_parent,
                         GError **error)
 {
-	char *device = NULL, *physdev = NULL, *pkey_id = NULL, *end;
+	char *device = NULL, *physdev = NULL, *pkey_id = NULL;
 	char *ifname = NULL;
-	guint32 id = G_MAXUINT32;
+	int id;
 	gboolean ret = FALSE;
 
 	device = svGetValue (ifcfg, "DEVICE", FALSE);
@@ -3968,19 +3994,14 @@ parse_infiniband_p_key (shvarFile *ifcfg,
 		goto done;
 	}
 
-	if (g_str_has_prefix (pkey_id, "0x"))
-		id = strtoul (pkey_id, &end, 16);
-	else if (!g_str_has_prefix (pkey_id, "0"))
-		id = strtoul (pkey_id, &end, 10);
-	else
-		end = pkey_id;
-	if (end == pkey_id || *end || id > 0xFFFF) {
+	id = _nm_utils_ascii_str_to_int64 (pkey_id, 0, 0, 0xFFFF, -1);
+	if (id == -1) {
 		PARSE_WARNING ("invalid InfiniBand PKEY_ID '%s'", pkey_id);
 		goto done;
 	}
 	id = (id | 0x8000);
 
-	ifname = g_strdup_printf ("%s.%04x", physdev, id);
+	ifname = g_strdup_printf ("%s.%04x", physdev, (unsigned) id);
 	if (strcmp (device, ifname) != 0) {
 		PARSE_WARNING ("InfiniBand DEVICE (%s) does not match PHYSDEV+PKEY_ID (%s)",
 		               device, ifname);
@@ -4012,19 +4033,18 @@ make_infiniband_setting (shvarFile *ifcfg,
 {
 	NMSettingInfiniband *s_infiniband;
 	char *value = NULL;
-	int mtu;
 
 	s_infiniband = NM_SETTING_INFINIBAND (nm_setting_infiniband_new ());
 
 	value = svGetValue (ifcfg, "MTU", FALSE);
 	if (value) {
-		if (get_int (value, &mtu)) {
-			if (mtu >= 0 && mtu < 65536)
-				g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu, NULL);
-		} else {
-			/* Shouldn't be fatal... */
+		int mtu;
+
+		mtu = _nm_utils_ascii_str_to_int64 (value, 0, 0, 65535, -1);
+		if (mtu >= 0)
+			g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, (guint) mtu, NULL);
+		else
 			PARSE_WARNING ("invalid MTU '%s'", value);
-		}
 		g_free (value);
 	}
 
@@ -4648,16 +4668,14 @@ make_vlan_setting (shvarFile *ifcfg,
 	char *iface_name = NULL;
 	char *parent = NULL;
 	const char *p = NULL;
-	char *end = NULL;
-	gint vlan_id = -1;
+	int vlan_id = -1;
 	guint32 vlan_flags = 0;
 	gint gvrp, reorder_hdr;
 
 	value = svGetValue (ifcfg, "VLAN_ID", FALSE);
 	if (value) {
-		errno = 0;
-		vlan_id = (gint) g_ascii_strtoll (value, NULL, 10);
-		if (vlan_id < 0 || vlan_id > 4096 || errno) {
+		vlan_id = _nm_utils_ascii_str_to_int64 (value, 10, 0, 4095, -1);
+		if (vlan_id == -1) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid VLAN_ID '%s'", value);
 			g_free (value);
@@ -4701,12 +4719,13 @@ make_vlan_setting (shvarFile *ifcfg,
 		}
 
 		if (p) {
+			int device_vlan_id;
+
 			/* Grab VLAN ID from interface name; this takes precedence over the
 			 * separate VLAN_ID property for backwards compat.
 			 */
-
-			gint device_vlan_id = (gint) g_ascii_strtoll (p, &end, 10);
-			if (device_vlan_id >= 0 && device_vlan_id <= 4095 && end != p && !*end)
+			device_vlan_id = _nm_utils_ascii_str_to_int64 (p, 10, 0, 4095, -1);
+			if (device_vlan_id != -1)
 				vlan_id = device_vlan_id;
 		}
 	}
diff --git a/src/settings/plugins/ifcfg-rh/reader.h b/src/settings/plugins/ifcfg-rh/reader.h
index 88f9a720..35464474 100644
--- a/src/settings/plugins/ifcfg-rh/reader.h
+++ b/src/settings/plugins/ifcfg-rh/reader.h
@@ -21,10 +21,7 @@
 #ifndef __READER_H__
 #define __READER_H__
 
-#include <nm-connection.h>
-
-#include "nm-default.h"
-#include "shvar.h"
+#include "nm-connection.h"
 
 NMConnection *connection_from_file (const char *filename,
                                     char **out_unhandled,
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 4e756349..75b19d53 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -25,6 +25,8 @@
 
 #include "nm-default.h"
 
+#include "shvar.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -34,12 +36,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "shvar.h"
-
 #include "nm-core-internal.h"
 
-#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, "    " msg)
-
 /* Open the file <name>, returning a shvarFile on success and NULL on failure.
  * Add a wrinkle to let the caller specify whether or not to create the file
  * (actually, return a structure anyway) if it doesn't exist.
@@ -273,26 +271,23 @@ svGetValueFull (shvarFile *s, const char *key, gboolean verbatim)
 {
 	char *value = NULL;
 	char *line;
-	char *keyString;
-	int len;
+	guint len;
 
 	g_return_val_if_fail (s != NULL, NULL);
 	g_return_val_if_fail (key != NULL, NULL);
 
-	keyString = g_strdup_printf ("%s=", key);
-	len = strlen (keyString);
+	len = strlen (key);
 
 	for (s->current = s->lineList; s->current; s->current = s->current->next) {
 		line = s->current->data;
-		if (!strncmp (keyString, line, len)) {
+		if (!strncmp (key, line, len) && line[len] == '=') {
 			/* Strip trailing spaces before unescaping to preserve spaces quoted whitespace */
-			value = g_strchomp (g_strdup (line + len));
+			value = g_strchomp (g_strdup (line + len + 1));
 			if (!verbatim)
 				svUnescape (value);
 			break;
 		}
 	}
-	g_free (keyString);
 
 	return value;
 }
@@ -373,11 +368,10 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 
 	result = _nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback);
 	errsv = errno;
-	if (errsv != 0)
-		PARSE_WARNING ("Error reading '%s' value '%s' as integer (%d)", key, tmp, errsv);
 
 	g_free (tmp);
 
+	errno = errsv;
 	return result;
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index 227a44db..97df8128 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -31,10 +31,6 @@
 #ifndef _SHVAR_H
 #define _SHVAR_H
 
-#include "nm-default.h"
-
-G_BEGIN_DECLS
-
 typedef struct _shvarFile shvarFile;
 struct _shvarFile {
 	char      *fileName;    /* read-only */
@@ -95,6 +91,4 @@ const char *svEscape (const char *s, char **to_free);
 /* Unescape a string in-place */
 void svUnescape (char *s);
 
-G_END_DECLS
-
-#endif /* ! _SHVAR_H */
+#endif /* _SHVAR_H */
diff --git a/src/settings/plugins/ifcfg-rh/tests/Makefile.am b/src/settings/plugins/ifcfg-rh/tests/Makefile.am
index 73ddfeef..fedbd93f 100644
--- a/src/settings/plugins/ifcfg-rh/tests/Makefile.am
+++ b/src/settings/plugins/ifcfg-rh/tests/Makefile.am
@@ -49,5 +49,8 @@ test_ifcfg_rh_utils_LDADD = \
 @VALGRIND_RULES@
 TESTS = test-ifcfg-rh-utils test-ifcfg-rh
 
+check-local:
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifcfg-rh.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 endif
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
index 4bc35da2..ac21e19b 100644
--- a/src/settings/plugins/ifcfg-rh/tests/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/Makefile.in
@@ -94,8 +94,9 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@	test-ifcfg-rh$(EXEEXT)
 subdir = src/settings/plugins/ifcfg-rh/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -478,6 +479,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -534,13 +536,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -552,6 +555,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -612,6 +617,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -647,6 +654,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -1220,8 +1228,9 @@ distdir: $(DISTFILES)
 	      || exit 1; \
 	  fi; \
 	done
+@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
 check: check-recursive
 all-am: Makefile $(PROGRAMS)
 installdirs: installdirs-recursive
@@ -1333,19 +1342,19 @@ uninstall-am:
 .MAKE: $(am__recursive_targets) check-am install-am install-strip
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
-	check-TESTS check-am clean clean-generic clean-libtool \
-	clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
-	distclean-compile distclean-generic distclean-libtool \
-	distclean-tags distdir dvi dvi-am html html-am info info-am \
-	install install-am install-data install-data-am install-dvi \
-	install-dvi-am install-exec install-exec-am install-html \
-	install-html-am install-info install-info-am install-man \
-	install-pdf install-pdf-am install-ps install-ps-am \
-	install-strip installcheck installcheck-am installdirs \
-	installdirs-am maintainer-clean maintainer-clean-generic \
-	mostlyclean mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
-	uninstall uninstall-am
+	check-TESTS check-am check-local clean clean-generic \
+	clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
@@ -1354,6 +1363,9 @@ uninstall-am:
 
 @ENABLE_TESTS_TRUE@@VALGRIND_RULES@
 
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifcfg-rh.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
index 74f3bf05..30cc7c05 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/ifcfg-rh/tests/network-scripts
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -165,6 +166,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -221,13 +223,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -239,6 +242,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -299,6 +304,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -334,6 +341,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
index 5a0f6451..d2a348bb 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c
@@ -28,7 +28,7 @@
 #include "common.h"
 #include "utils.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static void
 test_get_ifcfg_name (const char *desc,
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 5ed25fda..60938f6e 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -55,7 +55,7 @@
 #include "writer.h"
 #include "utils.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /*****************************************************************************/
 
@@ -1734,6 +1734,69 @@ test_read_dns_options (void)
 }
 
 static void
+test_clear_master (void)
+{
+	NMSettingConnection *s_con;
+	NMConnection *connection;
+	char *unmanaged = NULL;
+	char *testfile = NULL, *keyfile;
+	GError *error = NULL;
+	gboolean success;
+	shvarFile *f;
+	char *val;
+
+	/* 1. load the bridge slave connection from disk */
+	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bridge-component",
+	                                    NULL, TYPE_ETHERNET, &unmanaged);
+	g_assert_cmpstr (unmanaged, ==, NULL);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, "br0");
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, "bridge");
+
+	/* 2. write the connection to a new file */
+	_writer_new_connection (connection,
+	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        &testfile);
+
+	/* 3. clear master and slave-type */
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_MASTER, NULL,
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, NULL,
+	              NULL);
+
+	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, NULL);
+	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NULL);
+
+	/* 4. update the connection on disk */
+	keyfile = utils_get_keys_path (testfile);
+	success = writer_update_connection (connection,
+	                                    TEST_SCRATCH_DIR "/network-scripts/",
+	                                    testfile,
+	                                    keyfile,
+	                                    &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	unlink (keyfile);
+	g_free (keyfile);
+
+	/* 5. check that BRIDGE variable has been removed */
+	f = svOpenFile (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (f);
+
+	val = svGetValue (f, "BRIDGE", FALSE);
+	g_assert (!val);
+	svCloseFile (f);
+
+	unlink (testfile);
+	g_free (testfile);
+	g_object_unref (connection);
+}
+
+static void
 test_write_dns_options (void)
 {
 	NMConnection *connection;
@@ -8781,6 +8844,7 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "static-ip6-only-gw/2001:db8:8:4::2", "2001:db8:8:4::2", test_write_wired_static_ip6_only_gw);
 	g_test_add_data_func (TPATH "static-ip6-only-gw/::ffff:255.255.255.255", "::ffff:255.255.255.255", test_write_wired_static_ip6_only_gw);
 	g_test_add_func (TPATH "read-dns-options", test_read_dns_options);
+	g_test_add_func (TPATH "clear-master", test_clear_master);
 
 	nmtst_add_test_func (TPATH "read-static",           test_read_wired_static, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static",           "System test-wired-static",           GINT_TO_POINTER (TRUE));
 	nmtst_add_test_func (TPATH "read-static-bootproto", test_read_wired_static, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-static-bootproto", "System test-wired-static-bootproto", GINT_TO_POINTER (FALSE));
diff --git a/src/settings/plugins/ifcfg-rh/utils.c b/src/settings/plugins/ifcfg-rh/utils.c
index fda900b2..b602a7d7 100644
--- a/src/settings/plugins/ifcfg-rh/utils.c
+++ b/src/settings/plugins/ifcfg-rh/utils.c
@@ -27,7 +27,8 @@
 
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "shvar.h"
+
+#include "common.h"
 
 /*
  * utils_single_quote_string
diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h
index 752d08a6..b8b172e7 100644
--- a/src/settings/plugins/ifcfg-rh/utils.h
+++ b/src/settings/plugins/ifcfg-rh/utils.h
@@ -21,10 +21,9 @@
 #ifndef _UTILS_H_
 #define _UTILS_H_
 
-#include <nm-connection.h>
-#include "nm-default.h"
+#include "nm-connection.h"
+
 #include "shvar.h"
-#include "common.h"
 
 #define NM_IFCFG_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NM_IFCFG_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index c17824d6..4817549e 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -42,7 +42,6 @@
 #include "nm-setting-vlan.h"
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
-#include "nm-core-internal.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
@@ -51,8 +50,20 @@
 #include "shvar.h"
 #include "reader.h"
 #include "utils.h"
-#include "crypto.h"
 
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_SETTINGS
+#define _NMLOG_PREFIX_NAME "ifcfg-rh"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), (_NMLOG_DOMAIN), \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
+/*****************************************************************************/
 
 static void
 save_secret_flags (shvarFile *ifcfg,
@@ -100,7 +111,7 @@ set_secret (shvarFile *ifcfg,
 {
 	shvarFile *keyfile;
 	GError *error = NULL;
-	
+
 	/* Clear the secret from the ifcfg and the associated "keys" file */
 	svSetValue (ifcfg, key, NULL, FALSE);
 
@@ -109,7 +120,7 @@ set_secret (shvarFile *ifcfg,
 
 	keyfile = utils_get_keys_ifcfg (ifcfg->fileName, TRUE);
 	if (!keyfile) {
-		nm_log_warn (LOGD_SETTINGS, "    could not create ifcfg file for '%s'", ifcfg->fileName);
+		_LOGW ("could not create ifcfg file for '%s'", ifcfg->fileName);
 		goto error;
 	}
 
@@ -121,8 +132,8 @@ set_secret (shvarFile *ifcfg,
 		svSetValue (keyfile, key, value, verbatim);
 
 	if (!svWriteFile (keyfile, 0600, &error)) {
-		nm_log_warn (LOGD_SETTINGS, "    could not update ifcfg file '%s': %s",
-		             keyfile->fileName, error->message);
+		_LOGW ("could not update ifcfg file '%s': %s",
+		       keyfile->fileName, error->message);
 		g_clear_error (&error);
 		svCloseFile (keyfile);
 		goto error;
@@ -715,7 +726,7 @@ write_wireless_security_setting (NMConnection *connection,
 						key = ascii_key;
 					}
 				} else {
-					nm_log_warn (LOGD_SETTINGS, "    invalid WEP key '%s'", key);
+					_LOGW ("invalid WEP key '%s'", key);
 					tmp = NULL;
 				}
 
@@ -842,6 +853,10 @@ write_wireless_setting (NMConnection *connection,
 	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
 	svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
 
+	svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	            nm_setting_wireless_get_generate_mac_address_mask (s_wireless),
+	            FALSE);
+
 	svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
 	macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
 	if (macaddr_blacklist[0]) {
@@ -1100,6 +1115,10 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
 	svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
 
+	svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	            nm_setting_wired_get_generate_mac_address_mask (s_wired),
+	            FALSE);
+
 	svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE);
 	macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
 	if (macaddr_blacklist[0]) {
@@ -1244,19 +1263,22 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg)
 		has_wired = TRUE;
 
 		device_mac = nm_setting_wired_get_mac_address (s_wired);
-		if (device_mac)
-			svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
+		svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
 
 		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		if (cloned_mac)
-			svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
+		svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
+
+		svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+		            nm_setting_wired_get_generate_mac_address_mask (s_wired),
+		            FALSE);
 
 		mtu = nm_setting_wired_get_mtu (s_wired);
 		if (mtu) {
 			tmp = g_strdup_printf ("%u", mtu);
 			svSetValue (ifcfg, "MTU", tmp, FALSE);
 			g_free (tmp);
-		}
+		} else
+			svSetValue (ifcfg, "MTU", NULL, FALSE);
 	}
 	return has_wired;
 }
@@ -1408,7 +1430,6 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	svSetValue (ifcfg, "DEVICE", iface, FALSE);
 	config = nm_setting_team_get_config (s_team);
 	svSetValue (ifcfg, "TEAM_CONFIG", config, FALSE);
-	svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM, FALSE);
 
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
@@ -1786,9 +1807,14 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	const char *master, *type;
 	char *tmp;
 	gint i_int;
+	const char *v_master = NULL;
+	const char *v_slave = NULL;
+	const char *v_bridge = NULL;
+	const char *v_team_master = NULL;
 
 	svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE);
 	svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE);
+	svSetValue (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con), FALSE);
 	svSetValue (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con), FALSE);
 	svSetValue (ifcfg, "ONBOOT",
 	            nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no",
@@ -1853,17 +1879,28 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	master = nm_setting_connection_get_master (s_con);
 	if (master) {
 		if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
-			svSetValue (ifcfg, "MASTER", master, FALSE);
-			svSetValue (ifcfg, "SLAVE", "yes", FALSE);
+			v_master = master;
+			v_slave = "yes";
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME))
-			svSetValue (ifcfg, "BRIDGE", master, FALSE);
+			v_bridge = master;
 		else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME)) {
-			svSetValue (ifcfg, "TEAM_MASTER", master, FALSE);
-			svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT, FALSE);
+			v_team_master = master;
 			svSetValue (ifcfg, "TYPE", NULL, FALSE);
 		}
 	}
 
+	svSetValue (ifcfg, "MASTER", v_master, FALSE);
+	svSetValue (ifcfg, "SLAVE", v_slave, FALSE);
+	svSetValue (ifcfg, "BRIDGE", v_bridge, FALSE);
+	svSetValue (ifcfg, "TEAM_MASTER", v_team_master, FALSE);
+
+	if (nm_streq0 (type, NM_SETTING_TEAM_SETTING_NAME))
+		svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM, FALSE);
+	else if (master && nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME))
+		svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT, FALSE);
+	else
+		svSetValue (ifcfg, "DEVICETYPE", NULL, FALSE);
+
 	/* secondary connection UUIDs */
 	svSetValue (ifcfg, "SECONDARY_UUIDS", NULL, FALSE);
 	n = nm_setting_connection_get_num_secondaries (s_con);
@@ -1970,6 +2007,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	gint32 j;
 	guint32 i, n, num;
 	gint64 route_metric;
+	gint priority;
 	int timeout;
 	GString *searches;
 	gboolean success = FALSE;
@@ -2281,6 +2319,12 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		svSetValueInt64 (ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1);
 	}
 
+	priority = nm_setting_ip_config_get_dns_priority (s_ip4);
+	if (priority)
+		svSetValueInt64 (ifcfg, "IPV4_DNS_PRIORITY", priority);
+	else
+		svSetValue (ifcfg, "IPV4_DNS_PRIORITY", NULL, FALSE);
+
 	success = TRUE;
 
 out:
@@ -2440,6 +2484,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	char *addr_key;
 	char *tmp;
 	guint32 i, num, num4;
+	gint priority;
 	GString *searches;
 	NMIPAddress *addr;
 	const char *dns;
@@ -2604,8 +2649,20 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		                            addr_gen_mode);
 		svSetValue (ifcfg, "IPV6_ADDR_GEN_MODE", tmp, FALSE);
 		g_free (tmp);
+	} else {
+		svSetValue (ifcfg, "IPV6_ADDR_GEN_MODE", NULL, FALSE);
 	}
 
+	/* IPv6 tokenized interface identifier */
+	value = nm_setting_ip6_config_get_token (NM_SETTING_IP6_CONFIG (s_ip6));
+	svSetValue (ifcfg, "IPV6_TOKEN", value, FALSE);
+
+	priority = nm_setting_ip_config_get_dns_priority (s_ip6);
+	if (priority)
+		svSetValueInt64 (ifcfg, "IPV6_DNS_PRIORITY", priority);
+	else
+		svSetValue (ifcfg, "IPV6_DNS_PRIORITY", NULL, FALSE);
+
 	/* Static routes go to route6-<dev> file */
 	route6_path = utils_get_route6_path (ifcfg->fileName);
 	if (!route6_path) {
diff --git a/src/settings/plugins/ifcfg-rh/writer.h b/src/settings/plugins/ifcfg-rh/writer.h
index 69389361..97a9f252 100644
--- a/src/settings/plugins/ifcfg-rh/writer.h
+++ b/src/settings/plugins/ifcfg-rh/writer.h
@@ -21,11 +21,7 @@
 #ifndef _WRITER_H_
 #define _WRITER_H_
 
-#include <sys/types.h>
-
-#include <nm-connection.h>
-
-#include "nm-default.h"
+#include "nm-connection.h"
 
 gboolean writer_can_write_connection (NMConnection *connection,
                                       GError **error);
diff --git a/src/settings/plugins/ifnet/Makefile.in b/src/settings/plugins/ifnet/Makefile.in
index 9f99e957..44295ada 100644
--- a/src/settings/plugins/ifnet/Makefile.in
+++ b/src/settings/plugins/ifnet/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/ifnet
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -297,6 +298,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -353,13 +355,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -371,6 +374,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -431,6 +436,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -466,6 +473,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index 80b82535..9917463f 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -679,7 +679,7 @@ make_ip4_setting (NMConnection *connection,
 	/* DNS searches */
 	value = ifnet_get_data (conn_name, "dns_search");
 	if (value) {
-		char *stripped = g_strdup (value);
+		gs_free char *stripped = g_strdup (value);
 		char **searches = NULL;
 
 		strip_string (stripped, '"');
diff --git a/src/settings/plugins/ifnet/net_parser.c b/src/settings/plugins/ifnet/net_parser.c
index 6e1061c6..db5e7a13 100644
--- a/src/settings/plugins/ifnet/net_parser.c
+++ b/src/settings/plugins/ifnet/net_parser.c
@@ -501,13 +501,13 @@ GList *
 ifnet_get_connection_names (void)
 {
 	GList *names = g_hash_table_get_keys (conn_table);
-	GList *result = NULL;
+	GList *iter, *result = NULL;
 
-	while (names) {
-		if (!ignore_connection_name (names->data))
-			result = g_list_prepend (result, names->data);
-		names = names->next;
+	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);
 }
diff --git a/src/settings/plugins/ifnet/net_parser.h b/src/settings/plugins/ifnet/net_parser.h
index 968feaf5..31fc9ead 100644
--- a/src/settings/plugins/ifnet/net_parser.h
+++ b/src/settings/plugins/ifnet/net_parser.h
@@ -22,8 +22,6 @@
 #ifndef _NET_PARSER_H
 #define _NET_PARSER_H
 
-#include "nm-default.h"
-
 #define CONF_NET_FILE SYSCONFDIR "/conf.d/net"
 
 gboolean ifnet_init (gchar * config_file);
@@ -40,4 +38,5 @@ 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/net_utils.h b/src/settings/plugins/ifnet/net_utils.h
index 45969369..85a5cb1c 100644
--- a/src/settings/plugins/ifnet/net_utils.h
+++ b/src/settings/plugins/ifnet/net_utils.h
@@ -21,12 +21,15 @@
 
 #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 "nm-default.h"
 #include "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)
 
diff --git a/src/settings/plugins/ifnet/nm-ifnet-connection.h b/src/settings/plugins/ifnet/nm-ifnet-connection.h
index baa4c07e..856996e0 100644
--- a/src/settings/plugins/ifnet/nm-ifnet-connection.h
+++ b/src/settings/plugins/ifnet/nm-ifnet-connection.h
@@ -25,8 +25,6 @@
 #include <nm-settings-connection.h>
 #include "net_parser.h"
 
-G_BEGIN_DECLS
-
 #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))
@@ -49,5 +47,4 @@ NMIfnetConnection *nm_ifnet_connection_new (NMConnection *source,
 
 const char *nm_ifnet_connection_get_conn_name (NMIfnetConnection *connection);
 
-G_END_DECLS
-#endif				/* NM_IFNET_CONNECTION_H */
+#endif /* __NETWORKMANAGER_IFNET_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifnet/plugin.h b/src/settings/plugins/ifnet/plugin.h
index 33dcc201..09640968 100644
--- a/src/settings/plugins/ifnet/plugin.h
+++ b/src/settings/plugins/ifnet/plugin.h
@@ -23,8 +23,6 @@
 #ifndef _PLUGIN_H_
 #define _PLUGIN_H_
 
-#include "nm-default.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))
diff --git a/src/settings/plugins/ifnet/tests/Makefile.am b/src/settings/plugins/ifnet/tests/Makefile.am
index 6590d4ae..8ece14c6 100644
--- a/src/settings/plugins/ifnet/tests/Makefile.am
+++ b/src/settings/plugins/ifnet/tests/Makefile.am
@@ -34,9 +34,12 @@ test_ifnet_LDFLAGS = \
 
 test_ifnet_LDADD = $(top_builddir)/src/libNetworkManager.la
 
-#@VALGRIND_RULES@
+@VALGRIND_RULES@
 TESTS = test-ifnet
 
+check-local:
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifnet.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 endif
 
 EXTRA_DIST = \
diff --git a/src/settings/plugins/ifnet/tests/Makefile.in b/src/settings/plugins/ifnet/tests/Makefile.in
index 4646a342..0aee0582 100644
--- a/src/settings/plugins/ifnet/tests/Makefile.in
+++ b/src/settings/plugins/ifnet/tests/Makefile.in
@@ -92,8 +92,9 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@TESTS = test-ifnet$(EXEEXT)
 subdir = src/settings/plugins/ifnet/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -430,6 +431,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -486,13 +488,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -504,6 +507,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -564,6 +569,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -599,6 +606,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -1084,8 +1092,9 @@ distdir: $(DISTFILES)
 	    || exit 1; \
 	  fi; \
 	done
+@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
 check: check-am
 all-am: Makefile $(PROGRAMS)
 installdirs:
@@ -1195,25 +1204,31 @@ uninstall-am:
 
 .MAKE: check-am install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
-	clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
-	ctags ctags-am distclean distclean-compile distclean-generic \
-	distclean-libtool distclean-tags distdir dvi dvi-am html \
-	html-am info info-am install install-am install-data \
-	install-data-am install-dvi install-dvi-am install-exec \
-	install-exec-am install-html install-html-am install-info \
-	install-info-am install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	recheck tags tags-am uninstall uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am \
+	check-local clean clean-generic clean-libtool \
+	clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+	uninstall-am
 
 .PRECIOUS: Makefile
 
 
 @ENABLE_TESTS_TRUE@@GNOME_CODE_COVERAGE_RULES@
 
+@ENABLE_TESTS_TRUE@@VALGRIND_RULES@
+
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifnet.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/src/settings/plugins/ifnet/tests/test-ifnet.c b/src/settings/plugins/ifnet/tests/test-ifnet.c
index 3035fdb1..49f88f1c 100644
--- a/src/settings/plugins/ifnet/tests/test-ifnet.c
+++ b/src/settings/plugins/ifnet/tests/test-ifnet.c
@@ -38,7 +38,7 @@
 #include "connection_parser.h"
 #include "nm-config.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /* Fake NMConfig handling; the values it returns don't matter, so this
  * is easier than forcing it to read our own config file, etc.
@@ -346,7 +346,7 @@ test_delete_connection (void)
 static void
 test_missing_config (void)
 {
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 
 	connection = ifnet_update_connection_from_config_block ("eth8", NULL, &error);
diff --git a/src/settings/plugins/ifnet/wpa_parser.c b/src/settings/plugins/ifnet/wpa_parser.c
index 8e2559b3..505222ab 100644
--- a/src/settings/plugins/ifnet/wpa_parser.c
+++ b/src/settings/plugins/ifnet/wpa_parser.c
@@ -77,8 +77,10 @@ add_security (GHashTable *security)
 	/* Hex format begins with " */
 	is_hex_ssid = (ssid[0] != '"');
 	if ((value = g_hash_table_lookup (security, "disabled")) != NULL) {
-		if (strcmp (value, "1") == 0)
+		if (strcmp (value, "1") == 0) {
+			destroy_security (security);
 			return NULL;
+		}
 	}
 
 	/* Default priority is 1 */
@@ -111,6 +113,7 @@ add_security (GHashTable *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={"))
@@ -133,6 +136,14 @@ add_key_value (GHashTable * network, gchar * line)
 	    && !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);
diff --git a/src/settings/plugins/ifnet/wpa_parser.h b/src/settings/plugins/ifnet/wpa_parser.h
index 8099d19c..d096f468 100644
--- a/src/settings/plugins/ifnet/wpa_parser.h
+++ b/src/settings/plugins/ifnet/wpa_parser.h
@@ -22,8 +22,6 @@
 #ifndef _WPA_PARSER_H
 #define _WPA_PARSER_H
 
-#include "nm-default.h"
-
 #define WPA_SUPPLICANT_CONF SYSCONFDIR "/wpa_supplicant/wpa_supplicant.conf"
 
 void wpa_parser_init (const char *wpa_supplicant_conf);
diff --git a/src/settings/plugins/ifupdown/Makefile.in b/src/settings/plugins/ifupdown/Makefile.in
index 8b153c6d..4e1c657b 100644
--- a/src/settings/plugins/ifupdown/Makefile.in
+++ b/src/settings/plugins/ifupdown/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/ifupdown
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -296,6 +297,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -352,13 +354,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -370,6 +373,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -430,6 +435,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -465,6 +472,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/ifupdown/interface_parser.h b/src/settings/plugins/ifupdown/interface_parser.h
index d4bb8515..bf9c4465 100644
--- a/src/settings/plugins/ifupdown/interface_parser.h
+++ b/src/settings/plugins/ifupdown/interface_parser.h
@@ -24,8 +24,6 @@
 #ifndef _INTERFACE_PARSER_H
 #define _INTERFACE_PARSER_H
 
-#include "nm-default.h"
-
 typedef struct _if_data
 {
 	char *key;
diff --git a/src/settings/plugins/ifupdown/nm-ifupdown-connection.h b/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
index 8085b831..e1999bef 100644
--- a/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
+++ b/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
@@ -25,10 +25,8 @@
 #define __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__
 
 #include <nm-settings-connection.h>
-#include "nm-default.h"
-#include "interface_parser.h"
 
-G_BEGIN_DECLS
+#include "interface_parser.h"
 
 #define NM_TYPE_IFUPDOWN_CONNECTION            (nm_ifupdown_connection_get_type ())
 #define NM_IFUPDOWN_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnection))
@@ -49,6 +47,4 @@ GType nm_ifupdown_connection_get_type (void);
 
 NMIfupdownConnection *nm_ifupdown_connection_new (if_block *block);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ */
diff --git a/src/settings/plugins/ifupdown/parser.h b/src/settings/plugins/ifupdown/parser.h
index ee88c500..549f1ad3 100644
--- a/src/settings/plugins/ifupdown/parser.h
+++ b/src/settings/plugins/ifupdown/parser.h
@@ -21,14 +21,15 @@
  * (C) Copyright 2008 Canonical Ltd.
  */
 
+#ifndef __PARSER_H__
+#define __PARSER_H__
+
 #include <nm-connection.h>
 #include "interface_parser.h"
 
-G_BEGIN_DECLS
-
 gboolean
 ifupdown_update_connection_from_if_block (NMConnection *connection,
                                           if_block *block,
                                           GError **error);
 
-G_END_DECLS
+#endif /* __PARSER_H__ */
diff --git a/src/settings/plugins/ifupdown/plugin.h b/src/settings/plugins/ifupdown/plugin.h
index 10ba2287..b568ea78 100644
--- a/src/settings/plugins/ifupdown/plugin.h
+++ b/src/settings/plugins/ifupdown/plugin.h
@@ -24,8 +24,6 @@
 #ifndef _PLUGIN_H_
 #define _PLUGIN_H_
 
-#include "nm-default.h"
-
 #define PLUGIN_NAME "ifupdown"
 
 #define SETTINGS_TYPE_PLUGIN_IFUPDOWN            (settings_plugin_ifupdown_get_type ())
diff --git a/src/settings/plugins/ifupdown/tests/Makefile.am b/src/settings/plugins/ifupdown/tests/Makefile.am
index e12c0181..1287a716 100644
--- a/src/settings/plugins/ifupdown/tests/Makefile.am
+++ b/src/settings/plugins/ifupdown/tests/Makefile.am
@@ -23,10 +23,12 @@ test_ifupdown_SOURCES = \
 test_ifupdown_LDADD = \
 	$(top_builddir)/src/libNetworkManager.la
 
-# TODO: enable valgrind for ifupdown. Currently it fails.
-#@VALGRIND_RULES@
+@VALGRIND_RULES@
 TESTS = test-ifupdown
 
+check-local:
+	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifupdown.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 endif
 
 EXTRA_DIST = \
diff --git a/src/settings/plugins/ifupdown/tests/Makefile.in b/src/settings/plugins/ifupdown/tests/Makefile.in
index f744e620..b63f9a89 100644
--- a/src/settings/plugins/ifupdown/tests/Makefile.in
+++ b/src/settings/plugins/ifupdown/tests/Makefile.in
@@ -92,8 +92,9 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@TESTS = test-ifupdown$(EXEEXT)
 subdir = src/settings/plugins/ifupdown/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -425,6 +426,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -481,13 +483,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -499,6 +502,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -559,6 +564,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -594,6 +601,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -1039,8 +1047,9 @@ distdir: $(DISTFILES)
 	    || exit 1; \
 	  fi; \
 	done
+@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
 check: check-am
 all-am: Makefile $(PROGRAMS)
 installdirs:
@@ -1150,23 +1159,29 @@ uninstall-am:
 
 .MAKE: check-am install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
-	clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
-	ctags ctags-am distclean distclean-compile distclean-generic \
-	distclean-libtool distclean-tags distdir dvi dvi-am html \
-	html-am info info-am install install-am install-data \
-	install-data-am install-dvi install-dvi-am install-exec \
-	install-exec-am install-html install-html-am install-info \
-	install-info-am install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	recheck tags tags-am uninstall uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am \
+	check-local clean clean-generic clean-libtool \
+	clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+	uninstall-am
 
 .PRECIOUS: Makefile
 
 
+@ENABLE_TESTS_TRUE@@VALGRIND_RULES@
+
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	LD_BIND_NOW=1 LD_PRELOAD=$(builddir)/../.libs/libnm-settings-plugin-ifupdown.so $(top_builddir)/src/NetworkManager --version >/dev/null
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index f383821c..7ce08aee 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -26,7 +26,7 @@
 #include "interface_parser.h"
 #include "parser.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 typedef struct {
 	char *key;
@@ -506,6 +506,7 @@ test17_read_static_ipv4 (const char *path)
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com");
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com");
 
+	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
@@ -560,6 +561,7 @@ test18_read_static_ipv6 (const char *path)
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com");
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com");
 
+	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
@@ -594,6 +596,7 @@ test19_read_static_ipv4_plen (const char *path)
 	g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3");
 	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8);
 
+	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
diff --git a/src/settings/plugins/keyfile/Makefile.in b/src/settings/plugins/keyfile/Makefile.in
index bace6af4..e6f4b4cf 100644
--- a/src/settings/plugins/keyfile/Makefile.in
+++ b/src/settings/plugins/keyfile/Makefile.in
@@ -90,8 +90,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/keyfile
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -265,6 +266,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -321,13 +323,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -339,6 +342,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -399,6 +404,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -434,6 +441,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/keyfile/nm-keyfile-connection.h b/src/settings/plugins/keyfile/nm-keyfile-connection.h
index f6fa432b..31dbe754 100644
--- a/src/settings/plugins/keyfile/nm-keyfile-connection.h
+++ b/src/settings/plugins/keyfile/nm-keyfile-connection.h
@@ -24,8 +24,6 @@
 
 #include <nm-settings-connection.h>
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_KEYFILE_CONNECTION            (nm_keyfile_connection_get_type ())
 #define NM_KEYFILE_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_KEYFILE_CONNECTION, NMKeyfileConnection))
 #define NM_KEYFILE_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_KEYFILE_CONNECTION, NMKeyfileConnectionClass))
@@ -47,6 +45,4 @@ NMKeyfileConnection *nm_keyfile_connection_new (NMConnection *source,
                                                 const char *filename,
                                                 GError **error);
 
-G_END_DECLS
-
 #endif /* __NETWORKMANAGER_KEYFILE_CONNECTION_H__ */
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index 6003c745..c5598fc5 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -26,7 +26,6 @@
 #include <sys/types.h>
 #include <string.h>
 
-#include <gmodule.h>
 #include <glib/gstdio.h>
 
 #include "nm-connection.h"
diff --git a/src/settings/plugins/keyfile/plugin.h b/src/settings/plugins/keyfile/plugin.h
index 07403270..a22b9c49 100644
--- a/src/settings/plugins/keyfile/plugin.h
+++ b/src/settings/plugins/keyfile/plugin.h
@@ -22,8 +22,6 @@
 #ifndef _PLUGIN_H_
 #define _PLUGIN_H_
 
-#include "nm-default.h"
-
 #define SETTINGS_TYPE_PLUGIN_KEYFILE            (settings_plugin_keyfile_get_type ())
 #define SETTINGS_PLUGIN_KEYFILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_KEYFILE, SettingsPluginKeyfile))
 #define SETTINGS_PLUGIN_KEYFILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_KEYFILE, SettingsPluginKeyfileClass))
diff --git a/src/settings/plugins/keyfile/reader.h b/src/settings/plugins/keyfile/reader.h
index a8021cb7..7bbc6005 100644
--- a/src/settings/plugins/keyfile/reader.h
+++ b/src/settings/plugins/keyfile/reader.h
@@ -24,8 +24,6 @@
 
 #include <nm-connection.h>
 
-#include "nm-default.h"
-
 NMConnection *nm_keyfile_plugin_connection_from_file (const char *filename, GError **error);
 
 #endif /* _KEYFILE_PLUGIN_READER_H */
diff --git a/src/settings/plugins/keyfile/tests/Makefile.in b/src/settings/plugins/keyfile/tests/Makefile.in
index 89708a18..e2e56d4e 100644
--- a/src/settings/plugins/keyfile/tests/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/Makefile.in
@@ -92,8 +92,9 @@ host_triplet = @host@
 @ENABLE_TESTS_TRUE@TESTS = test-keyfile$(EXEEXT)
 subdir = src/settings/plugins/keyfile/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -469,6 +470,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -525,13 +527,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -543,6 +546,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -603,6 +608,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -638,6 +645,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
index 8be2893c..88e03515 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/settings/plugins/keyfile/tests/keyfiles
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -165,6 +166,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -221,13 +223,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -239,6 +242,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -299,6 +304,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -334,6 +341,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index 7b66e198..e63fa495 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -34,7 +34,7 @@
 #include "writer.h"
 #include "utils.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static void
 check_ip_address (NMSettingIPConfig *config, int idx, const char *address, int plen)
@@ -1546,7 +1546,7 @@ test_read_wired_8021x_tls_blob_connection (void)
 
 	/* Make sure it's not a path, since it's a blob */
 	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL,
-	                       "*assertion*scheme == NM_SETTING_802_1X_CK_SCHEME_PATH*");
+	                       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);
diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h
index c18fb2bc..85aa5458 100644
--- a/src/settings/plugins/keyfile/utils.h
+++ b/src/settings/plugins/keyfile/utils.h
@@ -21,7 +21,6 @@
 #ifndef _UTILS_H_
 #define _UTILS_H_
 
-#include "nm-default.h"
 #include "NetworkManagerUtils.h"
 
 #define KEYFILE_PLUGIN_NAME "keyfile"
diff --git a/src/settings/plugins/keyfile/writer.h b/src/settings/plugins/keyfile/writer.h
index 25f44e94..89435651 100644
--- a/src/settings/plugins/keyfile/writer.h
+++ b/src/settings/plugins/keyfile/writer.h
@@ -24,8 +24,6 @@
 
 #include <nm-connection.h>
 
-#include "nm-default.h"
-
 gboolean nm_keyfile_plugin_write_connection (NMConnection *connection,
                                              const char *existing_path,
                                              gboolean force_rename,
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index 5ce8bb31..6283edd6 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -47,7 +47,6 @@ typedef struct
 	GHashTable *config;
 	GHashTable *blobs;
 	guint32    ap_scan;
-	NMSettingMacRandomization mac_randomization;
 	gboolean   fast_required;
 	gboolean   dispose_has_run;
 } NMSupplicantConfigPrivate;
@@ -85,7 +84,6 @@ nm_supplicant_config_init (NMSupplicantConfig * self)
 	                                     (GDestroyNotify) blob_free);
 
 	priv->ap_scan = 1;
-	priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT;
 	priv->dispose_has_run = FALSE;
 }
 
@@ -272,32 +270,6 @@ nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self)
 	return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan;
 }
 
-const char *
-nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self)
-{
-	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 0);
-
-	/**
-	 * mac_addr - MAC address policy default
-	 *
-	 * 0 = use permanent MAC address
-	 * 1 = use random MAC address for each ESS connection
-	 * 2 = like 1, but maintain OUI (with local admin bit set)
-	 *
-	 * By default, permanent MAC address is used unless policy is changed by
-	 * the per-network mac_addr parameter.
-	 */
-
-	switch (NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->mac_randomization) {
-	case NM_SETTING_MAC_RANDOMIZATION_ALWAYS:
-		return "1";
-	case NM_SETTING_MAC_RANDOMIZATION_NEVER:
-	case NM_SETTING_MAC_RANDOMIZATION_DEFAULT:
-	default:
-		return "0";
-	}
-}
-
 gboolean
 nm_supplicant_config_fast_required (NMSupplicantConfig *self)
 {
@@ -385,8 +357,6 @@ gboolean
 nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
                                            NMSettingWireless * setting,
                                            guint32 fixed_freq,
-                                           NMSupplicantFeature mac_randomization_support,
-                                           NMSettingMacRandomization mac_randomization_fallback,
                                            GError **error)
 {
 	NMSupplicantConfigPrivate *priv;
@@ -477,23 +447,6 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
 		}
 	}
 
-	priv->mac_randomization = nm_setting_wireless_get_mac_address_randomization (setting);
-	if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) {
-		priv->mac_randomization = mac_randomization_fallback;
-		if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) {
-			/* Don't use randomization, unless explicitly enabled.
-			 * Randomization can work badly with captive portals. */
-			priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER;
-		}
-	}
-
-	if (   priv->mac_randomization != NM_SETTING_MAC_RANDOMIZATION_NEVER
-	    && mac_randomization_support != NM_SUPPLICANT_FEATURE_YES) {
-		g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
-		             "cannot enable mac-randomization due to missing supplicant support");
-		return FALSE;
-	}
-
 	return TRUE;
 }
 
diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h
index 921bc16c..bf3e64c5 100644
--- a/src/supplicant-manager/nm-supplicant-config.h
+++ b/src/supplicant-manager/nm-supplicant-config.h
@@ -25,10 +25,8 @@
 #include <nm-setting-wireless.h>
 #include <nm-setting-wireless-security.h>
 #include <nm-setting-8021x.h>
-#include "nm-default.h"
-#include "nm-supplicant-types.h"
 
-G_BEGIN_DECLS
+#include "nm-supplicant-types.h"
 
 #define NM_TYPE_SUPPLICANT_CONFIG            (nm_supplicant_config_get_type ())
 #define NM_SUPPLICANT_CONFIG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_CONFIG, NMSupplicantConfig))
@@ -54,8 +52,6 @@ NMSupplicantConfig *nm_supplicant_config_new (void);
 
 guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self);
 
-const char *nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self);
-
 gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self);
 
 GVariant *nm_supplicant_config_to_variant (NMSupplicantConfig *self);
@@ -65,8 +61,6 @@ GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self);
 gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self,
                                                     NMSettingWireless *setting,
                                                     guint32 fixed_freq,
-                                                    NMSupplicantFeature mac_randomization_support,
-                                                    NMSettingMacRandomization mac_randomization_fallback,
                                                     GError **error);
 
 gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
@@ -86,6 +80,4 @@ gboolean nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
                                                  gboolean wired,
                                                  GError **error);
 
-G_END_DECLS
-
-#endif	/* NM_SUPPLICANT_CONFIG_H */
+#endif /* __NETWORKMANAGER_SUPPLICANT_CONFIG_H__ */
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index ce7a2a2c..626472d0 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -385,10 +385,11 @@ parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities)
 
 	g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT));
 
-	if (g_variant_lookup (capabilities, "Scan", "^a&s", &array)) {
-		if (_nm_utils_string_in_list ("active", array))
+	if (   g_variant_lookup (capabilities, "Scan", "^a&s", &array)
+	    && array) {
+		if (g_strv_contains (array, "active"))
 			have_active = TRUE;
-		if (_nm_utils_string_in_list ("ssid", array))
+		if (g_strv_contains (array, "ssid"))
 			have_ssid = TRUE;
 		g_free (array);
 	}
@@ -502,12 +503,6 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
 		priv->ap_support = ap_support;
 }
 
-NMSupplicantFeature
-nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self)
-{
-	return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mac_randomization_support;
-}
-
 static void
 set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
@@ -562,7 +557,7 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
 			                   g_variant_new ("(ssv)",
 			                                  WPAS_DBUS_IFACE_INTERFACE,
 			                                  "PreassocMacAddr",
-			                                  g_variant_new_string ("1")),
+			                                  g_variant_new_string ("0")),
 			                   G_DBUS_CALL_FLAGS_NONE,
 			                   -1,
 			                   priv->init_cancellable,
@@ -1222,9 +1217,7 @@ set_mac_randomization_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user
 		return;
 	}
 
-	_LOGI ("config: set MAC randomization to %s",
-	       nm_supplicant_config_get_mac_randomization (priv->cfg));
-
+	_LOGT ("config: set MAC randomization to 0");
 	add_network (self);
 }
 
@@ -1255,23 +1248,20 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	       nm_supplicant_config_get_ap_scan (priv->cfg));
 
 	if (priv->mac_randomization_support == NM_SUPPLICANT_FEATURE_YES) {
-		const char *mac_randomization = nm_supplicant_config_get_mac_randomization (priv->cfg);
-
 		/* Enable/disable association MAC address randomization */
 		g_dbus_proxy_call (priv->iface_proxy,
 		                   DBUS_INTERFACE_PROPERTIES ".Set",
 		                   g_variant_new ("(ssv)",
 		                                  WPAS_DBUS_IFACE_INTERFACE,
 		                                  "MacAddr",
-		                                  g_variant_new_string (mac_randomization)),
+		                                  g_variant_new_string ("0")),
 		                   G_DBUS_CALL_FLAGS_NONE,
 		                   -1,
 		                   priv->assoc_cancellable,
 		                   (GAsyncReadyCallback) set_mac_randomization_cb,
 		                   self);
-	} else {
+	} else
 		add_network (self);
-	}
 }
 
 gboolean
diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h
index dbe77b1c..a586e7ea 100644
--- a/src/supplicant-manager/nm-supplicant-interface.h
+++ b/src/supplicant-manager/nm-supplicant-interface.h
@@ -22,7 +22,6 @@
 #ifndef __NETWORKMANAGER_SUPPLICANT_INTERFACE_H__
 #define __NETWORKMANAGER_SUPPLICANT_INTERFACE_H__
 
-#include "nm-default.h"
 #include "nm-supplicant-types.h"
 
 /*
@@ -165,6 +164,4 @@ NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterfac
 void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self,
                                              NMSupplicantFeature apmode);
 
-NMSupplicantFeature nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self);
-
 #endif	/* NM_SUPPLICANT_INTERFACE_H */
diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c
index f2c63364..57cd5713 100644
--- a/src/supplicant-manager/nm-supplicant-manager.c
+++ b/src/supplicant-manager/nm-supplicant-manager.c
@@ -194,9 +194,11 @@ update_capabilities (NMSupplicantManager *self)
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
 			array = g_variant_get_strv (value, NULL);
 			priv->ap_support = NM_SUPPLICANT_FEATURE_NO;
-			if (_nm_utils_string_in_list ("ap", array))
-				priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
-			g_free (array);
+			if (array) {
+				if (g_strv_contains (array, "ap"))
+					priv->ap_support = NM_SUPPLICANT_FEATURE_YES;
+				g_free (array);
+			}
 		}
 		g_variant_unref (value);
 	}
@@ -215,9 +217,11 @@ update_capabilities (NMSupplicantManager *self)
 	if (value) {
 		if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
 			array = g_variant_get_strv (value, NULL);
-			if (_nm_utils_string_in_list ("fast", array))
-				priv->fast_supported = TRUE;
-			g_free (array);
+			if (array) {
+				if (g_strv_contains (array, "fast"))
+					priv->fast_supported = TRUE;
+				g_free (array);
+			}
 		}
 		g_variant_unref (value);
 	}
diff --git a/src/supplicant-manager/nm-supplicant-manager.h b/src/supplicant-manager/nm-supplicant-manager.h
index b0ce6a52..4cd7a0bd 100644
--- a/src/supplicant-manager/nm-supplicant-manager.h
+++ b/src/supplicant-manager/nm-supplicant-manager.h
@@ -22,12 +22,9 @@
 #ifndef __NETWORKMANAGER_SUPPLICANT_MANAGER_H__
 #define __NETWORKMANAGER_SUPPLICANT_MANAGER_H__
 
-#include "nm-default.h"
 #include "nm-supplicant-types.h"
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_SUPPLICANT_MANAGER				(nm_supplicant_manager_get_type ())
 #define NM_SUPPLICANT_MANAGER(obj)				(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager))
 #define NM_SUPPLICANT_MANAGER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass))
diff --git a/src/supplicant-manager/tests/Makefile.am b/src/supplicant-manager/tests/Makefile.am
index 6672c673..3ab9122e 100644
--- a/src/supplicant-manager/tests/Makefile.am
+++ b/src/supplicant-manager/tests/Makefile.am
@@ -9,7 +9,7 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/supplicant-manager \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	-DTEST_CERT_DIR=\"$(srcdir)/certs/\" \
+	-DTEST_CERT_DIR=\"$(abs_srcdir)/certs\" \
 	$(GLIB_CFLAGS)
 
 noinst_PROGRAMS = test-supplicant-config
diff --git a/src/supplicant-manager/tests/Makefile.in b/src/supplicant-manager/tests/Makefile.in
index 7c822d91..bb5efff7 100644
--- a/src/supplicant-manager/tests/Makefile.in
+++ b/src/supplicant-manager/tests/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-supplicant-config$(EXEEXT)
 TESTS = test-supplicant-config$(EXEEXT)
 subdir = src/supplicant-manager/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -463,6 +464,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -519,13 +521,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -537,6 +540,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -597,6 +602,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -632,6 +639,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -703,7 +711,7 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/supplicant-manager \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	-DTEST_CERT_DIR=\"$(srcdir)/certs/\" \
+	-DTEST_CERT_DIR=\"$(abs_srcdir)/certs\" \
 	$(GLIB_CFLAGS)
 
 test_supplicant_config_SOURCES = \
diff --git a/src/supplicant-manager/tests/certs/Makefile.in b/src/supplicant-manager/tests/certs/Makefile.in
index 819f36cc..a1be6bcb 100644
--- a/src/supplicant-manager/tests/certs/Makefile.in
+++ b/src/supplicant-manager/tests/certs/Makefile.in
@@ -89,8 +89,9 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = src/supplicant-manager/tests/certs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -165,6 +166,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -221,13 +223,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -239,6 +242,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -299,6 +304,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -334,6 +341,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c
index 881f44f6..e8f84448 100644
--- a/src/supplicant-manager/tests/test-supplicant-config.c
+++ b/src/supplicant-manager/tests/test-supplicant-config.c
@@ -35,7 +35,7 @@
 #include "nm-supplicant-config.h"
 #include "nm-supplicant-settings-verify.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static gboolean
 validate_opt (const char *detail,
@@ -160,8 +160,6 @@ test_wifi_open (void)
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
 	                                                     0,
-	                                                     NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                                                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
 	                                                     &error));
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
@@ -265,8 +263,6 @@ test_wifi_wep_key (const char *detail,
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
 	                                                     0,
-	                                                     NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                                                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
 	                                                     &error));
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
@@ -410,8 +406,6 @@ test_wifi_wpa_psk (const char *detail,
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
 	                                                     0,
-	                                                     NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                                                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
 	                                                     &error));
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
@@ -530,9 +524,9 @@ test_wifi_eap (void)
 	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 	nm_setting_802_1x_add_eap_method (s_8021x, "tls");
-	nm_setting_802_1x_set_client_cert (s_8021x, TEST_CERT_DIR "test-cert.p12", NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
-	nm_setting_802_1x_set_ca_cert (s_8021x, TEST_CERT_DIR "test-ca-cert.pem", NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
-	nm_setting_802_1x_set_private_key (s_8021x, TEST_CERT_DIR "test-cert.p12", NULL, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
+	nm_setting_802_1x_set_client_cert (s_8021x, TEST_CERT_DIR "/test-cert.p12", NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
+	nm_setting_802_1x_set_ca_cert (s_8021x, TEST_CERT_DIR "/test-ca-cert.pem", NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
+	nm_setting_802_1x_set_private_key (s_8021x, TEST_CERT_DIR "/test-cert.p12", NULL, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL, NULL);
 
 	/* IP4 setting */
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
@@ -557,8 +551,6 @@ test_wifi_eap (void)
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
 	                                                     0,
-	                                                     NM_SUPPLICANT_FEATURE_UNKNOWN,
-	                                                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
 	                                                     &error));
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c
index 4d596020..43ae0a8b 100644
--- a/src/systemd/nm-sd.c
+++ b/src/systemd/nm-sd.c
@@ -134,3 +134,10 @@ nm_sd_event_attach_default (void)
 
 /*****************************************************************************/
 
+/* ensure that defines in nm-sd.h correspond to the internal defines. */
+
+#include "nm-sd-adapt.h"
+#include "dhcp-lease-internal.h"
+
+/*****************************************************************************/
+
diff --git a/src/systemd/nm-sd.h b/src/systemd/nm-sd.h
index 888d44ab..f07a7082 100644
--- a/src/systemd/nm-sd.h
+++ b/src/systemd/nm-sd.h
@@ -21,5 +21,16 @@
 
 guint nm_sd_event_attach_default (void);
 
+/*****************************************************************************
+ * expose internal systemd API
+ *
+ * FIXME: don't use any internal systemd API.
+ *****************************************************************************/
+
+struct sd_dhcp_lease;
+
+int dhcp_lease_save(struct sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(struct sd_dhcp_lease **ret, const char *lease_file);
+
 #endif /* __NM_SD_H__ */
 
diff --git a/src/systemd/sd-adapt/build.h b/src/systemd/sd-adapt/build.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/build.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/cgroup-util.h b/src/systemd/sd-adapt/cgroup-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/cgroup-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/condition.h b/src/systemd/sd-adapt/condition.h
new file mode 100644
index 00000000..d3a6812a
--- /dev/null
+++ b/src/systemd/sd-adapt/condition.h
@@ -0,0 +1,5 @@
+#pragma once
+
+/* dummy header */
+
+typedef struct _sd_adapt_Condition Condition;
diff --git a/src/systemd/sd-adapt/conf-parser.h b/src/systemd/sd-adapt/conf-parser.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/conf-parser.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/def.h b/src/systemd/sd-adapt/def.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/def.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/dirent-util.h b/src/systemd/sd-adapt/dirent-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/dirent-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/formats-util.h b/src/systemd/sd-adapt/formats-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/formats-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/gunicode.h b/src/systemd/sd-adapt/gunicode.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/gunicode.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/libudev.h b/src/systemd/sd-adapt/libudev.h
new file mode 100644
index 00000000..8c49f075
--- /dev/null
+++ b/src/systemd/sd-adapt/libudev.h
@@ -0,0 +1,5 @@
+#pragma once
+
+/* dummy header */
+
+struct udev_device;
diff --git a/src/systemd/sd-adapt/missing.h b/src/systemd/sd-adapt/missing.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/missing.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/mkdir.h b/src/systemd/sd-adapt/mkdir.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/mkdir.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/nm-sd-adapt.c b/src/systemd/sd-adapt/nm-sd-adapt.c
index 4e308276..4e308276 100644
--- a/src/systemd/nm-sd-adapt.c
+++ b/src/systemd/sd-adapt/nm-sd-adapt.c
diff --git a/src/systemd/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h
index 010c8a80..cf27c1af 100644
--- a/src/systemd/nm-sd-adapt.h
+++ b/src/systemd/sd-adapt/nm-sd-adapt.h
@@ -140,8 +140,12 @@ sd_notify (int unset_environment, const char *state)
 }
 
 /* Can't include both net/if.h and linux/if.h; so have to define this here */
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE 16
+#endif
+
 #ifndef IFNAMSIZ
-#define IFNAMSIZ 16
+#define IFNAMSIZ IF_NAMESIZE
 #endif
 
 #ifndef MAX_HANDLE_SZ
diff --git a/src/systemd/sd-adapt/process-util.h b/src/systemd/sd-adapt/process-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/process-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/sd-daemon.h b/src/systemd/sd-adapt/sd-daemon.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/sd-daemon.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/stat-util.h b/src/systemd/sd-adapt/stat-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/stat-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/udev-util.h b/src/systemd/sd-adapt/udev-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/udev-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/udev.h b/src/systemd/sd-adapt/udev.h
new file mode 100644
index 00000000..00f60f92
--- /dev/null
+++ b/src/systemd/sd-adapt/udev.h
@@ -0,0 +1,5 @@
+#pragma once
+
+/* dummy header */
+
+#include "libudev.h"
diff --git a/src/systemd/sd-adapt/user-util.h b/src/systemd/sd-adapt/user-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/user-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/virt.h b/src/systemd/sd-adapt/virt.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/virt.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h
index b8ec96ad..24729dc1 100644
--- a/src/systemd/src/basic/escape.h
+++ b/src/systemd/src/basic/escape.h
@@ -28,9 +28,7 @@
 #endif /* NM_IGNORED */
 
 #include "string-util.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 
 /* What characters are special in the shell? */
 /* must be escaped outside and inside double-quotes */
diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c
index 0219300d..a793219c 100644
--- a/src/systemd/src/basic/ether-addr-util.c
+++ b/src/systemd/src/basic/ether-addr-util.c
@@ -25,6 +25,7 @@
 
 #include "ether-addr-util.h"
 #include "macro.h"
+#include "string-util.h"
 
 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
         assert(addr);
@@ -56,3 +57,71 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
                 a->ether_addr_octet[4] == b->ether_addr_octet[4] &&
                 a->ether_addr_octet[5] == b->ether_addr_octet[5];
 }
+
+int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) {
+        size_t pos = 0, n, field;
+        char sep = '\0';
+        const char *hex = HEXDIGITS, *hexoff;
+        size_t x;
+        bool touched;
+
+#define parse_fields(v)                                         \
+        for (field = 0; field < ELEMENTSOF(v); field++) {       \
+                touched = false;                                \
+                for (n = 0; n < (2 * sizeof(v[0])); n++) {      \
+                        if (s[pos] == '\0')                     \
+                                break;                          \
+                        hexoff = strchr(hex, s[pos]);           \
+                        if (hexoff == NULL)                     \
+                                break;                          \
+                        assert(hexoff >= hex);                  \
+                        x = hexoff - hex;                       \
+                        if (x >= 16)                            \
+                                x -= 6; /* A-F */               \
+                        assert(x < 16);                         \
+                        touched = true;                         \
+                        v[field] <<= 4;                         \
+                        v[field] += x;                          \
+                        pos++;                                  \
+                }                                               \
+                if (!touched)                                   \
+                        return -EINVAL;                         \
+                if (field < (ELEMENTSOF(v)-1)) {                \
+                        if (s[pos] != sep)                      \
+                                return -EINVAL;                 \
+                        else                                    \
+                                pos++;                          \
+                }                                               \
+        }
+
+        assert(s);
+        assert(ret);
+
+        sep = s[strspn(s, hex)];
+        if (sep == '\n')
+                return -EINVAL;
+        if (strchr(":.-", sep) == NULL)
+                return -EINVAL;
+
+        if (sep == '.') {
+                uint16_t shorts[3] = { 0 };
+
+                parse_fields(shorts);
+
+                for (n = 0; n < ELEMENTSOF(shorts); n++) {
+                        ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
+                        ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
+                }
+        } else {
+                struct ether_addr out = { .ether_addr_octet = { 0 } };
+
+                parse_fields(out.ether_addr_octet);
+
+                for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
+                        ret->ether_addr_octet[n] = out.ether_addr_octet[n];
+        }
+
+        if (offset)
+                *offset = pos;
+        return 0;
+}
diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h
index 07436379..74e125a9 100644
--- a/src/systemd/src/basic/ether-addr-util.h
+++ b/src/systemd/src/basic/ether-addr-util.h
@@ -35,3 +35,5 @@ bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b);
 static inline bool ether_addr_is_null(const struct ether_addr *addr) {
         return ether_addr_equal(addr, &ETHER_ADDR_NULL);
 }
+
+int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset);
diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c
new file mode 100644
index 00000000..eb54daa3
--- /dev/null
+++ b/src/systemd/src/basic/extract-word.c
@@ -0,0 +1,302 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "extract-word.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "utf8.h"
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
+        _cleanup_free_ char *s = NULL;
+        size_t allocated = 0, sz = 0;
+        char c;
+        int r;
+
+        char quote = 0;                 /* 0 or ' or " */
+        bool backslash = false;         /* whether we've just seen a backslash */
+
+        assert(p);
+        assert(ret);
+
+        /* Bail early if called after last value or with no input */
+        if (!*p)
+                goto finish_force_terminate;
+        c = **p;
+
+        if (!separators)
+                separators = WHITESPACE;
+
+        /* Parses the first word of a string, and returns it in
+         * *ret. Removes all quotes in the process. When parsing fails
+         * (because of an uneven number of quotes or similar), leaves
+         * the pointer *p at the first invalid character. */
+
+        if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+                if (!GREEDY_REALLOC(s, allocated, sz+1))
+                        return -ENOMEM;
+
+        for (;; (*p)++, c = **p) {
+                if (c == 0)
+                        goto finish_force_terminate;
+                else if (strchr(separators, c)) {
+                        if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+                                (*p)++;
+                                goto finish_force_next;
+                        }
+                } else {
+                        /* We found a non-blank character, so we will always
+                         * want to return a string (even if it is empty),
+                         * allocate it here. */
+                        if (!GREEDY_REALLOC(s, allocated, sz+1))
+                                return -ENOMEM;
+                        break;
+                }
+        }
+
+        for (;; (*p)++, c = **p) {
+                if (backslash) {
+                        if (!GREEDY_REALLOC(s, allocated, sz+7))
+                                return -ENOMEM;
+
+                        if (c == 0) {
+                                if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
+                                    (!quote || flags & EXTRACT_RELAX)) {
+                                        /* If we find an unquoted trailing backslash and we're in
+                                         * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
+                                         * output.
+                                         *
+                                         * Unbalanced quotes will only be allowed in EXTRACT_RELAX
+                                         * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
+                                         */
+                                        s[sz++] = '\\';
+                                        goto finish_force_terminate;
+                                }
+                                if (flags & EXTRACT_RELAX)
+                                        goto finish_force_terminate;
+                                return -EINVAL;
+                        }
+
+                        if (flags & EXTRACT_CUNESCAPE) {
+                                bool eight_bit = false;
+                                char32_t u;
+
+                                r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
+                                if (r < 0) {
+                                        if (flags & EXTRACT_CUNESCAPE_RELAX) {
+                                                s[sz++] = '\\';
+                                                s[sz++] = c;
+                                        } else
+                                                return -EINVAL;
+                                } else {
+                                        (*p) += r - 1;
+
+                                        if (eight_bit)
+                                                s[sz++] = u;
+                                        else
+                                                sz += utf8_encode_unichar(s + sz, u);
+                                }
+                        } else
+                                s[sz++] = c;
+
+                        backslash = false;
+
+                } else if (quote) {     /* inside either single or double quotes */
+                        for (;; (*p)++, c = **p) {
+                                if (c == 0) {
+                                        if (flags & EXTRACT_RELAX)
+                                                goto finish_force_terminate;
+                                        return -EINVAL;
+                                } else if (c == quote) {        /* found the end quote */
+                                        quote = 0;
+                                        break;
+                                } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
+                                        backslash = true;
+                                        break;
+                                } else {
+                                        if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                                return -ENOMEM;
+
+                                        s[sz++] = c;
+                                }
+                        }
+
+                } else {
+                        for (;; (*p)++, c = **p) {
+                                if (c == 0)
+                                        goto finish_force_terminate;
+                                else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) {
+                                        quote = c;
+                                        break;
+                                } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
+                                        backslash = true;
+                                        break;
+                                } else if (strchr(separators, c)) {
+                                        if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+                                                (*p)++;
+                                                goto finish_force_next;
+                                        }
+                                        /* Skip additional coalesced separators. */
+                                        for (;; (*p)++, c = **p) {
+                                                if (c == 0)
+                                                        goto finish_force_terminate;
+                                                if (!strchr(separators, c))
+                                                        break;
+                                        }
+                                        goto finish;
+
+                                } else {
+                                        if (!GREEDY_REALLOC(s, allocated, sz+2))
+                                                return -ENOMEM;
+
+                                        s[sz++] = c;
+                                }
+                        }
+                }
+        }
+
+finish_force_terminate:
+        *p = NULL;
+finish:
+        if (!s) {
+                *p = NULL;
+                *ret = NULL;
+                return 0;
+        }
+
+finish_force_next:
+        s[sz] = 0;
+        *ret = s;
+        s = NULL;
+
+        return 1;
+}
+
+#if 0 /* NM_IGNORED */
+int extract_first_word_and_warn(
+                const char **p,
+                char **ret,
+                const char *separators,
+                ExtractFlags flags,
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *rvalue) {
+
+        /* Try to unquote it, if it fails, warn about it and try again
+         * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
+         * backslashes verbatim in invalid escape sequences. */
+
+        const char *save;
+        int r;
+
+        save = *p;
+        r = extract_first_word(p, ret, separators, flags);
+        if (r >= 0)
+                return r;
+
+        if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
+
+                /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
+                *p = save;
+                r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
+                if (r >= 0) {
+                        /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
+                        log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
+                        return r;
+                }
+
+                /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
+                if (r == -EINVAL)
+                        return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
+        }
+
+        /* Can be any error, report it */
+        return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
+}
+
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
+        va_list ap;
+        char **l;
+        int n = 0, i, c, r;
+
+        /* Parses a number of words from a string, stripping any
+         * quotes if necessary. */
+
+        assert(p);
+
+        /* Count how many words are expected */
+        va_start(ap, flags);
+        for (;;) {
+                if (!va_arg(ap, char **))
+                        break;
+                n++;
+        }
+        va_end(ap);
+
+        if (n <= 0)
+                return 0;
+
+        /* Read all words into a temporary array */
+        l = newa0(char*, n);
+        for (c = 0; c < n; c++) {
+
+                r = extract_first_word(p, &l[c], separators, flags);
+                if (r < 0) {
+                        int j;
+
+                        for (j = 0; j < c; j++)
+                                free(l[j]);
+
+                        return r;
+                }
+
+                if (r == 0)
+                        break;
+        }
+
+        /* If we managed to parse all words, return them in the passed
+         * in parameters */
+        va_start(ap, flags);
+        for (i = 0; i < n; i++) {
+                char **v;
+
+                v = va_arg(ap, char **);
+                assert(v);
+
+                *v = l[i];
+        }
+        va_end(ap);
+
+        return c;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h
new file mode 100644
index 00000000..21db5ef3
--- /dev/null
+++ b/src/systemd/src/basic/extract-word.h
@@ -0,0 +1,35 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+typedef enum ExtractFlags {
+        EXTRACT_RELAX                    = 1,
+        EXTRACT_CUNESCAPE                = 2,
+        EXTRACT_CUNESCAPE_RELAX          = 4,
+        EXTRACT_QUOTES                   = 8,
+        EXTRACT_DONT_COALESCE_SEPARATORS = 16,
+        EXTRACT_RETAIN_ESCAPE            = 32,
+} ExtractFlags;
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
+int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c
index e92b3740..c518f757 100644
--- a/src/systemd/src/basic/fd-util.c
+++ b/src/systemd/src/basic/fd-util.c
@@ -27,13 +27,13 @@
 #include <unistd.h>
 
 #include "fd-util.h"
+#include "fs-util.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 #include "parse-util.h"
 #include "path-util.h"
 #include "socket-util.h"
+#include "stdio-util.h"
 #include "util.h"
 
 int close_nointr(int fd) {
@@ -189,6 +189,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;
 
@@ -234,7 +240,7 @@ int close_all_fds(const int except[], unsigned n_except) {
         while ((de = readdir(d))) {
                 int fd = -1;
 
-                if (hidden_file(de->d_name))
+                if (hidden_or_backup_file(de->d_name))
                         continue;
 
                 if (safe_atoi(de->d_name, &fd) < 0)
@@ -362,3 +368,17 @@ bool fdname_is_valid(const char *s) {
 
         return p - s < 256;
 }
+
+int fd_get_path(int fd, char **ret) {
+        char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+        int r;
+
+        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+
+        r = readlink_malloc(procfs_path, ret);
+
+        if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */
+                return -EBADF;
+
+        return r;
+}
diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h
index 44528c6e..34b98d4a 100644
--- a/src/systemd/src/basic/fd-util.h
+++ b/src/systemd/src/basic/fd-util.h
@@ -63,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);
 
@@ -72,6 +73,8 @@ void cmsg_close_all(struct msghdr *mh);
 
 bool fdname_is_valid(const char *s);
 
+int fd_get_path(int fd, char **ret);
+
 /* 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)
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index e940e10e..b2fe15ca 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -49,6 +49,8 @@
 #include "umask-util.h"
 #include "utf8.h"
 
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+
 int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
 
         assert(f);
@@ -232,7 +234,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
         if (S_ISREG(st.st_mode)) {
 
                 /* Safety check */
-                if (st.st_size > 4*1024*1024)
+                if (st.st_size > READ_FULL_BYTES_MAX)
                         return -E2BIG;
 
                 /* Start with the right file size, but be prepared for
@@ -247,26 +249,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
                 char *t;
                 size_t k;
 
-                t = realloc(buf, n+1);
+                t = realloc(buf, n + 1);
                 if (!t)
                         return -ENOMEM;
 
                 buf = t;
                 k = fread(buf + l, 1, n - l, f);
+                if (k > 0)
+                        l += k;
 
-                if (k <= 0) {
-                        if (ferror(f))
-                                return -errno;
+                if (ferror(f))
+                        return -errno;
 
+                if (feof(f))
                         break;
-                }
 
-                l += k;
-                n *= 2;
+                /* We aren't expecting fread() to return a short read outside
+                 * of (error && eof), assert buffer is full and enlarge buffer.
+                 */
+                assert(l == n);
 
                 /* Safety check */
-                if (n > 4*1024*1024)
+                if (n >= READ_FULL_BYTES_MAX)
                         return -E2BIG;
+
+                n = MIN(n * 2, READ_FULL_BYTES_MAX);
         }
 
         buf[l] = 0;
@@ -1071,7 +1078,7 @@ int fflush_and_check(FILE *f) {
         return 0;
 }
 
-/* This is much like like mkostemp() but is subject to umask(). */
+/* This is much like mkostemp() but is subject to umask(). */
 int mkostemp_safe(char *pattern, int flags) {
         _cleanup_umask_ mode_t u = 0;
         int fd;
@@ -1087,30 +1094,6 @@ int mkostemp_safe(char *pattern, int flags) {
         return fd;
 }
 
-int open_tmpfile(const char *path, int flags) {
-        char *p;
-        int fd;
-
-        assert(path);
-
-#ifdef O_TMPFILE
-        /* Try O_TMPFILE first, if it is supported */
-        fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
-        if (fd >= 0)
-                return fd;
-#endif
-
-        /* Fall back to unguessable name + unlinking */
-        p = strjoina(path, "/systemd-tmp-XXXXXX");
-
-        fd = mkostemp_safe(p, flags);
-        if (fd < 0)
-                return fd;
-
-        unlink(p);
-        return fd;
-}
-
 int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
         const char *fn;
         char *t;
@@ -1185,12 +1168,13 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 int tempfn_random_child(const char *p, const char *extra, char **ret) {
         char *t, *x;
         uint64_t u;
         unsigned i;
+        int r;
 
-        assert(p);
         assert(ret);
 
         /* Turns this:
@@ -1199,6 +1183,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
          *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
          */
 
+        if (!p) {
+                r = tmp_dir(&p);
+                if (r < 0)
+                        return r;
+        }
+
         if (!extra)
                 extra = "";
 
@@ -1220,7 +1210,6 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 int write_timestamp_file_atomic(const char *fn, usec_t n) {
         char ln[DECIMAL_STR_MAX(n)+2];
 
@@ -1234,7 +1223,6 @@ int write_timestamp_file_atomic(const char *fn, usec_t n) {
 
         return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
 }
-#endif /* NM_IGNORED */
 
 int read_timestamp_file(const char *fn, usec_t *ret) {
         _cleanup_free_ char *ln = NULL;
@@ -1255,6 +1243,7 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
         *ret = (usec_t) t;
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
         int r;
@@ -1284,3 +1273,150 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
 
         return fputs(s, f);
 }
+
+#if 0 /* NM_IGNORED */
+int open_tmpfile_unlinkable(const char *directory, int flags) {
+        char *p;
+        int fd, r;
+
+        if (!directory) {
+                r = tmp_dir(&directory);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
+
+#ifdef O_TMPFILE
+        /* Try O_TMPFILE first, if it is supported */
+        fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+        if (fd >= 0)
+                return fd;
+#endif
+
+        /* Fall back to unguessable name + unlinking */
+        p = strjoina(directory, "/systemd-tmp-XXXXXX");
+
+        fd = mkostemp_safe(p, flags);
+        if (fd < 0)
+                return fd;
+
+        (void) unlink(p);
+
+        return fd;
+}
+
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
+        _cleanup_free_ char *tmp = NULL;
+        int r, fd;
+
+        assert(target);
+        assert(ret_path);
+
+        /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
+        assert((flags & O_EXCL) == 0);
+
+        /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
+         * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+         * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
+
+#ifdef O_TMPFILE
+        {
+                _cleanup_free_ char *dn = NULL;
+
+                dn = dirname_malloc(target);
+                if (!dn)
+                        return -ENOMEM;
+
+                fd = open(dn, O_TMPFILE|flags, 0640);
+                if (fd >= 0) {
+                        *ret_path = NULL;
+                        return fd;
+                }
+
+                log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+        }
+#endif
+
+        r = tempfn_random(target, NULL, &tmp);
+        if (r < 0)
+                return r;
+
+        fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
+        if (fd < 0)
+                return -errno;
+
+        *ret_path = tmp;
+        tmp = NULL;
+
+        return fd;
+}
+
+int link_tmpfile(int fd, const char *path, const char *target) {
+
+        assert(fd >= 0);
+        assert(target);
+
+        /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
+         * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
+         * on the directory, and renameat2() is used instead.
+         *
+         * Note that in both cases we will not replace existing files. This is because linkat() does not support this
+         * operation currently (renameat2() does), and there is no nice way to emulate this. */
+
+        if (path) {
+                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];
+
+                xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
+
+                if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
+int read_nul_string(FILE *f, char **ret) {
+        _cleanup_free_ char *x = NULL;
+        size_t allocated = 0, n = 0;
+
+        assert(f);
+        assert(ret);
+
+        /* Reads a NUL-terminated string from the specified file. */
+
+        for (;;) {
+                int c;
+
+                if (!GREEDY_REALLOC(x, allocated, n+2))
+                        return -ENOMEM;
+
+                c = fgetc(f);
+                if (c == 0) /* Terminate at NUL byte */
+                        break;
+                if (c == EOF) {
+                        if (ferror(f))
+                                return -errno;
+                        break; /* Terminate at EOF */
+                }
+
+                x[n++] = (char) c;
+        }
+
+        if (x)
+                x[n] = 0;
+        else {
+                x = new0(char, 1);
+                if (!x)
+                        return -ENOMEM;
+        }
+
+        *ret = x;
+        x = NULL;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h
index 8084895f..9ac497d9 100644
--- a/src/systemd/src/basic/fileio.h
+++ b/src/systemd/src/basic/fileio.h
@@ -72,7 +72,6 @@ int fflush_and_check(FILE *f);
 
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
 int mkostemp_safe(char *pattern, int flags);
-int open_tmpfile(const char *path, int flags);
 
 int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
 int tempfn_random(const char *p, const char *extra, char **ret);
@@ -82,3 +81,10 @@ int write_timestamp_file_atomic(const char *fn, usec_t n);
 int read_timestamp_file(const char *fn, usec_t *ret);
 
 int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+
+int open_tmpfile_unlinkable(const char *directory, int flags);
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+
+int link_tmpfile(int fd, const char *path, const char *target);
+
+int read_nul_string(FILE *f, char **ret);
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
index 9a843644..de0cd8fb 100644
--- a/src/systemd/src/basic/fs-util.c
+++ b/src/systemd/src/basic/fs-util.c
@@ -30,26 +30,22 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "dirent-util.h"
-#endif /* NM_IGNORED */
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "log.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
 #include "mkdir.h"
-#endif /* NM_IGNORED */
 #include "parse-util.h"
 #include "path-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
-#if 0 /* NM_IGNORED */
 #include "user-util.h"
-#endif /* NM_IGNORED */
 #include "util.h"
 
 int unlink_noerrno(const char *path) {
@@ -151,6 +147,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
 
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int readlinkat_malloc(int fd, const char *p, char **ret) {
         size_t l = 100;
@@ -189,6 +186,7 @@ int readlink_malloc(const char *p, char **ret) {
         return readlinkat_malloc(AT_FDCWD, p, ret);
 }
 
+#if 0 /* NM_IGNORED */
 int readlink_value(const char *p, char **ret) {
         _cleanup_free_ char *link = NULL;
         char *value;
@@ -504,4 +502,106 @@ int get_files_in_directory(const char *path, char ***list) {
 
         return n;
 }
+
+static int getenv_tmp_dir(const char **ret_path) {
+        const char *n;
+        int r, ret = 0;
+
+        assert(ret_path);
+
+        /* We use the same order of environment variables python uses in tempfile.gettempdir():
+         * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
+        FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
+                const char *e;
+
+                e = secure_getenv(n);
+                if (!e)
+                        continue;
+                if (!path_is_absolute(e)) {
+                        r = -ENOTDIR;
+                        goto next;
+                }
+                if (!path_is_safe(e)) {
+                        r = -EPERM;
+                        goto next;
+                }
+
+                r = is_dir(e, true);
+                if (r < 0)
+                        goto next;
+                if (r == 0) {
+                        r = -ENOTDIR;
+                        goto next;
+                }
+
+                *ret_path = e;
+                return 1;
+
+        next:
+                /* Remember first error, to make this more debuggable */
+                if (ret >= 0)
+                        ret = r;
+        }
+
+        if (ret < 0)
+                return ret;
+
+        *ret_path = NULL;
+        return ret;
+}
+
+static int tmp_dir_internal(const char *def, const char **ret) {
+        const char *e;
+        int r, k;
+
+        assert(def);
+        assert(ret);
+
+        r = getenv_tmp_dir(&e);
+        if (r > 0) {
+                *ret = e;
+                return 0;
+        }
+
+        k = is_dir(def, true);
+        if (k == 0)
+                k = -ENOTDIR;
+        if (k < 0)
+                return r < 0 ? r : k;
+
+        *ret = def;
+        return 0;
+}
+
+int var_tmp_dir(const char **ret) {
+
+        /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
+         * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
+         * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
+         * making it a variable that overrides all temporary file storage locations. */
+
+        return tmp_dir_internal("/var/tmp", ret);
+}
+
+int tmp_dir(const char **ret) {
+
+        /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
+         * backed by an in-memory file system: /tmp. */
+
+        return tmp_dir_internal("/tmp", ret);
+}
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
+        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 */
+        xsprintf(path, "/proc/self/fd/%i", what);
+
+        r = inotify_add_watch(fd, path, mask);
+        if (r < 0)
+                return -errno;
+
+        return r;
+}
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
index 0d23f863..2c3b9a1c 100644
--- a/src/systemd/src/basic/fs-util.h
+++ b/src/systemd/src/basic/fs-util.h
@@ -61,6 +61,9 @@ int mkfifo_atomic(const char *path, mode_t mode);
 
 int get_files_in_directory(const char *path, char ***list);
 
+int tmp_dir(const char **ret);
+int var_tmp_dir(const char **ret);
+
 #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
 
 #define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
@@ -72,3 +75,5 @@ union inotify_event_buffer {
         struct inotify_event ev;
         uint8_t raw[INOTIFY_EVENT_MAX];
 };
+
+int inotify_add_watch_fd(int fd, int what, uint32_t mask);
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
index 4df3ae73..dc6bcab0 100644
--- a/src/systemd/src/basic/hashmap.c
+++ b/src/systemd/src/basic/hashmap.c
@@ -29,9 +29,7 @@
 #include "hashmap.h"
 #include "macro.h"
 #include "mempool.h"
-#if 0 /* NM_IGNORED */
 #include "process-util.h"
-#endif /* NM_IGNORED */
 #include "random-util.h"
 #include "set.h"
 #include "siphash24.h"
@@ -1768,6 +1766,9 @@ void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
 int set_consume(Set *s, void *value) {
         int r;
 
+        assert(s);
+        assert(value);
+
         r = set_put(s, value);
         if (r <= 0)
                 free(value);
@@ -1777,26 +1778,26 @@ int set_consume(Set *s, void *value) {
 
 int set_put_strdup(Set *s, const char *p) {
         char *c;
-        int r;
 
         assert(s);
         assert(p);
 
+        if (set_contains(s, (char*) p))
+                return 0;
+
         c = strdup(p);
         if (!c)
                 return -ENOMEM;
 
-        r = set_consume(s, c);
-        if (r == -EEXIST)
-                return 0;
-
-        return r;
+        return set_consume(s, c);
 }
 
 int set_put_strdupv(Set *s, char **l) {
         int n = 0, r;
         char **i;
 
+        assert(s);
+
         STRV_FOREACH(i, l) {
                 r = set_put_strdup(s, *i);
                 if (r < 0)
@@ -1807,3 +1808,23 @@ int set_put_strdupv(Set *s, char **l) {
 
         return n;
 }
+
+int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) {
+        const char *p = v;
+        int r;
+
+        assert(s);
+        assert(v);
+
+        for (;;) {
+                char *word;
+
+                r = extract_first_word(&p, &word, separators, flags);
+                if (r <= 0)
+                        return r;
+
+                r = set_consume(s, word);
+                if (r < 0)
+                        return r;
+        }
+}
diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c
index ee48b86d..480f9d9d 100644
--- a/src/systemd/src/basic/hostname-util.c
+++ b/src/systemd/src/basic/hostname-util.c
@@ -180,16 +180,16 @@ bool is_localhost(const char *hostname) {
         assert(hostname);
 
         /* This tries to identify local host and domain names
-         * described in RFC6761 plus the redhatism of .localdomain */
+         * described in RFC6761 plus the redhatism of localdomain */
 
         return strcaseeq(hostname, "localhost") ||
                strcaseeq(hostname, "localhost.") ||
-               strcaseeq(hostname, "localdomain.") ||
-               strcaseeq(hostname, "localdomain") ||
+               strcaseeq(hostname, "localhost.localdomain") ||
+               strcaseeq(hostname, "localhost.localdomain.") ||
                endswith_no_case(hostname, ".localhost") ||
                endswith_no_case(hostname, ".localhost.") ||
-               endswith_no_case(hostname, ".localdomain") ||
-               endswith_no_case(hostname, ".localdomain.");
+               endswith_no_case(hostname, ".localhost.localdomain") ||
+               endswith_no_case(hostname, ".localhost.localdomain.");
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c
index 10898dae..95230e7d 100644
--- a/src/systemd/src/basic/in-addr-util.c
+++ b/src/systemd/src/basic/in-addr-util.c
@@ -22,26 +22,36 @@
 #include <arpa/inet.h>
 #include <endian.h>
 #include <errno.h>
+#include <net/if.h>
 #include <stdint.h>
 #include <stdlib.h>
 
 #include "alloc-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
+#include "parse-util.h"
 #include "util.h"
 
+bool in4_addr_is_null(const struct in_addr *a) {
+        return a->s_addr == 0;
+}
+
+bool in6_addr_is_null(const struct in6_addr *a) {
+        return
+                a->s6_addr32[0] == 0 &&
+                a->s6_addr32[1] == 0 &&
+                a->s6_addr32[2] == 0 &&
+                a->s6_addr32[3] == 0;
+}
+
 int in_addr_is_null(int family, const union in_addr_union *u) {
         assert(u);
 
         if (family == AF_INET)
-                return u->in.s_addr == 0;
+                return in4_addr_is_null(&u->in);
 
         if (family == AF_INET6)
-                return
-                        u->in6.s6_addr32[0] == 0 &&
-                        u->in6.s6_addr32[1] == 0 &&
-                        u->in6.s6_addr32[2] == 0 &&
-                        u->in6.s6_addr32[3] == 0;
+                return in6_addr_is_null(&u->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -226,6 +236,48 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
         return 0;
 }
 
+int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
+        size_t l;
+        char *x;
+        int r;
+
+        assert(u);
+        assert(ret);
+
+        /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
+         * handle IPv6 link-local addresses. */
+
+        if (family != AF_INET6)
+                goto fallback;
+        if (ifindex <= 0)
+                goto fallback;
+
+        r = in_addr_is_link_local(family, u);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                goto fallback;
+
+        l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1;
+        x = new(char, l);
+        if (!x)
+                return -ENOMEM;
+
+        errno = 0;
+        if (!inet_ntop(family, u, x, l)) {
+                free(x);
+                return errno > 0 ? -errno : -EINVAL;
+        }
+
+        sprintf(strchr(x, 0), "%%%i", ifindex);
+        *ret = x;
+
+        return 0;
+
+fallback:
+        return in_addr_to_string(family, u, ret);
+}
+
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
 
         assert(s);
@@ -263,6 +315,47 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re
         return -EINVAL;
 }
 
+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;
+
+        assert(s);
+        assert(family);
+        assert(ret);
+
+        /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
+         * if one is found. */
+
+        suffix = strchr(s, '%');
+        if (suffix) {
+
+                if (ifindex) {
+                        /* If we shall return the interface index, try to parse it */
+                        r = parse_ifindex(suffix + 1, &ifi);
+                        if (r < 0) {
+                                unsigned u;
+
+                                u = if_nametoindex(suffix + 1);
+                                if (u <= 0)
+                                        return -errno;
+
+                                ifi = (int) u;
+                        }
+                }
+
+                s = strndupa(s, suffix - s);
+        }
+
+        r = in_addr_from_string_auto(s, family, ret);
+        if (r < 0)
+                return r;
+
+        if (ifindex)
+                *ifindex = ifi;
+
+        return r;
+}
+
 unsigned char in_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 17798ce8..d60064ae 100644
--- a/src/systemd/src/basic/in-addr-util.h
+++ b/src/systemd/src/basic/in-addr-util.h
@@ -36,6 +36,9 @@ struct in_addr_data {
         union in_addr_union address;
 };
 
+bool in4_addr_is_null(const struct in_addr *a);
+bool in6_addr_is_null(const struct in6_addr *a);
+
 int in_addr_is_null(int family, const union in_addr_union *u);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
 int in_addr_is_localhost(int family, const union in_addr_union *u);
@@ -43,8 +46,10 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
+int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
 int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
+int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
 unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
 struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
 int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c
index aaf414f4..61b667f0 100644
--- a/src/systemd/src/basic/io-util.c
+++ b/src/systemd/src/basic/io-util.c
@@ -35,6 +35,11 @@ int flush_fd(int fd) {
                 .events = POLLIN,
         };
 
+        /* 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
+         * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used
+         * was set to non-blocking too. */
+
         for (;;) {
                 char buf[LINE_MAX];
                 ssize_t l;
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
index d6a877ea..7db87b49 100644
--- a/src/systemd/src/basic/macro.h
+++ b/src/systemd/src/basic/macro.h
@@ -98,6 +98,15 @@
 #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
 #define UNIQ __COUNTER__
 
+/* builtins */
+#if __SIZEOF_INT__ == 4
+#define BUILTIN_FFS_U32(x) __builtin_ffs(x);
+#elif __SIZEOF_LONG__ == 4
+#define BUILTIN_FFS_U32(x) __builtin_ffsl(x);
+#else
+#error "neither int nor long are four bytes long?!?"
+#endif
+
 /* Rounds up */
 
 #define ALIGN4(l) (((l) + 3) & ~3)
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
index eb53c42d..27386631 100644
--- a/src/systemd/src/basic/parse-util.c
+++ b/src/systemd/src/basic/parse-util.c
@@ -28,11 +28,10 @@
 #include <xlocale.h>
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "extract-word.h"
-#endif /* NM_IGNORED */
 #include "macro.h"
 #include "parse-util.h"
+#include "process-util.h"
 #include "string-util.h"
 
 int parse_boolean(const char *v) {
@@ -538,3 +537,46 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
 
         return 0;
 }
+
+int parse_percent_unbounded(const char *p) {
+        const char *pc, *n;
+        unsigned v;
+        int r;
+
+        pc = endswith(p, "%");
+        if (!pc)
+                return -EINVAL;
+
+        n = strndupa(p, pc - p);
+        r = safe_atou(n, &v);
+        if (r < 0)
+                return r;
+
+        return (int) v;
+}
+
+int parse_percent(const char *p) {
+        int v;
+
+        v = parse_percent_unbounded(p);
+        if (v > 100)
+                return -ERANGE;
+
+        return v;
+}
+
+#if 0 /* NM_IGNORED */
+int parse_nice(const char *p, int *ret) {
+        int n, r;
+
+        r = safe_atoi(p, &n);
+        if (r < 0)
+                return r;
+
+        if (!nice_is_valid(n))
+                return -ERANGE;
+
+        *ret = n;
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h
index d8dc26a3..461e1cd4 100644
--- a/src/systemd/src/basic/parse-util.h
+++ b/src/systemd/src/basic/parse-util.h
@@ -90,6 +90,23 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
 }
 #endif
 
+#if SIZE_MAX == UINT_MAX
+static inline int safe_atozu(const char *s, size_t *ret_u) {
+        assert_cc(sizeof(size_t) == sizeof(unsigned));
+        return safe_atou(s, (unsigned *) ret_u);
+}
+#else
+static inline int safe_atozu(const char *s, size_t *ret_u) {
+        assert_cc(sizeof(size_t) == sizeof(long unsigned));
+        return safe_atolu(s, ret_u);
+}
+#endif
+
 int safe_atod(const char *s, double *ret_d);
 
 int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
+
+int parse_percent_unbounded(const char *p);
+int parse_percent(const char *p);
+
+int parse_nice(const char *p, int *ret);
diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c
index c3b7d187..86d35bbe 100644
--- a/src/systemd/src/basic/path-util.c
+++ b/src/systemd/src/basic/path-util.c
@@ -34,19 +34,13 @@
 #undef basename
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "extract-word.h"
-#endif /* NM_IGNORED */
 #include "fs-util.h"
 #include "log.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 #include "path-util.h"
-#if 0 /* NM_IGNORED */
 #include "stat-util.h"
-#endif /* NM_IGNORED */
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
@@ -580,10 +574,10 @@ static int binary_is_good(const char *binary) {
         if (r < 0)
                 return r;
 
-        return !path_equal(d, "true") &&
-               !path_equal(d, "/bin/true") &&
-               !path_equal(d, "/usr/bin/true") &&
-               !path_equal(d, "/dev/null");
+        return !PATH_IN_SET(d, "true"
+                               "/bin/true",
+                               "/usr/bin/true",
+                               "/dev/null");
 }
 
 int fsck_exists(const char *fstype) {
@@ -769,34 +763,53 @@ char *file_in_same_dir(const char *path, const char *filename) {
         return ret;
 }
 
-bool hidden_file_allow_backup(const char *filename) {
-        assert(filename);
-
-        return
-                filename[0] == '.' ||
-                streq(filename, "lost+found") ||
-                streq(filename, "aquota.user") ||
-                streq(filename, "aquota.group") ||
-                endswith(filename, ".rpmnew") ||
-                endswith(filename, ".rpmsave") ||
-                endswith(filename, ".rpmorig") ||
-                endswith(filename, ".dpkg-old") ||
-                endswith(filename, ".dpkg-new") ||
-                endswith(filename, ".dpkg-tmp") ||
-                endswith(filename, ".dpkg-dist") ||
-                endswith(filename, ".dpkg-bak") ||
-                endswith(filename, ".dpkg-backup") ||
-                endswith(filename, ".dpkg-remove") ||
-                endswith(filename, ".swp");
-}
+bool hidden_or_backup_file(const char *filename) {
+        const char *p;
 
-bool hidden_file(const char *filename) {
         assert(filename);
 
-        if (endswith(filename, "~"))
+        if (filename[0] == '.' ||
+            streq(filename, "lost+found") ||
+            streq(filename, "aquota.user") ||
+            streq(filename, "aquota.group") ||
+            endswith(filename, "~"))
                 return true;
 
-        return hidden_file_allow_backup(filename);
+        p = strrchr(filename, '.');
+        if (!p)
+                return false;
+
+        /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
+         * with always new suffixes and that everybody else should just adjust to that, then it really should be on
+         * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
+         * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
+         * string. Specifically: there's now:
+         *
+         *    The generic suffixes "~" and ".bak" for backup files
+         *    The generic prefix "." for hidden files
+         *
+         * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
+         * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
+         */
+
+        return STR_IN_SET(p + 1,
+                          "rpmnew",
+                          "rpmsave",
+                          "rpmorig",
+                          "dpkg-old",
+                          "dpkg-new",
+                          "dpkg-tmp",
+                          "dpkg-dist",
+                          "dpkg-bak",
+                          "dpkg-backup",
+                          "dpkg-remove",
+                          "ucf-new",
+                          "ucf-old",
+                          "ucf-dist",
+                          "swp",
+                          "bak",
+                          "old",
+                          "new");
 }
 
 bool is_device_path(const char *path) {
diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h
index 2c2f87a9..a27c13fc 100644
--- a/src/systemd/src/basic/path-util.h
+++ b/src/systemd/src/basic/path-util.h
@@ -48,6 +48,23 @@ bool path_equal(const char *a, const char *b) _pure_;
 bool path_equal_or_files_same(const char *a, const char *b);
 char* path_join(const char *root, const char *path, const char *rest);
 
+static inline bool path_equal_ptr(const char *a, const char *b) {
+        return !!a == !!b && (!a || path_equal(a, b));
+}
+
+/* Note: the search terminates on the first NULL item. */
+#define PATH_IN_SET(p, ...)                                     \
+        ({                                                      \
+                char **s;                                       \
+                bool _found = false;                            \
+                STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__))         \
+                        if (path_equal(p, *s)) {                \
+                               _found = true;                   \
+                               break;                           \
+                        }                                       \
+                _found;                                         \
+        })
+
 int path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *prefix);
 char** path_strv_resolve_uniq(char **l, const char *prefix);
@@ -105,7 +122,6 @@ bool path_is_safe(const char *p) _pure_;
 
 char *file_in_same_dir(const char *path, const char *filename);
 
-bool hidden_file_allow_backup(const char *filename);
-bool hidden_file(const char *filename) _pure_;
+bool hidden_or_backup_file(const char *filename) _pure_;
 
 bool is_device_path(const char *path);
diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c
index 5500fd28..1d8ca882 100644
--- a/src/systemd/src/basic/random-util.c
+++ b/src/systemd/src/basic/random-util.c
@@ -34,9 +34,7 @@
 
 #include "fd-util.h"
 #include "io-util.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif
 #include "random-util.h"
 #include "time-util.h"
 
@@ -51,7 +49,7 @@ int dev_urandom(void *p, size_t n) {
          * never block, and will always return some data from the
          * kernel, regardless if the random pool is fully initialized
          * or not. It thus makes no guarantee for the quality of the
-         * returned entropy, but is good enough for or usual usecases
+         * returned entropy, but is good enough for our usual usecases
          * of seeding the hash functions for hashtable */
 
         /* Use the getrandom() syscall unless we know we don't have
diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h
index e0d9dd00..a5f8beb0 100644
--- a/src/systemd/src/basic/set.h
+++ b/src/systemd/src/basic/set.h
@@ -19,11 +19,12 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include "extract-word.h"
 #include "hashmap.h"
 #include "macro.h"
 
-Set *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
 
 static inline Set *set_free(Set *s) {
         internal_hashmap_free(HASHMAP_BASE(s));
@@ -41,8 +42,8 @@ static inline Set *set_copy(Set *s) {
         return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
 }
 
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
 
 int set_put(Set *s, const void *key);
 /* no set_update */
@@ -122,6 +123,7 @@ static inline char **set_get_strv(Set *s) {
 int set_consume(Set *s, void *value);
 int set_put_strdup(Set *s, const char *p);
 int set_put_strdupv(Set *s, char **l);
+int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
 
 #define SET_FOREACH(e, s, i) \
         for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h
new file mode 100644
index 00000000..dfd6eb56
--- /dev/null
+++ b/src/systemd/src/basic/signal-util.h
@@ -0,0 +1,56 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2015 Lennart Poettering
+
+  systemd 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.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <signal.h>
+
+#include "macro.h"
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+int sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, sigset_t *old, ...);
+
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
+
+int signal_from_string_try_harder(const char *s);
+
+void nop_signal_handler(int sig);
+
+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;                                                                \
+        })
+
+static inline bool SIGNAL_VALID(int signo) {
+        return signo > 0 && signo < _NSIG;
+}
diff --git a/src/systemd/src/basic/siphash24.c b/src/systemd/src/basic/siphash24.c
index 145f64b2..4db444f5 100644
--- a/src/systemd/src/basic/siphash24.c
+++ b/src/systemd/src/basic/siphash24.c
@@ -19,6 +19,8 @@
 
 #include "nm-sd-adapt.h"
 
+#include <stdio.h>
+
 #include "macro.h"
 #include "siphash24.h"
 #include "unaligned.h"
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c
index c68db7c3..be57d829 100644
--- a/src/systemd/src/basic/socket-util.c
+++ b/src/systemd/src/basic/socket-util.c
@@ -25,6 +25,7 @@
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/ip.h>
+#include <poll.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -35,22 +36,18 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "fileio.h"
-#if 0 /* NM_IGNORED */
 #include "formats-util.h"
-#endif /* NM_IGNORED */
 #include "log.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 #include "parse-util.h"
 #include "path-util.h"
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
-#if 0 /* NM_IGNORED */
+#include "strv.h"
 #include "user-util.h"
-#endif /* NM_IGNORED */
+#include "utf8.h"
 #include "util.h"
 
 #if 0 /* NM_IGNORED */
@@ -91,7 +88,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                         return -EINVAL;
 
                 a->sockaddr.in6.sin6_family = AF_INET6;
-                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
                 a->size = sizeof(struct sockaddr_in6);
 
         } else if (*s == '/') {
@@ -139,7 +136,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 = htons((uint16_t) u);
+                                a->sockaddr.in.sin_port = htobe16((uint16_t)u);
                                 a->size = sizeof(struct sockaddr_in);
                         } else {
                                 unsigned idx;
@@ -153,7 +150,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                                         return -EINVAL;
 
                                 a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                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);
@@ -170,12 +167,12 @@ int socket_address_parse(SocketAddress *a, const char *s) {
 
                         if (socket_ipv6_is_supported()) {
                                 a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                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 = htons((uint16_t) u);
+                                a->sockaddr.in.sin_port = htobe16((uint16_t)u);
                                 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
                                 a->size = sizeof(struct sockaddr_in);
                         }
@@ -494,9 +491,7 @@ int sockaddr_port(const struct sockaddr *_sa) {
         if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
                 return -EAFNOSUPPORT;
 
-        return ntohs(sa->sa.sa_family == AF_INET6 ?
-                       sa->in6.sin6_port :
-                       sa->in.sin_port);
+        return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port);
 }
 
 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
@@ -512,13 +507,13 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
         case AF_INET: {
                 uint32_t a;
 
-                a = ntohl(sa->in.sin_addr.s_addr);
+                a = be32toh(sa->in.sin_addr.s_addr);
 
                 if (include_port)
                         r = asprintf(&p,
                                      "%u.%u.%u.%u:%u",
                                      a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
-                                     ntohs(sa->in.sin_port));
+                                     be16toh(sa->in.sin_port));
                 else
                         r = asprintf(&p,
                                      "%u.%u.%u.%u",
@@ -540,7 +535,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
                                 r = asprintf(&p,
                                              "%u.%u.%u.%u:%u",
                                              a[0], a[1], a[2], a[3],
-                                             ntohs(sa->in6.sin6_port));
+                                             be16toh(sa->in6.sin6_port));
                         else
                                 r = asprintf(&p,
                                              "%u.%u.%u.%u",
@@ -556,7 +551,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
                                 r = asprintf(&p,
                                              "[%s]:%u",
                                              a,
-                                             ntohs(sa->in6.sin6_port));
+                                             be16toh(sa->in6.sin6_port));
                                 if (r < 0)
                                         return -ENOMEM;
                         } else {
@@ -803,6 +798,42 @@ static const char* const ip_tos_table[] = {
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
 
+bool ifname_valid(const char *p) {
+        bool numeric = true;
+
+        /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources
+         * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We
+         * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */
+
+        if (isempty(p))
+                return false;
+
+        if (strlen(p) >= IFNAMSIZ)
+                return false;
+
+        if (STR_IN_SET(p, ".", ".."))
+                return false;
+
+        while (*p) {
+                if ((unsigned char) *p >= 127U)
+                        return false;
+
+                if ((unsigned char) *p <= 32U)
+                        return false;
+
+                if (*p == ':' || *p == '/')
+                        return false;
+
+                numeric = numeric && (*p >= '0' && *p <= '9');
+                p++;
+        }
+
+        if (numeric)
+                return false;
+
+        return true;
+}
+
 int getpeercred(int fd, struct ucred *ucred) {
         socklen_t n = sizeof(struct ucred);
         struct ucred u;
@@ -959,7 +990,7 @@ ssize_t next_datagram_size_fd(int fd) {
 
         l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC);
         if (l < 0) {
-                if (errno == EOPNOTSUPP)
+                if (errno == EOPNOTSUPP || errno == EFAULT)
                         goto fallback;
 
                 return -errno;
@@ -980,3 +1011,56 @@ fallback:
 
         return (ssize_t) k;
 }
+
+int flush_accept(int fd) {
+
+        struct pollfd pollfd = {
+                .fd = fd,
+                .events = POLLIN,
+        };
+        int r;
+
+
+        /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */
+
+        for (;;) {
+                int cfd;
+
+                r = poll(&pollfd, 1, 0);
+                if (r < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+
+                } else if (r == 0)
+                        return 0;
+
+                cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+                if (cfd < 0) {
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN)
+                                return 0;
+
+                        return -errno;
+                }
+
+                close(cfd);
+        }
+}
+
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
+        struct cmsghdr *cmsg;
+
+        assert(mh);
+
+        CMSG_FOREACH(cmsg, mh)
+                if (cmsg->cmsg_level == level &&
+                    cmsg->cmsg_type == type &&
+                    (length == (socklen_t) -1 || length == cmsg->cmsg_len))
+                        return cmsg;
+
+        return NULL;
+}
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
index d17a2f35..2536b085 100644
--- a/src/systemd/src/basic/socket-util.h
+++ b/src/systemd/src/basic/socket-util.h
@@ -123,6 +123,8 @@ int fd_inc_rcvbuf(int fd, size_t n);
 int ip_tos_to_string_alloc(int i, char **s);
 int ip_tos_from_string(const char *s);
 
+bool ifname_valid(const char *p);
+
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
 
@@ -135,5 +137,20 @@ int receive_one_fd(int transport_fd, int flags);
 
 ssize_t next_datagram_size_fd(int fd);
 
+int flush_accept(int fd);
+
 #define CMSG_FOREACH(cmsg, mh)                                          \
         for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
+
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
+
+/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
+#define SOCKADDR_UN_LEN(sa)                                             \
+        ({                                                              \
+                const struct sockaddr_un *_sa = &(sa);                  \
+                assert(_sa->sun_family == AF_UNIX);                     \
+                offsetof(struct sockaddr_un, sun_path) +                \
+                        (_sa->sun_path[0] == 0 ?                        \
+                         1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
+                         strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
+        })
diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h
index b180488f..369610ef 100644
--- a/src/systemd/src/basic/string-table.h
+++ b/src/systemd/src/basic/string-table.h
@@ -48,6 +48,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
         scope type name##_from_string(const char *s) {                  \
                 int b;                                                  \
+                if (!s)                                                 \
+                        return -1;                                      \
                 b = parse_boolean(s);                                   \
                 if (b == 0)                                             \
                         return (type) 0;                                \
@@ -56,26 +58,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                 return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
         }
 
-#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)        \
-        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) \
-        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)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
-#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
-
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
-
-/* For string conversions where numbers are also acceptable */
-#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max)         \
-        int name##_to_string_alloc(type i, char **str) {                \
+#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \
+        scope int name##_to_string_alloc(type i, char **str) {          \
                 char *s;                                                \
                 if (i < 0 || i > max)                                   \
                         return -ERANGE;                                 \
@@ -89,7 +73,9 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                 }                                                       \
                 *str = s;                                               \
                 return 0;                                               \
-        }                                                               \
+        }
+
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
         type name##_from_string(const char *s) {                        \
                 type i;                                                 \
                 unsigned u = 0;                                         \
@@ -102,4 +88,32 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                         return (type) u;                                \
                 return (type) -1;                                       \
         }                                                               \
+
+
+#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)        \
+        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) \
+        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)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
+
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
+
+/* 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,) \
         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)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_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 68de772e..b4e774cc 100644
--- a/src/systemd/src/basic/string-util.c
+++ b/src/systemd/src/basic/string-util.c
@@ -24,11 +24,10 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "gunicode.h"
-#endif /* NM_IGNORED */
 #include "macro.h"
 #include "string-util.h"
 #include "utf8.h"
@@ -327,6 +326,14 @@ char ascii_tolower(char x) {
         return x;
 }
 
+char ascii_toupper(char x) {
+
+        if (x >= 'a' && x <= 'z')
+                return x - 'a' + 'A';
+
+        return x;
+}
+
 char *ascii_strlower(char *t) {
         char *p;
 
@@ -338,6 +345,17 @@ char *ascii_strlower(char *t) {
         return t;
 }
 
+char *ascii_strupper(char *t) {
+        char *p;
+
+        assert(t);
+
+        for (p = t; *p; p++)
+                *p = ascii_toupper(*p);
+
+        return t;
+}
+
 char *ascii_strlower_n(char *t, size_t n) {
         size_t i;
 
@@ -809,25 +827,20 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
-#pragma GCC push_options
-#pragma GCC optimize("O0")
+/*
+ * Pointer to memset is volatile so that compiler must de-reference
+ * the pointer and can't assume that it points to any function in
+ * particular (such as memset, which it then might further "optimize")
+ * This approach is inspired by openssl's crypto/mem_clr.c.
+ */
+typedef void *(*memset_t)(void *,int,size_t);
 
-void* memory_erase(void *p, size_t l) {
-        volatile uint8_t* x = (volatile uint8_t*) p;
-
-        /* This basically does what memset() does, but hopefully isn't
-         * optimized away by the compiler. One of those days, when
-         * glibc learns memset_s() we should replace this call by
-         * memset_s(), but until then this has to do. */
-
-        for (; l > 0; l--)
-                *(x++) = 'x';
+static volatile memset_t memset_func = memset;
 
-        return p;
+void* memory_erase(void *p, size_t l) {
+        return memset_func(p, 'x', l);
 }
 
-#pragma GCC pop_options
-
 char* string_erase(char *x) {
 
         if (!x)
diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h
index ad0c8137..b75aba63 100644
--- a/src/systemd/src/basic/string-util.h
+++ b/src/systemd/src/basic/string-util.h
@@ -37,6 +37,7 @@
 #define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 #define LETTERS           LOWERCASE_LETTERS UPPERCASE_LETTERS
 #define ALPHANUMERICAL    LETTERS DIGITS
+#define HEXDIGITS         DIGITS "abcdefABCDEF"
 
 #define streq(a,b) (strcmp((a),(b)) == 0)
 #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
@@ -65,6 +66,10 @@ static inline bool isempty(const char *p) {
         return !p || !p[0];
 }
 
+static inline const char *empty_to_null(const char *p) {
+        return isempty(p) ? NULL : p;
+}
+
 static inline char *startswith(const char *s, const char *prefix) {
         size_t l;
 
@@ -132,6 +137,9 @@ char ascii_tolower(char x);
 char *ascii_strlower(char *s);
 char *ascii_strlower_n(char *s, size_t n);
 
+char ascii_toupper(char x);
+char *ascii_strupper(char *s);
+
 int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
 int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
 
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
index e885c598..eeb8c1e0 100644
--- a/src/systemd/src/basic/strv.c
+++ b/src/systemd/src/basic/strv.c
@@ -28,9 +28,7 @@
 
 #include "alloc-util.h"
 #include "escape.h"
-#if 0 /* NM_IGNORED */
 #include "extract-word.h"
-#endif /* NM_IGNORED */
 #include "fileio.h"
 #include "string-util.h"
 #include "strv.h"
@@ -143,16 +141,16 @@ char **strv_new_ap(const char *x, va_list ap) {
         va_list aq;
 
         /* As a special trick we ignore all listed strings that equal
-         * (const char*) -1. This is supposed to be used with the
+         * STRV_IGNORE. This is supposed to be used with the
          * STRV_IFNOTNULL() macro to include possibly NULL strings in
          * the string list. */
 
         if (x) {
-                n = x == (const char*) -1 ? 0 : 1;
+                n = x == STRV_IGNORE ? 0 : 1;
 
                 va_copy(aq, ap);
                 while ((s = va_arg(aq, const char*))) {
-                        if (s == (const char*) -1)
+                        if (s == STRV_IGNORE)
                                 continue;
 
                         n++;
@@ -166,7 +164,7 @@ char **strv_new_ap(const char *x, va_list ap) {
                 return NULL;
 
         if (x) {
-                if (x != (const char*) -1) {
+                if (x != STRV_IGNORE) {
                         a[i] = strdup(x);
                         if (!a[i])
                                 goto fail;
@@ -175,7 +173,7 @@ char **strv_new_ap(const char *x, va_list ap) {
 
                 while ((s = va_arg(ap, const char*))) {
 
-                        if (s == (const char*) -1)
+                        if (s == STRV_IGNORE)
                                 continue;
 
                         a[i] = strdup(s);
@@ -564,6 +562,42 @@ int strv_extend(char ***l, const char *value) {
         return strv_consume(l, v);
 }
 
+int strv_extend_front(char ***l, const char *value) {
+        size_t n, m;
+        char *v, **c;
+
+        assert(l);
+
+        /* Like strv_extend(), but prepends rather than appends the new entry */
+
+        if (!value)
+                return 0;
+
+        n = strv_length(*l);
+
+        /* Increase and overflow check. */
+        m = n + 2;
+        if (m < n)
+                return -ENOMEM;
+
+        v = strdup(value);
+        if (!v)
+                return -ENOMEM;
+
+        c = realloc_multiply(*l, sizeof(char*), m);
+        if (!c) {
+                free(v);
+                return -ENOMEM;
+        }
+
+        memmove(c+1, c, n * sizeof(char*));
+        c[0] = v;
+        c[n+1] = NULL;
+
+        *l = c;
+        return 0;
+}
+
 char **strv_uniq(char **l) {
         char **i;
 
@@ -608,6 +642,17 @@ char **strv_remove(char **l, const char *s) {
 }
 
 char **strv_parse_nulstr(const char *s, size_t l) {
+        /* l is the length of the input data, which will be split at NULs into
+         * elements of the resulting strv. Hence, the number of items in the resulting strv
+         * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
+         * unless s[l-1] is NUL, in which case the final empty string is not stored in
+         * the resulting strv, and length is equal to the number of NUL bytes.
+         *
+         * Note that contrary to a normal nulstr which cannot contain empty strings, because
+         * the input data is terminated by any two consequent NUL bytes, this parser accepts
+         * empty strings in s.
+         */
+
         const char *p;
         unsigned c = 0, i = 0;
         char **v;
@@ -670,6 +715,13 @@ char **strv_split_nulstr(const char *s) {
 }
 
 int strv_make_nulstr(char **l, char **p, size_t *q) {
+        /* A valid nulstr with two NULs at the end will be created, but
+         * q will be the length without the two trailing NULs. Thus the output
+         * string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
+         * and can also be parsed by strv_parse_nulstr as long as the length
+         * is provided separately.
+         */
+
         size_t n_allocated = 0, n = 0;
         _cleanup_free_ char *m = NULL;
         char **i;
@@ -682,7 +734,7 @@ int strv_make_nulstr(char **l, char **p, size_t *q) {
 
                 z = strlen(*i);
 
-                if (!GREEDY_REALLOC(m, n_allocated, n + z + 1))
+                if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
                         return -ENOMEM;
 
                 memcpy(m + n, *i, z + 1);
@@ -693,11 +745,14 @@ int strv_make_nulstr(char **l, char **p, size_t *q) {
                 m = new0(char, 1);
                 if (!m)
                         return -ENOMEM;
-                n = 0;
-        }
+                n = 1;
+        } else
+                /* make sure there is a second extra NUL at the end of resulting nulstr */
+                m[n] = '\0';
 
+        assert(n > 0);
         *p = m;
-        *q = n;
+        *q = n - 1;
 
         m = NULL;
 
@@ -773,13 +828,8 @@ char **strv_reverse(char **l) {
         if (n <= 1)
                 return l;
 
-        for (i = 0; i < n / 2; i++) {
-                char *t;
-
-                t = l[i];
-                l[i] = l[n-1-i];
-                l[n-1-i] = t;
-        }
+        for (i = 0; i < n / 2; i++)
+                SWAP_TWO(l[i], l[n-1-i]);
 
         return l;
 }
@@ -808,7 +858,7 @@ bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
         char* const* p;
 
         STRV_FOREACH(p, patterns)
-                if (fnmatch(*p, s, 0) == 0)
+                if (fnmatch(*p, s, flags) == 0)
                         return true;
 
         return false;
@@ -850,7 +900,7 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
         if (n == 0)
                 return 0;
 
-        /* Adds the value value n times to l */
+        /* Adds the value n times to l */
 
         k = strv_length(*l);
 
diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h
index 4a93818a..683ce83a 100644
--- a/src/systemd/src/basic/strv.h
+++ b/src/systemd/src/basic/strv.h
@@ -25,9 +25,7 @@
 #include <stddef.h>
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "extract-word.h"
-#endif /* NM_IGNORED */
 #include "macro.h"
 #include "util.h"
 
@@ -52,6 +50,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
 int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
 int strv_extend(char ***l, const char *value);
 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_push_prepend(char ***l, char *value);
@@ -70,8 +69,10 @@ bool strv_equal(char **a, char **b);
 char **strv_new(const char *x, ...) _sentinel_;
 char **strv_new_ap(const char *x, va_list ap);
 
+#define STRV_IGNORE ((const char *) -1)
+
 static inline const char* STRV_IFNOTNULL(const char *x) {
-        return x ? x : (const char *) -1;
+        return x ? x : STRV_IGNORE;
 }
 
 static inline bool strv_isempty(char * const *l) {
@@ -81,9 +82,7 @@ static inline bool strv_isempty(char * const *l) {
 char **strv_split(const char *s, const char *separator);
 char **strv_split_newlines(const char *s);
 
-#if 0 /* NM_IGNORED */
 int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
-#endif /* NM_IGNORED */
 
 char *strv_join(char **l, const char *separator);
 char *strv_join_quoted(char **l);
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
index 5b2b2180..212c8f4e 100644
--- a/src/systemd/src/basic/time-util.c
+++ b/src/systemd/src/basic/time-util.c
@@ -49,15 +49,12 @@ static clockid_t map_clock_id(clockid_t c) {
         /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
          * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
          * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
-         * those archs.
-         *
-         * Also, older kernels don't support CLOCK_BOOTTIME: fall back to CLOCK_MONOTONIC. */
+         * those archs. */
 
         switch (c) {
 
-        case CLOCK_BOOTTIME:
         case CLOCK_BOOTTIME_ALARM:
-                return clock_boottime_or_monotonic ();
+                return CLOCK_BOOTTIME;
 
         case CLOCK_REALTIME_ALARM:
                 return CLOCK_REALTIME;
@@ -92,6 +89,16 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
         return ts;
 }
 
+triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
+        assert(ts);
+
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+        ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
+
+        return ts;
+}
+
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
         int64_t delta;
         assert(ts);
@@ -109,6 +116,24 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
         return ts;
 }
 
+triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
+        int64_t delta;
+
+        assert(ts);
+
+        if (u == USEC_INFINITY || u <= 0) {
+                ts->realtime = ts->monotonic = ts->boottime = u;
+                return ts;
+        }
+
+        ts->realtime = u;
+        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+        ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta);
+        ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
+
+        return ts;
+}
+
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
         int64_t delta;
         assert(ts);
@@ -141,6 +166,26 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
         return ts;
 }
 
+usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
+
+        switch (clock) {
+
+        case CLOCK_REALTIME:
+        case CLOCK_REALTIME_ALARM:
+                return ts->realtime;
+
+        case CLOCK_MONOTONIC:
+                return ts->monotonic;
+
+        case CLOCK_BOOTTIME:
+        case CLOCK_BOOTTIME_ALARM:
+                return ts->boottime;
+
+        default:
+                return USEC_INFINITY;
+        }
+}
+
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
@@ -212,32 +257,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         return tv;
 }
 
-static char *format_timestamp_internal(char *buf, size_t l, usec_t t,
-                                       bool utc, bool us) {
+static char *format_timestamp_internal(
+                char *buf,
+                size_t l,
+                usec_t t,
+                bool utc,
+                bool us) {
+
+        /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
+         * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
+        static const char * const weekdays[] = {
+                [0] = "Sun",
+                [1] = "Mon",
+                [2] = "Tue",
+                [3] = "Wed",
+                [4] = "Thu",
+                [5] = "Fri",
+                [6] = "Sat",
+        };
+
         struct tm tm;
         time_t sec;
-        int k;
+        size_t n;
 
         assert(buf);
-        assert(l > 0);
 
+        if (l <
+            3 +                  /* week day */
+            1 + 10 +             /* space and date */
+            1 + 8 +              /* space and time */
+            (us ? 1 + 6 : 0) +   /* "." and microsecond part */
+            1 + 1 +              /* space and shortest possible zone */
+            1)
+                return NULL; /* Not enough space even for the shortest form. */
         if (t <= 0 || t == USEC_INFINITY)
+                return NULL; /* Timestamp is unset */
+
+        sec = (time_t) (t / USEC_PER_SEC); /* Round down */
+        if ((usec_t) sec != (t / USEC_PER_SEC))
+                return NULL; /* overflow? */
+
+        if (!localtime_or_gmtime_r(&sec, &tm, utc))
                 return NULL;
 
-        sec = (time_t) (t / USEC_PER_SEC);
-        localtime_or_gmtime_r(&sec, &tm, utc);
+        /* Start with the week day */
+        assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
+        memcpy(buf, weekdays[tm.tm_wday], 4);
 
-        if (us)
-                k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm);
-        else
-                k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm);
+        /* Add the main components */
+        if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
+                return NULL; /* Doesn't fit */
 
-        if (k <= 0)
-                return NULL;
+        /* Append the microseconds part, if that's requested */
         if (us) {
-                snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
-                if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
-                        return NULL;
+                n = strlen(buf);
+                if (n + 8 > l)
+                        return NULL; /* Microseconds part doesn't fit. */
+
+                sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+        }
+
+        /* Append the timezone */
+        n = strlen(buf);
+        if (utc) {
+                /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
+                 * obsolete "GMT" instead. */
+                if (n + 5 > l)
+                        return NULL; /* "UTC" doesn't fit. */
+
+                strcpy(buf + n, " UTC");
+
+        } else if (!isempty(tm.tm_zone)) {
+                size_t tn;
+
+                /* An explicit timezone is specified, let's use it, if it fits */
+                tn = strlen(tm.tm_zone);
+                if (n + 1 + tn + 1 > l) {
+                        /* The full time zone does not fit in. Yuck. */
+
+                        if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
+                                return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
+
+                        /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
+                         * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
+                         * an overly long, hard to read string on the user. This should be safe, because the user will
+                         * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
+                } else {
+                        buf[n++] = ' ';
+                        strcpy(buf + n, tm.tm_zone);
+                }
         }
 
         return buf;
@@ -499,12 +607,11 @@ int parse_timestamp(const char *t, usec_t *usec) {
                 { "Sat",       6 },
         };
 
-        const char *k;
-        const char *utc;
+        const char *k, *utc, *tzn = NULL;
         struct tm tm, copy;
         time_t x;
         usec_t x_usec, plus = 0, minus = 0, ret;
-        int r, weekday = -1;
+        int r, weekday = -1, dst = -1;
         unsigned i;
 
         /*
@@ -569,15 +676,55 @@ int parse_timestamp(const char *t, usec_t *usec) {
                 goto finish;
         }
 
+        /* See if the timestamp is suffixed with UTC */
         utc = endswith_no_case(t, " UTC");
         if (utc)
                 t = strndupa(t, utc - t);
+        else {
+                const char *e = NULL;
+                int j;
+
+                tzset();
+
+                /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
+                 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
+                 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
+                 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
+                 * support arbitrary timezone specifications.  */
 
-        x = ret / USEC_PER_SEC;
+                for (j = 0; j <= 1; j++) {
+
+                        if (isempty(tzname[j]))
+                                continue;
+
+                        e = endswith_no_case(t, tzname[j]);
+                        if (!e)
+                                continue;
+                        if (e == t)
+                                continue;
+                        if (e[-1] != ' ')
+                                continue;
+
+                        break;
+                }
+
+                if (IN_SET(j, 0, 1)) {
+                        /* Found one of the two timezones specified. */
+                        t = strndupa(t, e - t - 1);
+                        dst = j;
+                        tzn = tzname[j];
+                }
+        }
+
+        x = (time_t) (ret / USEC_PER_SEC);
         x_usec = 0;
 
-        assert_se(localtime_or_gmtime_r(&x, &tm, utc));
-        tm.tm_isdst = -1;
+        if (!localtime_or_gmtime_r(&x, &tm, utc))
+                return -EINVAL;
+
+        tm.tm_isdst = dst;
+        if (tzn)
+                tm.tm_zone = tzn;
 
         if (streq(t, "today")) {
                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
@@ -594,7 +741,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
                 goto from_tm;
         }
 
-
         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
                 size_t skip;
 
@@ -687,7 +833,6 @@ parse_usec:
                         return -EINVAL;
 
                 x_usec = add;
-
         }
 
 from_tm:
@@ -1089,22 +1234,55 @@ bool timezone_is_valid(const char *name) {
         return true;
 }
 
-clockid_t clock_boottime_or_monotonic(void) {
-        static clockid_t clock = -1;
-        int fd;
+bool clock_boottime_supported(void) {
+        static int supported = -1;
 
-        if (clock != -1)
-                return clock;
+        /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
 
-        fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
-        if (fd < 0)
-                clock = CLOCK_MONOTONIC;
-        else {
-                safe_close(fd);
-                clock = CLOCK_BOOTTIME;
+        if (supported < 0) {
+                int fd;
+
+                fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+                if (fd < 0)
+                        supported = false;
+                else {
+                        safe_close(fd);
+                        supported = true;
+                }
         }
 
-        return clock;
+        return supported;
+}
+
+clockid_t clock_boottime_or_monotonic(void) {
+        if (clock_boottime_supported())
+                return CLOCK_BOOTTIME;
+        else
+                return CLOCK_MONOTONIC;
+}
+
+bool clock_supported(clockid_t clock) {
+        struct timespec ts;
+
+        switch (clock) {
+
+        case CLOCK_MONOTONIC:
+        case CLOCK_REALTIME:
+                return true;
+
+        case CLOCK_BOOTTIME:
+                return clock_boottime_supported();
+
+        case CLOCK_BOOTTIME_ALARM:
+                if (!clock_boottime_supported())
+                        return false;
+
+                /* fall through, after checking the cached value for CLOCK_BOOTTIME. */
+
+        default:
+                /* For everything else, check properly */
+                return clock_gettime(clock, &ts) >= 0;
+        }
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index 77e3cd08..99be5ce6 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -39,6 +39,12 @@ typedef struct dual_timestamp {
         usec_t monotonic;
 } dual_timestamp;
 
+typedef struct triple_timestamp {
+        usec_t realtime;
+        usec_t monotonic;
+        usec_t boottime;
+} triple_timestamp;
+
 #define USEC_INFINITY ((usec_t) -1)
 #define NSEC_INFINITY ((nsec_t) -1)
 
@@ -62,14 +68,17 @@ typedef struct dual_timestamp {
 #define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
 #define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
 
-#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
+ * to 6. Let's rely on that. */
+#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
 #define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
 #define FORMAT_TIMESTAMP_RELATIVE_MAX 256
 #define FORMAT_TIMESPAN_MAX 64
 
 #define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
 
-#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
+#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
+#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
 
 usec_t now(clockid_t clock);
 nsec_t now_nsec(clockid_t clock);
@@ -79,11 +88,28 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
 
+triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
+triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
+
+#define DUAL_TIMESTAMP_HAS_CLOCK(clock)                               \
+        IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
+
+#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(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(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));
+}
+
+usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
+
 usec_t timespec_load(const struct timespec *ts) _pure_;
 struct timespec *timespec_store(struct timespec *ts, usec_t u);
 
@@ -112,6 +138,8 @@ bool ntp_synced(void);
 int get_timezones(char ***l);
 bool timezone_is_valid(const char *name);
 
+bool clock_boottime_supported(void);
+bool clock_supported(clockid_t clock);
 clockid_t clock_boottime_or_monotonic(void);
 
 #define xstrftime(buf, fmt, tm) \
diff --git a/src/systemd/src/basic/unaligned.h b/src/systemd/src/basic/unaligned.h
index 79be645b..7c847a3c 100644
--- a/src/systemd/src/basic/unaligned.h
+++ b/src/systemd/src/basic/unaligned.h
@@ -109,3 +109,21 @@ static inline void unaligned_write_le64(void *_u, uint64_t a) {
         unaligned_write_le32(u, (uint32_t) a);
         unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
 }
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define unaligned_read_ne16 unaligned_read_be16
+#define unaligned_read_ne32 unaligned_read_be32
+#define unaligned_read_ne64 unaligned_read_be64
+
+#define unaligned_write_ne16 unaligned_write_be16
+#define unaligned_write_ne32 unaligned_write_be32
+#define unaligned_write_ne64 unaligned_write_be64
+#else
+#define unaligned_read_ne16 unaligned_read_le16
+#define unaligned_read_ne32 unaligned_read_le32
+#define unaligned_read_ne64 unaligned_read_le64
+
+#define unaligned_write_ne16 unaligned_write_le16
+#define unaligned_write_ne32 unaligned_write_le32
+#define unaligned_write_ne64 unaligned_write_le64
+#endif
diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h
index 1bc23dc1..322dac20 100644
--- a/src/systemd/src/basic/utf8.h
+++ b/src/systemd/src/basic/utf8.h
@@ -27,9 +27,7 @@
 #endif /* NM_IGNORED */
 
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 
 #define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
 #define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
index 6d641a4a..79c7aa46 100644
--- a/src/systemd/src/basic/util.c
+++ b/src/systemd/src/basic/util.c
@@ -37,39 +37,29 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "build.h"
+#include "cgroup-util.h"
 #include "def.h"
 #include "dirent-util.h"
-#endif /* NM_IGNORED */
 #include "fd-util.h"
 #include "fileio.h"
-#if 0 /* NM_IGNORED */
 #include "formats-util.h"
-#endif /* NM_IGNORED */
 #include "hashmap.h"
 #include "hostname-util.h"
 #include "log.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 #include "parse-util.h"
 #include "path-util.h"
-#if 0 /* NM_IGNORED */
 #include "process-util.h"
-#endif /* NM_IGNORED */
 #include "set.h"
-#if 0 /* NM_IGNORED */
 #include "signal-util.h"
 #include "stat-util.h"
-#endif /* NM_IGNORED */
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
-#if 0 /* NM_IGNORED */
+#include "umask-util.h"
 #include "user-util.h"
-#endif /* NM_IGNORED */
 #include "util.h"
 
 /* Put this test here for a lack of better place */
@@ -78,6 +68,7 @@ assert_cc(EAGAIN == EWOULDBLOCK);
 #if 0 /* NM_IGNORED */
 int saved_argc = 0;
 char **saved_argv = NULL;
+static int saved_in_initrd = -1;
 #endif /* NM_IGNORED */
 
 size_t page_size(void) {
@@ -436,13 +427,17 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
                         _exit(EXIT_FAILURE);
                 }
 
-                if (!stdout_is_tty)
-                        dup2(fd, STDOUT_FILENO);
+                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);
+                if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+                        _exit(EXIT_FAILURE);
+                }
 
-                if (fd > 2)
+                if (fd > STDERR_FILENO)
                         close(fd);
         }
 
@@ -466,11 +461,10 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa
 }
 
 bool in_initrd(void) {
-        static int saved = -1;
         struct statfs s;
 
-        if (saved >= 0)
-                return saved;
+        if (saved_in_initrd >= 0)
+                return saved_in_initrd;
 
         /* We make two checks here:
          *
@@ -482,11 +476,15 @@ bool in_initrd(void) {
          * emptying when transititioning to the main systemd.
          */
 
-        saved = access("/etc/initrd-release", F_OK) >= 0 &&
-                statfs("/", &s) >= 0 &&
-                is_temporary_fs(&s);
+        saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
+                          statfs("/", &s) >= 0 &&
+                          is_temporary_fs(&s);
+
+        return saved_in_initrd;
+}
 
-        return saved;
+void in_initrd_force(bool value) {
+        saved_in_initrd = value;
 }
 
 /* hey glibc, APIs with callbacks without a user pointer are so useless */
@@ -534,7 +532,7 @@ int on_ac_power(void) {
                 if (!de)
                         break;
 
-                if (hidden_file(de->d_name))
+                if (hidden_or_backup_file(de->d_name))
                         continue;
 
                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
@@ -588,47 +586,6 @@ int on_ac_power(void) {
         return found_online || !found_offline;
 }
 
-bool id128_is_valid(const char *s) {
-        size_t i, l;
-
-        l = strlen(s);
-        if (l == 32) {
-
-                /* Simple formatted 128bit hex string */
-
-                for (i = 0; i < l; i++) {
-                        char c = s[i];
-
-                        if (!(c >= '0' && c <= '9') &&
-                            !(c >= 'a' && c <= 'z') &&
-                            !(c >= 'A' && c <= 'Z'))
-                                return false;
-                }
-
-        } else if (l == 36) {
-
-                /* Formatted UUID */
-
-                for (i = 0; i < l; i++) {
-                        char c = s[i];
-
-                        if ((i == 8 || i == 13 || i == 18 || i == 23)) {
-                                if (c != '-')
-                                        return false;
-                        } else {
-                                if (!(c >= '0' && c <= '9') &&
-                                    !(c >= 'a' && c <= 'z') &&
-                                    !(c >= 'A' && c <= 'Z'))
-                                        return false;
-                        }
-                }
-
-        } else
-                return false;
-
-        return true;
-}
-
 int container_get_leader(const char *machine, pid_t *pid) {
         _cleanup_free_ char *s = NULL, *class = NULL;
         const char *p;
@@ -779,26 +736,140 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
 }
 
 uint64_t physical_memory(void) {
-        long mem;
+        _cleanup_free_ char *root = NULL, *value = NULL;
+        uint64_t mem, lim;
+        size_t ps;
+        long sc;
+
+        /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
+         * memory.
+         *
+         * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
+         * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
+
+        sc = sysconf(_SC_PHYS_PAGES);
+        assert(sc > 0);
+
+        ps = page_size();
+        mem = (uint64_t) sc * (uint64_t) ps;
+
+        if (cg_get_root_path(&root) < 0)
+                return mem;
+
+        if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
+                return mem;
 
-        /* We return this as uint64_t in case we are running as 32bit
-         * process on a 64bit kernel with huge amounts of memory */
+        if (safe_atou64(value, &lim) < 0)
+                return mem;
 
-        mem = sysconf(_SC_PHYS_PAGES);
-        assert(mem > 0);
+        /* Make sure the limit is a multiple of our own page size */
+        lim /= ps;
+        lim *= ps;
 
-        return (uint64_t) mem * (uint64_t) page_size();
+        return MIN(mem, lim);
 }
 
-int update_reboot_param_file(const char *param) {
-        int r = 0;
+uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
+        uint64_t p, m, ps, r;
 
-        if (param) {
-                r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
+        assert(max > 0);
+
+        /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
+         * the result is a multiple of the page size (rounds down). */
+
+        ps = page_size();
+        assert(ps > 0);
+
+        p = physical_memory() / ps;
+        assert(p > 0);
+
+        m = p * v;
+        if (m / p != v)
+                return UINT64_MAX;
+
+        m /= max;
+
+        r = m * ps;
+        if (r / ps != m)
+                return UINT64_MAX;
+
+        return r;
+}
+
+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;
+
+        /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
+         * limit:
+         *
+         * a) the maximum value for the pid_t type
+         * b) the cgroups pids_max attribute for the system
+         * c) the kernel's configure maximum PID value
+         *
+         * And then pick the smallest of the three */
+
+        if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
+                (void) safe_atou64(value, &a);
+
+        if (cg_get_root_path(&root) >= 0) {
+                value = mfree(value);
+
+                if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
+                        (void) safe_atou64(value, &b);
+        }
+
+        return MIN3(TASKS_MAX,
+                    a <= 0 ? TASKS_MAX : a,
+                    b <= 0 ? TASKS_MAX : b);
+}
+
+uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
+        uint64_t t, m;
+
+        assert(max > 0);
+
+        /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
+         * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
+
+        t = system_tasks_max();
+        assert(t > 0);
+
+        m = t * v;
+        if (m / t != v) /* overflow? */
+                return UINT64_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_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
-        } else
-                (void) unlink(REBOOT_PARAM_FILE);
+                        return log_warning_errno(r, "Failed to write reboot parameter file: %m");
+        }
 
         return 0;
 }
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index 3225269a..bb2fc318 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -41,13 +41,9 @@
 #include <time.h>
 #include <unistd.h>
 
-#if 0 /* NM_IGNORED */
 #include "formats-util.h"
-#endif /* NM_IGNORED */
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif /* NM_IGNORED */
 #include "time-util.h"
 
 size_t page_size(void) _pure_;
@@ -65,6 +61,10 @@ static inline const char* one_zero(bool b) {
         return b ? "1" : "0";
 }
 
+static inline const char* enable_disable(bool b) {
+        return b ? "enable" : "disable";
+}
+
 void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
 
 bool plymouth_running(void);
@@ -90,6 +90,7 @@ 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);
 
 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
                  int (*compar) (const void *, const void *, void *),
@@ -179,15 +180,17 @@ static inline unsigned log2u_round_up(unsigned x) {
         return log2u(x - 1) + 1;
 }
 
-bool id128_is_valid(const char *s) _pure_;
-
 int container_get_leader(const char *machine, pid_t *pid);
 
 int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
 
 uint64_t physical_memory(void);
+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 update_reboot_param_file(const char *param);
+int update_reboot_parameter_and_warn(const char *param);
 
 int version(void);
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
index 0c898145..8c678626 100644
--- a/src/systemd/src/libsystemd-network/arp-util.c
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -81,7 +81,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
         };
         union sockaddr_union link = {
                 .ll.sll_family = AF_PACKET,
-                .ll.sll_protocol = htons(ETH_P_ARP),
+                .ll.sll_protocol = htobe16(ETH_P_ARP),
                 .ll.sll_ifindex = ifindex,
                 .ll.sll_halen = ETH_ALEN,
                 .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
@@ -114,17 +114,17 @@ static int arp_send_packet(int fd, int ifindex,
                            bool announce) {
         union sockaddr_union link = {
                 .ll.sll_family = AF_PACKET,
-                .ll.sll_protocol = htons(ETH_P_ARP),
+                .ll.sll_protocol = htobe16(ETH_P_ARP),
                 .ll.sll_ifindex = ifindex,
                 .ll.sll_halen = ETH_ALEN,
                 .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
         };
         struct ether_arp arp = {
-                .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
-                .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
+                .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
+                .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
                 .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
                 .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
-                .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
+                .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
         };
         int r;
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
index c7a3eac5..c1fa8763 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.c
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -19,9 +19,7 @@
 
 #include "nm-sd-adapt.h"
 
-#if 0 /* NM_IGNORED */
 #include "libudev.h"
-#endif /* NM_IGNORED */
 #include "sd-id128.h"
 
 #include "dhcp-identifier.h"
@@ -29,9 +27,10 @@
 #include "network-internal.h"
 #include "siphash24.h"
 #include "sparse-endian.h"
-#if 0 /* NM_IGNORED */
 #include "udev-util.h"
 #include "virt.h"
+
+#if 0 /* NM_IGNORED */
 #else /* NM_IGNORED */
 #include <net/if.h>
 #endif /* NM_IGNORED */
@@ -39,6 +38,37 @@
 #define SYSTEMD_PEN 43793
 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
 
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
+        struct duid d;
+
+        assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
+        if (duid_len > MAX_DUID_LEN)
+                return -EINVAL;
+
+        switch (duid_type) {
+        case DUID_TYPE_LLT:
+                if (duid_len <= sizeof(d.llt))
+                        return -EINVAL;
+                break;
+        case DUID_TYPE_EN:
+                if (duid_len != sizeof(d.en))
+                        return -EINVAL;
+                break;
+        case DUID_TYPE_LL:
+                if (duid_len <= sizeof(d.ll))
+                        return -EINVAL;
+                break;
+        case DUID_TYPE_UUID:
+                if (duid_len != sizeof(d.uuid))
+                        return -EINVAL;
+                break;
+        default:
+                /* accept unknown type in order to be forward compatible */
+                break;
+        }
+        return 0;
+}
+
 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
         sd_id128_t machine_id;
         uint64_t hash;
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h
index babae15c..1cc0f9fb 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.h
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h
@@ -26,7 +26,6 @@
 #include "unaligned.h"
 
 typedef enum DUIDType {
-        DUID_TYPE_RAW       = 0,
         DUID_TYPE_LLT       = 1,
         DUID_TYPE_EN        = 2,
         DUID_TYPE_LL        = 3,
@@ -40,27 +39,28 @@ typedef enum DUIDType {
  */
 #define MAX_DUID_LEN 128
 
+/* https://tools.ietf.org/html/rfc3315#section-9.1 */
 struct duid {
         be16_t type;
         union {
                 struct {
-                        /* DHCP6_DUID_LLT */
+                        /* DUID_TYPE_LLT */
                         uint16_t htype;
                         uint32_t time;
                         uint8_t haddr[0];
                 } _packed_ llt;
                 struct {
-                        /* DHCP6_DUID_EN */
+                        /* DUID_TYPE_EN */
                         uint32_t pen;
                         uint8_t id[8];
                 } _packed_ en;
                 struct {
-                        /* DHCP6_DUID_LL */
+                        /* DUID_TYPE_LL */
                         int16_t htype;
                         uint8_t haddr[0];
                 } _packed_ ll;
                 struct {
-                        /* DHCP6_DUID_UUID */
+                        /* DUID_TYPE_UUID */
                         sd_id128_t uuid;
                 } _packed_ uuid;
                 struct {
@@ -69,34 +69,6 @@ struct duid {
         };
 } _packed_;
 
+int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len);
 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
 int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
-
-static inline int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) {
-        struct duid d;
-
-        assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
-
-        switch (duid_type) {
-        case DUID_TYPE_LLT:
-                if (duid_len <= sizeof(d.llt))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_EN:
-                if (duid_len != sizeof(d.en))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_LL:
-                if (duid_len <= sizeof(d.ll))
-                        return -EINVAL;
-                break;
-        case DUID_TYPE_UUID:
-                if (duid_len != sizeof(d.uuid))
-                        return -EINVAL;
-                break;
-        default:
-                /* accept unknown type in order to be forward compatible */
-                break;
-        }
-        return 0;
-}
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index 4662b0d8..99f69089 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -65,4 +65,5 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
 #define DHCP_CLIENT_DONT_DESTROY(client) \
         _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
 
-#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c
index 15a0bb7b..f1856d0a 100644
--- a/src/systemd/src/libsystemd-network/dhcp-network.c
+++ b/src/systemd/src/libsystemd-network/dhcp-network.c
@@ -109,9 +109,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                 return -errno;
 
         link->ll.sll_family = AF_PACKET;
-        link->ll.sll_protocol = htons(ETH_P_IP);
+        link->ll.sll_protocol = htobe16(ETH_P_IP);
         link->ll.sll_ifindex = ifindex;
-        link->ll.sll_hatype = htons(arp_type);
+        link->ll.sll_hatype = htobe16(arp_type);
         link->ll.sll_halen = mac_addr_len;
         memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len);
 
diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h
index 3e32484c..5cf7abbf 100644
--- a/src/systemd/src/libsystemd-network/dhcp-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h
@@ -59,7 +59,7 @@ typedef struct DHCPPacket DHCPPacket;
 #define DHCP_IP_UDP_SIZE        (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
 #define DHCP_MESSAGE_SIZE       (int32_t)(sizeof(DHCPMessage))
 #define DHCP_DEFAULT_MIN_SIZE   576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE   DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE
+#define DHCP_MIN_OPTIONS_SIZE   (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
 #define DHCP_MAGIC_COOKIE       (uint32_t)(0x63825363)
 
 enum {
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index 749086d3..945c3b97 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -55,7 +55,8 @@ struct DHCP6IA {
 
 typedef struct DHCP6IA DHCP6IA;
 
-#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__)
 
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
                         size_t optlen, const void *optval);
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h
index 7592bc43..becc162f 100644
--- a/src/systemd/src/libsystemd-network/lldp-internal.h
+++ b/src/systemd/src/libsystemd-network/lldp-internal.h
@@ -28,6 +28,8 @@
 #include "prioq.h"
 
 struct sd_lldp {
+        unsigned n_ref;
+
         int ifindex;
         int fd;
 
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
index ce1d89b6..afede1e7 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.c
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -199,7 +199,7 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
         assert(n);
 
         if (n->raw_size < sizeof(struct ether_header)) {
-                log_lldp("Recieved truncated packet, ignoring.");
+                log_lldp("Received truncated packet, ignoring.");
                 return -EBADMSG;
         }
 
@@ -362,9 +362,16 @@ end_marker:
 void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
         assert(n);
 
-        if (n->ttl > 0)
-                n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
-        else
+        if (n->ttl > 0) {
+                usec_t base;
+
+                /* Use the packet's timestamp if there is one known */
+                base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
+                if (base <= 0 || base == USEC_INFINITY)
+                        base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
+
+                n->until = usec_add(base, n->ttl * USEC_PER_SEC);
+        } else
                 n->until = 0;
 
         if (n->lldp)
@@ -590,11 +597,11 @@ done:
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
+_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
         assert_return(n, -EINVAL);
-        assert_return(ret, -EINVAL);
+        assert_return(ret_sec, -EINVAL);
 
-        *ret = n->ttl;
+        *ret_sec = n->ttl;
         return 0;
 }
 
@@ -653,7 +660,7 @@ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint
         return 0;
 }
 
-int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
+_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
         int r;
 
@@ -670,7 +677,7 @@ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t ra
                 return r;
 
         *ret = n;
-        n = 0;
+        n = NULL;
 
         return r;
 }
@@ -681,7 +688,7 @@ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
         assert(n->raw_size >= sizeof(struct ether_header));
         n->rindex = sizeof(struct ether_header);
 
-        return 0;
+        return n->rindex < n->raw_size;
 }
 
 _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
@@ -695,7 +702,7 @@ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
         if (n->rindex + 2 > n->raw_size) /* Truncated message */
                 return -EBADMSG;
 
-        length = LLDP_NEIGHBOR_LENGTH(n);
+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
         if (n->rindex + 2 + length > n->raw_size)
                 return -EBADMSG;
 
@@ -713,7 +720,7 @@ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
         if (n->rindex + 2 > n->raw_size)
                 return -EBADMSG;
 
-        *type = LLDP_NEIGHBOR_TYPE(n);
+        *type = LLDP_NEIGHBOR_TLV_TYPE(n);
         return 0;
 }
 
@@ -745,14 +752,14 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], u
         if (r == 0)
                 return -ENXIO;
 
-        length = LLDP_NEIGHBOR_LENGTH(n);
+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
         if (length < 4)
                 return -EBADMSG;
 
         if (n->rindex + 2 + length > n->raw_size)
                 return -EBADMSG;
 
-        d = LLDP_NEIGHBOR_DATA(n);
+        d = LLDP_NEIGHBOR_TLV_DATA(n);
         memcpy(oui, d, 3);
         *subtype = d[3];
 
@@ -784,8 +791,7 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
         if (n->rindex + 2 > n->raw_size)
                 return -EBADMSG;
 
-        length = LLDP_NEIGHBOR_LENGTH(n);
-
+        length = LLDP_NEIGHBOR_TLV_LENGTH(n);
         if (n->rindex + 2 + length > n->raw_size)
                 return -EBADMSG;
 
@@ -794,3 +800,16 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
 
         return 0;
 }
+
+_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
+        assert_return(n, -EINVAL);
+        assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
+        assert_return(clock_supported(clock), -EOPNOTSUPP);
+        assert_return(ret, -EINVAL);
+
+        if (!triple_timestamp_is_set(&n->timestamp))
+                return -ENODATA;
+
+        *ret = triple_timestamp_by_clock(&n->timestamp, clock);
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h
index f203bfa6..c1a7606d 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.h
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h
@@ -43,6 +43,8 @@ struct sd_lldp_neighbor {
         sd_lldp *lldp;
         unsigned n_ref;
 
+        triple_timestamp timestamp;
+
         usec_t until;
         unsigned prioq_idx;
 
@@ -81,18 +83,18 @@ static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
         return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
 }
 
-static inline uint8_t LLDP_NEIGHBOR_TYPE(const sd_lldp_neighbor *n) {
+static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) {
         return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
 }
 
-static inline size_t LLDP_NEIGHBOR_LENGTH(const sd_lldp_neighbor *n) {
+static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) {
         uint8_t *p;
 
         p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
         return p[1] + (((size_t) (p[0] & 1)) << 8);
 }
 
-static inline void* LLDP_NEIGHBOR_DATA(const sd_lldp_neighbor *n) {
+static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
         return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
 }
 
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
index 658448f6..bba3a752 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.c
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -59,7 +59,8 @@ int lldp_network_bind_raw_socket(int ifindex) {
 
         assert(ifindex > 0);
 
-        fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, htons(ETHERTYPE_LLDP));
+        fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK,
+                    htobe16(ETHERTYPE_LLDP));
         if (fd < 0)
                 return -errno;
 
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 55fcc1da..46bddfeb 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -26,16 +26,16 @@
 #include "sd-ndisc.h"
 
 #include "alloc-util.h"
-#if 0 /* NM_IGNORED */
 #include "condition.h"
 #include "conf-parser.h"
-#endif /* NM_IGNORED */
 #include "dhcp-lease-internal.h"
+#include "ether-addr-util.h"
 #include "hexdecoct.h"
 #include "log.h"
 #include "network-internal.h"
 #include "parse-util.h"
 #include "siphash24.h"
+#include "socket-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "utf8.h"
@@ -105,16 +105,16 @@ bool net_match_config(const struct ether_addr *match_mac,
                       const char *dev_type,
                       const char *dev_name) {
 
-        if (match_host && !condition_test(match_host))
+        if (match_host && condition_test(match_host) <= 0)
                 return false;
 
-        if (match_virt && !condition_test(match_virt))
+        if (match_virt && condition_test(match_virt) <= 0)
                 return false;
 
-        if (match_kernel && !condition_test(match_kernel))
+        if (match_kernel && condition_test(match_kernel) <= 0)
                 return false;
 
-        if (match_arch && !condition_test(match_arch))
+        if (match_arch && condition_test(match_arch) <= 0)
                 return false;
 
         if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
@@ -180,58 +180,19 @@ int config_parse_net_condition(const char *unit,
         return 0;
 }
 
-int config_parse_ifname(const char *unit,
-                        const char *filename,
-                        unsigned line,
-                        const char *section,
-                        unsigned section_line,
-                        const char *lvalue,
-                        int ltype,
-                        const char *rvalue,
-                        void *data,
-                        void *userdata) {
-
-        char **s = data;
-        _cleanup_free_ char *n = NULL;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        n = strdup(rvalue);
-        if (!n)
-                return log_oom();
-
-        if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-
-        free(*s);
-        if (*n) {
-                *s = n;
-                n = NULL;
-        } else
-                *s = NULL;
-
-        return 0;
-}
-
-int config_parse_ifnames(const char *unit,
-                        const char *filename,
-                        unsigned line,
-                        const char *section,
-                        unsigned section_line,
-                        const char *lvalue,
-                        int ltype,
-                        const char *rvalue,
-                        void *data,
-                        void *userdata) {
+int config_parse_ifnames(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         char ***sv = data;
-        const char *word, *state;
-        size_t l;
         int r;
 
         assert(filename);
@@ -239,22 +200,27 @@ int config_parse_ifnames(const char *unit,
         assert(rvalue);
         assert(data);
 
-        FOREACH_WORD(word, l, rvalue, state) {
-                char *n;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
 
-                n = strndup(word, l);
-                if (!n)
-                        return log_oom();
+                r = extract_first_word(&rvalue, &word, NULL, 0);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        break;
 
-                if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
-                        free(n);
+                if (!ifname_valid(word)) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
                         return 0;
                 }
 
-                r = strv_consume(sv, n);
+                r = strv_push(sv, word);
                 if (r < 0)
                         return log_oom();
+
+                word = NULL;
         }
 
         return 0;
@@ -310,6 +276,8 @@ int config_parse_hwaddr(const char *unit,
                         void *userdata) {
         struct ether_addr **hwaddr = data;
         struct ether_addr *n;
+        const char *start;
+        size_t offset;
         int r;
 
         assert(filename);
@@ -321,14 +289,10 @@ int config_parse_hwaddr(const char *unit,
         if (!n)
                 return log_oom();
 
-        r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
-                   &n->ether_addr_octet[0],
-                   &n->ether_addr_octet[1],
-                   &n->ether_addr_octet[2],
-                   &n->ether_addr_octet[3],
-                   &n->ether_addr_octet[4],
-                   &n->ether_addr_octet[5]);
-        if (r != 6) {
+        start = rvalue + strspn(rvalue, WHITESPACE);
+        r = ether_addr_from_string(start, n, &offset);
+
+        if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
                 free(n);
                 return 0;
@@ -360,8 +324,9 @@ int config_parse_iaid(const char *unit,
 
         r = safe_atou32(rvalue, &iaid);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to read IAID: %s", rvalue);
-                return r;
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Unable to read IAID, ignoring assignment: %s", rvalue);
+                return 0;
         }
 
         *((uint32_t *)data) = iaid;
@@ -385,28 +350,28 @@ void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) {
 int deserialize_in_addrs(struct in_addr **ret, const char *string) {
         _cleanup_free_ struct in_addr *addresses = NULL;
         int size = 0;
-        const char *word, *state;
-        size_t len;
 
         assert(ret);
         assert(string);
 
-        FOREACH_WORD(word, len, string, state) {
-                _cleanup_free_ char *addr_str = NULL;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
                 struct in_addr *new_addresses;
                 int r;
 
+                r = extract_first_word(&string, &word, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
                 new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr));
                 if (!new_addresses)
                         return -ENOMEM;
                 else
                         addresses = new_addresses;
 
-                addr_str = strndup(word, len);
-                if (!addr_str)
-                        return -ENOMEM;
-
-                r = inet_pton(AF_INET, addr_str, &(addresses[size]));
+                r = inet_pton(AF_INET, word, &(addresses[size]));
                 if (r <= 0)
                         continue;
 
@@ -419,45 +384,48 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
         return size;
 }
 
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
-                         size_t size) {
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
         unsigned i;
 
         assert(f);
         assert(addresses);
         assert(size);
 
-        for (i = 0; i < size; i++)
-                fprintf(f, SD_NDISC_ADDRESS_FORMAT_STR"%s",
-                        SD_NDISC_ADDRESS_FORMAT_VAL(addresses[i]),
-                        (i < (size - 1)) ? " ": "");
+        for (i = 0; i < size; i++) {
+                char buffer[INET6_ADDRSTRLEN];
+
+                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+
+                if (i < size - 1)
+                        fputc(' ', f);
+        }
 }
 
 int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
         _cleanup_free_ struct in6_addr *addresses = NULL;
         int size = 0;
-        const char *word, *state;
-        size_t len;
 
         assert(ret);
         assert(string);
 
-        FOREACH_WORD(word, len, string, state) {
-                _cleanup_free_ char *addr_str = NULL;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
                 struct in6_addr *new_addresses;
                 int r;
 
+                r = extract_first_word(&string, &word, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
                 new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr));
                 if (!new_addresses)
                         return -ENOMEM;
                 else
                         addresses = new_addresses;
 
-                addr_str = strndup(word, len);
-                if (!addr_str)
-                        return -ENOMEM;
-
-                r = inet_pton(AF_INET6, addr_str, &(addresses[size]));
+                r = inet_pton(AF_INET6, word, &(addresses[size]));
                 if (r <= 0)
                         continue;
 
@@ -498,29 +466,29 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
 int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
         _cleanup_free_ struct sd_dhcp_route *routes = NULL;
         size_t size = 0, allocated = 0;
-        const char *word, *state;
-        size_t len;
 
         assert(ret);
         assert(ret_size);
         assert(ret_allocated);
         assert(string);
 
-        FOREACH_WORD(word, len, string, state) {
-                /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
-                _cleanup_free_ char* entry = NULL;
+         /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
                 char *tok, *tok_end;
                 unsigned n;
                 int r;
 
-                if (!GREEDY_REALLOC(routes, allocated, size + 1))
-                        return -ENOMEM;
+                r = extract_first_word(&string, &word, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-                entry = strndup(word, len);
-                if (!entry)
+                if (!GREEDY_REALLOC(routes, allocated, size + 1))
                         return -ENOMEM;
 
-                tok = entry;
+                tok = word;
 
                 /* get the subnet */
                 tok_end = strchr(tok, '/');
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index 955cc898..5bcd5771 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -23,7 +23,6 @@
 
 #include "sd-dhcp-lease.h"
 
-#if 0 /* NM_IGNORED */
 #include "condition.h"
 #include "udev.h"
 
@@ -51,10 +50,6 @@ int config_parse_hwaddr(const char *unit, const char *filename, unsigned line,
                         const char *section, unsigned section_line, const char *lvalue,
                         int ltype, const char *rvalue, void *data, void *userdata);
 
-int config_parse_ifname(const char *unit, const char *filename, unsigned line,
-                        const char *section, unsigned section_line, const char *lvalue,
-                        int ltype, const char *rvalue, void *data, void *userdata);
-
 int config_parse_ifnames(const char *unit, const char *filename, unsigned line,
                          const char *section, unsigned section_line, const char *lvalue,
                          int ltype, const char *rvalue, void *data, void *userdata);
@@ -69,7 +64,6 @@ int config_parse_iaid(const char *unit, const char *filename, unsigned line,
 
 int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result);
 const char *net_get_name(struct udev_device *device);
-#endif /* NM_IGNORED */
 
 void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size);
 int deserialize_in_addrs(struct in_addr **addresses, const char *string);
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 758dba4b..f740075a 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -55,7 +55,7 @@ struct sd_dhcp_client {
         sd_event *event;
         int event_priority;
         sd_event_source *timeout_resend;
-        int index;
+        int ifindex;
         int fd;
         union sockaddr_union link;
         sd_event_source *receive_message;
@@ -103,7 +103,7 @@ struct sd_dhcp_client {
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
         sd_event_source *timeout_expire;
-        sd_dhcp_client_callback_t cb;
+        sd_dhcp_client_callback_t callback;
         void *userdata;
         sd_dhcp_lease *lease;
         usec_t start_delay;
@@ -117,17 +117,26 @@ static const uint8_t default_req_opts[] = {
         SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
 };
 
-static int client_receive_message_raw(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata);
-static int client_receive_message_udp(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata);
+static int client_receive_message_raw(
+                sd_event_source *s,
+                int fd,
+                uint32_t revents,
+                void *userdata);
+static int client_receive_message_udp(
+                sd_event_source *s,
+                int fd,
+                uint32_t revents,
+                void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
 
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
-                                void *userdata) {
+int sd_dhcp_client_set_callback(
+                sd_dhcp_client *client,
+                sd_dhcp_client_callback_t cb,
+                void *userdata) {
+
         assert_return(client, -EINVAL);
 
-        client->cb = cb;
+        client->callback = cb;
         client->userdata = userdata;
 
         return 0;
@@ -145,10 +154,10 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
         size_t i;
 
         assert_return(client, -EINVAL);
-        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
-                              DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
 
         switch(option) {
+
         case SD_DHCP_OPTION_PAD:
         case SD_DHCP_OPTION_OVERLOAD:
         case SD_DHCP_OPTION_MESSAGE_TYPE:
@@ -173,11 +182,12 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
         return 0;
 }
 
-int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
-                                       const struct in_addr *last_addr) {
+int sd_dhcp_client_set_request_address(
+                sd_dhcp_client *client,
+                const struct in_addr *last_addr) {
+
         assert_return(client, -EINVAL);
-        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
-                              DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
 
         if (last_addr)
                 client->last_addr = last_addr->s_addr;
@@ -187,19 +197,22 @@ int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
         return 0;
 }
 
-int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
-        assert_return(client, -EINVAL);
-        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
-                              DHCP_STATE_STOPPED), -EBUSY);
-        assert_return(interface_index > 0, -EINVAL);
+int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
 
-        client->index = interface_index;
+        assert_return(client, -EINVAL);
+        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(ifindex > 0, -EINVAL);
 
+        client->ifindex = ifindex;
         return 0;
 }
 
-int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
-                           size_t addr_len, uint16_t arp_type) {
+int sd_dhcp_client_set_mac(
+                sd_dhcp_client *client,
+                const uint8_t *addr,
+                size_t addr_len,
+                uint16_t arp_type) {
+
         DHCP_CLIENT_DONT_DESTROY(client);
         bool need_restart = false;
 
@@ -220,8 +233,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
                 return 0;
 
         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Changing MAC address on running DHCP "
-                                "client, restarting");
+                log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
                 need_restart = true;
                 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
         }
@@ -236,8 +248,11 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
         return 0;
 }
 
-int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
-                                 const uint8_t **data, size_t *data_len) {
+int sd_dhcp_client_get_client_id(
+                sd_dhcp_client *client,
+                uint8_t *type,
+                const uint8_t **data,
+                size_t *data_len) {
 
         assert_return(client, -EINVAL);
         assert_return(type, -EINVAL);
@@ -256,8 +271,12 @@ int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
         return 0;
 }
 
-int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
-                                 const uint8_t *data, size_t data_len) {
+int sd_dhcp_client_set_client_id(
+                sd_dhcp_client *client,
+                uint8_t type,
+                const uint8_t *data,
+                size_t data_len) {
+
         DHCP_CLIENT_DONT_DESTROY(client);
         bool need_restart = false;
 
@@ -266,14 +285,17 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
         assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
 
         switch (type) {
+
         case ARPHRD_ETHER:
                 if (data_len != ETH_ALEN)
                         return -EINVAL;
                 break;
+
         case ARPHRD_INFINIBAND:
                 if (data_len != INFINIBAND_ALEN)
                         return -EINVAL;
                 break;
+
         default:
                 break;
         }
@@ -301,18 +323,37 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
 }
 
 #if 0 /* NM_IGNORED */
-int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
-                                 uint16_t duid_type, uint8_t *duid, size_t duid_len) {
+/**
+ * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
+ * without further modification. Otherwise, if duid_type is supported, DUID
+ * is set based on that type. Otherwise, an error is returned.
+ */
+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) {
+
         DHCP_CLIENT_DONT_DESTROY(client);
         int r;
+        size_t len;
+
         assert_return(client, -EINVAL);
-        zero(client->client_id);
+        assert_return(duid_len == 0 || duid != NULL, -EINVAL);
 
+        if (duid != NULL) {
+                r = dhcp_validate_duid_len(duid_type, duid_len);
+                if (r < 0)
+                        return r;
+        }
+
+        zero(client->client_id);
         client->client_id.type = 255;
 
         /* If IAID is not configured, generate it. */
         if (iaid == 0) {
-                r = dhcp_identifier_set_iaid(client->index, client->mac_addr,
+                r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
                                              client->mac_addr_len,
                                              &client->client_id.ns.iaid);
                 if (r < 0)
@@ -320,22 +361,18 @@ int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
         } else
                 client->client_id.ns.iaid = htobe32(iaid);
 
-        /* If DUID is not configured, generate DUID-EN. */
-        if (duid_len == 0) {
-                r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid,
-                                                &duid_len);
-                if (r < 0)
-                        return r;
-        } else {
-                r = dhcp_validate_duid_len(client->client_id.type, duid_len);
-                if (r < 0)
-                        return r;
+        if (duid != NULL) {
                 client->client_id.ns.duid.type = htobe16(duid_type);
                 memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
-                duid_len += sizeof(client->client_id.ns.duid.type);
-        }
+                len = sizeof(client->client_id.ns.duid.type) + duid_len;
+        } else if (duid_type == DUID_TYPE_EN) {
+                r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
+                if (r < 0)
+                        return r;
+        } else
+                return -EOPNOTSUPP;
 
-        client->client_id_len = sizeof(client->client_id.type) + duid_len +
+        client->client_id_len = sizeof(client->client_id.type) + len +
                                 sizeof(client->client_id.ns.iaid);
 
         if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
@@ -348,8 +385,10 @@ int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
 }
 #endif /* NM_IGNORED */
 
-int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
-                                const char *hostname) {
+int sd_dhcp_client_set_hostname(
+                sd_dhcp_client *client,
+                const char *hostname) {
+
         char *new_hostname = NULL;
 
         assert_return(client, -EINVAL);
@@ -372,8 +411,10 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
         return 0;
 }
 
-int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
-                                               const char *vci) {
+int sd_dhcp_client_set_vendor_class_identifier(
+                sd_dhcp_client *client,
+                const char *vci) {
+
         char *new_vci = NULL;
 
         assert_return(client, -EINVAL);
@@ -400,28 +441,29 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
 
 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         assert_return(client, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         if (client->state != DHCP_STATE_BOUND &&
             client->state != DHCP_STATE_RENEWING &&
             client->state != DHCP_STATE_REBINDING)
                 return -EADDRNOTAVAIL;
 
-        *ret = client->lease;
+        if (ret)
+                *ret = client->lease;
 
         return 0;
 }
 
 static void client_notify(sd_dhcp_client *client, int event) {
-        if (client->cb)
-                client->cb(client, event, client->userdata);
+        assert(client);
+
+        if (client->callback)
+                client->callback(client, event, client->userdata);
 }
 
 static int client_initialize(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
 
-        client->receive_message =
-                sd_event_source_unref(client->receive_message);
+        client->receive_message = sd_event_source_unref(client->receive_message);
 
         client->fd = asynchronous_close(client->fd);
 
@@ -456,8 +498,13 @@ static void client_stop(sd_dhcp_client *client, int error) {
         client_initialize(client);
 }
 
-static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
-                               uint8_t type, size_t *_optlen, size_t *_optoffset) {
+static int client_message_init(
+                sd_dhcp_client *client,
+                DHCPPacket **ret,
+                uint8_t type,
+                size_t *_optlen,
+                size_t *_optoffset) {
+
         _cleanup_free_ DHCPPacket *packet = NULL;
         size_t optlen, optoffset, size;
         be16_t max_size;
@@ -525,7 +572,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
 
                 client->client_id.type = 255;
 
-                r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
+                r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
                 if (r < 0)
                         return r;
 
@@ -598,8 +645,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
         return 0;
 }
 
-static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
-                                     const char *fqdn) {
+static int client_append_fqdn_option(
+                DHCPMessage *message,
+                size_t optlen,
+                size_t *optoffset,
+                const char *fqdn) {
+
         uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
         int r;
 
@@ -616,8 +667,11 @@ static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t
         return r;
 }
 
-static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
-                                size_t len) {
+static int dhcp_client_send_raw(
+                sd_dhcp_client *client,
+                DHCPPacket *packet,
+                size_t len) {
+
         dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
                                       INADDR_BROADCAST, DHCP_PORT_SERVER, len);
 
@@ -704,8 +758,9 @@ static int client_send_request(sd_dhcp_client *client) {
         size_t optoffset, optlen;
         int r;
 
-        r = client_message_init(client, &request, DHCP_REQUEST,
-                                &optlen, &optoffset);
+        assert(client);
+
+        r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
         if (r < 0)
                 return r;
 
@@ -802,18 +857,23 @@ static int client_send_request(sd_dhcp_client *client) {
                 return r;
 
         switch (client->state) {
+
         case DHCP_STATE_REQUESTING:
                 log_dhcp_client(client, "REQUEST (requesting)");
                 break;
+
         case DHCP_STATE_INIT_REBOOT:
                 log_dhcp_client(client, "REQUEST (init-reboot)");
                 break;
+
         case DHCP_STATE_RENEWING:
                 log_dhcp_client(client, "REQUEST (renewing)");
                 break;
+
         case DHCP_STATE_REBINDING:
                 log_dhcp_client(client, "REQUEST (rebinding)");
                 break;
+
         default:
                 log_dhcp_client(client, "REQUEST (invalid)");
                 break;
@@ -824,8 +884,11 @@ static int client_send_request(sd_dhcp_client *client) {
 
 static int client_start(sd_dhcp_client *client);
 
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
-                                 void *userdata) {
+static int client_timeout_resend(
+                sd_event_source *s,
+                uint64_t usec,
+                void *userdata) {
+
         sd_dhcp_client *client = userdata;
         DHCP_CLIENT_DONT_DESTROY(client);
         usec_t next_timeout = 0;
@@ -842,6 +905,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 goto error;
 
         switch (client->state) {
+
         case DHCP_STATE_RENEWING:
 
                 time_left = (client->lease->t2 - client->lease->t1) / 2;
@@ -969,8 +1033,10 @@ error:
         return 0;
 }
 
-static int client_initialize_io_events(sd_dhcp_client *client,
-                                       sd_event_io_handler_t io_callback) {
+static int client_initialize_io_events(
+                sd_dhcp_client *client,
+                sd_event_io_handler_t io_callback) {
+
         int r;
 
         assert(client);
@@ -1037,8 +1103,7 @@ error:
 
 }
 
-static int client_initialize_events(sd_dhcp_client *client,
-                                    sd_event_io_handler_t io_callback) {
+static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
         client_initialize_io_events(client, io_callback);
         client_initialize_time_events(client);
 
@@ -1050,15 +1115,14 @@ static int client_start_delayed(sd_dhcp_client *client) {
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
-        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->ifindex > 0, -EINVAL);
         assert_return(client->fd < 0, -EBUSY);
         assert_return(client->xid == 0, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT ||
-                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
 
         client->xid = random_u32();
 
-        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+        r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
                                          client->xid, client->mac_addr,
                                          client->mac_addr_len, client->arp_type);
         if (r < 0) {
@@ -1078,8 +1142,7 @@ static int client_start(sd_dhcp_client *client) {
         return client_start_delayed(client);
 }
 
-static int client_timeout_expire(sd_event_source *s, uint64_t usec,
-                                 void *userdata) {
+static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp_client *client = userdata;
         DHCP_CLIENT_DONT_DESTROY(client);
 
@@ -1101,13 +1164,15 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         DHCP_CLIENT_DONT_DESTROY(client);
         int r;
 
+        assert(client);
+
         client->receive_message = sd_event_source_unref(client->receive_message);
         client->fd = asynchronous_close(client->fd);
 
         client->state = DHCP_STATE_REBINDING;
         client->attempt = 1;
 
-        r = dhcp_network_bind_raw_socket(client->index, &client->link,
+        r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
                                          client->xid, client->mac_addr,
                                          client->mac_addr_len, client->arp_type);
         if (r < 0) {
@@ -1119,8 +1184,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         return client_initialize_events(client, client_receive_message_raw);
 }
 
-static int client_timeout_t1(sd_event_source *s, uint64_t usec,
-                             void *userdata) {
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp_client *client = userdata;
         DHCP_CLIENT_DONT_DESTROY(client);
 
@@ -1130,8 +1194,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec,
         return client_initialize_time_events(client);
 }
 
-static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
-                               size_t len) {
+static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) {
         _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
         int r;
 
@@ -1182,8 +1245,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
         return 0;
 }
 
-static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
-                                    size_t len) {
+static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
         int r;
 
         r = dhcp_option_parse(force, len, NULL, NULL, NULL);
@@ -1195,8 +1257,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
         return 0;
 }
 
-static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
-                             size_t len) {
+static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
         _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
         _cleanup_free_ char *error_message = NULL;
         int r;
@@ -1424,8 +1485,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
         return 0;
 }
 
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
-                                 int len) {
+static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
         DHCP_CLIENT_DONT_DESTROY(client);
         char time_string[FORMAT_TIMESPAN_MAX];
         int r = 0, notify_event = 0;
@@ -1571,11 +1631,15 @@ error:
         return r;
 }
 
-static int client_receive_message_udp(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata) {
+static int client_receive_message_udp(
+                sd_event_source *s,
+                int fd,
+                uint32_t revents,
+                void *userdata) {
+
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPMessage *message = NULL;
-        const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
+        const struct ether_addr zero_mac = {};
         const struct ether_addr *expected_chaddr = NULL;
         uint8_t expected_hlen = 0;
         ssize_t len, buflen;
@@ -1591,14 +1655,14 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
         if (!message)
                 return -ENOMEM;
 
-        len = read(fd, message, buflen);
+        len = recv(fd, message, buflen, 0);
         if (len < 0) {
                 if (errno == EAGAIN || errno == EINTR)
                         return 0;
 
-                log_dhcp_client(client, "Could not receive message from UDP socket: %m");
-                return -errno;
-        } else if ((size_t)len < sizeof(DHCPMessage)) {
+                return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m");
+        }
+        if ((size_t) len < sizeof(DHCPMessage)) {
                 log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
                 return 0;
         }
@@ -1649,8 +1713,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
         return client_handle_message(client, message, len);
 }
 
-static int client_receive_message_raw(sd_event_source *s, int fd,
-                                      uint32_t revents, void *userdata) {
+static int client_receive_message_raw(
+                sd_event_source *s,
+                int fd,
+                uint32_t revents,
+                void *userdata) {
+
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPPacket *packet = NULL;
         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
@@ -1725,7 +1793,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 
         r = client_start(client);
         if (r >= 0)
-                log_dhcp_client(client, "STARTED on ifindex %i", client->index);
+                log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
 
         return r;
 }
@@ -1769,8 +1837,7 @@ int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
 }
 
 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
-        if (!client)
-                return NULL;
+        assert_return(client, NULL);
 
         return client->event;
 }
@@ -1826,13 +1893,12 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
 
         client->n_ref = 1;
         client->state = DHCP_STATE_INIT;
-        client->index = -1;
+        client->ifindex = -1;
         client->fd = -1;
         client->attempt = 1;
         client->mtu = DHCP_DEFAULT_MIN_SIZE;
 
         client->req_opts_size = ELEMENTSOF(default_req_opts);
-
         client->req_opts = memdup(default_req_opts, client->req_opts_size);
         if (!client->req_opts)
                 return -ENOMEM;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 8fa1822f..a05bde63 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -47,7 +47,7 @@ struct sd_dhcp6_client {
         enum DHCP6State state;
         sd_event *event;
         int event_priority;
-        int index;
+        int ifindex;
         struct in6_addr local_address;
         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
         size_t mac_addr_len;
@@ -66,7 +66,7 @@ struct sd_dhcp6_client {
         uint8_t retransmit_count;
         sd_event_source *timeout_resend;
         sd_event_source *timeout_resend_expire;
-        sd_dhcp6_client_callback_t cb;
+        sd_dhcp6_client_callback_t callback;
         void *userdata;
         struct duid duid;
         size_t duid_len;
@@ -113,27 +113,33 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
 
 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
 
-int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_callback_t cb, void *userdata) {
+int sd_dhcp6_client_set_callback(
+                sd_dhcp6_client *client,
+                sd_dhcp6_client_callback_t cb,
+                void *userdata) {
+
         assert_return(client, -EINVAL);
 
-        client->cb = cb;
+        client->callback = cb;
         client->userdata = userdata;
 
         return 0;
 }
 
-int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
-        assert_return(client, -EINVAL);
-        assert_return(interface_index >= -1, -EINVAL);
+int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
 
+        assert_return(client, -EINVAL);
+        assert_return(ifindex >= -1, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
-        client->index = interface_index;
-
+        client->ifindex = ifindex;
         return 0;
 }
 
-int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) {
+int sd_dhcp6_client_set_local_address(
+                sd_dhcp6_client *client,
+                const struct in6_addr *local_address) {
+
         assert_return(client, -EINVAL);
         assert_return(local_address, -EINVAL);
         assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
@@ -182,20 +188,38 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
         return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
 }
 
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t duid_type,
-                             uint8_t *duid, size_t duid_len) {
+/**
+ * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
+ * without further modification. Otherwise, if duid_type is supported, DUID
+ * is set based on that type. Otherwise, an error is returned.
+ */
+int sd_dhcp6_client_set_duid(
+                sd_dhcp6_client *client,
+                uint16_t duid_type,
+                const void *duid,
+                size_t duid_len) {
+
         int r;
         assert_return(client, -EINVAL);
+        assert_return(duid_len == 0 || duid != NULL, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
-        if (duid_len > 0) {
+        if (duid != NULL) {
                 r = dhcp_validate_duid_len(duid_type, duid_len);
                 if (r < 0)
                         return r;
+        }
+
+        if (duid != NULL) {
                 client->duid.type = htobe16(duid_type);
                 memcpy(&client->duid.raw.data, duid, duid_len);
-                client->duid_len = duid_len + sizeof(client->duid.type);
-        }
+                client->duid_len = sizeof(client->duid.type) + duid_len;
+        } else if (duid_type == DUID_TYPE_EN) {
+                r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+                if (r < 0)
+                        return r;
+        } else
+                return -EOPNOTSUPP;
 
         return 0;
 }
@@ -236,6 +260,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
 
         switch(option) {
+
         case SD_DHCP6_OPTION_DNS_SERVERS:
         case SD_DHCP6_OPTION_DOMAIN_LIST:
         case SD_DHCP6_OPTION_SNTP_SERVERS:
@@ -272,20 +297,25 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
 }
 
 static void client_notify(sd_dhcp6_client *client, int event) {
-        if (client->cb)
-                client->cb(client, event, client->userdata);
+        assert(client);
+
+        if (client->callback)
+                client->callback(client, event, client->userdata);
 }
 
 static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
+        assert(client);
+
         if (client->lease) {
                 dhcp6_lease_clear_timers(&client->lease->ia);
                 sd_dhcp6_lease_unref(client->lease);
         }
+
         client->lease = lease;
 }
 
 static int client_reset(sd_dhcp6_client *client) {
-        assert_return(client, -EINVAL);
+        assert(client);
 
         client_set_lease(client, NULL);
 
@@ -333,6 +363,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         usec_t elapsed_usec;
         be16_t elapsed_time;
 
+        assert(client);
+
         len = sizeof(DHCP6Message) + optlen;
 
         message = malloc0(len);
@@ -431,13 +463,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         return 0;
 }
 
-static int client_timeout_t2(sd_event_source *s, uint64_t usec,
-                             void *userdata) {
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp6_client *client = userdata;
 
-        assert_return(s, -EINVAL);
-        assert_return(client, -EINVAL);
-        assert_return(client->lease, -EINVAL);
+        assert(s);
+        assert(client);
+        assert(client->lease);
 
         client->lease->ia.timeout_t2 =
                 sd_event_source_unref(client->lease->ia.timeout_t2);
@@ -449,13 +480,12 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec,
         return 0;
 }
 
-static int client_timeout_t1(sd_event_source *s, uint64_t usec,
-                             void *userdata) {
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp6_client *client = userdata;
 
-        assert_return(s, -EINVAL);
-        assert_return(client, -EINVAL);
-        assert_return(client->lease, -EINVAL);
+        assert(s);
+        assert(client);
+        assert(client->lease);
 
         client->lease->ia.timeout_t1 =
                 sd_event_source_unref(client->lease->ia.timeout_t1);
@@ -467,8 +497,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec,
         return 0;
 }
 
-static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
-                                        void *userdata) {
+static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
         enum DHCP6State state;
@@ -494,8 +523,7 @@ static usec_t client_timeout_compute_random(usec_t val) {
                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
 }
 
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
-                                 void *userdata) {
+static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
         int r = 0;
         sd_dhcp6_client *client = userdata;
         usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
@@ -655,16 +683,18 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         if (client->ia_na.id)
                 return 0;
 
-        r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-static int client_parse_message(sd_dhcp6_client *client,
-                                DHCP6Message *message, size_t len,
-                                sd_dhcp6_lease *lease) {
+static int client_parse_message(
+                sd_dhcp6_client *client,
+                DHCP6Message *message,
+                size_t len,
+                sd_dhcp6_lease *lease) {
         int r;
         uint8_t *optval, *option, *id = NULL;
         uint16_t optcode, status;
@@ -672,6 +702,11 @@ static int client_parse_message(sd_dhcp6_client *client,
         bool clientid = false;
         be32_t iaid_lease;
 
+        assert(client);
+        assert(message);
+        assert(len >= sizeof(DHCP6Message));
+        assert(lease);
+
         option = (uint8_t *)message + sizeof(DHCP6Message);
         len -= sizeof(DHCP6Message);
 
@@ -816,9 +851,12 @@ static int client_parse_message(sd_dhcp6_client *client,
 }
 
 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
-        int r;
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         bool rapid_commit;
+        int r;
+
+        assert(client);
+        assert(reply);
 
         if (reply->type != DHCP6_REPLY)
                 return 0;
@@ -847,9 +885,9 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
 }
 
 static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
-        int r;
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         uint8_t pref_advertise = 0, pref_lease = 0;
+        int r;
 
         if (advertise->type != DHCP6_ADVERTISE)
                 return 0;
@@ -880,7 +918,12 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
         return r;
 }
 
-static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+static int client_receive_message(
+                sd_event_source *s,
+                int fd, uint32_t
+                revents,
+                void *userdata) {
+
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
         _cleanup_free_ DHCP6Message *message = NULL;
@@ -899,16 +942,18 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
         if (!message)
                 return -ENOMEM;
 
-        len = read(fd, message, buflen);
+        len = recv(fd, message, buflen, 0);
         if (len < 0) {
                 if (errno == EAGAIN || errno == EINTR)
                         return 0;
 
-                log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
+                return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
 
-                return -errno;
-        } else if ((size_t)len < sizeof(DHCP6Message))
+        }
+        if ((size_t) len < sizeof(DHCP6Message)) {
+                log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
                 return 0;
+        }
 
         switch(message->type) {
         case DHCP6_SOLICIT:
@@ -929,8 +974,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                 break;
 
         default:
-                log_dhcp6_client(client, "unknown message type %d",
-                                 message->type);
+                log_dhcp6_client(client, "Unknown message type %d", message->type);
                 return 0;
         }
 
@@ -989,10 +1033,9 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
                 return 0;
         }
 
-        if (r >= 0) {
+        if (r >= 0)
                 log_dhcp6_client(client, "Recv %s",
                                  dhcp6_message_type_to_string(message->type));
-        }
 
         return 0;
 }
@@ -1004,7 +1047,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
-        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->ifindex > 0, -EINVAL);
         assert_return(client->state != state, -EINVAL);
 
         client->timeout_resend_expire =
@@ -1045,7 +1088,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                 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",
+                        log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
                                          be32toh(client->lease->ia.lifetime_t1),
                                          be32toh(client->lease->ia.lifetime_t2));
 
@@ -1137,12 +1180,12 @@ int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
 }
 
 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
-        int r = 0;
         enum DHCP6State state = DHCP6_STATE_SOLICITATION;
+        int r = 0;
 
         assert_return(client, -EINVAL);
         assert_return(client->event, -EINVAL);
-        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->ifindex > 0, -EINVAL);
         assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
 
         if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
@@ -1160,9 +1203,14 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
-        if (r < 0)
-                return r;
+        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));
+        }
 
         client->fd = r;
 
@@ -1178,7 +1226,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
                 goto error;
 
         r = sd_event_source_set_description(client->receive_message,
-                                        "dhcp6-receive-message");
+                                            "dhcp6-receive-message");
         if (r < 0)
                 goto error;
 
@@ -1186,8 +1234,8 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
                 state = DHCP6_STATE_INFORMATION_REQUEST;
 
         log_dhcp6_client(client, "Started in %s mode",
-                        client->information_request? "Information request":
-                        "Managed");
+                         client->information_request? "Information request":
+                         "Managed");
 
         return client_start(client, state);
 
@@ -1224,8 +1272,7 @@ int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
 }
 
 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
-        if (!client)
-                return NULL;
+        assert_return(client, NULL);
 
         return client->event;
 }
@@ -1273,15 +1320,11 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
                 return -ENOMEM;
 
         client->n_ref = 1;
-
         client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
-
-        client->index = -1;
-
+        client->ifindex = -1;
         client->fd = -1;
 
         client->req_opts_len = ELEMENTSOF(default_req_opts);
-
         client->req_opts = new0(be16_t, client->req_opts_len);
         if (!client->req_opts)
                 return -ENOMEM;
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
index 9b5ce720..59830795 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -30,45 +30,30 @@
 
 #include "alloc-util.h"
 #include "arp-util.h"
+#include "ether-addr-util.h"
 #include "fd-util.h"
 #include "in-addr-util.h"
 #include "list.h"
 #include "random-util.h"
-#include "refcnt.h"
 #include "siphash24.h"
+#include "string-util.h"
 #include "util.h"
 
 /* Constants from the RFC */
-#define PROBE_WAIT 1
-#define PROBE_NUM 3
-#define PROBE_MIN 1
-#define PROBE_MAX 2
-#define ANNOUNCE_WAIT 2
-#define ANNOUNCE_NUM 2
-#define ANNOUNCE_INTERVAL 2
-#define MAX_CONFLICTS 10
-#define RATE_LIMIT_INTERVAL 60
-#define DEFEND_INTERVAL 10
-
-#define IPV4ACD_NETWORK 0xA9FE0000L
-#define IPV4ACD_NETMASK 0xFFFF0000L
-
-#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
-
-#define log_ipv4acd_debug(ll, ...)   log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_ipv4acd_info(ll, ...)    log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_ipv4acd_notice(ll, ...)  log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
-#define log_ipv4acd_error(ll, ...)   log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
-
-#define log_ipv4acd_debug_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
-#define log_ipv4acd_info_errno(ll, error, ...)    log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
-#define log_ipv4acd_notice_errno(ll, error, ...)  log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
-#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
-#define log_ipv4acd_error_errno(ll, error, ...)   log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
+#define PROBE_NUM 3U
+#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
+#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
+#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
+#define ANNOUNCE_NUM 2U
+#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
+#define MAX_CONFLICTS 10U
+#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
+#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
 
 typedef enum IPv4ACDState {
         IPV4ACD_STATE_INIT,
+        IPV4ACD_STATE_STARTED,
         IPV4ACD_STATE_WAITING_PROBE,
         IPV4ACD_STATE_PROBING,
         IPV4ACD_STATE_WAITING_ANNOUNCE,
@@ -79,156 +64,164 @@ typedef enum IPv4ACDState {
 } IPv4ACDState;
 
 struct sd_ipv4acd {
-        RefCount n_ref;
+        unsigned n_ref;
 
         IPv4ACDState state;
-        int index;
+        int ifindex;
         int fd;
-        int iteration;
-        int conflict;
-        sd_event_source *receive_message;
-        sd_event_source *timer;
+
+        unsigned n_iteration;
+        unsigned n_conflict;
+
+        sd_event_source *receive_message_event_source;
+        sd_event_source *timer_event_source;
+
         usec_t defend_window;
         be32_t address;
+
         /* External */
         struct ether_addr mac_addr;
+
         sd_event *event;
         int event_priority;
-        sd_ipv4acd_callback_t cb;
+        sd_ipv4acd_callback_t callback;
         void* userdata;
 };
 
-sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
-        if (ll)
-                assert_se(REFCNT_INC(ll->n_ref) >= 2);
+#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
+#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
+
+static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
+        assert(acd);
+        assert(st < _IPV4ACD_STATE_MAX);
+
+        if (st == acd->state && !reset_counter)
+                acd->n_iteration++;
+        else {
+                acd->state = st;
+                acd->n_iteration = 0;
+        }
+}
+
+static void ipv4acd_reset(sd_ipv4acd *acd) {
+        assert(acd);
+
+        acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
+        acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
+
+        acd->fd = safe_close(acd->fd);
+
+        ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
+}
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) {
+        if (!acd)
+                return NULL;
+
+        assert_se(acd->n_ref >= 1);
+        acd->n_ref++;
 
-        return ll;
+        return acd;
 }
 
-sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
-        if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) {
+        if (!acd)
                 return NULL;
 
-        ll->receive_message = sd_event_source_unref(ll->receive_message);
-        ll->fd = safe_close(ll->fd);
+        assert_se(acd->n_ref >= 1);
+        acd->n_ref--;
 
-        ll->timer = sd_event_source_unref(ll->timer);
+        if (acd->n_ref > 0)
+                return NULL;
 
-        sd_ipv4acd_detach_event(ll);
+        ipv4acd_reset(acd);
+        sd_ipv4acd_detach_event(acd);
 
-        free(ll);
+        free(acd);
 
         return NULL;
 }
 
 int sd_ipv4acd_new(sd_ipv4acd **ret) {
-        _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
+        _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
 
         assert_return(ret, -EINVAL);
 
-        ll = new0(sd_ipv4acd, 1);
-        if (!ll)
+        acd = new0(sd_ipv4acd, 1);
+        if (!acd)
                 return -ENOMEM;
 
-        ll->n_ref = REFCNT_INIT;
-        ll->state = IPV4ACD_STATE_INIT;
-        ll->index = -1;
-        ll->fd = -1;
+        acd->n_ref = 1;
+        acd->state = IPV4ACD_STATE_INIT;
+        acd->ifindex = -1;
+        acd->fd = -1;
 
-        *ret = ll;
-        ll = NULL;
+        *ret = acd;
+        acd = NULL;
 
         return 0;
 }
 
-static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
-
-        assert(ll);
-        assert(st < _IPV4ACD_STATE_MAX);
-
-        if (st == ll->state && !reset_counter)
-                ll->iteration++;
-        else {
-                ll->state = st;
-                ll->iteration = 0;
-        }
-}
+static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
+        assert(acd);
 
-static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
-        assert(ll);
+        if (!acd->callback)
+                return;
 
-        if (ll->cb)
-                ll->cb(ll, event, ll->userdata);
+        acd->callback(acd, event, acd->userdata);
 }
 
-static void ipv4acd_stop(sd_ipv4acd *ll) {
-        assert(ll);
+int sd_ipv4acd_stop(sd_ipv4acd *acd) {
+        assert_return(acd, -EINVAL);
 
-        ll->receive_message = sd_event_source_unref(ll->receive_message);
-        ll->fd = safe_close(ll->fd);
+        ipv4acd_reset(acd);
 
-        ll->timer = sd_event_source_unref(ll->timer);
+        log_ipv4acd(acd, "STOPPED");
 
-        log_ipv4acd_debug(ll, "STOPPED");
-
-        ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
-}
-
-int sd_ipv4acd_stop(sd_ipv4acd *ll) {
-        assert_return(ll, -EINVAL);
-
-        ipv4acd_stop(ll);
-
-        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
+        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
 
         return 0;
 }
 
 static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
 
-static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
         _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
-        usec_t next_timeout;
-        usec_t time_now;
+        usec_t next_timeout, time_now;
         int r;
 
-        assert(sec >= 0);
-        assert(random_sec >= 0);
-        assert(ll);
+        assert(acd);
 
-        next_timeout = sec * USEC_PER_SEC;
+        next_timeout = usec;
 
-        if (random_sec)
-                next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+        if (random_usec > 0)
+                next_timeout += (usec_t) random_u64() % random_usec;
 
-        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
 
-        r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
-                              time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+        r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd);
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_priority(timer, ll->event_priority);
+        r = sd_event_source_set_priority(timer, acd->event_priority);
         if (r < 0)
                 return r;
 
-        r = sd_event_source_set_description(timer, "ipv4acd-timer");
-        if (r < 0)
-                return r;
+        (void) sd_event_source_set_description(timer, "ipv4acd-timer");
 
-        ll->timer = sd_event_source_unref(ll->timer);
-        ll->timer = timer;
+        sd_event_source_unref(acd->timer_event_source);
+        acd->timer_event_source = timer;
         timer = NULL;
 
         return 0;
 }
 
-static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
-        assert(ll);
+static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
+        assert(acd);
         assert(arp);
 
         /* see the BPF */
-        if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+        if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
                 return true;
 
         /* the TPA matched instead of the SPA, this is not a conflict */
@@ -236,294 +229,300 @@ static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
 }
 
 static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_ipv4acd *ll = userdata;
+        sd_ipv4acd *acd = userdata;
         int r = 0;
 
-        assert(ll);
+        assert(acd);
+
+        switch (acd->state) {
 
-        switch (ll->state) {
-        case IPV4ACD_STATE_INIT:
+        case IPV4ACD_STATE_STARTED:
+                ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
 
-                ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+                if (acd->n_conflict >= MAX_CONFLICTS) {
+                        char ts[FORMAT_TIMESPAN_MAX];
+                        log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
 
-                if (ll->conflict >= MAX_CONFLICTS) {
-                        log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
-                        r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+                        r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
                         if (r < 0)
-                                goto out;
+                                goto fail;
 
-                        ll->conflict = 0;
+                        acd->n_conflict = 0;
                 } else {
-                        r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+                        r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
                         if (r < 0)
-                                goto out;
+                                goto fail;
                 }
 
                 break;
+
         case IPV4ACD_STATE_WAITING_PROBE:
         case IPV4ACD_STATE_PROBING:
                 /* Send a probe */
-                r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
                 if (r < 0) {
-                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
-                        goto out;
+                        log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
+                        goto fail;
                 } else {
                         _cleanup_free_ char *address = NULL;
-                        union in_addr_union addr = { .in.s_addr = ll->address };
+                        union in_addr_union addr = { .in.s_addr = acd->address };
 
-                        r = in_addr_to_string(AF_INET, &addr, &address);
-                        if (r >= 0)
-                                log_ipv4acd_debug(ll, "Probing %s", address);
+                        (void) in_addr_to_string(AF_INET, &addr, &address);
+                        log_ipv4acd(acd, "Probing %s", strna(address));
                 }
 
-                if (ll->iteration < PROBE_NUM - 2) {
-                        ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+                if (acd->n_iteration < PROBE_NUM - 2) {
+                        ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
 
-                        r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+                        r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
                         if (r < 0)
-                                goto out;
+                                goto fail;
                 } else {
-                        ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+                        ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
 
-                        r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+                        r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
                         if (r < 0)
-                                goto out;
+                                goto fail;
                 }
 
                 break;
 
         case IPV4ACD_STATE_ANNOUNCING:
-                if (ll->iteration >= ANNOUNCE_NUM - 1) {
-                        ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
-
+                if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
+                        ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
                         break;
                 }
+
+                /* fall through */
+
         case IPV4ACD_STATE_WAITING_ANNOUNCE:
                 /* Send announcement packet */
-                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
                 if (r < 0) {
-                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
-                        goto out;
+                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
+                        goto fail;
                 } else
-                        log_ipv4acd_debug(ll, "ANNOUNCE");
+                        log_ipv4acd(acd, "ANNOUNCE");
 
-                ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+                ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
 
-                r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+                r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
                 if (r < 0)
-                        goto out;
+                        goto fail;
 
-                if (ll->iteration == 0) {
-                        ll->conflict = 0;
-                        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
+                if (acd->n_iteration == 0) {
+                        acd->n_conflict = 0;
+                        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
                 }
 
                 break;
+
         default:
                 assert_not_reached("Invalid state.");
         }
 
-out:
-        if (r < 0)
-                sd_ipv4acd_stop(ll);
+        return 0;
 
-        return 1;
+fail:
+        sd_ipv4acd_stop(acd);
+        return 0;
 }
 
-static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
         _cleanup_free_ char *address = NULL;
-        union in_addr_union addr = { .in.s_addr = ll->address };
-        int r;
+        union in_addr_union addr = { .in.s_addr = acd->address };
 
-        assert(ll);
+        assert(acd);
 
-        ll->conflict++;
+        acd->n_conflict++;
 
-        r = in_addr_to_string(AF_INET, &addr, &address);
-        if (r >= 0)
-                log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+        (void) in_addr_to_string(AF_INET, &addr, &address);
+        log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
 
-        ipv4acd_stop(ll);
-
-        ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
+        ipv4acd_reset(acd);
+        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
 }
 
-static int ipv4acd_on_packet(sd_event_source *s, int fd,
-                            uint32_t revents, void *userdata) {
-        sd_ipv4acd *ll = userdata;
+static int ipv4acd_on_packet(
+                sd_event_source *s,
+                int fd,
+                uint32_t revents,
+                void *userdata) {
+
+        sd_ipv4acd *acd = userdata;
         struct ether_arp packet;
+        ssize_t n;
         int r;
 
-        assert(ll);
+        assert(s);
+        assert(acd);
         assert(fd >= 0);
 
-        r = read(fd, &packet, sizeof(struct ether_arp));
-        if (r < (int) sizeof(struct ether_arp))
-                goto out;
+        n = recv(fd, &packet, sizeof(struct ether_arp), 0);
+        if (n < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
+                goto fail;
+        }
+        if ((size_t) n != sizeof(struct ether_arp)) {
+                log_ipv4acd(acd, "Ignoring too short ARP packet.");
+                return 0;
+        }
+
+        switch (acd->state) {
 
-        switch (ll->state) {
         case IPV4ACD_STATE_ANNOUNCING:
         case IPV4ACD_STATE_RUNNING:
-                if (ipv4acd_arp_conflict(ll, &packet)) {
+
+                if (ipv4acd_arp_conflict(acd, &packet)) {
                         usec_t ts;
 
-                        assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+                        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
 
                         /* Defend address */
-                        if (ts > ll->defend_window) {
-                                ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
-                                r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+                        if (ts > acd->defend_window) {
+                                acd->defend_window = ts + DEFEND_INTERVAL_USEC;
+                                r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
                                 if (r < 0) {
-                                        log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
-                                        goto out;
+                                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
+                                        goto fail;
                                 } else
-                                        log_ipv4acd_debug(ll, "DEFEND");
+                                        log_ipv4acd(acd, "DEFEND");
 
                         } else
-                                ipv4acd_on_conflict(ll);
+                                ipv4acd_on_conflict(acd);
                 }
-
                 break;
+
         case IPV4ACD_STATE_WAITING_PROBE:
         case IPV4ACD_STATE_PROBING:
         case IPV4ACD_STATE_WAITING_ANNOUNCE:
                 /* BPF ensures this packet indicates a conflict */
-                ipv4acd_on_conflict(ll);
-
+                ipv4acd_on_conflict(acd);
                 break;
+
         default:
                 assert_not_reached("Invalid state.");
         }
 
-out:
-        if (r < 0)
-                sd_ipv4acd_stop(ll);
+        return 0;
 
-        return 1;
+fail:
+        sd_ipv4acd_stop(acd);
+        return 0;
 }
 
-int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
-        assert_return(ll, -EINVAL);
-        assert_return(interface_index > 0, -EINVAL);
-        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
+        assert_return(acd, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
 
-        ll->index = interface_index;
+        acd->ifindex = ifindex;
 
         return 0;
 }
 
-int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
-        assert_return(ll, -EINVAL);
+int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
+        assert_return(acd, -EINVAL);
         assert_return(addr, -EINVAL);
-        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
 
-        memcpy(&ll->mac_addr, addr, ETH_ALEN);
+        acd->mac_addr = *addr;
 
         return 0;
 }
 
-int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
-        assert_return(ll, -EINVAL);
+int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
+        assert_return(acd, -EINVAL);
 
-        ll->event = sd_event_unref(ll->event);
+        acd->event = sd_event_unref(acd->event);
 
         return 0;
 }
 
-int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
+int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
         int r;
 
-        assert_return(ll, -EINVAL);
-        assert_return(!ll->event, -EBUSY);
+        assert_return(acd, -EINVAL);
+        assert_return(!acd->event, -EBUSY);
 
         if (event)
-                ll->event = sd_event_ref(event);
+                acd->event = sd_event_ref(event);
         else {
-                r = sd_event_default(&ll->event);
+                r = sd_event_default(&acd->event);
                 if (r < 0)
                         return r;
         }
 
-        ll->event_priority = priority;
+        acd->event_priority = priority;
 
         return 0;
 }
 
-int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
-        assert_return(ll, -EINVAL);
+int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
+        assert_return(acd, -EINVAL);
 
-        ll->cb = cb;
-        ll->userdata = userdata;
+        acd->callback = cb;
+        acd->userdata = userdata;
 
         return 0;
 }
 
-int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
-        assert_return(ll, -EINVAL);
+int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
+        assert_return(acd, -EINVAL);
         assert_return(address, -EINVAL);
-        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
 
-        ll->address = address->s_addr;
+        acd->address = address->s_addr;
 
         return 0;
 }
 
-int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
-        assert_return(ll, false);
+int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
+        assert_return(acd, false);
 
-        return ll->state != IPV4ACD_STATE_INIT;
+        return acd->state != IPV4ACD_STATE_INIT;
 }
 
-static bool ether_addr_is_nul(const struct ether_addr *addr) {
-        const struct ether_addr nul_addr = {};
-
-        assert(addr);
-
-        return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
-}
-
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-int sd_ipv4acd_start(sd_ipv4acd *ll) {
+int sd_ipv4acd_start(sd_ipv4acd *acd) {
         int r;
 
-        assert_return(ll, -EINVAL);
-        assert_return(ll->event, -EINVAL);
-        assert_return(ll->index > 0, -EINVAL);
-        assert_return(ll->address != 0, -EINVAL);
-        assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
-        assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+        assert_return(acd, -EINVAL);
+        assert_return(acd->event, -EINVAL);
+        assert_return(acd->ifindex > 0, -EINVAL);
+        assert_return(acd->address != 0, -EINVAL);
+        assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
+        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
 
-        ll->defend_window = 0;
-
-        r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+        r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
         if (r < 0)
-                goto out;
+                return r;
 
-        ll->fd = safe_close(ll->fd);
-        ll->fd = r;
+        safe_close(acd->fd);
+        acd->fd = r;
+        acd->defend_window = 0;
+        acd->n_conflict = 0;
 
-        r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
-                            EPOLLIN, ipv4acd_on_packet, ll);
+        r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
         if (r < 0)
-                goto out;
+                goto fail;
 
-        r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+        r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
         if (r < 0)
-                goto out;
+                goto fail;
 
-        r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
-        if (r < 0)
-                goto out;
+        (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
 
-        r = ipv4acd_set_next_wakeup(ll, 0, 0);
+        r = ipv4acd_set_next_wakeup(acd, 0, 0);
         if (r < 0)
-                goto out;
-out:
-        if (r < 0) {
-                ipv4acd_stop(ll);
-                return r;
-        }
+                goto fail;
 
+        ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
         return 0;
+
+fail:
+        ipv4acd_reset(acd);
+        return r;
 }
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index 391be15a..dc2fa370 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -30,16 +30,17 @@
 #include "sd-ipv4ll.h"
 
 #include "alloc-util.h"
+#include "ether-addr-util.h"
 #include "in-addr-util.h"
 #include "list.h"
 #include "random-util.h"
-#include "refcnt.h"
 #include "siphash24.h"
 #include "sparse-endian.h"
+#include "string-util.h"
 #include "util.h"
 
-#define IPV4LL_NETWORK 0xA9FE0000L
-#define IPV4LL_NETMASK 0xFFFF0000L
+#define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
+#define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
 
 #define IPV4LL_DONT_DESTROY(ll) \
         _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
@@ -48,16 +49,28 @@ struct sd_ipv4ll {
         unsigned n_ref;
 
         sd_ipv4acd *acd;
+
         be32_t address; /* the address pushed to ACD */
-        struct random_data *random_data;
-        char *random_data_state;
+        struct ether_addr mac;
+
+        struct {
+                le64_t value;
+                le64_t generation;
+        } seed;
+        bool seed_set;
 
         /* External */
         be32_t claimed_address;
-        sd_ipv4ll_callback_t cb;
+
+        sd_ipv4ll_callback_t callback;
         void* userdata;
 };
 
+#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
+#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
+
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
+
 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
         if (!ll)
                 return NULL;
@@ -79,16 +92,11 @@ sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
                 return NULL;
 
         sd_ipv4acd_unref(ll->acd);
-
-        free(ll->random_data);
-        free(ll->random_data_state);
         free(ll);
 
         return NULL;
 }
 
-static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
-
 int sd_ipv4ll_new(sd_ipv4ll **ret) {
         _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
         int r;
@@ -116,44 +124,32 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
 }
 
 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
-        int r;
-
         assert_return(ll, -EINVAL);
 
-        r = sd_ipv4acd_stop(ll->acd);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return sd_ipv4acd_stop(ll->acd);
 }
 
-int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
+int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
         assert_return(ll, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
 
-        return sd_ipv4acd_set_index(ll->acd, interface_index);
+        return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
 }
 
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
         int r;
 
         assert_return(ll, -EINVAL);
+        assert_return(addr, -EINVAL);
+        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
 
-        if (!ll->random_data) {
-                uint64_t seed;
-
-                /* If no random data is set, generate some from the MAC */
-                seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
-
-                assert_cc(sizeof(unsigned) <= 8);
-
-                r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
-                if (r < 0)
-                        return r;
-        }
+        r = sd_ipv4acd_set_mac(ll->acd, addr);
+        if (r < 0)
+                return r;
 
-        return sd_ipv4acd_set_mac(ll->acd, addr);
+        ll->mac = *addr;
+        return 0;
 }
 
 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
@@ -163,21 +159,15 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
 }
 
 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
-        int r;
-
         assert_return(ll, -EINVAL);
 
-        r = sd_ipv4acd_attach_event(ll->acd, event, priority);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return sd_ipv4acd_attach_event(ll->acd, event, priority);
 }
 
 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
         assert_return(ll, -EINVAL);
 
-        ll->cb = cb;
+        ll->callback = cb;
         ll->userdata = userdata;
 
         return 0;
@@ -195,32 +185,12 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
         return 0;
 }
 
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
-        _cleanup_free_ struct random_data *random_data = NULL;
-        _cleanup_free_ char *random_data_state = NULL;
-        int r;
-
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
         assert_return(ll, -EINVAL);
+        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
 
-        random_data = new0(struct random_data, 1);
-        if (!random_data)
-                return -ENOMEM;
-
-        random_data_state = new0(char, 128);
-        if (!random_data_state)
-                return -ENOMEM;
-
-        r = initstate_r(seed, random_data_state, 128, random_data);
-        if (r < 0)
-                return r;
-
-        free(ll->random_data);
-        ll->random_data = random_data;
-        random_data = NULL;
-
-        free(ll->random_data_state);
-        ll->random_data_state = random_data_state;
-        random_data_state = NULL;
+        ll->seed.value = htole64(seed);
+        ll->seed_set = true;
 
         return 0;
 }
@@ -232,20 +202,12 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
 }
 
 static bool ipv4ll_address_is_valid(const struct in_addr *address) {
-        uint32_t addr;
-
         assert(address);
 
         if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
                 return false;
 
-        addr = be32toh(address->s_addr);
-
-        if ((addr & 0x0000FF00) == 0x0000 ||
-            (addr & 0x0000FF00) == 0xFF00)
-                return false;
-
-        return true;
+        return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
 }
 
 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
@@ -264,48 +226,67 @@ int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
         return 0;
 }
 
+#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
+
 static int ipv4ll_pick_address(sd_ipv4ll *ll) {
-        struct in_addr in_addr;
+        _cleanup_free_ char *address = NULL;
         be32_t addr;
-        int r;
-        int32_t random;
 
         assert(ll);
-        assert(ll->random_data);
 
         do {
-                r = random_r(ll->random_data, &random);
-                if (r < 0)
-                        return r;
-                addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
-        } while (addr == ll->address ||
-                (ntohl(addr) & 0x0000FF00) == 0x0000 ||
-                (ntohl(addr) & 0x0000FF00) == 0xFF00);
+                uint64_t h;
 
-        in_addr.s_addr = addr;
+                h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
 
-        r = sd_ipv4ll_set_address(ll, &in_addr);
-        if (r < 0)
-                return r;
+                /* Increase the generation counter by one */
+                ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
 
-        return 0;
+                addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
+        } while (addr == ll->address ||
+                 IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
+
+        (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
+        log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
+
+        return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
 }
 
+#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
 int sd_ipv4ll_start(sd_ipv4ll *ll) {
         int r;
+        bool picked_address = false;
 
         assert_return(ll, -EINVAL);
-        assert_return(ll->random_data, -EINVAL);
+        assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
+        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
+
+        /* If no random seed is set, generate some from the MAC address */
+        if (!ll->seed_set)
+                ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
+
+        /* Restart the generation counter. */
+        ll->seed.generation = 0;
 
         if (ll->address == 0) {
                 r = ipv4ll_pick_address(ll);
                 if (r < 0)
                         return r;
+
+                picked_address = true;
         }
 
         r = sd_ipv4acd_start(ll->acd);
-        if (r < 0)
+        if (r < 0) {
+
+                /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
+                 * retry, and we want the new data to take effect when picking an address. */
+                if (picked_address)
+                        ll->address = 0;
+
                 return r;
+        }
 
         return 0;
 }
@@ -313,8 +294,8 @@ int sd_ipv4ll_start(sd_ipv4ll *ll) {
 static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
         assert(ll);
 
-        if (ll->cb)
-                ll->cb(ll, event, ll->userdata);
+        if (ll->callback)
+                ll->callback(ll, event, ll->userdata);
 }
 
 void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
@@ -326,17 +307,17 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
         assert(ll);
 
         switch (event) {
+
         case SD_IPV4ACD_EVENT_STOP:
                 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
-
                 ll->claimed_address = 0;
-
                 break;
+
         case SD_IPV4ACD_EVENT_BIND:
                 ll->claimed_address = ll->address;
                 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
-
                 break;
+
         case SD_IPV4ACD_EVENT_CONFLICT:
                 /* if an address was already bound we must call up to the
                    user to handle this, otherwise we just try again */
@@ -355,6 +336,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
                 }
 
                 break;
+
         default:
                 assert_not_reached("Invalid IPv4ACD event.");
         }
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
index 71d15812..f1ae6dac 100644
--- a/src/systemd/src/libsystemd-network/sd-lldp.c
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -45,7 +45,6 @@ static void lldp_flush_neighbors(sd_lldp *lldp) {
 
 static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
         assert(lldp);
-        assert(n);
 
         log_lldp("Invoking callback for '%c'.", event);
 
@@ -140,6 +139,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
 
                 if (lldp_neighbor_equal(n, old)) {
                         /* Is this equal, then restart the TTL counter, but don't do anyting else. */
+                        old->timestamp = n->timestamp;
                         lldp_start_timer(lldp, old);
                         lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
                         return 0;
@@ -173,7 +173,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
 
 finish:
         if (old)
-                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
+                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
 
         return r;
 }
@@ -204,6 +204,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v
         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
         ssize_t space, length;
         sd_lldp *lldp = userdata;
+        struct timespec ts;
 
         assert(fd >= 0);
         assert(lldp);
@@ -217,21 +218,41 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v
                 return -ENOMEM;
 
         length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
-        if (length < 0)
+        if (length < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
                 return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
+        }
 
         if ((size_t) length != n->raw_size) {
                 log_lldp("Packet size mismatch.");
                 return -EINVAL;
         }
 
+        /* Try to get the timestamp of this packet if it is known */
+        if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
+                triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
+        else
+                triple_timestamp_get(&n->timestamp);
+
         return lldp_handle_datagram(lldp, n);
 }
 
+static void lldp_reset(sd_lldp *lldp) {
+        assert(lldp);
+
+        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
+        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
+        lldp->fd = safe_close(lldp->fd);
+}
+
 _public_ int sd_lldp_start(sd_lldp *lldp) {
         int r;
 
         assert_return(lldp, -EINVAL);
+        assert_return(lldp->event, -EINVAL);
+        assert_return(lldp->ifindex > 0, -EINVAL);
 
         if (lldp->fd >= 0)
                 return 0;
@@ -242,24 +263,21 @@ _public_ int sd_lldp_start(sd_lldp *lldp) {
         if (lldp->fd < 0)
                 return lldp->fd;
 
-        if (lldp->event) {
-                r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
-                if (r < 0)
-                        goto fail;
+        r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
+        if (r < 0)
+                goto fail;
 
-                r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
-                if (r < 0)
-                        goto fail;
+        r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
+        if (r < 0)
+                goto fail;
 
-                (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
-        }
+        (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
 
+        log_lldp("Started LLDP client");
         return 1;
 
 fail:
-        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
-        lldp->fd = safe_close(lldp->fd);
-
+        lldp_reset(lldp);
         return r;
 }
 
@@ -269,10 +287,9 @@ _public_ int sd_lldp_stop(sd_lldp *lldp) {
         if (lldp->fd < 0)
                 return 0;
 
-        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
-        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
-        lldp->fd = safe_close(lldp->fd);
+        log_lldp("Stopping LLDP client");
 
+        lldp_reset(lldp);
         lldp_flush_neighbors(lldp);
 
         return 1;
@@ -307,6 +324,12 @@ _public_ int sd_lldp_detach_event(sd_lldp *lldp) {
         return 0;
 }
 
+_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
+        assert_return(lldp, NULL);
+
+        return lldp->event;
+}
+
 _public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
         assert_return(lldp, -EINVAL);
 
@@ -316,39 +339,60 @@ _public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *us
         return 0;
 }
 
+_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
+        assert_return(lldp, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+        assert_return(lldp->fd < 0, -EBUSY);
+
+        lldp->ifindex = ifindex;
+        return 0;
+}
+
+_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) {
+
+        if (!lldp)
+                return NULL;
+
+        assert(lldp->n_ref > 0);
+        lldp->n_ref++;
+
+        return lldp;
+}
+
 _public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
 
         if (!lldp)
                 return NULL;
 
+        assert(lldp->n_ref > 0);
+        lldp->n_ref --;
+
+        if (lldp->n_ref > 0)
+                return NULL;
+
+        lldp_reset(lldp);
+        sd_lldp_detach_event(lldp);
         lldp_flush_neighbors(lldp);
 
         hashmap_free(lldp->neighbor_by_id);
         prioq_free(lldp->neighbor_by_expiry);
-
-        sd_event_source_unref(lldp->io_event_source);
-        sd_event_source_unref(lldp->timer_event_source);
-        sd_event_unref(lldp->event);
-        safe_close(lldp->fd);
-
         free(lldp);
 
         return NULL;
 }
 
-_public_ int sd_lldp_new(sd_lldp **ret, int ifindex) {
+_public_ int sd_lldp_new(sd_lldp **ret) {
         _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
 
         lldp = new0(sd_lldp, 1);
         if (!lldp)
                 return -ENOMEM;
 
+        lldp->n_ref = 1;
         lldp->fd = -1;
-        lldp->ifindex = ifindex;
         lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
         lldp->capability_mask = (uint16_t) -1;
 
@@ -488,11 +532,10 @@ _public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *
         /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
          * that our own can be filtered out here. */
 
-        if (!addr) {
+        if (addr)
+                lldp->filter_address = *addr;
+        else
                 zero(lldp->filter_address);
-                return 0;
-        }
 
-        lldp->filter_address = *addr;
         return 0;
 }
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
index 31e8e7fa..0d400661 100644
--- a/src/systemd/src/libsystemd/sd-event/sd-event.c
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -23,9 +23,7 @@
 #include <sys/timerfd.h>
 #include <sys/wait.h>
 
-#if 0 /* NM_IGNORED */
 #include "sd-daemon.h"
-#endif
 #include "sd-event.h"
 #include "sd-id128.h"
 
@@ -34,17 +32,11 @@
 #include "hashmap.h"
 #include "list.h"
 #include "macro.h"
-#if 0 /* NM_IGNORED */
 #include "missing.h"
-#endif
 #include "prioq.h"
-#if 0 /* NM_IGNORED */
 #include "process-util.h"
-#endif
 #include "set.h"
-#if 0 /* NM_IGNORED */
 #include "signal-util.h"
-#endif
 #include "string-table.h"
 #include "string-util.h"
 #include "time-util.h"
@@ -119,8 +111,8 @@ struct sd_event_source {
         int64_t priority;
         unsigned pending_index;
         unsigned prepare_index;
-        unsigned pending_iteration;
-        unsigned prepare_iteration;
+        uint64_t pending_iteration;
+        uint64_t prepare_iteration;
 
         LIST_FIELDS(sd_event_source, sources);
 
@@ -225,9 +217,8 @@ struct sd_event {
 
         pid_t original_pid;
 
-        unsigned iteration;
-        dual_timestamp timestamp;
-        usec_t timestamp_boottime;
+        uint64_t iteration;
+        triple_timestamp timestamp;
         int state;
 
         bool exit_requested:1;
@@ -1082,12 +1073,16 @@ _public_ int sd_event_add_time(
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
+        if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */
+                return -EOPNOTSUPP;
+
+        type = clock_to_event_source_type(clock); /* checks whether sd-event supports this clock */
+        if (type < 0)
+                return -EOPNOTSUPP;
+
         if (!callback)
                 callback = time_exit_callback;
 
-        type = clock_to_event_source_type(clock);
-        assert_return(type >= 0, -EOPNOTSUPP);
-
         d = event_get_clock_data(e, type);
         assert(d);
 
@@ -1156,8 +1151,7 @@ _public_ int sd_event_add_signal(
         int r;
 
         assert_return(e, -EINVAL);
-        assert_return(sig > 0, -EINVAL);
-        assert_return(sig < _NSIG, -EINVAL);
+        assert_return(SIGNAL_VALID(sig), -EINVAL);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2212,7 +2206,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
                 if (_unlikely_(n != sizeof(si)))
                         return -EIO;
 
-                assert(si.ssi_signo < _NSIG);
+                assert(SIGNAL_VALID(si.ssi_signo));
 
                 read_one = true;
 
@@ -2539,8 +2533,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
                 goto finish;
         }
 
-        dual_timestamp_get(&e->timestamp);
-        e->timestamp_boottime = now(CLOCK_BOOTTIME);
+        triple_timestamp_get(&e->timestamp);
 
         for (i = 0; i < m; i++) {
 
@@ -2581,7 +2574,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         if (r < 0)
                 goto finish;
 
-        r = process_timer(e, e->timestamp_boottime, &e->boottime);
+        r = process_timer(e, e->timestamp.boottime, &e->boottime);
         if (r < 0)
                 goto finish;
 
@@ -2593,7 +2586,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         if (r < 0)
                 goto finish;
 
-        r = process_timer(e, e->timestamp_boottime, &e->boottime_alarm);
+        r = process_timer(e, e->timestamp.boottime, &e->boottime_alarm);
         if (r < 0)
                 goto finish;
 
@@ -2767,40 +2760,24 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
         assert_return(e, -EINVAL);
         assert_return(usec, -EINVAL);
         assert_return(!event_pid_changed(e), -ECHILD);
-        assert_return(IN_SET(clock,
-                             CLOCK_REALTIME,
-                             CLOCK_REALTIME_ALARM,
-                             CLOCK_MONOTONIC,
-                             CLOCK_BOOTTIME,
-                             CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
-
-        if (!dual_timestamp_is_set(&e->timestamp)) {
+
+        if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
+                return -EOPNOTSUPP;
+
+        /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here,
+         * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for
+         * the purpose of getting the time this doesn't matter. */
+        if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
+                return -EOPNOTSUPP;
+
+        if (!triple_timestamp_is_set(&e->timestamp)) {
                 /* Implicitly fall back to now() if we never ran
                  * before and thus have no cached time. */
                 *usec = now(clock);
                 return 1;
         }
 
-        switch (clock) {
-
-        case CLOCK_REALTIME:
-        case CLOCK_REALTIME_ALARM:
-                *usec = e->timestamp.realtime;
-                break;
-
-        case CLOCK_MONOTONIC:
-                *usec = e->timestamp.monotonic;
-                break;
-
-        case CLOCK_BOOTTIME:
-        case CLOCK_BOOTTIME_ALARM:
-                *usec = e->timestamp_boottime;
-                break;
-
-        default:
-                assert_not_reached("Unknown clock?");
-        }
-
+        *usec = triple_timestamp_by_clock(&e->timestamp, clock);
         return 0;
 }
 
@@ -2902,4 +2879,12 @@ _public_ int sd_event_get_watchdog(sd_event *e) {
 
         return e->watchdog;
 }
+
+_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) {
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        *ret = e->iteration;
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c
new file mode 100644
index 00000000..c16052d8
--- /dev/null
+++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c
@@ -0,0 +1,200 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd 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.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
+#include "io-util.h"
+#include "stdio-util.h"
+
+#if 0 /* NM_IGNORED */
+char *id128_to_uuid_string(sd_id128_t id, char s[37]) {
+        unsigned n, k = 0;
+
+        assert(s);
+
+        /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
+
+        for (n = 0; n < 16; n++) {
+
+                if (IN_SET(n, 4, 6, 8, 10))
+                        s[k++] = '-';
+
+                s[k++] = hexchar(id.bytes[n] >> 4);
+                s[k++] = hexchar(id.bytes[n] & 0xF);
+        }
+
+        assert(k == 36);
+
+        s[k] = 0;
+
+        return s;
+}
+
+bool id128_is_valid(const char *s) {
+        size_t i, l;
+
+        assert(s);
+
+        l = strlen(s);
+        if (l == 32) {
+
+                /* Plain formatted 128bit hex string */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if (!(c >= '0' && c <= '9') &&
+                            !(c >= 'a' && c <= 'z') &&
+                            !(c >= 'A' && c <= 'Z'))
+                                return false;
+                }
+
+        } else if (l == 36) {
+
+                /* Formatted UUID */
+
+                for (i = 0; i < l; i++) {
+                        char c = s[i];
+
+                        if ((i == 8 || i == 13 || i == 18 || i == 23)) {
+                                if (c != '-')
+                                        return false;
+                        } else {
+                                if (!(c >= '0' && c <= '9') &&
+                                    !(c >= 'a' && c <= 'z') &&
+                                    !(c >= 'A' && c <= 'Z'))
+                                        return false;
+                        }
+                }
+
+        } else
+                return false;
+
+        return true;
+}
+#endif /* NM_IGNORED */
+
+int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
+        char buffer[36 + 2];
+        ssize_t l;
+
+        assert(fd >= 0);
+        assert(f < _ID128_FORMAT_MAX);
+
+        /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
+         * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
+         * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
+         * accept". */
+
+        l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
+        if (l < 0)
+                return (int) l;
+        if (l == 0) /* empty? */
+                return -ENOMEDIUM;
+
+        switch (l) {
+
+        case 33: /* plain UUID with trailing newline */
+                if (buffer[32] != '\n')
+                        return -EINVAL;
+
+                /* fall through */
+        case 32: /* plain UUID without trailing newline */
+                if (f == ID128_UUID)
+                        return -EINVAL;
+
+                buffer[32] = 0;
+                break;
+
+        case 37: /* RFC UUID with trailing newline */
+                if (buffer[36] != '\n')
+                        return -EINVAL;
+
+                /* fall through */
+        case 36: /* RFC UUID without trailing newline */
+                if (f == ID128_PLAIN)
+                        return -EINVAL;
+
+                buffer[36] = 0;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        return sd_id128_from_string(buffer, ret);
+}
+
+int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+
+        return id128_read_fd(fd, f, ret);
+}
+
+#if 0 /* NM_IGNORED */
+int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
+        char buffer[36 + 2];
+        size_t sz;
+        int r;
+
+        assert(fd >= 0);
+        assert(f < _ID128_FORMAT_MAX);
+
+        if (f != ID128_UUID) {
+                sd_id128_to_string(id, buffer);
+                buffer[32] = '\n';
+                sz = 33;
+        } else {
+                id128_to_uuid_string(id, buffer);
+                buffer[36] = '\n';
+                sz = 37;
+        }
+
+        r = loop_write(fd, buffer, sz, false);
+        if (r < 0)
+                return r;
+
+        if (do_sync) {
+                if (fsync(fd) < 0)
+                        return -errno;
+        }
+
+        return r;
+}
+
+int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
+        if (fd < 0)
+                return -errno;
+
+        return id128_write_fd(fd, f, id, do_sync);
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h
new file mode 100644
index 00000000..3ba59acb
--- /dev/null
+++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h
@@ -0,0 +1,45 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd 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.1 of the License, or
+  (at your option) any later version.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "sd-id128.h"
+#include "macro.h"
+
+char *id128_to_uuid_string(sd_id128_t id, char s[37]);
+
+/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */
+#define ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
+
+bool id128_is_valid(const char *s) _pure_;
+
+typedef enum Id128Format {
+        ID128_ANY,
+        ID128_PLAIN,  /* formatted as 32 hex chars as-is */
+        ID128_UUID,   /* formatted as 36 character uuid string */
+        _ID128_FORMAT_MAX,
+} Id128Format;
+
+int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
+int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
+
+int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
+int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
index 01b805a1..9408c32b 100644
--- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -27,6 +27,7 @@
 
 #include "fd-util.h"
 #include "hexdecoct.h"
+#include "id128-util.h"
 #include "io-util.h"
 #include "macro.h"
 #include "random-util.h"
@@ -47,6 +48,7 @@ _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
 
         return s;
 }
+#endif /* NM_IGNORED */
 
 _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
         unsigned n, i;
@@ -54,7 +56,6 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
         bool is_guid = false;
 
         assert_return(s, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         for (n = 0, i = 0; n < 16;) {
                 int a, b;
@@ -92,125 +93,60 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
         if (s[i] != 0)
                 return -EINVAL;
 
-        *ret = t;
+        if (ret)
+                *ret = t;
         return 0;
 }
 
-static sd_id128_t make_v4_uuid(sd_id128_t id) {
-        /* Stolen from generate_random_uuid() of drivers/char/random.c
-         * in the kernel sources */
-
-        /* Set UUID version to 4 --- truly random generation */
-        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
-
-        /* Set the UUID variant to DCE */
-        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
-
-        return id;
-}
-#endif
-
 _public_ int sd_id128_get_machine(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_machine_id;
-        static thread_local bool saved_machine_id_valid = false;
-        _cleanup_close_ int fd = -1;
-        char buf[33];
-        unsigned j;
-        sd_id128_t t;
+        static thread_local sd_id128_t saved_machine_id = {};
         int r;
 
         assert_return(ret, -EINVAL);
 
-        if (saved_machine_id_valid) {
-                *ret = saved_machine_id;
-                return 0;
-        }
+        if (sd_id128_is_null(saved_machine_id)) {
+                r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
+                if (r < 0)
+                        return r;
 
-        fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return -errno;
-
-        r = loop_read_exact(fd, buf, 33, false);
-        if (r < 0)
-                return r;
-        if (buf[32] !='\n')
-                return -EIO;
-
-        for (j = 0; j < 16; j++) {
-                int a, b;
-
-                a = unhexchar(buf[j*2]);
-                b = unhexchar(buf[j*2+1]);
-
-                if (a < 0 || b < 0)
-                        return -EIO;
-
-                t.bytes[j] = a << 4 | b;
+                if (sd_id128_is_null(saved_machine_id))
+                        return -EINVAL;
         }
 
-        saved_machine_id = t;
-        saved_machine_id_valid = true;
-
-        *ret = t;
+        *ret = saved_machine_id;
         return 0;
 }
 
 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
-        static thread_local sd_id128_t saved_boot_id;
-        static thread_local bool saved_boot_id_valid = false;
-        _cleanup_close_ int fd = -1;
-        char buf[36];
-        unsigned j;
-        sd_id128_t t;
-        char *p;
+        static thread_local sd_id128_t saved_boot_id = {};
         int r;
 
         assert_return(ret, -EINVAL);
 
-        if (saved_boot_id_valid) {
-                *ret = saved_boot_id;
-                return 0;
+        if (sd_id128_is_null(saved_boot_id)) {
+                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
+                if (r < 0)
+                        return r;
         }
 
-        fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return -errno;
-
-        r = loop_read_exact(fd, buf, 36, false);
-        if (r < 0)
-                return r;
-
-        for (j = 0, p = buf; j < 16; j++) {
-                int a, b;
-
-                if (p >= buf + 35)
-                        return -EIO;
-
-                if (*p == '-') {
-                        p++;
-                        if (p >= buf + 35)
-                                return -EIO;
-                }
-
-                a = unhexchar(p[0]);
-                b = unhexchar(p[1]);
-
-                if (a < 0 || b < 0)
-                        return -EIO;
+        *ret = saved_boot_id;
+        return 0;
+}
 
-                t.bytes[j] = a << 4 | b;
+#if 0 /* NM_IGNORED */
+static sd_id128_t make_v4_uuid(sd_id128_t id) {
+        /* Stolen from generate_random_uuid() of drivers/char/random.c
+         * in the kernel sources */
 
-                p += 2;
-        }
+        /* Set UUID version to 4 --- truly random generation */
+        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
 
-        saved_boot_id = t;
-        saved_boot_id_valid = true;
+        /* Set the UUID variant to DCE */
+        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
 
-        *ret = t;
-        return 0;
+        return id;
 }
 
-#if 0 /* NM_IGNORED */
 _public_ int sd_id128_randomize(sd_id128_t *ret) {
         sd_id128_t t;
         int r;
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index 374ff877..9a90c2ed 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -84,28 +84,57 @@ enum {
 
 typedef struct sd_dhcp_client sd_dhcp_client;
 
-typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event,
-                                    void *userdata);
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
-                                void *userdata);
-
-int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
-int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
-                                       const struct in_addr *last_address);
-int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast);
-int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
-int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
-                           size_t addr_len, uint16_t arp_type);
-int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
-                                 const uint8_t *data, size_t data_len);
-int sd_dhcp_client_set_iaid_duid(sd_dhcp_client *client, uint32_t iaid,
-                                 uint16_t duid_type, uint8_t *duid, size_t duid_len);
-int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
-                                 const uint8_t **data, size_t *data_len);
-int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu);
-int sd_dhcp_client_set_hostname(sd_dhcp_client *client, const char *hostname);
-int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, const char *vci);
-int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret);
+typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata);
+int sd_dhcp_client_set_callback(
+                sd_dhcp_client *client,
+                sd_dhcp_client_callback_t cb,
+                void *userdata);
+
+int sd_dhcp_client_set_request_option(
+                sd_dhcp_client *client,
+                uint8_t option);
+int sd_dhcp_client_set_request_address(
+                sd_dhcp_client *client,
+                const struct in_addr *last_address);
+int sd_dhcp_client_set_request_broadcast(
+                sd_dhcp_client *client,
+                int broadcast);
+int sd_dhcp_client_set_ifindex(
+                sd_dhcp_client *client,
+                int interface_index);
+int sd_dhcp_client_set_mac(
+                sd_dhcp_client *client,
+                const uint8_t *addr,
+                size_t addr_len,
+                uint16_t arp_type);
+int sd_dhcp_client_set_client_id(
+                sd_dhcp_client *client,
+                uint8_t type,
+                const uint8_t *data,
+                size_t data_len);
+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);
+int sd_dhcp_client_get_client_id(
+                sd_dhcp_client *client,
+                uint8_t *type,
+                const uint8_t **data,
+                size_t *data_len);
+int sd_dhcp_client_set_mtu(
+                sd_dhcp_client *client,
+                uint32_t mtu);
+int sd_dhcp_client_set_hostname(
+                sd_dhcp_client *client,
+                const char *hostname);
+int sd_dhcp_client_set_vendor_class_identifier(
+                sd_dhcp_client *client,
+                const char *vci);
+int sd_dhcp_client_get_lease(
+                sd_dhcp_client *client,
+                sd_dhcp_lease **ret);
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
@@ -115,7 +144,10 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
 
 int sd_dhcp_client_new(sd_dhcp_client **ret);
 
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority);
+int sd_dhcp_client_attach_event(
+                sd_dhcp_client *client,
+                sd_event *event,
+                int64_t priority);
 int sd_dhcp_client_detach_event(sd_dhcp_client *client);
 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
 
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 4604cb63..7819f0d2 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -76,29 +76,52 @@ enum {
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;
 
-typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event,
-                                     void *userdata);
-int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
-                                 sd_dhcp6_client_callback_t cb, void *userdata);
-
-int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
-int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
-int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
-                            size_t addr_len, uint16_t arp_type);
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t duid_type,
-                             uint8_t *duid, size_t duid_len);
-int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid);
-int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled);
-int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled);
-int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
-                                       uint16_t option);
-
-int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
+typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata);
+int sd_dhcp6_client_set_callback(
+                sd_dhcp6_client *client,
+                sd_dhcp6_client_callback_t cb,
+                void *userdata);
+
+int sd_dhcp6_client_set_ifindex(
+                sd_dhcp6_client *client,
+                int interface_index);
+int sd_dhcp6_client_set_local_address(
+                sd_dhcp6_client *client,
+                const struct in6_addr *local_address);
+int sd_dhcp6_client_set_mac(
+                sd_dhcp6_client *client,
+                const uint8_t *addr,
+                size_t addr_len,
+                uint16_t arp_type);
+int sd_dhcp6_client_set_duid(
+                sd_dhcp6_client *client,
+                uint16_t duid_type,
+                const void *duid,
+                size_t duid_len);
+int sd_dhcp6_client_set_iaid(
+                sd_dhcp6_client *client,
+                uint32_t iaid);
+int sd_dhcp6_client_set_information_request(
+                sd_dhcp6_client *client,
+                int enabled);
+int sd_dhcp6_client_get_information_request(
+                sd_dhcp6_client *client,
+                int *enabled);
+int sd_dhcp6_client_set_request_option(
+                sd_dhcp6_client *client,
+                uint16_t option);
+
+int sd_dhcp6_client_get_lease(
+                sd_dhcp6_client *client,
+                sd_dhcp6_lease **ret);
 
 int sd_dhcp6_client_stop(sd_dhcp6_client *client);
 int sd_dhcp6_client_start(sd_dhcp6_client *client);
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority);
+int sd_dhcp6_client_attach_event(
+                sd_dhcp6_client *client,
+                sd_event *event,
+                int64_t priority);
 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
index 531ace1c..cc26b7df 100644
--- a/src/systemd/src/systemd/sd-event.h
+++ b/src/systemd/src/systemd/sd-event.h
@@ -104,6 +104,7 @@ int sd_event_get_tid(sd_event *e, pid_t *tid);
 int sd_event_get_exit_code(sd_event *e, int *code);
 int sd_event_set_watchdog(sd_event *e, int b);
 int sd_event_get_watchdog(sd_event *e);
+int sd_event_get_iteration(sd_event *e, uint64_t *ret);
 
 sd_event_source* sd_event_source_ref(sd_event_source *s);
 sd_event_source* sd_event_source_unref(sd_event_source *s);
diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h
index 9e3e14a3..16d99983 100644
--- a/src/systemd/src/systemd/sd-ipv4acd.h
+++ b/src/systemd/src/systemd/sd-ipv4acd.h
@@ -37,20 +37,20 @@ enum {
 };
 
 typedef struct sd_ipv4acd sd_ipv4acd;
-typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata);
-
-int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
-int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority);
-int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
-int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata);
-int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
-int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
-int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
-int sd_ipv4acd_is_running(sd_ipv4acd *ll);
-int sd_ipv4acd_start(sd_ipv4acd *ll);
-int sd_ipv4acd_stop(sd_ipv4acd *ll);
-sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
-sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
+int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
+int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
+int sd_ipv4acd_is_running(sd_ipv4acd *acd);
+int sd_ipv4acd_start(sd_ipv4acd *acd);
+int sd_ipv4acd_stop(sd_ipv4acd *acd);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd);
 int sd_ipv4acd_new(sd_ipv4acd **ret);
 
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
index 6fa38a22..1109ec52 100644
--- a/src/systemd/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -43,15 +43,15 @@ int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
-int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
+int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
 int sd_ipv4ll_is_running(sd_ipv4ll *ll);
 int sd_ipv4ll_start(sd_ipv4ll *ll);
 int sd_ipv4ll_stop(sd_ipv4ll *ll);
 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
 sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
-int sd_ipv4ll_new (sd_ipv4ll **ret);
+int sd_ipv4ll_new(sd_ipv4ll **ret);
 
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
 
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
index 4f2a3b50..3f35eebe 100644
--- a/src/systemd/src/systemd/sd-lldp.h
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -23,6 +23,7 @@
 
 #include <inttypes.h>
 #include <net/ethernet.h>
+#include <sys/types.h>
 
 #include "sd-event.h"
 
@@ -30,23 +31,18 @@
 
 _SD_BEGIN_DECLARATIONS;
 
-typedef struct sd_lldp sd_lldp;
-typedef struct sd_lldp_neighbor sd_lldp_neighbor;
-
-#define SD_LLDP_MULTICAST_ADDR     { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
-
 /* IEEE 802.3AB Clause 9: TLV Types */
 enum {
-        SD_LLDP_TYPE_END                  =   0,
-        SD_LLDP_TYPE_CHASSIS_ID           =   1,
-        SD_LLDP_TYPE_PORT_ID              =   2,
-        SD_LLDP_TYPE_TTL                  =   3,
-        SD_LLDP_TYPE_PORT_DESCRIPTION     =   4,
-        SD_LLDP_TYPE_SYSTEM_NAME          =   5,
-        SD_LLDP_TYPE_SYSTEM_DESCRIPTION   =   6,
-        SD_LLDP_TYPE_SYSTEM_CAPABILITIES  =   7,
-        SD_LLDP_TYPE_MGMT_ADDRESS         =   8,
-        SD_LLDP_TYPE_PRIVATE              =   127,
+        SD_LLDP_TYPE_END                 = 0,
+        SD_LLDP_TYPE_CHASSIS_ID          = 1,
+        SD_LLDP_TYPE_PORT_ID             = 2,
+        SD_LLDP_TYPE_TTL                 = 3,
+        SD_LLDP_TYPE_PORT_DESCRIPTION    = 4,
+        SD_LLDP_TYPE_SYSTEM_NAME         = 5,
+        SD_LLDP_TYPE_SYSTEM_DESCRIPTION  = 6,
+        SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
+        SD_LLDP_TYPE_MGMT_ADDRESS        = 8,
+        SD_LLDP_TYPE_PRIVATE             = 127,
 };
 
 /* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
@@ -63,28 +59,28 @@ enum {
 
 /* IEEE 802.3AB Clause 9.5.3: Port subtype */
 enum {
-        SD_LLDP_PORT_SUBTYPE_RESERVED           = 0,
-        SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS    = 1,
-        SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT     = 2,
-        SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS        = 3,
-        SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS    = 4,
-        SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME     = 5,
-        SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID   = 6,
-        SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED   = 7,
+        SD_LLDP_PORT_SUBTYPE_RESERVED         = 0,
+        SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS  = 1,
+        SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT   = 2,
+        SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS      = 3,
+        SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS  = 4,
+        SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME   = 5,
+        SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
+        SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
 };
 
 enum {
-        SD_LLDP_SYSTEM_CAPABILITIES_OTHER        = 1 << 0,
-        SD_LLDP_SYSTEM_CAPABILITIES_REPEATER     = 1 << 1,
-        SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE       = 1 << 2,
-        SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP      = 1 << 3,
-        SD_LLDP_SYSTEM_CAPABILITIES_ROUTER       = 1 << 4,
-        SD_LLDP_SYSTEM_CAPABILITIES_PHONE        = 1 << 5,
-        SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS       = 1 << 6,
-        SD_LLDP_SYSTEM_CAPABILITIES_STATION      = 1 << 7,
-        SD_LLDP_SYSTEM_CAPABILITIES_CVLAN        = 1 << 8,
-        SD_LLDP_SYSTEM_CAPABILITIES_SVLAN        = 1 << 9,
-        SD_LLDP_SYSTEM_CAPABILITIES_TPMR         = 1 << 10,
+        SD_LLDP_SYSTEM_CAPABILITIES_OTHER    = 1 << 0,
+        SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
+        SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE   = 1 << 2,
+        SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP  = 1 << 3,
+        SD_LLDP_SYSTEM_CAPABILITIES_ROUTER   = 1 << 4,
+        SD_LLDP_SYSTEM_CAPABILITIES_PHONE    = 1 << 5,
+        SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS   = 1 << 6,
+        SD_LLDP_SYSTEM_CAPABILITIES_STATION  = 1 << 7,
+        SD_LLDP_SYSTEM_CAPABILITIES_CVLAN    = 1 << 8,
+        SD_LLDP_SYSTEM_CAPABILITIES_SVLAN    = 1 << 9,
+        SD_LLDP_SYSTEM_CAPABILITIES_TPMR     = 1 << 10,
 };
 
 #define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
@@ -100,20 +96,22 @@ enum {
           SD_LLDP_SYSTEM_CAPABILITIES_SVLAN|                            \
           SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
 
-
 #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
 #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
 
 enum {
-        SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID            = 1,
-        SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID   = 2,
-        SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME               = 3,
-        SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY       = 4,
-        SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST        = 5,
-        SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID          = 6,
-        SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION        = 7,
+        SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID          = 1,
+        SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
+        SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME             = 3,
+        SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY     = 4,
+        SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST      = 5,
+        SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID        = 6,
+        SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION      = 7,
 };
 
+typedef struct sd_lldp sd_lldp;
+typedef struct sd_lldp_neighbor sd_lldp_neighbor;
+
 typedef enum sd_lldp_event {
         SD_LLDP_EVENT_ADDED     = 'a',
         SD_LLDP_EVENT_REMOVED   = 'r',
@@ -123,7 +121,8 @@ typedef enum sd_lldp_event {
 
 typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
 
-int sd_lldp_new(sd_lldp **ret, int ifindex);
+int sd_lldp_new(sd_lldp **ret);
+sd_lldp* sd_lldp_ref(sd_lldp *lldp);
 sd_lldp* sd_lldp_unref(sd_lldp *lldp);
 
 int sd_lldp_start(sd_lldp *lldp);
@@ -131,8 +130,10 @@ int sd_lldp_stop(sd_lldp *lldp);
 
 int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
 int sd_lldp_detach_event(sd_lldp *lldp);
+sd_event *sd_lldp_get_event(sd_lldp *lldp);
 
 int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
+int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex);
 
 /* Controls how much and what to store in the neighbors database */
 int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
@@ -148,6 +149,7 @@ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
 /* Access to LLDP frame metadata */
 int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
 int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
+int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
 int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
 
 /* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
@@ -155,7 +157,7 @@ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const vo
 int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
 int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
-int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret);
+int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
 int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
index 29bcbe8e..9f7d4ef7 100644
--- a/src/systemd/src/systemd/sd-ndisc.h
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -22,6 +22,8 @@
 
 #include <inttypes.h>
 #include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
 
 #include "sd-event.h"
 
@@ -29,55 +31,99 @@
 
 _SD_BEGIN_DECLARATIONS;
 
+/* Neightbor Discovery Options, RFC 4861, Section 4.6 and
+ * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
 enum {
-        SD_NDISC_EVENT_STOP     = 0,
-        SD_NDISC_EVENT_TIMEOUT  = 1,
+        SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,
+        SD_NDISC_OPTION_TARGET_LL_ADDRESS  = 2,
+        SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
+        SD_NDISC_OPTION_MTU                = 5,
+        SD_NDISC_OPTION_ROUTE_INFORMATION  = 24,
+        SD_NDISC_OPTION_RDNSS              = 25,
+        SD_NDISC_OPTION_FLAGS_EXTENSION    = 26,
+        SD_NDISC_OPTION_DNSSL              = 31,
+        SD_NDISC_OPTION_CAPTIVE_PORTAL     = 37,
+};
+
+/* Route preference, RFC 4191, Section 2.1 */
+enum {
+        SD_NDISC_PREFERENCE_LOW    = 3U,
+        SD_NDISC_PREFERENCE_MEDIUM = 0U,
+        SD_NDISC_PREFERENCE_HIGH   = 1U,
 };
 
 typedef struct sd_ndisc sd_ndisc;
+typedef struct sd_ndisc_router sd_ndisc_router;
 
-typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata);
-typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
-                                                 unsigned lifetime, void *userdata);
-typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
-                                                     unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata);
-typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata);
-
-int sd_ndisc_set_callback(sd_ndisc *nd,
-                          sd_ndisc_router_callback_t rcb,
-                          sd_ndisc_prefix_onlink_callback_t plcb,
-                          sd_ndisc_prefix_autonomous_callback_t pacb,
-                          sd_ndisc_callback_t cb,
-                          void *userdata);
-int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
-int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
+typedef enum sd_ndisc_event {
+        SD_NDISC_EVENT_TIMEOUT = 't',
+        SD_NDISC_EVENT_ROUTER  = 'r',
+} sd_ndisc_event;
 
-int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
-int sd_ndisc_detach_event(sd_ndisc *nd);
-sd_event *sd_ndisc_get_event(sd_ndisc *nd);
+typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata);
 
+int sd_ndisc_new(sd_ndisc **ret);
 sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
 sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
-int sd_ndisc_new(sd_ndisc **ret);
-
-int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu);
 
+int sd_ndisc_start(sd_ndisc *nd);
 int sd_ndisc_stop(sd_ndisc *nd);
-int sd_ndisc_router_discovery_start(sd_ndisc *nd);
 
-#define SD_NDISC_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
+int sd_ndisc_detach_event(sd_ndisc *nd);
+sd_event *sd_ndisc_get_event(sd_ndisc *nd);
+
+int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
+int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
+int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
 
-#define SD_NDISC_ADDRESS_FORMAT_VAL(address) \
-        be16toh((address).s6_addr16[0]),        \
-        be16toh((address).s6_addr16[1]),        \
-        be16toh((address).s6_addr16[2]),        \
-        be16toh((address).s6_addr16[3]),        \
-        be16toh((address).s6_addr16[4]),        \
-        be16toh((address).s6_addr16[5]),        \
-        be16toh((address).s6_addr16[6]),        \
-        be16toh((address).s6_addr16[7])
+int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
+int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
+
+int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
+sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
+sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
+
+int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
+int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
+int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
+
+int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
+int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
+int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
+int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
+int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
+
+/* Generic option access */
+int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
+int sd_ndisc_router_option_next(sd_ndisc_router *rt);
+int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
+int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
+int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
+
+/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
+int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
+int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
+int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
+
+/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
+int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
+int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
+int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
+
+/* Specific option access: SD_NDISC_OPTION_RDNSS */
+int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
+int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
+
+/* Specific option access: SD_NDISC_OPTION_DNSSL */
+int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
+int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
 
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
 
 _SD_END_DECLARATIONS;
 
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index e1734b6d..8851c242 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -1,5 +1,7 @@
 SUBDIRS = config
 
+@GNOME_CODE_COVERAGE_RULES@
+
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/shared \
 	-I$(top_builddir)/shared \
@@ -12,7 +14,10 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	$(GLIB_CFLAGS)
+	$(GLIB_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS)
+
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
 
 noinst_PROGRAMS = \
 	test-general \
@@ -47,6 +52,7 @@ test_ip6_config_LDADD = \
 
 test_route_manager_fake_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
+	$(GUDEV_CFLAGS) \
 	-I$(top_srcdir)/src/platform/tests \
 	-DSETUP=nm_fake_platform_setup \
 	-DKERNEL_HACKS=0
@@ -56,7 +62,7 @@ test_route_manager_fake_SOURCES = \
 	test-route-manager.c
 
 test_route_manager_fake_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 test_route_manager_linux_SOURCES = \
 	$(top_srcdir)/src/platform/tests/test-common.c \
@@ -64,6 +70,7 @@ test_route_manager_linux_SOURCES = \
 
 test_route_manager_linux_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
+	$(GUDEV_CFLAGS) \
 	-I$(top_srcdir)/src/platform/tests \
 	-DSETUP=nm_linux_platform_setup \
 	-DKERNEL_HACKS=1
@@ -123,7 +130,6 @@ test_systemd_SOURCES = \
 	test-systemd.c
 
 test_systemd_LDADD = \
-	$(top_builddir)/src/libNetworkManager-base.la \
 	$(top_builddir)/src/libsystemd-nm.la
 
 ####### utils test #######
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 2296c2b0..a4ca2212 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -102,8 +102,9 @@ TESTS = test-ip4-config$(EXEEXT) test-ip6-config$(EXEEXT) \
 	test-wired-defname$(EXEEXT) test-utils$(EXEEXT)
 subdir = src/tests
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -159,7 +160,7 @@ am_test_route_manager_fake_OBJECTS =  \
 test_route_manager_fake_OBJECTS =  \
 	$(am_test_route_manager_fake_OBJECTS)
 test_route_manager_fake_DEPENDENCIES =  \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 am_test_route_manager_linux_OBJECTS =  \
 	test_route_manager_linux-test-common.$(OBJEXT) \
 	test_route_manager_linux-test-route-manager.$(OBJEXT)
@@ -169,9 +170,7 @@ test_route_manager_linux_DEPENDENCIES =  \
 	$(top_builddir)/src/libNetworkManager.la
 am_test_systemd_OBJECTS = test_systemd-test-systemd.$(OBJEXT)
 test_systemd_OBJECTS = $(am_test_systemd_OBJECTS)
-test_systemd_DEPENDENCIES =  \
-	$(top_builddir)/src/libNetworkManager-base.la \
-	$(top_builddir)/src/libsystemd-nm.la
+test_systemd_DEPENDENCIES = $(top_builddir)/src/libsystemd-nm.la
 test_systemd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_systemd_CFLAGS) \
 	$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
@@ -533,6 +532,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -589,13 +589,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -607,6 +608,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -667,6 +670,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -702,6 +707,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -776,8 +782,10 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/src \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
-	$(GLIB_CFLAGS)
+	$(GLIB_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS)
 
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
 
 ####### ip4 config test #######
 test_ip4_config_SOURCES = \
@@ -798,6 +806,7 @@ test_ip6_config_LDADD = \
 ####### route manager test #######
 test_route_manager_fake_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
+	$(GUDEV_CFLAGS) \
 	-I$(top_srcdir)/src/platform/tests \
 	-DSETUP=nm_fake_platform_setup \
 	-DKERNEL_HACKS=0
@@ -807,7 +816,7 @@ test_route_manager_fake_SOURCES = \
 	test-route-manager.c
 
 test_route_manager_fake_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 test_route_manager_linux_SOURCES = \
 	$(top_srcdir)/src/platform/tests/test-common.c \
@@ -815,6 +824,7 @@ test_route_manager_linux_SOURCES = \
 
 test_route_manager_linux_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
+	$(GUDEV_CFLAGS) \
 	-I$(top_srcdir)/src/platform/tests \
 	-DSETUP=nm_linux_platform_setup \
 	-DKERNEL_HACKS=1
@@ -874,7 +884,6 @@ test_systemd_SOURCES = \
 	test-systemd.c
 
 test_systemd_LDADD = \
-	$(top_builddir)/src/libNetworkManager-base.la \
 	$(top_builddir)/src/libsystemd-nm.la
 
 
@@ -1633,6 +1642,8 @@ uninstall-am:
 .PRECIOUS: Makefile
 
 
+@GNOME_CODE_COVERAGE_RULES@
+
 ###########################################
 
 @VALGRIND_RULES@
diff --git a/src/tests/config/Makefile.am b/src/tests/config/Makefile.am
index 17876e4d..d0cb797d 100644
--- a/src/tests/config/Makefile.am
+++ b/src/tests/config/Makefile.am
@@ -9,8 +9,8 @@ AM_CPPFLAGS = \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	$(GLIB_CFLAGS) \
-	-DSRCDIR=\""$(srcdir)"\" \
-	-DBUILDDIR=\""$(builddir)"\"
+	-DSRCDIR=\""$(abs_srcdir)"\" \
+	-DBUILDDIR=\""$(abs_builddir)"\"
 
 noinst_PROGRAMS = \
 	test-config
@@ -21,13 +21,14 @@ test_config_SOURCES = \
 	test-config.c
 
 test_config_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 @VALGRIND_RULES@
 TESTS = test-config
 
 EXTRA_DIST = \
 	NetworkManager.conf \
+	NetworkManager.state \
 	bad.conf \
 	global-dns-invalid.conf \
 	conf.d/00-overrides.conf \
diff --git a/src/tests/config/Makefile.in b/src/tests/config/Makefile.in
index 7197ee1f..89c18941 100644
--- a/src/tests/config/Makefile.in
+++ b/src/tests/config/Makefile.in
@@ -92,8 +92,9 @@ noinst_PROGRAMS = test-config$(EXEEXT)
 TESTS = test-config$(EXEEXT)
 subdir = src/tests/config
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
-	$(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \
+	$(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_options.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -116,7 +117,8 @@ PROGRAMS = $(noinst_PROGRAMS)
 am_test_config_OBJECTS = nm-test-device.$(OBJEXT) \
 	test-config.$(OBJEXT)
 test_config_OBJECTS = $(am_test_config_OBJECTS)
-test_config_DEPENDENCIES = $(top_builddir)/src/libNetworkManager.la
+test_config_DEPENDENCIES =  \
+	$(top_builddir)/src/libNetworkManagerTest.la
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
@@ -422,6 +424,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
 DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
+DL_LIBS = @DL_LIBS@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@
 DSYMUTIL = @DSYMUTIL@
@@ -478,13 +481,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
 INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
 INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
 IPTABLES_PATH = @IPTABLES_PATH@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
 LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@
 LIBAUDIT_LIBS = @LIBAUDIT_LIBS@
-LIBDL = @LIBDL@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
@@ -496,6 +500,8 @@ LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
+LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@
 LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
 LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
@@ -556,6 +562,8 @@ QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
 READLINE_LIBS = @READLINE_LIBS@
+SANITIZERS = @SANITIZERS@
+SANITIZER_ENV = @SANITIZER_ENV@
 SED = @SED@
 SELINUX_CFLAGS = @SELINUX_CFLAGS@
 SELINUX_LIBS = @SELINUX_LIBS@
@@ -591,6 +599,7 @@ ac_ct_AR = @ac_ct_AR@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_RANLIB = @ac_ct_RANLIB@
 am__include = @am__include@
 am__leading_dot = @am__leading_dot@
 am__quote = @am__quote@
@@ -663,8 +672,8 @@ AM_CPPFLAGS = \
 	-DG_LOG_DOMAIN=\""NetworkManager"\" \
 	-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \
 	$(GLIB_CFLAGS) \
-	-DSRCDIR=\""$(srcdir)"\" \
-	-DBUILDDIR=\""$(builddir)"\"
+	-DSRCDIR=\""$(abs_srcdir)"\" \
+	-DBUILDDIR=\""$(abs_builddir)"\"
 
 test_config_SOURCES = \
 	nm-test-device.c \
@@ -672,10 +681,11 @@ test_config_SOURCES = \
 	test-config.c
 
 test_config_LDADD = \
-	$(top_builddir)/src/libNetworkManager.la
+	$(top_builddir)/src/libNetworkManagerTest.la
 
 EXTRA_DIST = \
 	NetworkManager.conf \
+	NetworkManager.state \
 	bad.conf \
 	global-dns-invalid.conf \
 	conf.d/00-overrides.conf \
diff --git a/src/tests/config/NetworkManager.state b/src/tests/config/NetworkManager.state
new file mode 100644
index 00000000..1b9d604a
--- /dev/null
+++ b/src/tests/config/NetworkManager.state
@@ -0,0 +1,4 @@
+[main]
+NetworkingEnabled=true
+WirelessEnabled=true
+WWANEnabled=true
diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c
index 04ccaec2..1ae964ee 100644
--- a/src/tests/config/nm-test-device.c
+++ b/src/tests/config/nm-test-device.c
@@ -38,17 +38,6 @@ nm_test_device_init (NMTestDevice *self)
 /* We jump over NMDevice's construct/destruct methods, which require NMPlatform
  * and NMConnectionProvider to be initialized.
  */
-
-static GObject*
-constructor (GType type,
-             guint n_construct_params,
-             GObjectConstructParam *construct_params)
-{
-	return PARENT_CLASS->constructor (type,
-	                                  n_construct_params,
-	                                  construct_params);
-}
-
 static void
 constructed (GObject *object)
 {
@@ -73,7 +62,6 @@ nm_test_device_class_init (NMTestDeviceClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	object_class->constructor = constructor;
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 
@@ -85,6 +73,6 @@ nm_test_device_new (const char *hwaddr)
 {
 	return g_object_new (NM_TYPE_TEST_DEVICE,
 	                     NM_DEVICE_IFACE, "dummy",
-	                     NM_DEVICE_HW_ADDRESS, hwaddr,
+	                     NM_DEVICE_PERM_HW_ADDRESS, hwaddr,
 	                     NULL);
 }
diff --git a/src/tests/config/nm-test-device.h b/src/tests/config/nm-test-device.h
index c3532c9c..dfb8ff78 100644
--- a/src/tests/config/nm-test-device.h
+++ b/src/tests/config/nm-test-device.h
@@ -23,8 +23,6 @@
 
 #include "nm-device.h"
 
-G_BEGIN_DECLS
-
 #define NM_TYPE_TEST_DEVICE            (nm_test_device_get_type ())
 #define NM_TEST_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TEST_DEVICE, NMTestDevice))
 #define NM_TEST_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_TEST_DEVICE, NMTestDeviceClass))
@@ -45,6 +43,4 @@ GType nm_test_device_get_type (void);
 
 NMDevice *nm_test_device_new (const char *hwaddr);
 
-G_END_DECLS
-
-#endif	/* NM_DEVICE_H */
+#endif /* __NETWORKMANAGER_TEST_DEVICE_H__ */
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index b6d88edf..73358d02 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -27,7 +27,7 @@
 #include "nm-fake-platform.h"
 #include "nm-bus-manager.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /********************************************************************************/
 
@@ -930,6 +930,65 @@ test_config_enable (void)
 
 /*****************************************************************************/
 
+static void
+test_config_state_file (void)
+{
+	NMConfig *config;
+	const NMConfigState *state;
+	gs_free_error GError *error = NULL;
+	gboolean ret;
+	gs_free char *file_data = NULL;
+	gsize file_size;
+	const char *const TMP_FILE = BUILDDIR "/tmp.state";
+
+	ret = g_file_get_contents (SRCDIR "/NetworkManager.state", &file_data, &file_size, &error);
+	nmtst_assert_success (ret, error);
+	ret = g_file_set_contents (TMP_FILE, file_data, file_size, &error);
+	nmtst_assert_success (ret, error);
+
+	config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "",
+	                       "--state-file", TMP_FILE, NULL);
+	g_assert (config);
+
+	state = nm_config_state_get (config);
+	g_assert (state);
+
+	g_assert_cmpint (state->net_enabled, ==, TRUE);
+	g_assert_cmpint (state->wifi_enabled, ==, TRUE);
+	g_assert_cmpint (state->wwan_enabled, ==, TRUE);
+
+	nm_config_state_set (config, TRUE, TRUE,
+	                     NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, FALSE,
+	                     NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED, TRUE,
+	                     NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED, FALSE);
+
+	state = nm_config_state_get (config);
+	g_assert (state);
+
+	g_assert_cmpint (state->net_enabled, ==, FALSE);
+	g_assert_cmpint (state->wifi_enabled, ==, TRUE);
+	g_assert_cmpint (state->wwan_enabled, ==, FALSE);
+
+	g_object_unref (config);
+
+	/* Reload configuration */
+	config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "",
+	                       "--state-file", TMP_FILE, NULL);
+	g_assert (config);
+
+	state = nm_config_state_get (config);
+	g_assert (state);
+
+	g_assert_cmpint (state->net_enabled, ==, FALSE);
+	g_assert_cmpint (state->wifi_enabled, ==, TRUE);
+	g_assert_cmpint (state->wwan_enabled, ==, FALSE);
+
+	g_object_unref (config);
+	unlink (TMP_FILE);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -960,6 +1019,8 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/config/enable", test_config_enable);
 
+	g_test_add_func ("/config/state-file", test_config_state_file);
+
 	/* This one has to come last, because it leaves its values in
 	 * nm-config.c's global variables, and there's no way to reset
 	 * those to NULL.
diff --git a/src/tests/test-dcb.c b/src/tests/test-dcb.c
index e8e1ec86..0a133de3 100644
--- a/src/tests/test-dcb.c
+++ b/src/tests/test-dcb.c
@@ -24,7 +24,7 @@
 
 #include "nm-dcb.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 typedef struct {
 	guint num;
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index 64b47bd3..ab2b15b5 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -30,7 +30,7 @@
 #include "NetworkManagerUtils.h"
 #include "nm-multi-index.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 #ifndef CLOCK_BOOTTIME
 #define CLOCK_BOOTTIME 7
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index f714deec..9769ead1 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -26,7 +26,7 @@
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 /* Reference implementation for nm_utils_ip6_address_clear_host_address.
  * Taken originally from set_address_masked(), src/rdisc/nm-lndp-rdisc.c
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index 9d38dc27..95b221bc 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -26,7 +26,7 @@
 #include "nm-ip4-config.h"
 #include "nm-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static NMIP4Config *
 build_test_config (void)
@@ -161,18 +161,18 @@ test_compare_with_source (void)
 
 	/* Address */
 	addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24);
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_address (a, &addr);
 
-	addr.source = NM_IP_CONFIG_SOURCE_VPN;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip4_config_add_address (b, &addr);
 
 	/* Route */
 	route = *nmtst_platform_ip4_route ("10.0.0.0", 8, "192.168.1.1");
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_route (a, &route);
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip4_config_add_route (b, &route);
 
 	/* Assert that the configs are basically the same, eg that the source is ignored */
@@ -193,31 +193,31 @@ test_add_address_with_source (void)
 
 	/* Test that a higher priority source is not overwritten */
 	addr = *nmtst_platform_ip4_address ("1.2.3.4", NULL, 24);
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_address (a, &addr);
 
 	test_addr = nm_ip4_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
-	addr.source = NM_IP_CONFIG_SOURCE_VPN;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip4_config_add_address (a, &addr);
 
 	test_addr = nm_ip4_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	/* Test that a lower priority address source is overwritten */
 	nm_ip4_config_del_address (a, 0);
-	addr.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	nm_ip4_config_add_address (a, &addr);
 
 	test_addr = nm_ip4_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
 
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_address (a, &addr);
 
 	test_addr = nm_ip4_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	g_object_unref (a);
 }
@@ -233,31 +233,31 @@ test_add_route_with_source (void)
 
 	/* Test that a higher priority source is not overwritten */
 	route = *nmtst_platform_ip4_route ("1.2.3.4", 24, "1.2.3.1");
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_route (a, &route);
 
 	test_route = nm_ip4_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip4_config_add_route (a, &route);
 
 	test_route = nm_ip4_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	/* Test that a lower priority address source is overwritten */
 	nm_ip4_config_del_route (a, 0);
-	route.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	nm_ip4_config_add_route (a, &route);
 
 	test_route = nm_ip4_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip4_config_add_route (a, &route);
 
 	test_route = nm_ip4_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	g_object_unref (a);
 }
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index 5a2bb589..3eceec0c 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -26,7 +26,7 @@
 #include "nm-ip6-config.h"
 
 #include "nm-platform.h"
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static NMIP6Config *
 build_test_config (void)
@@ -132,18 +132,18 @@ test_compare_with_source (void)
 
 	/* Address */
 	addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64);
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_address (a, &addr);
 
-	addr.source = NM_IP_CONFIG_SOURCE_VPN;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip6_config_add_address (b, &addr);
 
 	/* Route */
 	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_route (a, &route);
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip6_config_add_route (b, &route);
 
 	/* Assert that the configs are basically the same, eg that the source is ignored */
@@ -164,31 +164,31 @@ test_add_address_with_source (void)
 
 	/* Test that a higher priority source is not overwritten */
 	addr = *nmtst_platform_ip6_address ("1122:3344:5566::7788", NULL, 64);
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_address (a, &addr);
 
 	test_addr = nm_ip6_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
-	addr.source = NM_IP_CONFIG_SOURCE_VPN;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip6_config_add_address (a, &addr);
 
 	test_addr = nm_ip6_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	/* Test that a lower priority address source is overwritten */
 	nm_ip6_config_del_address (a, 0);
-	addr.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	nm_ip6_config_add_address (a, &addr);
 
 	test_addr = nm_ip6_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
 
-	addr.source = NM_IP_CONFIG_SOURCE_USER;
+	addr.addr_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_address (a, &addr);
 
 	test_addr = nm_ip6_config_get_address (a, 0);
-	g_assert_cmpint (test_addr->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	g_object_unref (a);
 }
@@ -204,31 +204,31 @@ test_add_route_with_source (void)
 
 	/* Test that a higher priority source is not overwritten */
 	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_route (a, &route);
 
 	test_route = nm_ip6_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	nm_ip6_config_add_route (a, &route);
 
 	test_route = nm_ip6_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	/* Test that a lower priority address source is overwritten */
 	nm_ip6_config_del_route (a, 0);
-	route.source = NM_IP_CONFIG_SOURCE_KERNEL;
+	route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
 	nm_ip6_config_add_route (a, &route);
 
 	test_route = nm_ip6_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_route (a, &route);
 
 	test_route = nm_ip6_config_get_route (a, 0);
-	g_assert_cmpint (test_route->source, ==, NM_IP_CONFIG_SOURCE_USER);
+	g_assert_cmpint (test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER);
 
 	g_object_unref (a);
 }
diff --git a/src/tests/test-resolvconf-capture.c b/src/tests/test-resolvconf-capture.c
index 31b6e177..32bc9734 100644
--- a/src/tests/test-resolvconf-capture.c
+++ b/src/tests/test-resolvconf-capture.c
@@ -28,7 +28,7 @@
 #include "nm-ip6-config.h"
 #include "nm-platform.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static void
 test_capture_empty (void)
diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c
index c753174d..b81c263c 100644
--- a/src/tests/test-route-manager.c
+++ b/src/tests/test-route-manager.c
@@ -23,12 +23,11 @@
 #include <arpa/inet.h>
 #include <linux/rtnetlink.h>
 
-#include "test-common.h"
-
 #include "nm-platform.h"
+#include "nm-platform-utils.h"
 #include "nm-route-manager.h"
 
-#include "nm-test-utils.h"
+#include "test-common.h"
 
 typedef struct {
 	int ifindex0, ifindex1;
@@ -45,7 +44,7 @@ setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_
 	route.ifindex = ifindex;
 	route.mss = 0;
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "6.6.6.0", &route.network);
 	route.plen = 24;
 	route.gateway = INADDR_ANY;
@@ -53,7 +52,7 @@ setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_
 	route.mss = mss_of_first_route;
 	g_array_append_val (routes, route);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "7.0.0.0", &route.network);
 	route.plen = 8;
 	inet_pton (AF_INET, "6.6.6.1", &route.gateway);
@@ -87,21 +86,21 @@ setup_dev1_ip4 (int ifindex)
 	                                route.mss))
 		g_assert_not_reached ();
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "6.6.6.0", &route.network);
 	route.plen = 24;
 	route.gateway = INADDR_ANY;
 	route.metric = 20;
 	g_array_append_val (routes, route);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "7.0.0.0", &route.network);
 	route.plen = 8;
 	route.gateway = INADDR_ANY;
 	route.metric = 22;
 	g_array_append_val (routes, route);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "8.0.0.0", &route.network);
 	route.plen = 8;
 	inet_pton (AF_INET, "6.6.6.2", &route.gateway);
@@ -121,14 +120,14 @@ update_dev0_ip4 (int ifindex)
 	route.ifindex = ifindex;
 	route.mss = 0;
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "6.6.6.0", &route.network);
 	route.plen = 24;
 	route.gateway = INADDR_ANY;
 	route.metric = 20;
 	g_array_append_val (routes, route);
 
-	route.source = NM_IP_CONFIG_SOURCE_USER;
+	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "7.0.0.0", &route.network);
 	route.plen = 8;
 	route.gateway = INADDR_ANY;
@@ -163,7 +162,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP4Route state1[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex0,
@@ -173,7 +172,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("7.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex0,
@@ -183,7 +182,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("7.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -193,7 +192,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex1,
@@ -203,7 +202,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("8.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -216,7 +215,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP4Route state2[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex0,
@@ -226,7 +225,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("7.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex0,
@@ -236,7 +235,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("7.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -246,7 +245,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex1,
@@ -256,7 +255,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("8.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -269,7 +268,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP4Route state3[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("7.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -279,7 +278,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex1,
@@ -289,7 +288,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("8.0.0.0"),
 			.plen = 8,
 			.ifindex = fixture->ifindex1,
@@ -299,7 +298,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 			.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = nmtst_inet4_from_string ("6.6.6.0"),
 			.plen = 24,
 			.ifindex = fixture->ifindex1,
@@ -542,7 +541,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP6Route state1[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex0,
@@ -551,7 +550,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex0,
@@ -560,7 +559,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex0,
@@ -569,7 +568,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -578,7 +577,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -587,7 +586,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -596,7 +595,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -608,7 +607,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP6Route state2[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex0,
@@ -617,7 +616,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex0,
@@ -626,7 +625,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex0,
@@ -635,7 +634,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -644,7 +643,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -653,7 +652,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -662,7 +661,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -674,7 +673,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 
 	NMPlatformIP6Route state3[] = {
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -683,7 +682,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -692,7 +691,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -701,7 +700,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -710,7 +709,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
 			.plen = 48,
 			.ifindex = fixture->ifindex1,
@@ -719,7 +718,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 			.mss = 0,
 		},
 		{
-			.source = NM_IP_CONFIG_SOURCE_USER,
+			.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
 			.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
 			.plen = 64,
 			.ifindex = fixture->ifindex1,
@@ -787,6 +786,7 @@ static void
 _assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route)
 {
 	const NMPlatformIPXRoute *r;
+	NMPlatformIPXRoute c;
 
 	g_assert (route);
 
@@ -800,11 +800,18 @@ _assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NM
 	} else {
 		char buf[sizeof (_nm_utils_to_string_buffer)];
 
-		if (!r || vtable->route_cmp (route, r) != 0)
+		if (r) {
+			if (vtable->is_ip4)
+				c.r4 = route->r4;
+			else
+				c.r6 = route->r6;
+			c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source);
+		}
+		if (!r || vtable->route_cmp (r, &c) != 0) {
 			g_error ("Invalid route. Expect %s, has %s",
-			         vtable->route_to_string (route, NULL, 0),
+			         vtable->route_to_string (&c, NULL, 0),
 			         vtable->route_to_string (r, buf, sizeof (buf)));
-		g_assert (r);
+		}
 	}
 }
 
diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c
index 936bff20..744b55c9 100644
--- a/src/tests/test-systemd.c
+++ b/src/tests/test-systemd.c
@@ -25,7 +25,52 @@
 #include "sd-lldp.h"
 #include "sd-event.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
+
+/*****************************************************************************
+ * Stub implementations of libNetworkManagerBase symbols
+ *****************************************************************************/
+
+gboolean
+nm_utils_get_testing_initialized (void)
+{
+	return TRUE;
+}
+
+void
+_nm_utils_set_testing (NMUtilsTestFlags flags)
+{
+	g_assert_not_reached ();
+}
+
+gint32
+nm_utils_get_monotonic_timestamp_s (void)
+{
+	return 1;
+}
+
+NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL];
+
+void
+_nm_log_impl (const char *file,
+              guint line,
+              const char *func,
+              NMLogLevel level,
+              NMLogDomain domain,
+              int error,
+              const char *fmt,
+              ...)
+{
+}
+
+gboolean
+nm_logging_setup (const char  *level,
+                  const char  *domains,
+                  char       **bad_domains,
+                  GError     **error)
+{
+	return TRUE;
+}
 
 /*****************************************************************************/
 
@@ -39,6 +84,12 @@ test_dhcp_create (void)
 	g_assert (r == 0);
 	g_assert (client4);
 
+	if (/* never true */ client4 == (gpointer) &r) {
+		/* we don't want to call this, but ensure that the linker
+		 * includes all these symbols. */
+		sd_dhcp_client_start (client4);
+	}
+
 	sd_dhcp_client_unref (client4);
 }
 
@@ -48,10 +99,9 @@ static void
 test_lldp_create (void)
 {
 	sd_lldp *lldp = NULL;
-	int ifindex = 1;
 	int r;
 
-	r = sd_lldp_new (&lldp, ifindex);
+	r = sd_lldp_new (&lldp);
 	g_assert (r == 0);
 	g_assert (lldp);
 
diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c
index 6fc3978b..000a9d1e 100644
--- a/src/tests/test-utils.c
+++ b/src/tests/test-utils.c
@@ -26,7 +26,7 @@
 
 #include "nm-core-utils.c"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static void
 test_stable_privacy (void)
@@ -34,21 +34,93 @@ test_stable_privacy (void)
 	struct in6_addr addr1;
 
 	inet_pton (AF_INET6, "1234::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL);
+	_set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f");
 
 	/* We get an address without the UUID. */
 	inet_pton (AF_INET6, "1::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
+	_set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa");
 
 	/* We get a different address in a different network. */
 	inet_pton (AF_INET6, "2::", &addr1);
-	_set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
+	_set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL);
 	nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726");
+
+	inet_pton (AF_INET6, "1234::", &addr1);
+	_set_stable_privacy (NM_UTILS_STABLE_TYPE_STABLE_ID, &addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL);
+	nmtst_assert_ip6_address (&addr1, "1234::ad4c:ae44:3d30:af1e");
+
+	inet_pton (AF_INET6, "1234::", &addr1);
+	_set_stable_privacy (NM_UTILS_STABLE_TYPE_STABLE_ID, &addr1, "eth666", "stable-id-1", 0, (guint8 *) "key", 3, NULL);
+	nmtst_assert_ip6_address (&addr1, "1234::4944:67b0:7a6c:1cf");
+}
+
+/*****************************************************************************/
+
+static void
+_do_test_hw_addr (NMUtilsStableType stable_type,
+                  const char *stable_id,
+                  const guint8 *secret_key,
+                  gsize key_len,
+                  const char *ifname,
+                  const char *current_mac_address,
+                  const char *generate_mac_address_mask,
+                  const char **expected)
+{
+	gs_free char *generated = NULL;
+	const char **e;
+	gboolean found = FALSE;
+
+	for (e = expected; *e; e++) {
+		g_assert (*e);
+		g_assert (nm_utils_hwaddr_valid (*e, ETH_ALEN));
+	}
+
+	generated = _hw_addr_gen_stable_eth (stable_type,
+	                                     stable_id,
+	                                     secret_key,
+	                                     key_len,
+	                                     ifname,
+	                                     current_mac_address,
+	                                     generate_mac_address_mask);
+
+	g_assert (generated);
+	g_assert (nm_utils_hwaddr_valid (generated, ETH_ALEN));
+	for (e = expected; *e; e++) {
+		if (!nm_utils_hwaddr_matches (generated, -1, *e, -1))
+			continue;
+		g_assert (!found);
+		found = TRUE;
+		g_assert_cmpstr (generated, ==, *e);
+	}
+	g_assert (found);
+}
+#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, current_mac_address, generate_mac_address_mask, ...) \
+	_do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""current_mac_address"", generate_mac_address_mask, (const char *[]) { __VA_ARGS__, NULL })
+
+static void
+test_hw_addr_gen_stable_eth (void)
+{
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "06:0D:CD:0C:9E:2C");
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "C6:AE:A9:9A:76:09");
+
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "FF:FF:FF:00:00:00", "00:23:45:0C:9E:2C");
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "03:23:45:67:89:ab", "FF:FF:FF:00:00:00", "02:23:45:0C:9E:2C");
+
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00", "06:0D:CD:0C:9E:2C");
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C");
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C");
+
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00", "04:0D:CD:0C:9E:2C");
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C");
+
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00 E9:60:CE:F5:ED:2F", "06:0D:CD:0C:9E:2C");
+
+	do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID,      "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C", "04:0D:CD:0C:9E:2C");
 }
 
-/*******************************************/
+/*****************************************************************************/
 
 NMTST_DEFINE ();
 
@@ -58,6 +130,7 @@ main (int argc, char **argv)
 	nmtst_init_with_logging (&argc, &argv, NULL, "ALL");
 
 	g_test_add_func ("/utils/stable_privacy", test_stable_privacy);
+	g_test_add_func ("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth);
 
 	return g_test_run ();
 }
diff --git a/src/tests/test-wired-defname.c b/src/tests/test-wired-defname.c
index 6edd5ff2..3ae3a19d 100644
--- a/src/tests/test-wired-defname.c
+++ b/src/tests/test-wired-defname.c
@@ -24,7 +24,7 @@
 #include "nm-setting-connection.h"
 #include "nm-device-ethernet-utils.h"
 
-#include "nm-test-utils.h"
+#include "nm-test-utils-core.h"
 
 static NMConnection *
 _new_connection (const char *id)
@@ -41,12 +41,28 @@ _new_connection (const char *id)
 
 /*******************************************/
 
+static char *
+_get_default_wired_name (GSList *list)
+{
+	gs_free NMConnection **v = NULL;
+	guint l, i;
+
+	l = g_slist_length (list);
+	v = g_new0 (NMConnection *, l + 1);
+	for (i = 0; list; list = list->next, i++)
+		v[i] = NM_CONNECTION (list->data);
+	g_assert (i == l);
+	return nm_device_ethernet_utils_get_default_wired_name (v);
+}
+
+/*******************************************/
+
 static void
 test_defname_no_connections (void)
 {
 	gs_free char *name = NULL;
 
-	name = nm_device_ethernet_utils_get_default_wired_name (NULL);
+	name = _get_default_wired_name (NULL);
 	g_assert_cmpstr (name, ==, "Wired connection 1");
 }
 
@@ -62,7 +78,7 @@ test_defname_no_conflict (void)
 	list = g_slist_append (list, _new_connection ("work wifi"));
 	list = g_slist_append (list, _new_connection ("random gsm connection"));
 
-	name = nm_device_ethernet_utils_get_default_wired_name (list);
+	name = _get_default_wired_name (list);
 	g_assert_cmpstr (name, ==, "Wired connection 1");
 
 	g_slist_free_full (list, g_object_unref);
@@ -80,7 +96,7 @@ test_defname_conflict (void)
 	list = g_slist_append (list, _new_connection ("Wired connection 1"));
 	list = g_slist_append (list, _new_connection ("random gsm connection"));
 
-	name = nm_device_ethernet_utils_get_default_wired_name (list);
+	name = _get_default_wired_name (list);
 	g_assert_cmpstr (name, ==, "Wired connection 2");
 
 	g_slist_free_full (list, g_object_unref);
@@ -102,7 +118,7 @@ test_defname_multiple_conflicts (void)
 	list = g_slist_append (list, _new_connection ("work wifi"));
 	list = g_slist_append (list, _new_connection ("a vpn"));
 
-	name = nm_device_ethernet_utils_get_default_wired_name (list);
+	name = _get_default_wired_name (list);
 	g_assert_cmpstr (name, ==, "Wired connection 4");
 
 	g_slist_free_full (list, g_object_unref);
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index cb8474aa..92c5bd8f 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -27,6 +27,8 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
 
 #include "nm-vpn-connection.h"
 #include "nm-ip4-config.h"
@@ -48,8 +50,6 @@
 
 #include "nmdbus-vpn-connection.h"
 
-G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
-
 typedef enum {
 	/* Only system secrets */
 	SECRETS_REQ_SYSTEM = 0,
@@ -78,6 +78,24 @@ typedef enum {
 	STATE_FAILED,
 } VpnState;
 
+enum {
+	VPN_STATE_CHANGED,
+	INTERNAL_STATE_CHANGED,
+	INTERNAL_RETRY_AFTER_FAILURE,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMVpnConnection,
+	PROP_VPN_STATE,
+	PROP_BANNER,
+#define PROP_IP4_CONFIG 2000
+#define PROP_IP6_CONFIG 2001
+#define PROP_MASTER     2002
+);
+
 typedef struct {
 	gboolean service_can_persist;
 	gboolean connection_can_persist;
@@ -126,29 +144,45 @@ typedef struct {
 	guint32 mtu;
 } NMVpnConnectionPrivate;
 
-#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVpnConnectionPrivate))
-
-enum {
-	VPN_STATE_CHANGED,
-	INTERNAL_STATE_CHANGED,
-	INTERNAL_RETRY_AFTER_FAILURE,
-
-	LAST_SIGNAL
+struct _NMVpnConnection {
+	NMActiveConnection parent;
+	NMVpnConnectionPrivate _priv;
 };
 
-static guint signals[LAST_SIGNAL] = { 0 };
+struct _NMVpnConnectionClass {
+	NMActiveConnectionClass parent;
 
-enum {
-	PROP_0,
-	PROP_VPN_STATE,
-	PROP_BANNER,
-	PROP_IP4_CONFIG,
-	PROP_IP6_CONFIG,
-	PROP_MASTER = 2000,
+	/* Signals */
+	void (*vpn_state_changed) (NMVpnConnection *self,
+	                           NMVpnConnectionState new_state,
+	                           NMVpnConnectionStateReason reason);
+
+	/* not exported over D-Bus */
+	void (*internal_state_changed) (NMVpnConnection *self,
+	                                NMVpnConnectionState new_state,
+	                                NMVpnConnectionState old_state,
+	                                NMVpnConnectionStateReason reason);
 
-	LAST_PROP
+	void (*internal_failed_retry)  (NMVpnConnection *self);
 };
 
+G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
+
+#define NM_VPN_CONNECTION_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMVpnConnection *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_VPN_CONNECTION (_self)); \
+		&_self->_priv; \
+	})
+
+/*****************************************************************************/
+
 static NMSettingsConnection *_get_settings_connection (NMVpnConnection *self,
                                                        gboolean allow_missing);
 
@@ -493,7 +527,7 @@ _set_vpn_state (NMVpnConnection *self,
 		               new_external_state,
 		               old_external_state,
 		               reason);
-		g_object_notify (G_OBJECT (self), NM_VPN_CONNECTION_VPN_STATE);
+		_notify (self, PROP_VPN_STATE);
 	}
 
 	switch (vpn_state) {
@@ -681,7 +715,7 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32
 	if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32))
 		route.gateway = 0;
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	route.metric = route_metric;
 	nm_ip4_config_add_route (config, &route);
 
@@ -693,7 +727,7 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32
 	memset (&route, 0, sizeof (route));
 	route.network = parent_gw;
 	route.plen = 32;
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	route.metric = route_metric;
 
 	nm_ip4_config_add_route (config, &route);
@@ -733,7 +767,7 @@ add_ip6_vpn_gateway_route (NMIP6Config *config,
 	if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128))
 		route.gateway = in6addr_any;
 
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	route.metric = route_metric;
 	nm_ip6_config_add_route (config, &route);
 
@@ -745,7 +779,7 @@ add_ip6_vpn_gateway_route (NMIP6Config *config,
 	memset (&route, 0, sizeof (route));
 	route.network = *parent_gw;
 	route.plen = 128;
-	route.source = NM_IP_CONFIG_SOURCE_VPN;
+	route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 	route.metric = route_metric;
 
 	nm_ip6_config_add_route (config, &route);
@@ -1246,7 +1280,7 @@ process_generic_config (NMVpnConnection *self, GVariant *dict)
 	g_clear_pointer (&priv->banner, g_free);
 	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_BANNER, "&s", &str)) {
 		priv->banner = g_strdup (str);
-		g_object_notify (G_OBJECT (self), NM_VPN_CONNECTION_BANNER);
+		_notify (self, PROP_BANNER);
 	}
 
 	/* External world-visible address of the VPN server */
@@ -1338,6 +1372,8 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	const char *str;
 	GVariant *v;
 	gboolean b;
+	guint i, n;
+	int ip_ifindex;
 
 	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
 
@@ -1365,7 +1401,11 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 		priv->has_ip6 = FALSE;
 	}
 
-	config = nm_ip4_config_new (nm_vpn_connection_get_ip_ifindex (self, TRUE));
+	ip_ifindex = nm_vpn_connection_get_ip_ifindex (self, TRUE);
+	if (ip_ifindex <= 0)
+		g_return_if_reached ();
+
+	config = nm_ip4_config_new (ip_ifindex);
 	nm_ip4_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN);
 
 	memset (&address, 0, sizeof (address));
@@ -1389,7 +1429,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 		address.plen = u32;
 
 	if (address.address && address.plen && address.plen <= 32) {
-		address.source = NM_IP_CONFIG_SOURCE_VPN;
+		address.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 		nm_ip4_config_add_address (config, &address);
 	} else {
 		_LOGW ("invalid IP4 config received!");
@@ -1424,9 +1464,17 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 
 	route_metric = nm_vpn_connection_get_ip4_route_metric (self);
 
-	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) {
+	if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b)
+	    && b) {
+		if (priv->ip4_config) {
+			n = nm_ip4_config_get_num_routes (priv->ip4_config);
+			for (i = 0; i < n; i++)
+				nm_ip4_config_add_route (config, nm_ip4_config_get_route (priv->ip4_config, i));
+		}
+	} else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) {
 		while (g_variant_iter_next (iter, "@au", &v)) {
 			NMPlatformIP4Route route = { 0, };
+			guint32 plen;
 
 			switch (g_variant_n_children (v)) {
 			case 5:
@@ -1434,14 +1482,15 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 				/* fallthrough */
 			case 4:
 				g_variant_get_child (v, 0, "u", &route.network);
-				g_variant_get_child (v, 1, "u", &route.plen);
+				g_variant_get_child (v, 1, "u", &plen);
 				g_variant_get_child (v, 2, "u", &route.gateway);
 				/* 4th item is unused route metric */
 				route.metric = route_metric;
-				route.source = NM_IP_CONFIG_SOURCE_VPN;
+				route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 
-				if (route.plen > 32)
+				if (plen > 32 || plen == 0)
 					break;
+				route.plen = plen;
 
 				/* Ignore host routes to the VPN gateway since NM adds one itself
 				 * below.  Since NM knows more about the routing situation than
@@ -1473,7 +1522,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	} else {
 		priv->ip4_config = config;
 		nm_exported_object_export (NM_EXPORTED_OBJECT (config));
-		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_IP4_CONFIG);
+		g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP4_CONFIG);
 	}
 
 	nm_vpn_connection_config_maybe_complete (self, TRUE);
@@ -1490,6 +1539,8 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 	const char *str;
 	GVariant *v;
 	gboolean b;
+	guint i, n;
+	int ip_ifindex;
 
 	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
 
@@ -1504,7 +1555,11 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 		return;
 	}
 
-	config = nm_ip6_config_new (priv->ip_ifindex);
+	ip_ifindex = nm_vpn_connection_get_ip_ifindex (self, TRUE);
+	if (ip_ifindex <= 0)
+		g_return_if_reached ();
+
+	config = nm_ip6_config_new (ip_ifindex);
 	nm_ip6_config_set_dns_priority (config, NM_DNS_PRIORITY_DEFAULT_VPN);
 
 	memset (&address, 0, sizeof (address));
@@ -1532,7 +1587,7 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 		address.plen = u32;
 
 	if (!IN6_IS_ADDR_UNSPECIFIED (&address.address) && address.plen && address.plen <= 128) {
-		address.source = NM_IP_CONFIG_SOURCE_VPN;
+		address.addr_source = NM_IP_CONFIG_SOURCE_VPN;
 		nm_ip6_config_add_address (config, &address);
 	} else {
 		_LOGW ("invalid IP6 config received!");
@@ -1566,7 +1621,14 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 
 	route_metric = nm_vpn_connection_get_ip6_route_metric (self);
 
-	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) {
+	if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b)
+	    && b) {
+		if (priv->ip6_config) {
+			n = nm_ip6_config_get_num_routes (priv->ip6_config);
+			for (i = 0; i < n; i++)
+				nm_ip6_config_add_route (config, nm_ip6_config_get_route (priv->ip6_config, i));
+		}
+	} else if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) {
 		GVariant *dest, *next_hop;
 		guint32 prefix, metric;
 
@@ -1578,13 +1640,13 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 			if (!ip6_addr_from_variant (dest, &route.network))
 				goto next;
 
-			if (prefix > 128)
+			if (prefix > 128 || prefix == 0)
 				goto next;
 
 			route.plen = prefix;
 			ip6_addr_from_variant (next_hop, &route.gateway);
 			route.metric = route_metric;
-			route.source = NM_IP_CONFIG_SOURCE_VPN;
+			route.rt_source = NM_IP_CONFIG_SOURCE_VPN;
 
 			/* Ignore host routes to the VPN gateway since NM adds one itself.
 			 * Since NM knows more about the routing situation than the VPN
@@ -1615,7 +1677,7 @@ next:
 	} else {
 		priv->ip6_config = config;
 		nm_exported_object_export (NM_EXPORTED_OBJECT (config));
-		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_IP6_CONFIG);
+		g_object_notify ((GObject *) self, NM_ACTIVE_CONNECTION_IP6_CONFIG);
 	}
 
 	nm_vpn_connection_config_maybe_complete (self, TRUE);
@@ -1933,6 +1995,41 @@ _daemon_exec_timeout (gpointer data)
 	return G_SOURCE_REMOVE;
 }
 
+static int
+_get_log_level (void)
+{
+	NMLogLevel level;
+
+	/* curiously enough, nm-logging also uses syslog. But it
+	 * maps NMLogLevel differently to the syslog levels then we
+	 * do here.
+	 *
+	 * The reason is, that LOG_NOTICE is already something worth
+	 * highlighting in the journal, but we have 3 levels that are
+	 * lower then LOG_NOTICE (LOGL_TRACE, LOGL_DEBUG, LOGL_INFO),
+	 * On the other hand, syslog only defines LOG_DEBUG and LOG_INFO.
+	 * Thus, we must map them differently.
+	 *
+	 * Inside the VPN plugin, you might want to treat LOG_NOTICE as
+	 * as low severity, not worthy to be highlighted (like NM does). */
+
+	level = nm_logging_get_level (LOGD_VPN_PLUGIN);
+	if (level != _LOGL_OFF) {
+		if (level <= LOGL_TRACE)
+			return LOG_DEBUG;
+		if (level <= LOGL_DEBUG)
+			return LOG_INFO;
+		if (level <= LOGL_INFO)
+			return LOG_NOTICE;
+		if (level <= LOGL_WARN)
+			return LOG_WARNING;
+		if (level <= LOGL_ERR)
+			return LOG_ERR;
+	}
+
+	return LOG_EMERG;
+}
+
 static gboolean
 nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error)
 {
@@ -1941,20 +2038,50 @@ nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error)
 	char *vpn_argv[4];
 	gboolean success = FALSE;
 	GError *spawn_error = NULL;
-	int i = 0;
+	guint i, j, n_environ;
+	gs_free char **envp = NULL;
+	char env_log_level[NM_STRLEN ("NM_VPN_LOG_LEVEL=") + 100];
+	char env_log_syslog[NM_STRLEN ("NM_VPN_LOG_SYSLOG=") + 10];
+	const int N_ENVIRON_EXTRA = 3;
+	char **p_environ;
 
 	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE);
+
 	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
+	i = 0;
 	vpn_argv[i++] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info);
+	g_return_val_if_fail (vpn_argv[0], FALSE);
 	if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) {
 		vpn_argv[i++] = "--bus-name";
 		vpn_argv[i++] = priv->bus_name;
 	}
-	vpn_argv[i] = NULL;
-	g_assert (vpn_argv[0]);
+	vpn_argv[i++] = NULL;
+
+	/* we include <unistd.h> and "config.h" defines _GNU_SOURCE for us. So, we have @environ. */
+	p_environ = environ;
+	n_environ = p_environ ? g_strv_length (p_environ) : 0;
+	envp = g_new (char *, n_environ + N_ENVIRON_EXTRA);
+	for (i = 0, j = 0; j < n_environ; j++) {
+		if (   g_str_has_prefix (p_environ[j], "NM_VPN_LOG_LEVEL=")
+		    || g_str_has_prefix (p_environ[j], "NM_VPN_LOG_SYSLOG="))
+			continue;
+		envp[i++] = p_environ[j];
+	}
 
-	success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error);
+	/* NM_VPN_LOG_LEVEL: the syslog logging level for the plugin. */
+	envp[i++] = nm_sprintf_buf (env_log_level,  "NM_VPN_LOG_LEVEL=%d", _get_log_level ());
+
+	/* NM_VPN_LOG_SYSLOG: whether to log to stdout or syslog. If NetworkManager itself runs in
+	 * foreground, we also want the plugin to log to stdout.
+	 * If the plugin runs in background, the plugin should prefer logging to syslog. Otherwise
+	 * logging messages will be lost (unless using journald, in which case it wouldn't matter). */
+	envp[i++] = nm_sprintf_buf (env_log_syslog, "NM_VPN_LOG_SYSLOG=%c", nm_logging_syslog_enabled () ? '1' : '0');
+
+	envp[i++] = NULL;
+	nm_assert (i <= n_environ + N_ENVIRON_EXTRA);
+
+	success = g_spawn_async (NULL, vpn_argv, envp, 0, nm_utils_setpgid, NULL, &pid, &spawn_error);
 
 	if (success) {
 		_LOGI ("Started the VPN service, PID %ld", (long int) pid);
@@ -2030,10 +2157,8 @@ nm_vpn_connection_activate (NMVpnConnection *self,
 	s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self));
 	g_return_if_fail (s_vpn);
 
-	service = nm_vpn_plugin_info_lookup_property (plugin_info,
-	                                              NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION,
-	                                              "service");
-	g_return_if_fail (service);
+	service = nm_vpn_plugin_info_get_service (plugin_info);
+	nm_assert (service);
 
 	if (nm_vpn_plugin_info_supports_multiple (plugin_info)) {
 		const char *path;
@@ -2421,7 +2546,7 @@ device_changed (NMActiveConnection *active,
                 NMDevice *new_device,
                 NMDevice *old_device)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (active);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE ((NMVpnConnection *) active);
 
 	if (!_service_and_connection_can_persist (NM_VPN_CONNECTION (active)))
 		return;
@@ -2494,7 +2619,7 @@ dispose (GObject *object)
 static void
 finalize (GObject *object)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE ((NMVpnConnection *) object);
 
 	g_free (priv->banner);
 	g_free (priv->ip_iface);
@@ -2515,7 +2640,7 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE ((NMVpnConnection *) object);
 	NMDevice *parent_dev;
 
 	switch (prop_id) {
@@ -2547,8 +2672,6 @@ 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);
 
-	g_type_class_add_private (connection_class, sizeof (NMVpnConnectionPrivate));
-
 	/* virtual methods */
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
@@ -2556,51 +2679,49 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 	active_class->device_state_changed = device_state_changed;
 	active_class->device_changed = device_changed;
 
-	g_object_class_override_property (object_class, PROP_MASTER, NM_ACTIVE_CONNECTION_MASTER);
-
-	/* properties */
-	g_object_class_install_property
-		(object_class, PROP_VPN_STATE,
-		 g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, "", "",
-		                    NM_VPN_CONNECTION_STATE_UNKNOWN,
-		                    NM_VPN_CONNECTION_STATE_DISCONNECTED,
-		                    NM_VPN_CONNECTION_STATE_UNKNOWN,
-		                    G_PARAM_READABLE |
-		                    G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property
-		(object_class, PROP_BANNER,
-		 g_param_spec_string (NM_VPN_CONNECTION_BANNER, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VPN_STATE] =
+	    g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, "", "",
+	                       NM_VPN_CONNECTION_STATE_UNKNOWN,
+	                       NM_VPN_CONNECTION_STATE_DISCONNECTED,
+	                       NM_VPN_CONNECTION_STATE_UNKNOWN,
+	                       G_PARAM_READABLE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_BANNER] =
+	    g_param_spec_string (NM_VPN_CONNECTION_BANNER, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
+	g_object_class_override_property (object_class, PROP_MASTER,
+	                                  NM_ACTIVE_CONNECTION_MASTER);
 	g_object_class_override_property (object_class, PROP_IP4_CONFIG,
 	                                  NM_ACTIVE_CONNECTION_IP4_CONFIG);
 	g_object_class_override_property (object_class, PROP_IP6_CONFIG,
 	                                  NM_ACTIVE_CONNECTION_IP6_CONFIG);
 
-	/* signals */
 	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);
+	    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),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+	    g_signal_new (NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
 
 	signals[INTERNAL_RETRY_AFTER_FAILURE] =
-		g_signal_new (NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE,
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL, NULL,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE,
+	                  G_OBJECT_CLASS_TYPE (object_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,
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
index 68374329..3fe56054 100644
--- a/src/vpn-manager/nm-vpn-connection.h
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -47,27 +47,7 @@
 
 #define NM_VPN_ROUTE_METRIC_DEFAULT     50
 
-
-struct _NMVpnConnection {
-	NMActiveConnection parent;
-};
-
-typedef struct {
-	NMActiveConnectionClass parent;
-
-	/* Signals */
-	void (*vpn_state_changed) (NMVpnConnection *self,
-	                           NMVpnConnectionState new_state,
-	                           NMVpnConnectionStateReason reason);
-
-	/* not exported over D-Bus */
-	void (*internal_state_changed) (NMVpnConnection *self,
-	                                NMVpnConnectionState new_state,
-	                                NMVpnConnectionState old_state,
-	                                NMVpnConnectionStateReason reason);
-
-	void (*internal_failed_retry)  (NMVpnConnection *self);
-} NMVpnConnectionClass;
+typedef struct _NMVpnConnectionClass NMVpnConnectionClass;
 
 GType nm_vpn_connection_get_type (void);
 
diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h
index f557e27a..9ccd882d 100644
--- a/src/vpn-manager/nm-vpn-manager.h
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -22,7 +22,6 @@
 #ifndef __NETWORKMANAGER_VPN_MANAGER_H__
 #define __NETWORKMANAGER_VPN_MANAGER_H__
 
-#include "nm-default.h"
 #include "nm-vpn-connection.h"
 
 #define NM_TYPE_VPN_MANAGER            (nm_vpn_manager_get_type ())